Merge "Add case_name as constructor arg"
[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     @classmethod
76     def set_robotframework_vars(cls, odlusername="admin", odlpassword="admin"):
77         """Set credentials in csit/variables/Variables.py.
78
79         Returns:
80             True if credentials are set.
81             False otherwise.
82         """
83         odl_variables_files = os.path.join(cls.odl_test_repo,
84                                            'csit/variables/Variables.py')
85         try:
86             for line in fileinput.input(odl_variables_files,
87                                         inplace=True):
88                 print re.sub("AUTH = .*",
89                              ("AUTH = [u'" + odlusername + "', u'" +
90                               odlpassword + "']"),
91                              line.rstrip())
92             return True
93         except Exception as ex:  # pylint: disable=broad-except
94             cls.logger.error("Cannot set ODL creds: %s", str(ex))
95             return False
96
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)
106         self.details = {}
107         self.details['description'] = result.suite.name
108         self.details['tests'] = visitor.get_data()
109
110     def main(self, suites=None, **kwargs):
111         """Run the test suites
112
113         It has been designed to be called in any context.
114         It requires the following keyword arguments:
115            * odlusername,
116            * odlpassword,
117            * osauthurl,
118            * neutronip,
119            * osusername,
120            * ostenantname,
121            * ospassword,
122            * odlip,
123            * odlwebport,
124            * odlrestconfport.
125
126         Here are the steps:
127            * set all RobotFramework_variables,
128            * create the output directories if required,
129            * get the results in output.xml,
130            * delete temporary files.
131
132         Args:
133             **kwargs: Arbitrary keyword arguments.
134
135         Returns:
136             EX_OK if all suites ran well.
137             EX_RUN_ERROR otherwise.
138         """
139         try:
140             if not suites:
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 "
157                               "%s", str(ex))
158             return self.EX_RUN_ERROR
159         if self.set_robotframework_vars(odlusername, odlpassword):
160             try:
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,
171                           output=output_dir,
172                           log='NONE',
173                           report='NONE',
174                           stdout=stdout)
175                 stdout.seek(0, 0)
176                 self.logger.info("\n" + stdout.read())
177             self.logger.info("ODL results were successfully generated")
178             try:
179                 self.parse_results()
180                 self.logger.info("ODL results were successfully parsed")
181             except RobotError as ex:
182                 self.logger.error("Run tests before publishing: %s",
183                                   ex.message)
184                 return self.EX_RUN_ERROR
185             try:
186                 os.remove(stdout_file)
187             except OSError:
188                 self.logger.warning("Cannot remove %s", stdout_file)
189             return self.EX_OK
190         else:
191             return self.EX_RUN_ERROR
192
193     def run(self, **kwargs):
194         """Run suites in OPNFV environment
195
196         It basically check env vars to call main() with the keywords
197         required.
198
199         Args:
200             **kwargs: Arbitrary keyword arguments.
201
202         Returns:
203             EX_OK if all suites ran well.
204             EX_RUN_ERROR otherwise.
205         """
206         try:
207             suites = self.default_suites
208             try:
209                 suites = kwargs["suites"]
210             except KeyError:
211                 pass
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'
236             else:
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: "
241                               "%s", str(ex))
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
246
247         return self.main(suites, **kwargs)
248
249
250 class ODLParser(object):  # pylint: disable=too-few-public-methods
251     """Parser to run ODL test suites."""
252
253     def __init__(self):
254         self.parser = argparse.ArgumentParser()
255         self.parser.add_argument(
256             '-n', '--neutronip', help='Neutron IP',
257             default='127.0.0.1')
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',
263             default='admin')
264         self.parser.add_argument(
265             '-b', '--ostenantname', help='Tenantname for OpenStack',
266             default='admin')
267         self.parser.add_argument(
268             '-c', '--ospassword', help='Password for OpenStack',
269             default='admin')
270         self.parser.add_argument(
271             '-o', '--odlip', help='OpenDaylight IP',
272             default='127.0.0.1')
273         self.parser.add_argument(
274             '-w', '--odlwebport', help='OpenDaylight Web Portal Port',
275             default='8080')
276         self.parser.add_argument(
277             '-r', '--odlrestconfport', help='OpenDaylight RESTConf Port',
278             default='8181')
279         self.parser.add_argument(
280             '-d', '--odlusername', help='Username for ODL',
281             default='admin')
282         self.parser.add_argument(
283             '-e', '--odlpassword', help='Password for ODL',
284             default='admin')
285         self.parser.add_argument(
286             '-p', '--pushtodb', help='Push results to DB',
287             action='store_true')
288
289     def parse_args(self, argv=None):
290         """Parse arguments.
291
292         It can call sys.exit if arguments are incorrect.
293
294         Returns:
295             the arguments from cmdline
296         """
297         if not argv:
298             argv = []
299         return vars(self.parser.parse_args(argv))
300
301
302 if __name__ == '__main__':
303     ODL = ODLTests()
304     PARSER = ODLParser()
305     ARGS = PARSER.parse_args(sys.argv[1:])
306     try:
307         RESULT = ODL.main(ODLTests.default_suites, **ARGS)
308         if RESULT != testcase.TestCase.EX_OK:
309             sys.exit(RESULT)
310         if ARGS['pushtodb']:
311             sys.exit(ODL.push_to_db())
312     except Exception:  # pylint: disable=broad-except
313         sys.exit(testcase.TestCase.EX_RUN_ERROR)