Merge "Re-Enable Promise testcases"
[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 import StringIO
34 from six.moves import urllib
35
36 from functest.core import testcase
37 import functest.utils.openstack_utils as op_utils
38
39 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
40
41
42 class ODLResultVisitor(robot.api.ResultVisitor):
43     """Visitor to get result details."""
44
45     def __init__(self):
46         self._data = []
47
48     def visit_test(self, test):
49         output = {}
50         output['name'] = test.name
51         output['parent'] = test.parent.name
52         output['status'] = test.status
53         output['starttime'] = test.starttime
54         output['endtime'] = test.endtime
55         output['critical'] = test.critical
56         output['text'] = test.message
57         output['elapsedtime'] = test.elapsedtime
58         self._data.append(output)
59
60     def get_data(self):
61         """Get the details of the result."""
62         return self._data
63
64
65 class ODLTests(testcase.TestCase):
66     """ODL test runner."""
67
68     repos = "/home/opnfv/repos/"
69     odl_test_repo = os.path.join(repos, "odl_test")
70     neutron_suite_dir = os.path.join(odl_test_repo,
71                                      "csit/suites/openstack/neutron")
72     basic_suite_dir = os.path.join(odl_test_repo,
73                                    "csit/suites/integration/basic")
74     default_suites = [basic_suite_dir, neutron_suite_dir]
75     res_dir = '/home/opnfv/functest/results/odl/'
76     __logger = logging.getLogger(__name__)
77
78     @classmethod
79     def set_robotframework_vars(cls, odlusername="admin", odlpassword="admin"):
80         """Set credentials in csit/variables/Variables.py.
81
82         Returns:
83             True if credentials are set.
84             False otherwise.
85         """
86         odl_variables_files = os.path.join(cls.odl_test_repo,
87                                            'csit/variables/Variables.py')
88         try:
89             for line in fileinput.input(odl_variables_files,
90                                         inplace=True):
91                 print(re.sub("AUTH = .*",
92                              ("AUTH = [u'" + odlusername + "', u'" +
93                               odlpassword + "']"),
94                              line.rstrip()))
95             return True
96         except Exception as ex:  # pylint: disable=broad-except
97             cls.__logger.error("Cannot set ODL creds: %s", str(ex))
98             return False
99
100     def parse_results(self):
101         """Parse output.xml and get the details in it."""
102         xml_file = os.path.join(self.res_dir, 'output.xml')
103         result = robot.api.ExecutionResult(xml_file)
104         visitor = ODLResultVisitor()
105         result.visit(visitor)
106         try:
107             self.result = 100 * (
108                 result.suite.statistics.critical.passed /
109                 result.suite.statistics.critical.total)
110         except ZeroDivisionError:
111             self.__logger.error("No test has been run")
112         self.start_time = timestamp_to_secs(result.suite.starttime)
113         self.stop_time = timestamp_to_secs(result.suite.endtime)
114         self.details = {}
115         self.details['description'] = result.suite.name
116         self.details['tests'] = visitor.get_data()
117
118     def main(self, suites=None, **kwargs):
119         """Run the test suites
120
121         It has been designed to be called in any context.
122         It requires the following keyword arguments:
123
124            * odlusername,
125            * odlpassword,
126            * osauthurl,
127            * neutronip,
128            * osusername,
129            * ostenantname,
130            * ospassword,
131            * odlip,
132            * odlwebport,
133            * odlrestconfport.
134
135         Here are the steps:
136            * set all RobotFramework_variables,
137            * create the output directories if required,
138            * get the results in output.xml,
139            * delete temporary files.
140
141         Args:
142             kwargs: Arbitrary keyword arguments.
143
144         Returns:
145             EX_OK if all suites ran well.
146             EX_RUN_ERROR otherwise.
147         """
148         try:
149             if not suites:
150                 suites = self.default_suites
151             odlusername = kwargs['odlusername']
152             odlpassword = kwargs['odlpassword']
153             osauthurl = kwargs['osauthurl']
154             keystoneip = urllib.parse.urlparse(osauthurl).hostname
155             variables = ['KEYSTONE:' + keystoneip,
156                          'NEUTRON:' + kwargs['neutronip'],
157                          'OS_AUTH_URL:"' + osauthurl + '"',
158                          'OSUSERNAME:"' + kwargs['osusername'] + '"',
159                          'OSTENANTNAME:"' + kwargs['ostenantname'] + '"',
160                          'OSPASSWORD:"' + kwargs['ospassword'] + '"',
161                          'ODL_SYSTEM_IP:' + kwargs['odlip'],
162                          'PORT:' + kwargs['odlwebport'],
163                          'RESTCONFPORT:' + kwargs['odlrestconfport']]
164         except KeyError as ex:
165             self.__logger.error("Cannot run ODL testcases. Please check "
166                                 "%s", str(ex))
167             return self.EX_RUN_ERROR
168         if self.set_robotframework_vars(odlusername, odlpassword):
169             try:
170                 os.makedirs(self.res_dir)
171             except OSError as ex:
172                 if ex.errno != errno.EEXIST:
173                     self.__logger.exception(
174                         "Cannot create %s", self.res_dir)
175                     return self.EX_RUN_ERROR
176             output_dir = os.path.join(self.res_dir, 'output.xml')
177             stream = StringIO()
178             robot.run(*suites, variable=variables, output=output_dir,
179                       log='NONE', report='NONE', stdout=stream)
180             self.__logger.info("\n" + stream.getvalue())
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             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': urllib.parse.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     logging.basicConfig()
304     ODL = ODLTests()
305     PARSER = ODLParser()
306     ARGS = PARSER.parse_args(sys.argv[1:])
307     try:
308         RESULT = ODL.main(ODLTests.default_suites, **ARGS)
309         if RESULT != testcase.TestCase.EX_OK:
310             sys.exit(RESULT)
311         if ARGS['pushtodb']:
312             sys.exit(ODL.push_to_db())
313     except Exception:  # pylint: disable=broad-except
314         sys.exit(testcase.TestCase.EX_RUN_ERROR)