Leverage on Xtesting
[functest.git] / functest / opnfv_tests / openstack / refstack_client / refstack_client.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2017 Huawei Technologies Co.,Ltd 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 """Refstack client testcase implemenation."""
11
12 from __future__ import division
13
14 import argparse
15 import ConfigParser
16 import logging
17 import os
18 import re
19 import sys
20 import subprocess
21 import time
22
23 from xtesting.core import testcase
24 from xtesting.energy import energy
25
26 from functest.opnfv_tests.openstack.tempest import conf_utils
27 from functest.opnfv_tests.openstack.tempest import tempest
28 from functest.utils import config
29 from functest.utils import functest_utils
30
31
32 __author__ = ("Matthew Li <matthew.lijun@huawei.com>,"
33               "Linda Wang <wangwulin@huawei.com>")
34
35 # logging configuration """
36 LOGGER = logging.getLogger(__name__)
37
38
39 class RefstackClient(testcase.TestCase):
40     """RefstackClient testcase implementation class."""
41     # pylint: disable=too-many-instance-attributes
42
43     defcorelist = os.path.join(
44         getattr(config.CONF, 'dir_refstack_data'), 'defcore.txt')
45
46     def __init__(self, **kwargs):
47         """Initialize RefstackClient testcase object."""
48         if "case_name" not in kwargs:
49             kwargs["case_name"] = "refstack_defcore"
50         super(RefstackClient, self).__init__(**kwargs)
51         self.resdir = os.path.join(
52             getattr(config.CONF, 'dir_results'), 'refstack')
53         self.conf_path = os.path.join(self.resdir, 'refstack_tempest.conf')
54
55     @staticmethod
56     def run_defcore(conf, testlist):
57         """Run defcore sys command."""
58         insecure = ''
59         if ('https' in os.environ['OS_AUTH_URL'] and
60                 os.getenv('OS_INSECURE', '').lower() == 'true'):
61             insecure = '-k'
62         cmd = ("refstack-client test {0} -c {1} -v --test-list {2}"
63                .format(insecure, conf, testlist))
64         LOGGER.info("Starting Refstack_defcore test case: '%s'.", cmd)
65         functest_utils.execute_command(cmd)
66
67     def run_defcore_default(self):
68         """Run default defcore sys command."""
69         insecure = ''
70         if ('https' in os.environ['OS_AUTH_URL'] and
71                 os.getenv('OS_INSECURE', '').lower() == 'true'):
72             insecure = '-k'
73         options = ["-v"] if not insecure else ["-v", insecure]
74         cmd = (["refstack-client", "test", "-c", self.conf_path] +
75                options + ["--test-list", self.defcorelist])
76         LOGGER.info("Starting Refstack_defcore test case: '%s'.", cmd)
77         with open(os.path.join(self.resdir, "refstack.log"), 'w+') as f_stdout:
78             subprocess.call(cmd, shell=False, stdout=f_stdout,
79                             stderr=subprocess.STDOUT)
80
81     def parse_refstack_result(self):
82         """Parse Refstack results."""
83         try:
84             with open(os.path.join(self.resdir,
85                                    "refstack.log"), 'r') as logfile:
86                 for line in logfile.readlines():
87                     if 'Tests' in line:
88                         break
89                     if re.search(r"\} tempest\.", line):
90                         LOGGER.info(line.replace('\n', ''))
91
92             with open(os.path.join(self.resdir,
93                                    "refstack.log"), 'r') as logfile:
94                 output = logfile.read()
95
96             for match in re.findall(r"Ran: (\d+) tests in (\d+\.\d{4}) sec.",
97                                     output):
98                 num_tests = match[0]
99                 LOGGER.info("Ran: %s tests in %s sec.", num_tests, match[1])
100             for match in re.findall(r"(- Passed: )(\d+)", output):
101                 num_success = match[1]
102                 LOGGER.info("".join(match))
103             for match in re.findall(r"(- Skipped: )(\d+)", output):
104                 num_skipped = match[1]
105                 LOGGER.info("".join(match))
106             for match in re.findall(r"(- Failed: )(\d+)", output):
107                 num_failures = match[1]
108                 LOGGER.info("".join(match))
109             success_testcases = []
110             for match in re.findall(r"\{0\} (.*?) \.{3} ok", output):
111                 success_testcases.append(match)
112             failed_testcases = []
113             for match in re.findall(r"\{0\} (.*?) \.{3} FAILED", output):
114                 failed_testcases.append(match)
115             skipped_testcases = []
116             for match in re.findall(r"\{0\} (.*?) \.{3} SKIPPED:", output):
117                 skipped_testcases.append(match)
118
119             num_executed = int(num_tests) - int(num_skipped)
120
121             try:
122                 self.result = 100 * int(num_success) / int(num_executed)
123             except ZeroDivisionError:
124                 LOGGER.error("No test has been executed")
125
126             self.details = {"tests": int(num_tests),
127                             "failures": int(num_failures),
128                             "success": success_testcases,
129                             "errors": failed_testcases,
130                             "skipped": skipped_testcases}
131         except Exception:  # pylint: disable=broad-except
132             self.result = 0
133         LOGGER.info("Testcase %s success_rate is %s%%",
134                     self.case_name, self.result)
135
136     def configure_tempest_defcore(self):
137         # pylint: disable=too-many-arguments
138         """
139         Add/update needed parameters into tempest.conf file
140         """
141         resources = tempest.TempestResourcesManager().create(
142             create_project=True, use_custom_images=True,
143             use_custom_flavors=True)
144         verifier_id = conf_utils.get_verifier_id()
145         deployment_id = conf_utils.get_verifier_deployment_id()
146         deployment_dir = conf_utils.get_verifier_deployment_dir(
147             verifier_id, deployment_id)
148         conf_file = conf_utils.configure_verifier(deployment_dir)
149         conf_utils.configure_tempest_update_params(
150             conf_file, resources.get("network_name"),
151             resources.get("image_id"), resources.get("flavor_id"))
152         LOGGER.debug(
153             "Updating selected tempest.conf parameters for defcore...")
154         rconfig = ConfigParser.RawConfigParser()
155         rconfig.read(conf_file)
156         rconfig.set(
157             'DEFAULT', 'log_file', '{}/tempest.log'.format(deployment_dir))
158         rconfig.set('oslo_concurrency', 'lock_path',
159                     '{}/lock_files'.format(deployment_dir))
160         conf_utils.generate_test_accounts_file(
161             tenant_id=resources.get("project_id"))
162         rconfig.set('auth', 'test_accounts_file',
163                     conf_utils.TEST_ACCOUNTS_FILE)
164         rconfig.set('scenario', 'img_dir', '{}'.format(deployment_dir))
165         rconfig.set('scenario', 'img_file', 'tempest-image')
166         rconfig.set('compute', 'image_ref', resources.get("image_id"))
167         rconfig.set('compute', 'image_ref_alt', resources.get("image_id_alt"))
168         rconfig.set('compute', 'flavor_ref', resources.get("flavor_id"))
169         rconfig.set('compute', 'flavor_ref_alt',
170                     resources.get("flavor_id_alt"))
171         if not os.path.exists(self.resdir):
172             os.makedirs(self.resdir)
173         with open(self.conf_path, 'w') as config_fd:
174             rconfig.write(config_fd)
175
176     @energy.enable_recording
177     def run(self, **kwargs):
178         """
179         Start RefstackClient testcase.
180
181         used for functest command line,
182         functest testcase run refstack_defcore
183         """
184         self.start_time = time.time()
185         try:
186             # Make sure that Tempest is configured
187             self.configure_tempest_defcore()
188             self.run_defcore_default()
189             self.parse_refstack_result()
190             res = testcase.TestCase.EX_OK
191         except Exception:  # pylint: disable=broad-except
192             LOGGER.exception("Error with run")
193             res = testcase.TestCase.EX_RUN_ERROR
194         self.stop_time = time.time()
195         return res
196
197     @staticmethod
198     def main(**kwargs):
199         """
200         Execute RefstackClient testcase manually.
201
202         used for manually running,
203            python refstack_client.py -c <tempest_conf_path>
204            --testlist <testlist_path>
205            can generate a reference refstack_tempest.conf by
206            python tempest_conf.py
207         """
208         try:
209             conf_path = kwargs['config']
210             if not os.path.isfile(conf_path):
211                 LOGGER.error("Conf file not valid: %s", conf_path)
212                 return testcase.TestCase.EX_RUN_ERROR
213             testlist = kwargs['testlist']
214             if not os.path.isfile(testlist):
215                 LOGGER.error("testlist file not valid: %s", testlist)
216                 return testcase.TestCase.EX_RUN_ERROR
217         except KeyError as exc:
218             LOGGER.error("Cannot run refstack client. Please check "
219                          "%s", exc)
220             return testcase.TestCase.EX_RUN_ERROR
221         try:
222             RefstackClient.run_defcore(conf_path, testlist)
223         except Exception as exc:  # pylint: disable=broad-except
224             LOGGER.error('Error with run: %s', exc)
225             return testcase.TestCase.EX_RUN_ERROR
226         return testcase.TestCase.EX_OK
227
228
229 class RefstackClientParser(object):  # pylint: disable=too-few-public-methods
230     """Command line argument parser helper."""
231
232     def __init__(self):
233         """Initialize helper object."""
234         self.parser = argparse.ArgumentParser()
235         self.parser.add_argument(
236             '-c', '--config',
237             help='the file path of refstack_tempest.conf')
238         self.parser.add_argument(
239             '-t', '--testlist',
240             help='Specify the file path or URL of a test list text file. '
241                  'This test list will contain specific test cases that '
242                  'should be tested.',
243             default=RefstackClient.defcorelist)
244
245     def parse_args(self, argv=None):
246         """Parse command line arguments."""
247         return vars(self.parser.parse_args(argv))
248
249
250 def main():
251     """Run RefstackClient testcase with CLI."""
252     logging.basicConfig()
253     refstackclient = RefstackClient()
254     parser = RefstackClientParser()
255     args = parser.parse_args(sys.argv[1:])
256     try:
257         result = refstackclient.main(**args)
258         if result != testcase.TestCase.EX_OK:
259             return result
260     except Exception:  # pylint: disable=broad-except
261         return testcase.TestCase.EX_RUN_ERROR