17e024666a4c57f2c2cc0ddb5947e73eca2c5896
[functest.git] / functest / opnfv_tests / openstack / refstack_client / refstack_client.py
1 #!/usr/bin/env python
2 # Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
3 # matthew.lijun@huawei.com wangwulin@huawei.com
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8
9 """Refstack client testcase implemenation."""
10
11 from __future__ import division
12
13
14 import argparse
15 import logging
16 import os
17 import re
18 import sys
19 import subprocess
20 import time
21
22 import pkg_resources
23
24 from functest.core import testcase
25 from functest.energy import energy
26 from functest.opnfv_tests.openstack.refstack_client.tempest_conf \
27     import TempestConf
28 from functest.opnfv_tests.openstack.tempest import conf_utils
29 from functest.utils.constants import CONST
30 import functest.utils.functest_utils as ft_utils
31 import functest.utils.openstack_utils as os_utils
32
33 # logging configuration """
34 LOGGER = logging.getLogger(__name__)
35
36
37 class RefstackClient(testcase.TestCase):
38     """RefstackClient testcase implementation class."""
39
40     def __init__(self, **kwargs):
41         """Initialize RefstackClient testcase object."""
42         if "case_name" not in kwargs:
43             kwargs["case_name"] = "refstack_defcore"
44         super(RefstackClient, self).__init__(**kwargs)
45         self.tempestconf = None
46         self.conf_path = pkg_resources.resource_filename(
47             'functest',
48             'opnfv_tests/openstack/refstack_client/refstack_tempest.conf')
49         self.functest_test = pkg_resources.resource_filename(
50             'functest', 'opnfv_tests')
51         self.defcore_list = 'openstack/refstack_client/defcore.txt'
52         self.confpath = os.path.join(self.functest_test,
53                                      self.conf_path)
54         self.defcorelist = pkg_resources.resource_filename(
55             'functest', 'opnfv_tests/openstack/refstack_client/defcore.txt')
56         self.testlist = None
57         self.insecure = ''
58         if ('https' in CONST.__getattribute__('OS_AUTH_URL') and
59                 CONST.__getattribute__('OS_INSECURE').lower() == 'true'):
60             self.insecure = '-k'
61
62     def generate_conf(self):
63         if not os.path.exists(conf_utils.REFSTACK_RESULTS_DIR):
64             os.makedirs(conf_utils.REFSTACK_RESULTS_DIR)
65
66         self.tempestconf = TempestConf()
67         self.tempestconf.generate_tempestconf()
68
69     def run_defcore(self, conf, testlist):
70         """Run defcore sys command."""
71         cmd = ("refstack-client test {0} -c {1} -v --test-list {2}"
72                .format(self.insecure, conf, testlist))
73         LOGGER.info("Starting Refstack_defcore test case: '%s'.", cmd)
74         ft_utils.execute_command(cmd)
75
76     def run_defcore_default(self):
77         """Run default defcore sys command."""
78         options = ["-v"] if not self.insecure else ["-v", self.insecure]
79         cmd = (["refstack-client", "test", "-c", self.confpath] +
80                options + ["--test-list",  self.defcorelist])
81         LOGGER.info("Starting Refstack_defcore test case: '%s'.", cmd)
82
83         with open(os.path.join(conf_utils.REFSTACK_RESULTS_DIR,
84                                "environment.log"), 'w+') as f_env:
85             f_env.write(
86                 ("Refstack environment:\n"
87                  "  SUT: {}\n  Scenario: {}\n  Node: {}\n  Date: {}\n").format(
88                     CONST.__getattribute__('INSTALLER_TYPE'),
89                     CONST.__getattribute__('DEPLOY_SCENARIO'),
90                     CONST.__getattribute__('NODE_NAME'),
91                     time.strftime("%a %b %d %H:%M:%S %Z %Y")))
92
93         with open(os.path.join(conf_utils.REFSTACK_RESULTS_DIR,
94                                "refstack.log"), 'w+') as f_stdout:
95             subprocess.call(cmd, shell=False, stdout=f_stdout,
96                             stderr=subprocess.STDOUT)
97
98     def parse_refstack_result(self):
99         """Parse Refstack results."""
100         try:
101             with open(os.path.join(conf_utils.REFSTACK_RESULTS_DIR,
102                                    "refstack.log"), 'r') as logfile:
103                 for line in logfile.readlines():
104                     if 'Tests' in line:
105                         break
106                     if re.search(r"\} tempest\.", line):
107                         LOGGER.info(line.replace('\n', ''))
108
109             with open(os.path.join(conf_utils.REFSTACK_RESULTS_DIR,
110                                    "refstack.log"), 'r') as logfile:
111                 output = logfile.read()
112
113             for match in re.findall(r"Ran: (\d+) tests in (\d+\.\d{4}) sec.",
114                                     output):
115                 num_tests = match[0]
116                 LOGGER.info("Ran: %s tests in %s sec.", num_tests, match[1])
117             for match in re.findall(r"(- Passed: )(\d+)", output):
118                 num_success = match[1]
119                 LOGGER.info("".join(match))
120             for match in re.findall(r"(- Skipped: )(\d+)", output):
121                 num_skipped = match[1]
122                 LOGGER.info("".join(match))
123             for match in re.findall(r"(- Failed: )(\d+)", output):
124                 num_failures = match[1]
125                 LOGGER.info("".join(match))
126             success_testcases = []
127             for match in re.findall(r"\{0\} (.*?)[. ]*ok", output):
128                 success_testcases.append(match)
129             failed_testcases = []
130             for match in re.findall(r"\{0\} (.*?)[. ]*FAILED", output):
131                 failed_testcases.append(match)
132             skipped_testcases = []
133             for match in re.findall(r"\{0\} (.*?)[. ]*SKIPPED:", output):
134                 skipped_testcases.append(match)
135
136             num_executed = int(num_tests) - int(num_skipped)
137
138             try:
139                 self.result = 100 * int(num_success) / int(num_executed)
140             except ZeroDivisionError:
141                 LOGGER.error("No test has been executed")
142
143             self.details = {"tests": int(num_tests),
144                             "failures": int(num_failures),
145                             "success": success_testcases,
146                             "errors": failed_testcases,
147                             "skipped": skipped_testcases}
148         except Exception:
149             self.result = 0
150
151         LOGGER.info("Testcase %s success_rate is %s%%",
152                     self.case_name, self.result)
153
154     @energy.enable_recording
155     def run(self, **kwargs):
156         """
157         Start RefstackClient testcase.
158
159         used for functest command line,
160         functest testcase run refstack_defcore
161         """
162         self.start_time = time.time()
163
164         try:
165             # Make sure that Tempest is configured
166             if not self.tempestconf:
167                 self.generate_conf()
168             self.run_defcore_default()
169             self.parse_refstack_result()
170             res = testcase.TestCase.EX_OK
171         except Exception:
172             LOGGER.exception("Error with run")
173             res = testcase.TestCase.EX_RUN_ERROR
174         finally:
175             self.tempestconf.clean()
176
177         self.stop_time = time.time()
178         return res
179
180     def _prep_test(self):
181         """Check that the config file exists."""
182         if not os.path.isfile(self.confpath):
183             LOGGER.error("Conf file not valid: %s", self.confpath)
184         if not os.path.isfile(self.testlist):
185             LOGGER.error("testlist file not valid: %s", self.testlist)
186
187     def main(self, **kwargs):
188         """
189         Execute RefstackClient testcase manually.
190
191         used for manually running,
192            python refstack_client.py -c <tempest_conf_path>
193            --testlist <testlist_path>
194            can generate a reference refstack_tempest.conf by
195            python tempest_conf.py
196         """
197         try:
198             self.confpath = kwargs['config']
199             self.testlist = kwargs['testlist']
200         except KeyError as exc:
201             LOGGER.error("Cannot run refstack client. Please check "
202                          "%s", exc)
203             return self.EX_RUN_ERROR
204         try:
205             self._prep_test()
206             self.run_defcore(self.confpath, self.testlist)
207             res = testcase.TestCase.EX_OK
208         except Exception as exc:
209             LOGGER.error('Error with run: %s', exc)
210             res = testcase.TestCase.EX_RUN_ERROR
211
212         return res
213
214     def create_snapshot(self):
215         """
216         Run the Tempest cleanup utility to initialize OS state.
217         For details, see https://docs.openstack.org/tempest/latest/cleanup.html
218
219         :return: TestCase.EX_OK
220         """
221         LOGGER.info("Initializing the saved state of the OpenStack deployment")
222
223         # Make sure that Tempest is configured
224         if not self.tempestconf:
225             self.generate_conf()
226
227         try:
228             os_utils.init_tempest_cleanup(
229                 self.tempestconf.DEPLOYMENT_DIR, 'tempest.conf',
230                 os.path.join(conf_utils.REFSTACK_RESULTS_DIR,
231                              "tempest-cleanup-init.log"))
232         except Exception as err:
233             LOGGER.error(str(err))
234             return testcase.TestCase.EX_RUN_ERROR
235
236         return super(RefstackClient, self).create_snapshot()
237
238     def clean(self):
239         """
240         Run the Tempest cleanup utility to delete and destroy OS resources.
241         For details, see https://docs.openstack.org/tempest/latest/cleanup.html
242         """
243         LOGGER.info("Destroying the resources created for tempest")
244
245         os_utils.perform_tempest_cleanup(
246             self.tempestconf.DEPLOYMENT_DIR, 'tempest.conf',
247             os.path.join(conf_utils.REFSTACK_RESULTS_DIR,
248                          "tempest-cleanup.log")
249         )
250
251         return super(RefstackClient, self).clean()
252
253
254 class RefstackClientParser(object):  # pylint: disable=too-few-public-methods
255     """Command line argument parser helper."""
256
257     def __init__(self):
258         """Initialize helper object."""
259         self.functest_test = pkg_resources.resource_filename(
260             'functest', 'opnfv_tests')
261         self.conf_path = pkg_resources.resource_filename(
262             'functest',
263             'opnfv_tests/openstack/refstack_client/refstack_tempest.conf')
264         self.defcore_list = pkg_resources.resource_filename(
265             'functest', 'opnfv_tests/openstack/refstack_client/defcore.txt')
266         self.confpath = os.path.join(self.functest_test,
267                                      self.conf_path)
268         self.defcorelist = os.path.join(self.functest_test,
269                                         self.defcore_list)
270         self.parser = argparse.ArgumentParser()
271         self.parser.add_argument(
272             '-c', '--config',
273             help='the file path of refstack_tempest.conf',
274             default=self.confpath)
275         self.parser.add_argument(
276             '-t', '--testlist',
277             help='Specify the file path or URL of a test list text file. '
278                  'This test list will contain specific test cases that '
279                  'should be tested.',
280             default=self.defcorelist)
281
282     def parse_args(self, argv=None):
283         """Parse command line arguments."""
284         return vars(self.parser.parse_args(argv))
285
286
287 def main():
288     """Run RefstackClient testcase with CLI."""
289     logging.basicConfig()
290     refstackclient = RefstackClient()
291     parser = RefstackClientParser()
292     args = parser.parse_args(sys.argv[1:])
293     try:
294         result = refstackclient.main(**args)
295         if result != testcase.TestCase.EX_OK:
296             return result
297     except Exception:
298         return testcase.TestCase.EX_RUN_ERROR