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:
127 * set all RobotFramework_variables,
128 * create the output directories if required,
129 * get the results in output.xml,
130 * delete temporary files.
133 **kwargs: Arbitrary keyword arguments.
136 EX_OK if all suites ran well.
137 EX_RUN_ERROR otherwise.
141 suites = self.default_suites
142 odlusername = kwargs['odlusername']
143 odlpassword = kwargs['odlpassword']
144 osauthurl = kwargs['osauthurl']
145 keystoneip = urlparse.urlparse(osauthurl).hostname
146 variables = ['KEYSTONE:' + keystoneip,
147 'NEUTRON:' + kwargs['neutronip'],
148 'OS_AUTH_URL:"' + osauthurl + '"',
149 'OSUSERNAME:"' + kwargs['osusername'] + '"',
150 'OSTENANTNAME:"' + kwargs['ostenantname'] + '"',
151 'OSPASSWORD:"' + kwargs['ospassword'] + '"',
152 'ODL_SYSTEM_IP:' + kwargs['odlip'],
153 'PORT:' + kwargs['odlwebport'],
154 'RESTCONFPORT:' + kwargs['odlrestconfport']]
155 except KeyError as ex:
156 self.logger.error("Cannot run ODL testcases. Please check "
158 return self.EX_RUN_ERROR
159 if self.set_robotframework_vars(odlusername, odlpassword):
161 os.makedirs(self.res_dir)
162 except OSError as ex:
163 if ex.errno != errno.EEXIST:
164 self.logger.exception(
165 "Cannot create %s", self.res_dir)
166 return self.EX_RUN_ERROR
167 stdout_file = os.path.join(self.res_dir, 'stdout.txt')
168 output_dir = os.path.join(self.res_dir, 'output.xml')
169 with open(stdout_file, 'w+') as stdout:
170 robot.run(*suites, variable=variables,
176 self.logger.info("\n" + stdout.read())
177 self.logger.info("ODL results were successfully generated")
180 self.logger.info("ODL results were successfully parsed")
181 except RobotError as ex:
182 self.logger.error("Run tests before publishing: %s",
184 return self.EX_RUN_ERROR
186 os.remove(stdout_file)
188 self.logger.warning("Cannot remove %s", stdout_file)
191 return self.EX_RUN_ERROR
193 def run(self, **kwargs):
194 """Run suites in OPNFV environment
196 It basically check env vars to call main() with the keywords
200 **kwargs: Arbitrary keyword arguments.
203 EX_OK if all suites ran well.
204 EX_RUN_ERROR otherwise.
207 suites = self.default_suites
209 suites = kwargs["suites"]
212 neutron_url = op_utils.get_endpoint(service_type='network')
213 kwargs = {'neutronip': urlparse.urlparse(neutron_url).hostname}
214 kwargs['odlip'] = kwargs['neutronip']
215 kwargs['odlwebport'] = '8080'
216 kwargs['odlrestconfport'] = '8181'
217 kwargs['odlusername'] = 'admin'
218 kwargs['odlpassword'] = 'admin'
219 installer_type = None
220 if 'INSTALLER_TYPE' in os.environ:
221 installer_type = os.environ['INSTALLER_TYPE']
222 kwargs['osusername'] = os.environ['OS_USERNAME']
223 kwargs['ostenantname'] = os.environ['OS_TENANT_NAME']
224 kwargs['osauthurl'] = os.environ['OS_AUTH_URL']
225 kwargs['ospassword'] = os.environ['OS_PASSWORD']
226 if installer_type == 'fuel':
227 kwargs['odlwebport'] = '8282'
228 elif installer_type == 'apex' or installer_type == 'netvirt':
229 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
230 kwargs['odlwebport'] = '8081'
231 kwargs['odlrestconfport'] = '8081'
232 elif installer_type == 'joid':
233 kwargs['odlip'] = os.environ['SDN_CONTROLLER']
234 elif installer_type == 'compass':
235 kwargs['odlwebport'] = '8181'
237 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
238 except KeyError as ex:
239 self.logger.error("Cannot run ODL testcases. "
240 "Please check env var: "
242 return self.EX_RUN_ERROR
243 except Exception: # pylint: disable=broad-except
244 self.logger.exception("Cannot run ODL testcases.")
245 return self.EX_RUN_ERROR
247 return self.main(suites, **kwargs)
250 class ODLParser(object): # pylint: disable=too-few-public-methods
251 """Parser to run ODL test suites."""
254 self.parser = argparse.ArgumentParser()
255 self.parser.add_argument(
256 '-n', '--neutronip', help='Neutron IP',
258 self.parser.add_argument(
259 '-k', '--osauthurl', help='OS_AUTH_URL as defined by OpenStack',
260 default='http://127.0.0.1:5000/v2.0')
261 self.parser.add_argument(
262 '-a', '--osusername', help='Username for OpenStack',
264 self.parser.add_argument(
265 '-b', '--ostenantname', help='Tenantname for OpenStack',
267 self.parser.add_argument(
268 '-c', '--ospassword', help='Password for OpenStack',
270 self.parser.add_argument(
271 '-o', '--odlip', help='OpenDaylight IP',
273 self.parser.add_argument(
274 '-w', '--odlwebport', help='OpenDaylight Web Portal Port',
276 self.parser.add_argument(
277 '-r', '--odlrestconfport', help='OpenDaylight RESTConf Port',
279 self.parser.add_argument(
280 '-d', '--odlusername', help='Username for ODL',
282 self.parser.add_argument(
283 '-e', '--odlpassword', help='Password for ODL',
285 self.parser.add_argument(
286 '-p', '--pushtodb', help='Push results to DB',
289 def parse_args(self, argv=None):
292 It can call sys.exit if arguments are incorrect.
295 the arguments from cmdline
299 return vars(self.parser.parse_args(argv))
302 if __name__ == '__main__':
305 ARGS = PARSER.parse_args(sys.argv[1:])
307 RESULT = ODL.main(ODLTests.default_suites, **ARGS)
308 if RESULT != testcase.TestCase.EX_OK:
311 sys.exit(ODL.push_to_db())
312 except Exception: # pylint: disable=broad-except
313 sys.exit(testcase.TestCase.EX_RUN_ERROR)