Obtain scenario by CONST instead of get function
[functest.git] / functest / opnfv_tests / openstack / tempest / tempest.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2015 All rights reserved
4 # 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 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10
11 from __future__ import division
12
13 import logging
14 import os
15 import re
16 import shutil
17 import subprocess
18 import time
19
20 import yaml
21
22 from functest.core import testcase
23 from functest.opnfv_tests.openstack.tempest import conf_utils
24 from functest.utils.constants import CONST
25 import functest.utils.functest_utils as ft_utils
26 import functest.utils.openstack_utils as os_utils
27
28 from snaps.openstack import create_flavor
29 from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
30 from snaps.openstack.create_project import ProjectSettings
31 from snaps.openstack.create_network import NetworkSettings, SubnetSettings
32 from snaps.openstack.create_user import UserSettings
33 from snaps.openstack.tests import openstack_tests
34 from snaps.openstack.utils import deploy_utils
35
36
37 """ logging configuration """
38 logger = logging.getLogger(__name__)
39
40
41 class TempestCommon(testcase.TestCase):
42
43     def __init__(self, **kwargs):
44         super(TempestCommon, self).__init__(**kwargs)
45         self.resources = TempestResourcesManager(**kwargs)
46         self.MODE = ""
47         self.OPTION = ""
48         self.VERIFIER_ID = conf_utils.get_verifier_id()
49         self.VERIFIER_REPO_DIR = conf_utils.get_verifier_repo_dir(
50             self.VERIFIER_ID)
51         self.DEPLOYMENT_ID = conf_utils.get_verifier_deployment_id()
52         self.DEPLOYMENT_DIR = conf_utils.get_verifier_deployment_dir(
53             self.VERIFIER_ID, self.DEPLOYMENT_ID)
54         self.VERIFICATION_ID = None
55
56     @staticmethod
57     def read_file(filename):
58         with open(filename) as src:
59             return [line.strip() for line in src.readlines()]
60
61     def generate_test_list(self, verifier_repo_dir):
62         logger.debug("Generating test case list...")
63         if self.MODE == 'defcore':
64             shutil.copyfile(
65                 conf_utils.TEMPEST_DEFCORE, conf_utils.TEMPEST_RAW_LIST)
66         elif self.MODE == 'custom':
67             if os.path.isfile(conf_utils.TEMPEST_CUSTOM):
68                 shutil.copyfile(
69                     conf_utils.TEMPEST_CUSTOM, conf_utils.TEMPEST_RAW_LIST)
70             else:
71                 raise Exception("Tempest test list file %s NOT found."
72                                 % conf_utils.TEMPEST_CUSTOM)
73         else:
74             if self.MODE == 'smoke':
75                 testr_mode = "smoke"
76             elif self.MODE == 'full':
77                 testr_mode = ""
78             else:
79                 testr_mode = 'tempest.api.' + self.MODE
80             cmd = ("cd {0};"
81                    "testr list-tests {1} > {2};"
82                    "cd -;".format(verifier_repo_dir,
83                                   testr_mode,
84                                   conf_utils.TEMPEST_RAW_LIST))
85             ft_utils.execute_command(cmd)
86
87     def apply_tempest_blacklist(self):
88         logger.debug("Applying tempest blacklist...")
89         cases_file = self.read_file(conf_utils.TEMPEST_RAW_LIST)
90         result_file = open(conf_utils.TEMPEST_LIST, 'w')
91         black_tests = []
92         try:
93             installer_type = CONST.__getattribute__('INSTALLER_TYPE')
94             deploy_scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
95             if (bool(installer_type) * bool(deploy_scenario)):
96                 # if INSTALLER_TYPE and DEPLOY_SCENARIO are set we read the
97                 # file
98                 black_list_file = open(conf_utils.TEMPEST_BLACKLIST)
99                 black_list_yaml = yaml.safe_load(black_list_file)
100                 black_list_file.close()
101                 for item in black_list_yaml:
102                     scenarios = item['scenarios']
103                     installers = item['installers']
104                     if (deploy_scenario in scenarios and
105                             installer_type in installers):
106                         tests = item['tests']
107                         for test in tests:
108                             black_tests.append(test)
109                         break
110         except Exception:
111             black_tests = []
112             logger.debug("Tempest blacklist file does not exist.")
113
114         for cases_line in cases_file:
115             for black_tests_line in black_tests:
116                 if black_tests_line in cases_line:
117                     break
118             else:
119                 result_file.write(str(cases_line) + '\n')
120         result_file.close()
121
122     def run_verifier_tests(self):
123         self.OPTION += (" --load-list {} --detailed"
124                         .format(conf_utils.TEMPEST_LIST))
125
126         cmd_line = "rally verify start " + self.OPTION
127         logger.info("Starting Tempest test suite: '%s'." % cmd_line)
128
129         header = ("Tempest environment:\n"
130                   "  SUT: %s\n  Scenario: %s\n  Node: %s\n  Date: %s\n" %
131                   (CONST.__getattribute__('INSTALLER_TYPE'),
132                    CONST.__getattribute__('DEPLOY_SCENARIO'),
133                    CONST.__getattribute__('NODE_NAME'),
134                    time.strftime("%a %b %d %H:%M:%S %Z %Y")))
135
136         f_stdout = open(
137             os.path.join(conf_utils.TEMPEST_RESULTS_DIR, "tempest.log"), 'w+')
138         f_stderr = open(
139             os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
140                          "tempest-error.log"), 'w+')
141         f_env = open(os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
142                                   "environment.log"), 'w+')
143         f_env.write(header)
144
145         p = subprocess.Popen(
146             cmd_line, shell=True,
147             stdout=subprocess.PIPE,
148             stderr=f_stderr,
149             bufsize=1)
150
151         with p.stdout:
152             for line in iter(p.stdout.readline, b''):
153                 if re.search("\} tempest\.", line):
154                     logger.info(line.replace('\n', ''))
155                 elif re.search('Starting verification', line):
156                     logger.info(line.replace('\n', ''))
157                     first_pos = line.index("UUID=") + len("UUID=")
158                     last_pos = line.index(") for deployment")
159                     self.VERIFICATION_ID = line[first_pos:last_pos]
160                     logger.debug('Verification UUID: %s', self.VERIFICATION_ID)
161                 f_stdout.write(line)
162         p.wait()
163
164         f_stdout.close()
165         f_stderr.close()
166         f_env.close()
167
168     def parse_verifier_result(self):
169         if self.VERIFICATION_ID is None:
170             raise Exception('Verification UUID not found')
171
172         cmd_line = "rally verify show --uuid {}".format(self.VERIFICATION_ID)
173         logger.info("Showing result for a verification: '%s'." % cmd_line)
174         p = subprocess.Popen(cmd_line,
175                              shell=True,
176                              stdout=subprocess.PIPE,
177                              stderr=subprocess.STDOUT)
178         for line in p.stdout:
179             new_line = line.replace(' ', '').split('|')
180             if 'Tests' in new_line:
181                 break
182
183             logger.info(line)
184             if 'Testscount' in new_line:
185                 num_tests = new_line[2]
186             elif 'Success' in new_line:
187                 num_success = new_line[2]
188             elif 'Skipped' in new_line:
189                 num_skipped = new_line[2]
190             elif 'Failures' in new_line:
191                 num_failures = new_line[2]
192
193         try:
194             num_executed = int(num_tests) - int(num_skipped)
195             try:
196                 self.result = 100 * int(num_success) / int(num_executed)
197             except ZeroDivisionError:
198                 self.result = 0
199                 if int(num_tests) > 0:
200                     logger.info("All tests have been skipped")
201                 else:
202                     logger.error("No test has been executed")
203                     return
204
205             with open(os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
206                                    "tempest.log"), 'r') as logfile:
207                 output = logfile.read()
208
209             success_testcases = []
210             for match in re.findall('.*\{0\} (.*?)[. ]*success ', output):
211                 success_testcases.append(match)
212             failed_testcases = []
213             for match in re.findall('.*\{0\} (.*?)[. ]*fail ', output):
214                 failed_testcases.append(match)
215             skipped_testcases = []
216             for match in re.findall('.*\{0\} (.*?)[. ]*skip:', output):
217                 skipped_testcases.append(match)
218
219             self.details = {"tests": int(num_tests),
220                             "failures": int(num_failures),
221                             "success": success_testcases,
222                             "errors": failed_testcases,
223                             "skipped": skipped_testcases}
224         except Exception:
225             self.result = 0
226
227         logger.info("Tempest %s success_rate is %s%%"
228                     % (self.case_name, self.result))
229
230     def run(self):
231
232         self.start_time = time.time()
233         try:
234             if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR):
235                 os.makedirs(conf_utils.TEMPEST_RESULTS_DIR)
236             resources = self.resources.create()
237             conf_utils.configure_tempest(
238                 self.DEPLOYMENT_DIR,
239                 image_id=resources.get("image_id"),
240                 flavor_id=resources.get("flavor_id"),
241                 mode=self.MODE)
242             self.generate_test_list(self.VERIFIER_REPO_DIR)
243             self.apply_tempest_blacklist()
244             self.run_verifier_tests()
245             self.parse_verifier_result()
246             res = testcase.TestCase.EX_OK
247         except Exception as e:
248             logger.error('Error with run: %s' % e)
249             res = testcase.TestCase.EX_RUN_ERROR
250         finally:
251             self.resources.cleanup()
252
253         self.stop_time = time.time()
254         return res
255
256     def create_snapshot(self):
257         """
258         Run the Tempest cleanup utility to initialize OS state.
259
260         :return: TestCase.EX_OK
261         """
262         logger.info("Initializing the saved state of the OpenStack deployment")
263
264         if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR):
265             os.makedirs(conf_utils.TEMPEST_RESULTS_DIR)
266
267         # Make sure that the verifier is configured
268         conf_utils.configure_verifier(self.DEPLOYMENT_DIR)
269
270         os_utils.init_tempest_cleanup(
271             self.DEPLOYMENT_DIR, 'tempest.conf',
272             os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
273                          "tempest-cleanup-init.log")
274         )
275
276         return super(TempestCommon, self).create_snapshot()
277
278     def clean(self):
279         """
280         Run the Tempest cleanup utility to delete and destroy OS resources
281         created by Tempest.
282         """
283         logger.info("Destroying the resources created for refstack")
284
285         os_utils.perform_tempest_cleanup(
286             self.DEPLOYMENT_DIR, 'tempest.conf',
287             os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
288                          "tempest-cleanup.log")
289         )
290
291         return super(TempestCommon, self).clean()
292
293
294 class TempestSmokeSerial(TempestCommon):
295
296     def __init__(self, **kwargs):
297         if "case_name" not in kwargs:
298             kwargs["case_name"] = 'tempest_smoke_serial'
299         TempestCommon.__init__(self, **kwargs)
300         self.MODE = "smoke"
301         self.OPTION = "--concurrency 1"
302
303
304 class TempestSmokeParallel(TempestCommon):
305
306     def __init__(self, **kwargs):
307         if "case_name" not in kwargs:
308             kwargs["case_name"] = 'tempest_smoke_parallel'
309         TempestCommon.__init__(self, **kwargs)
310         self.MODE = "smoke"
311         self.OPTION = ""
312
313
314 class TempestFullParallel(TempestCommon):
315
316     def __init__(self, **kwargs):
317         if "case_name" not in kwargs:
318             kwargs["case_name"] = 'tempest_full_parallel'
319         TempestCommon.__init__(self, **kwargs)
320         self.MODE = "full"
321
322
323 class TempestCustom(TempestCommon):
324
325     def __init__(self, **kwargs):
326         if "case_name" not in kwargs:
327             kwargs["case_name"] = 'tempest_custom'
328         TempestCommon.__init__(self, **kwargs)
329         self.MODE = "custom"
330         self.OPTION = "--concurrency 1"
331
332
333 class TempestDefcore(TempestCommon):
334
335     def __init__(self, **kwargs):
336         if "case_name" not in kwargs:
337             kwargs["case_name"] = 'tempest_defcore'
338         TempestCommon.__init__(self, **kwargs)
339         self.MODE = "defcore"
340         self.OPTION = "--concurrency 1"
341
342
343 class TempestResourcesManager(object):
344
345     def __init__(self, **kwargs):
346         self.os_creds = None
347         if 'os_creds' in kwargs:
348             self.os_creds = kwargs['os_creds']
349         else:
350             self.os_creds = openstack_tests.get_credentials(
351                 os_env_file=CONST.__getattribute__('openstack_creds'))
352
353         self.creators = list()
354
355         if hasattr(CONST, 'snaps_images_cirros'):
356             self.cirros_image_config = CONST.__getattribute__(
357                 'snaps_images_cirros')
358         else:
359             self.cirros_image_config = None
360
361     def create(self, use_custom_images=False, use_custom_flavors=False,
362                create_project=False):
363         if create_project:
364             logger.debug("Creating project (tenant) for Tempest suite")
365             project_name = CONST.__getattribute__(
366                 'tempest_identity_tenant_name')
367             project_creator = deploy_utils.create_project(
368                 self.os_creds, ProjectSettings(
369                     name=project_name,
370                     description=CONST.__getattribute__(
371                         'tempest_identity_tenant_description')))
372             if (project_creator is None or
373                     project_creator.get_project() is None):
374                 raise Exception("Failed to create tenant")
375             project_id = project_creator.get_project().id
376             self.creators.append(project_creator)
377
378             logger.debug("Creating user for Tempest suite")
379             user_creator = deploy_utils.create_user(
380                 self.os_creds, UserSettings(
381                     name=CONST.__getattribute__('tempest_identity_user_name'),
382                     password=CONST.__getattribute__(
383                         'tempest_identity_user_password'),
384                     project_name=project_name))
385             if user_creator is None or user_creator.get_user() is None:
386                 raise Exception("Failed to create user")
387             user_id = user_creator.get_user().id
388             self.creators.append(user_creator)
389         else:
390             project_name = None
391             project_id = None
392             user_id = None
393
394         logger.debug("Creating private network for Tempest suite")
395         network_creator = deploy_utils.create_network(
396             self.os_creds, NetworkSettings(
397                 name=CONST.__getattribute__('tempest_private_net_name'),
398                 project_name=project_name,
399                 subnet_settings=[SubnetSettings(
400                     name=CONST.__getattribute__('tempest_private_subnet_name'),
401                     cidr=CONST.__getattribute__('tempest_private_subnet_cidr'))
402                 ]))
403         if network_creator is None or network_creator.get_network() is None:
404             raise Exception("Failed to create private network")
405         self.creators.append(network_creator)
406
407         image_id = None
408         image_id_alt = None
409         flavor_id = None
410         flavor_id_alt = None
411
412         if (CONST.__getattribute__('tempest_use_custom_images') or
413            use_custom_images):
414             logger.debug("Creating image for Tempest suite")
415             image_base_name = CONST.__getattribute__('openstack_image_name')
416             os_image_settings = openstack_tests.cirros_image_settings(
417                 image_base_name, public=True,
418                 image_metadata=self.cirros_image_config)
419             logger.debug("Creating image for Tempest suite")
420             image_creator = deploy_utils.create_image(
421                 self.os_creds, os_image_settings)
422             if image_creator is None:
423                 raise Exception('Failed to create image')
424             self.creators.append(image_creator)
425             image_id = image_creator.get_image().id
426
427         if use_custom_images:
428             logger.debug("Creating 2nd image for Tempest suite")
429             image_base_name_alt = CONST.__getattribute__(
430                 'openstack_image_name_alt')
431             os_image_settings_alt = openstack_tests.cirros_image_settings(
432                 image_base_name_alt, public=True,
433                 image_metadata=self.cirros_image_config)
434             logger.debug("Creating 2nd image for Tempest suite")
435             image_creator_alt = deploy_utils.create_image(
436                 self.os_creds, os_image_settings_alt)
437             if image_creator_alt is None:
438                 raise Exception('Failed to create image')
439             self.creators.append(image_creator_alt)
440             image_id_alt = image_creator_alt.get_image().id
441
442         if (CONST.__getattribute__('tempest_use_custom_flavors') or
443            use_custom_flavors):
444             logger.info("Creating flavor for Tempest suite")
445             scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
446             flavor_metadata = None
447             if 'ovs' in scenario or 'fdio' in scenario:
448                 flavor_metadata = create_flavor.MEM_PAGE_SIZE_LARGE
449             flavor_creator = OpenStackFlavor(
450                 self.os_creds, FlavorSettings(
451                     name=CONST.__getattribute__('openstack_flavor_name'),
452                     ram=CONST.__getattribute__('openstack_flavor_ram'),
453                     disk=CONST.__getattribute__('openstack_flavor_disk'),
454                     vcpus=CONST.__getattribute__('openstack_flavor_vcpus'),
455                     metadata=flavor_metadata))
456             flavor = flavor_creator.create()
457             if flavor is None:
458                 raise Exception('Failed to create flavor')
459             self.creators.append(flavor_creator)
460             flavor_id = flavor.id
461
462         if use_custom_flavors:
463             logger.info("Creating 2nd flavor for Tempest suite")
464             scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
465             flavor_metadata_alt = None
466             if 'ovs' in scenario or 'fdio' in scenario:
467                 flavor_metadata_alt = create_flavor.MEM_PAGE_SIZE_LARGE
468             flavor_creator_alt = OpenStackFlavor(
469                 self.os_creds, FlavorSettings(
470                     name=CONST.__getattribute__('openstack_flavor_name_alt'),
471                     ram=CONST.__getattribute__('openstack_flavor_ram'),
472                     disk=CONST.__getattribute__('openstack_flavor_disk'),
473                     vcpus=CONST.__getattribute__('openstack_flavor_vcpus'),
474                     metadata=flavor_metadata_alt))
475             flavor_alt = flavor_creator_alt.create()
476             if flavor_alt is None:
477                 raise Exception('Failed to create flavor')
478             self.creators.append(flavor_creator_alt)
479             flavor_id_alt = flavor_alt.id
480
481         print("RESOURCES CREATE: image_id: %s, image_id_alt: %s, "
482               "flavor_id: %s, flavor_id_alt: %s" % (
483                   image_id, image_id_alt, flavor_id, flavor_id_alt,))
484
485         result = {
486             'image_id': image_id,
487             'image_id_alt': image_id_alt,
488             'flavor_id': flavor_id,
489             'flavor_id_alt': flavor_id_alt
490         }
491
492         if create_project:
493             result['project_id'] = project_id
494             result['tenant_id'] = project_id  # for compatibility
495             result['user_id'] = user_id
496
497         return result
498
499     def cleanup(self):
500         """
501         Cleanup all OpenStack objects. Should be called on completion.
502         """
503         for creator in reversed(self.creators):
504             try:
505                 creator.clean()
506             except Exception as e:
507                 logger.error('Unexpected error cleaning - %s', e)