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