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.
28 from robot.errors import RobotError
30 from robot.utils.robottime import timestamp_to_secs
32 from functest.core import testcase
33 import functest.utils.functest_logger as ft_logger
34 import functest.utils.openstack_utils as op_utils
36 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
39 class ODLResultVisitor(robot.api.ResultVisitor):
40 """Visitor to get result details."""
45 def visit_test(self, test):
47 output['name'] = test.name
48 output['parent'] = test.parent.name
49 output['status'] = test.status
50 output['starttime'] = test.starttime
51 output['endtime'] = test.endtime
52 output['critical'] = test.critical
53 output['text'] = test.message
54 output['elapsedtime'] = test.elapsedtime
55 self._data.append(output)
58 """Get the details of the result."""
62 class ODLTests(testcase.TestCase):
63 """ODL test runner."""
65 repos = "/home/opnfv/repos/"
66 odl_test_repo = os.path.join(repos, "odl_test")
67 neutron_suite_dir = os.path.join(odl_test_repo,
68 "csit/suites/openstack/neutron")
69 basic_suite_dir = os.path.join(odl_test_repo,
70 "csit/suites/integration/basic")
71 default_suites = [basic_suite_dir, neutron_suite_dir]
72 res_dir = '/home/opnfv/functest/results/odl/'
73 logger = ft_logger.Logger("opendaylight").getLogger()
76 def set_robotframework_vars(cls, odlusername="admin", odlpassword="admin"):
77 """Set credentials in csit/variables/Variables.py.
80 True if credentials are set.
83 odl_variables_files = os.path.join(cls.odl_test_repo,
84 'csit/variables/Variables.py')
86 for line in fileinput.input(odl_variables_files,
88 print re.sub("AUTH = .*",
89 ("AUTH = [u'" + odlusername + "', u'" +
93 except Exception as ex: # pylint: disable=broad-except
94 cls.logger.error("Cannot set ODL creds: %s", str(ex))
97 def parse_results(self):
98 """Parse output.xml and get the details in it."""
99 xml_file = os.path.join(self.res_dir, 'output.xml')
100 result = robot.api.ExecutionResult(xml_file)
101 visitor = ODLResultVisitor()
102 result.visit(visitor)
103 self.criteria = result.suite.status
104 self.start_time = timestamp_to_secs(result.suite.starttime)
105 self.stop_time = timestamp_to_secs(result.suite.endtime)
107 self.details['description'] = result.suite.name
108 self.details['tests'] = visitor.get_data()
110 def main(self, suites=None, **kwargs):
111 """Run the test suites
113 It has been designed to be called in any context.
114 It requires the following keyword arguments:
128 * set all RobotFramework_variables,
129 * create the output directories if required,
130 * get the results in output.xml,
131 * delete temporary files.
134 kwargs: Arbitrary keyword arguments.
137 EX_OK if all suites ran well.
138 EX_RUN_ERROR otherwise.
142 suites = self.default_suites
143 odlusername = kwargs['odlusername']
144 odlpassword = kwargs['odlpassword']
145 osauthurl = kwargs['osauthurl']
146 keystoneip = urlparse.urlparse(osauthurl).hostname
147 variables = ['KEYSTONE:' + keystoneip,
148 'NEUTRON:' + kwargs['neutronip'],
149 'OS_AUTH_URL:"' + osauthurl + '"',
150 'OSUSERNAME:"' + kwargs['osusername'] + '"',
151 'OSTENANTNAME:"' + kwargs['ostenantname'] + '"',
152 'OSPASSWORD:"' + kwargs['ospassword'] + '"',
153 'ODL_SYSTEM_IP:' + kwargs['odlip'],
154 'PORT:' + kwargs['odlwebport'],
155 'RESTCONFPORT:' + kwargs['odlrestconfport']]
156 except KeyError as ex:
157 self.logger.error("Cannot run ODL testcases. Please check "
159 return self.EX_RUN_ERROR
160 if self.set_robotframework_vars(odlusername, odlpassword):
162 os.makedirs(self.res_dir)
163 except OSError as ex:
164 if ex.errno != errno.EEXIST:
165 self.logger.exception(
166 "Cannot create %s", self.res_dir)
167 return self.EX_RUN_ERROR
168 stdout_file = os.path.join(self.res_dir, 'stdout.txt')
169 output_dir = os.path.join(self.res_dir, 'output.xml')
170 with open(stdout_file, 'w+') as stdout:
171 robot.run(*suites, variable=variables,
177 self.logger.info("\n" + stdout.read())
178 self.logger.info("ODL results were successfully generated")
181 self.logger.info("ODL results were successfully parsed")
182 except RobotError as ex:
183 self.logger.error("Run tests before publishing: %s",
185 return self.EX_RUN_ERROR
187 os.remove(stdout_file)
189 self.logger.warning("Cannot remove %s", stdout_file)
192 return self.EX_RUN_ERROR
194 def run(self, **kwargs):
195 """Run suites in OPNFV environment
197 It basically check env vars to call main() with the keywords
201 kwargs: Arbitrary keyword arguments.
204 EX_OK if all suites ran well.
205 EX_RUN_ERROR otherwise.
208 suites = self.default_suites
210 suites = kwargs["suites"]
213 neutron_url = op_utils.get_endpoint(service_type='network')
214 kwargs = {'neutronip': urlparse.urlparse(neutron_url).hostname}
215 kwargs['odlip'] = kwargs['neutronip']
216 kwargs['odlwebport'] = '8080'
217 kwargs['odlrestconfport'] = '8181'
218 kwargs['odlusername'] = 'admin'
219 kwargs['odlpassword'] = 'admin'
220 installer_type = None
221 if 'INSTALLER_TYPE' in os.environ:
222 installer_type = os.environ['INSTALLER_TYPE']
223 kwargs['osusername'] = os.environ['OS_USERNAME']
224 kwargs['ostenantname'] = os.environ['OS_TENANT_NAME']
225 kwargs['osauthurl'] = os.environ['OS_AUTH_URL']
226 kwargs['ospassword'] = os.environ['OS_PASSWORD']
227 if installer_type == 'fuel':
228 kwargs['odlwebport'] = '8282'
229 elif installer_type == 'apex' or installer_type == 'netvirt':
230 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
231 kwargs['odlwebport'] = '8081'
232 kwargs['odlrestconfport'] = '8081'
233 elif installer_type == 'joid':
234 kwargs['odlip'] = os.environ['SDN_CONTROLLER']
235 elif installer_type == 'compass':
236 kwargs['odlwebport'] = '8181'
238 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
239 except KeyError as ex:
240 self.logger.error("Cannot run ODL testcases. "
241 "Please check env var: "
243 return self.EX_RUN_ERROR
244 except Exception: # pylint: disable=broad-except
245 self.logger.exception("Cannot run ODL testcases.")
246 return self.EX_RUN_ERROR
248 return self.main(suites, **kwargs)
251 class ODLParser(object): # pylint: disable=too-few-public-methods
252 """Parser to run ODL test suites."""
255 self.parser = argparse.ArgumentParser()
256 self.parser.add_argument(
257 '-n', '--neutronip', help='Neutron IP',
259 self.parser.add_argument(
260 '-k', '--osauthurl', help='OS_AUTH_URL as defined by OpenStack',
261 default='http://127.0.0.1:5000/v2.0')
262 self.parser.add_argument(
263 '-a', '--osusername', help='Username for OpenStack',
265 self.parser.add_argument(
266 '-b', '--ostenantname', help='Tenantname for OpenStack',
268 self.parser.add_argument(
269 '-c', '--ospassword', help='Password for OpenStack',
271 self.parser.add_argument(
272 '-o', '--odlip', help='OpenDaylight IP',
274 self.parser.add_argument(
275 '-w', '--odlwebport', help='OpenDaylight Web Portal Port',
277 self.parser.add_argument(
278 '-r', '--odlrestconfport', help='OpenDaylight RESTConf Port',
280 self.parser.add_argument(
281 '-d', '--odlusername', help='Username for ODL',
283 self.parser.add_argument(
284 '-e', '--odlpassword', help='Password for ODL',
286 self.parser.add_argument(
287 '-p', '--pushtodb', help='Push results to DB',
290 def parse_args(self, argv=None):
293 It can call sys.exit if arguments are incorrect.
296 the arguments from cmdline
300 return vars(self.parser.parse_args(argv))
303 if __name__ == '__main__':
306 ARGS = PARSER.parse_args(sys.argv[1:])
308 RESULT = ODL.main(ODLTests.default_suites, **ARGS)
309 if RESULT != testcase.TestCase.EX_OK:
312 sys.exit(ODL.push_to_db())
313 except Exception: # pylint: disable=broad-except
314 sys.exit(testcase.TestCase.EX_RUN_ERROR)