3 # Copyright (c) 2016 Orange and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 """Define classes required to run ODL suites.
12 It has been designed for any context. But helpers are given for
13 running test suites in OPNFV environment.
19 from __future__ import division
30 from robot.errors import RobotError
32 from robot.utils.robottime import timestamp_to_secs
33 from six import StringIO
34 from six.moves import urllib
36 from functest.core import testcase
37 from functest.utils import constants
38 import functest.utils.openstack_utils as op_utils
40 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
43 class ODLResultVisitor(robot.api.ResultVisitor):
44 """Visitor to get result details."""
49 def visit_test(self, test):
51 output['name'] = test.name
52 output['parent'] = test.parent.name
53 output['status'] = test.status
54 output['starttime'] = test.starttime
55 output['endtime'] = test.endtime
56 output['critical'] = test.critical
57 output['text'] = test.message
58 output['elapsedtime'] = test.elapsedtime
59 self._data.append(output)
62 """Get the details of the result."""
66 class ODLTests(testcase.TestCase):
67 """ODL test runner."""
69 odl_test_repo = constants.CONST.__getattribute__('dir_repo_odl_test')
70 neutron_suite_dir = os.path.join(odl_test_repo,
71 "csit/suites/openstack/neutron")
72 basic_suite_dir = os.path.join(odl_test_repo,
73 "csit/suites/integration/basic")
74 default_suites = [basic_suite_dir, neutron_suite_dir]
75 res_dir = os.path.join(
76 constants.CONST.__getattribute__('dir_results'), 'odl')
77 __logger = logging.getLogger(__name__)
80 def set_robotframework_vars(cls, odlusername="admin", odlpassword="admin"):
81 """Set credentials in csit/variables/Variables.robot.
84 True if credentials are set.
87 odl_variables_files = os.path.join(cls.odl_test_repo,
88 'csit/variables/Variables.robot')
90 for line in fileinput.input(odl_variables_files,
92 print(re.sub("@{AUTH}.*",
93 "@{{AUTH}} {} {}".format(
94 odlusername, odlpassword),
97 except Exception as ex: # pylint: disable=broad-except
98 cls.__logger.error("Cannot set ODL creds: %s", str(ex))
101 def parse_results(self):
102 """Parse output.xml and get the details in it."""
103 xml_file = os.path.join(self.res_dir, 'output.xml')
104 result = robot.api.ExecutionResult(xml_file)
105 visitor = ODLResultVisitor()
106 result.visit(visitor)
108 self.result = 100 * (
109 result.suite.statistics.critical.passed /
110 result.suite.statistics.critical.total)
111 except ZeroDivisionError:
112 self.__logger.error("No test has been run")
113 self.start_time = timestamp_to_secs(result.suite.starttime)
114 self.stop_time = timestamp_to_secs(result.suite.endtime)
116 self.details['description'] = result.suite.name
117 self.details['tests'] = visitor.get_data()
119 def run_suites(self, suites=None, **kwargs):
120 """Run the test suites
122 It has been designed to be called in any context.
123 It requires the following keyword arguments:
137 * set all RobotFramework_variables,
138 * create the output directories if required,
139 * get the results in output.xml,
140 * delete temporary files.
143 kwargs: Arbitrary keyword arguments.
146 EX_OK if all suites ran well.
147 EX_RUN_ERROR otherwise.
151 suites = self.default_suites
152 odlusername = kwargs['odlusername']
153 odlpassword = kwargs['odlpassword']
154 osauthurl = kwargs['osauthurl']
155 keystoneurl = "{}://{}".format(
156 urllib.parse.urlparse(osauthurl).scheme,
157 urllib.parse.urlparse(osauthurl).netloc)
158 variables = ['KEYSTONEURL:' + keystoneurl,
159 'NEUTRONURL:' + kwargs['neutronurl'],
160 'OS_AUTH_URL:"' + osauthurl + '"',
161 'OSUSERNAME:"' + kwargs['osusername'] + '"',
162 ('OSUSERDOMAINNAME:"' +
163 kwargs['osuserdomainname'] + '"'),
164 'OSTENANTNAME:"' + kwargs['ostenantname'] + '"',
165 ('OSPROJECTDOMAINNAME:"' +
166 kwargs['osprojectdomainname'] + '"'),
167 'OSPASSWORD:"' + kwargs['ospassword'] + '"',
168 'ODL_SYSTEM_IP:' + kwargs['odlip'],
169 'PORT:' + kwargs['odlwebport'],
170 'RESTCONFPORT:' + kwargs['odlrestconfport']]
171 except KeyError as ex:
172 self.__logger.exception("Cannot run ODL testcases. Please check")
173 return self.EX_RUN_ERROR
174 if self.set_robotframework_vars(odlusername, odlpassword):
176 os.makedirs(self.res_dir)
177 except OSError as ex:
178 if ex.errno != errno.EEXIST:
179 self.__logger.exception(
180 "Cannot create %s", self.res_dir)
181 return self.EX_RUN_ERROR
182 output_dir = os.path.join(self.res_dir, 'output.xml')
184 robot.run(*suites, variable=variables, output=output_dir,
185 log='NONE', report='NONE', stdout=stream)
186 self.__logger.info("\n" + stream.getvalue())
187 self.__logger.info("ODL results were successfully generated")
190 self.__logger.info("ODL results were successfully parsed")
191 except RobotError as ex:
192 self.__logger.error("Run tests before publishing: %s",
194 return self.EX_RUN_ERROR
197 return self.EX_RUN_ERROR
199 def run(self, **kwargs):
200 """Run suites in OPNFV environment
202 It basically check env vars to call main() with the keywords
206 kwargs: Arbitrary keyword arguments.
209 EX_OK if all suites ran well.
210 EX_RUN_ERROR otherwise.
213 suites = self.default_suites
215 suites = kwargs["suites"]
218 kwargs = {'neutronurl': op_utils.get_endpoint(
219 service_type='network')}
220 kwargs['odlip'] = urllib.parse.urlparse(
221 kwargs['neutronurl']).hostname
222 kwargs['odlwebport'] = '8080'
223 kwargs['odlrestconfport'] = '8181'
224 kwargs['odlusername'] = 'admin'
225 kwargs['odlpassword'] = 'admin'
226 installer_type = None
227 if 'INSTALLER_TYPE' in os.environ:
228 installer_type = os.environ['INSTALLER_TYPE']
229 kwargs['osusername'] = os.environ['OS_USERNAME']
230 kwargs['osuserdomainname'] = os.environ.get(
231 'OS_USER_DOMAIN_NAME', 'Default')
232 kwargs['ostenantname'] = os.environ['OS_TENANT_NAME']
233 kwargs['osprojectdomainname'] = os.environ.get(
234 'OS_PROJECT_DOMAIN_NAME', 'Default')
235 kwargs['osauthurl'] = os.environ['OS_AUTH_URL']
236 kwargs['ospassword'] = os.environ['OS_PASSWORD']
237 if installer_type == 'fuel':
238 kwargs['odlwebport'] = '8181'
239 kwargs['odlrestconfport'] = '8282'
240 elif installer_type == 'apex' or installer_type == 'netvirt':
241 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
242 kwargs['odlwebport'] = '8081'
243 kwargs['odlrestconfport'] = '8081'
244 elif installer_type == 'joid':
245 kwargs['odlip'] = os.environ['SDN_CONTROLLER']
246 elif installer_type == 'compass':
247 kwargs['odlrestconfport'] = '8080'
248 elif installer_type == 'daisy':
249 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
250 kwargs['odlwebport'] = '8181'
251 kwargs['odlrestconfport'] = '8087'
253 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
254 except KeyError as ex:
255 self.__logger.error("Cannot run ODL testcases. "
256 "Please check env var: "
258 return self.EX_RUN_ERROR
259 except Exception: # pylint: disable=broad-except
260 self.__logger.exception("Cannot run ODL testcases.")
261 return self.EX_RUN_ERROR
263 return self.run_suites(suites, **kwargs)
266 class ODLParser(object): # pylint: disable=too-few-public-methods
267 """Parser to run ODL test suites."""
270 self.parser = argparse.ArgumentParser()
271 self.parser.add_argument(
272 '-n', '--neutronurl', help='Neutron Endpoint',
273 default='http://127.0.0.1:9696')
274 self.parser.add_argument(
275 '-k', '--osauthurl', help='OS_AUTH_URL as defined by OpenStack',
276 default='http://127.0.0.1:5000/v3')
277 self.parser.add_argument(
278 '-a', '--osusername', help='Username for OpenStack',
280 self.parser.add_argument(
281 '-f', '--osuserdomainname', help='User domain name for OpenStack',
283 self.parser.add_argument(
284 '-b', '--ostenantname', help='Tenantname for OpenStack',
286 self.parser.add_argument(
287 '-g', '--osprojectdomainname',
288 help='Project domain name for OpenStack',
290 self.parser.add_argument(
291 '-c', '--ospassword', help='Password for OpenStack',
293 self.parser.add_argument(
294 '-o', '--odlip', help='OpenDaylight IP',
296 self.parser.add_argument(
297 '-w', '--odlwebport', help='OpenDaylight Web Portal Port',
299 self.parser.add_argument(
300 '-r', '--odlrestconfport', help='OpenDaylight RESTConf Port',
302 self.parser.add_argument(
303 '-d', '--odlusername', help='Username for ODL',
305 self.parser.add_argument(
306 '-e', '--odlpassword', help='Password for ODL',
308 self.parser.add_argument(
309 '-p', '--pushtodb', help='Push results to DB',
312 def parse_args(self, argv=None):
315 It can call sys.exit if arguments are incorrect.
318 the arguments from cmdline
322 return vars(self.parser.parse_args(argv))
327 logging.basicConfig()
330 args = parser.parse_args(sys.argv[1:])
332 result = odl.run_suites(ODLTests.default_suites, **args)
333 if result != testcase.TestCase.EX_OK:
336 return odl.push_to_db()
339 except Exception: # pylint: disable=broad-except
340 return testcase.TestCase.EX_RUN_ERROR