Merge "modify old naming testcase_base to testcase"
[functest-xtesting.git] / functest / opnfv_tests / sdn / odl / odl.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2016 Orange and others.
4 #
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
9
10 """Define classes required to run ODL suites.
11
12 It has been designed for any context. But helpers are given for
13 running test suites in OPNFV environment.
14
15 Example:
16         $ python odl.py
17 """
18
19 import argparse
20 import errno
21 import fileinput
22 import os
23 import re
24 import sys
25 import urlparse
26
27 import robot.api
28 from robot.errors import RobotError
29 import robot.run
30 from robot.utils.robottime import timestamp_to_secs
31
32 from functest.core import testcase
33 import functest.utils.functest_logger as ft_logger
34 import functest.utils.openstack_utils as op_utils
35
36 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
37
38
39 class ODLResultVisitor(robot.api.ResultVisitor):
40     """Visitor to get result details."""
41
42     def __init__(self):
43         self._data = []
44
45     def visit_test(self, test):
46         output = {}
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)
56
57     def get_data(self):
58         """Get the details of the result."""
59         return self._data
60
61
62 class ODLTests(testcase.TestCase):
63     """ODL test runner."""
64
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()
74
75     def __init__(self):
76         testcase.TestCase.__init__(self)
77         self.case_name = "odl"
78
79     @classmethod
80     def set_robotframework_vars(cls, odlusername="admin", odlpassword="admin"):
81         """Set credentials in csit/variables/Variables.py.
82
83         Returns:
84             True if credentials are set.
85             False otherwise.
86         """
87         odl_variables_files = os.path.join(cls.odl_test_repo,
88                                            'csit/variables/Variables.py')
89         try:
90             for line in fileinput.input(odl_variables_files,
91                                         inplace=True):
92                 print re.sub("AUTH = .*",
93                              ("AUTH = [u'" + odlusername + "', u'" +
94                               odlpassword + "']"),
95                              line.rstrip())
96             return True
97         except Exception as ex:  # pylint: disable=broad-except
98             cls.logger.error("Cannot set ODL creds: %s", str(ex))
99             return False
100
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)
107         self.criteria = result.suite.status
108         self.start_time = timestamp_to_secs(result.suite.starttime)
109         self.stop_time = timestamp_to_secs(result.suite.endtime)
110         self.details = {}
111         self.details['description'] = result.suite.name
112         self.details['tests'] = visitor.get_data()
113
114     def main(self, suites=None, **kwargs):
115         """Run the test suites
116
117         It has been designed to be called in any context.
118         It requires the following keyword arguments:
119            * odlusername,
120            * odlpassword,
121            * osauthurl,
122            * neutronip,
123            * osusername,
124            * ostenantname,
125            * ospassword,
126            * odlip,
127            * odlwebport,
128            * odlrestconfport.
129
130         Here are the steps:
131            * set all RobotFramework_variables,
132            * create the output directories if required,
133            * get the results in output.xml,
134            * delete temporary files.
135
136         Args:
137             **kwargs: Arbitrary keyword arguments.
138
139         Returns:
140             EX_OK if all suites ran well.
141             EX_RUN_ERROR otherwise.
142         """
143         try:
144             if not suites:
145                 suites = self.default_suites
146             odlusername = kwargs['odlusername']
147             odlpassword = kwargs['odlpassword']
148             osauthurl = kwargs['osauthurl']
149             keystoneip = urlparse.urlparse(osauthurl).hostname
150             variables = ['KEYSTONE:' + keystoneip,
151                          'NEUTRON:' + kwargs['neutronip'],
152                          'OS_AUTH_URL:"' + osauthurl + '"',
153                          'OSUSERNAME:"' + kwargs['osusername'] + '"',
154                          'OSTENANTNAME:"' + kwargs['ostenantname'] + '"',
155                          'OSPASSWORD:"' + kwargs['ospassword'] + '"',
156                          'ODL_SYSTEM_IP:' + kwargs['odlip'],
157                          'PORT:' + kwargs['odlwebport'],
158                          'RESTCONFPORT:' + kwargs['odlrestconfport']]
159         except KeyError as ex:
160             self.logger.error("Cannot run ODL testcases. Please check "
161                               "%s", str(ex))
162             return self.EX_RUN_ERROR
163         if self.set_robotframework_vars(odlusername, odlpassword):
164             try:
165                 os.makedirs(self.res_dir)
166             except OSError as ex:
167                 if ex.errno != errno.EEXIST:
168                     self.logger.exception(
169                         "Cannot create %s", self.res_dir)
170                     return self.EX_RUN_ERROR
171             stdout_file = os.path.join(self.res_dir, 'stdout.txt')
172             output_dir = os.path.join(self.res_dir, 'output.xml')
173             with open(stdout_file, 'w+') as stdout:
174                 robot.run(*suites, variable=variables,
175                           output=output_dir,
176                           log='NONE',
177                           report='NONE',
178                           stdout=stdout)
179                 stdout.seek(0, 0)
180                 self.logger.info("\n" + stdout.read())
181             self.logger.info("ODL results were successfully generated")
182             try:
183                 self.parse_results()
184                 self.logger.info("ODL results were successfully parsed")
185             except RobotError as ex:
186                 self.logger.error("Run tests before publishing: %s",
187                                   ex.message)
188                 return self.EX_RUN_ERROR
189             try:
190                 os.remove(stdout_file)
191             except OSError:
192                 self.logger.warning("Cannot remove %s", stdout_file)
193             return self.EX_OK
194         else:
195             return self.EX_RUN_ERROR
196
197     def run(self, **kwargs):
198         """Run suites in OPNFV environment
199
200         It basically check env vars to call main() with the keywords
201         required.
202
203         Args:
204             **kwargs: Arbitrary keyword arguments.
205
206         Returns:
207             EX_OK if all suites ran well.
208             EX_RUN_ERROR otherwise.
209         """
210         try:
211             suites = self.default_suites
212             try:
213                 suites = kwargs["suites"]
214             except KeyError:
215                 pass
216             neutron_url = op_utils.get_endpoint(service_type='network')
217             kwargs = {'neutronip': urlparse.urlparse(neutron_url).hostname}
218             kwargs['odlip'] = kwargs['neutronip']
219             kwargs['odlwebport'] = '8080'
220             kwargs['odlrestconfport'] = '8181'
221             kwargs['odlusername'] = 'admin'
222             kwargs['odlpassword'] = 'admin'
223             installer_type = None
224             if 'INSTALLER_TYPE' in os.environ:
225                 installer_type = os.environ['INSTALLER_TYPE']
226             kwargs['osusername'] = os.environ['OS_USERNAME']
227             kwargs['ostenantname'] = os.environ['OS_TENANT_NAME']
228             kwargs['osauthurl'] = os.environ['OS_AUTH_URL']
229             kwargs['ospassword'] = os.environ['OS_PASSWORD']
230             if installer_type == 'fuel':
231                 kwargs['odlwebport'] = '8282'
232             elif installer_type == 'apex' or installer_type == 'netvirt':
233                 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
234                 kwargs['odlwebport'] = '8081'
235                 kwargs['odlrestconfport'] = '8081'
236             elif installer_type == 'joid':
237                 kwargs['odlip'] = os.environ['SDN_CONTROLLER']
238             elif installer_type == 'compass':
239                 kwargs['odlwebport'] = '8181'
240             else:
241                 kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP']
242         except KeyError as ex:
243             self.logger.error("Cannot run ODL testcases. "
244                               "Please check env var: "
245                               "%s", str(ex))
246             return self.EX_RUN_ERROR
247         except Exception:  # pylint: disable=broad-except
248             self.logger.exception("Cannot run ODL testcases.")
249             return self.EX_RUN_ERROR
250
251         return self.main(suites, **kwargs)
252
253
254 class ODLParser(object):  # pylint: disable=too-few-public-methods
255     """Parser to run ODL test suites."""
256
257     def __init__(self):
258         self.parser = argparse.ArgumentParser()
259         self.parser.add_argument(
260             '-n', '--neutronip', help='Neutron IP',
261             default='127.0.0.1')
262         self.parser.add_argument(
263             '-k', '--osauthurl', help='OS_AUTH_URL as defined by OpenStack',
264             default='http://127.0.0.1:5000/v2.0')
265         self.parser.add_argument(
266             '-a', '--osusername', help='Username for OpenStack',
267             default='admin')
268         self.parser.add_argument(
269             '-b', '--ostenantname', help='Tenantname for OpenStack',
270             default='admin')
271         self.parser.add_argument(
272             '-c', '--ospassword', help='Password for OpenStack',
273             default='admin')
274         self.parser.add_argument(
275             '-o', '--odlip', help='OpenDaylight IP',
276             default='127.0.0.1')
277         self.parser.add_argument(
278             '-w', '--odlwebport', help='OpenDaylight Web Portal Port',
279             default='8080')
280         self.parser.add_argument(
281             '-r', '--odlrestconfport', help='OpenDaylight RESTConf Port',
282             default='8181')
283         self.parser.add_argument(
284             '-d', '--odlusername', help='Username for ODL',
285             default='admin')
286         self.parser.add_argument(
287             '-e', '--odlpassword', help='Password for ODL',
288             default='admin')
289         self.parser.add_argument(
290             '-p', '--pushtodb', help='Push results to DB',
291             action='store_true')
292
293     def parse_args(self, argv=None):
294         """Parse arguments.
295
296         It can call sys.exit if arguments are incorrect.
297
298         Returns:
299             the arguments from cmdline
300         """
301         if not argv:
302             argv = []
303         return vars(self.parser.parse_args(argv))
304
305
306 if __name__ == '__main__':
307     ODL = ODLTests()
308     PARSER = ODLParser()
309     ARGS = PARSER.parse_args(sys.argv[1:])
310     try:
311         RESULT = ODL.main(ODLTests.default_suites, **ARGS)
312         if RESULT != testcase.TestCase.EX_OK:
313             sys.exit(RESULT)
314         if ARGS['pushtodb']:
315             sys.exit(ODL.push_to_db())
316     except Exception:  # pylint: disable=broad-except
317         sys.exit(testcase.TestCase.EX_RUN_ERROR)