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
8 # http://www.apache.org/licenses/LICENSE-2.0
11 from __future__ import division
23 from functest.core import testcase
24 from functest.opnfv_tests.openstack.snaps import snaps_utils
25 from functest.opnfv_tests.openstack.tempest import conf_utils
26 from functest.utils.constants import CONST
27 import functest.utils.functest_utils as ft_utils
29 from snaps.config.flavor import FlavorConfig
30 from snaps.config.network import NetworkConfig, SubnetConfig
31 from snaps.config.project import ProjectConfig
32 from snaps.config.user import UserConfig
34 from snaps.openstack import create_flavor
35 from snaps.openstack.create_flavor import OpenStackFlavor
36 from snaps.openstack.tests import openstack_tests
37 from snaps.openstack.utils import deploy_utils
40 """ logging configuration """
41 logger = logging.getLogger(__name__)
44 class TempestCommon(testcase.TestCase):
46 def __init__(self, **kwargs):
47 super(TempestCommon, self).__init__(**kwargs)
48 self.resources = TempestResourcesManager(**kwargs)
51 self.VERIFIER_ID = conf_utils.get_verifier_id()
52 self.VERIFIER_REPO_DIR = conf_utils.get_verifier_repo_dir(
54 self.DEPLOYMENT_ID = conf_utils.get_verifier_deployment_id()
55 self.DEPLOYMENT_DIR = conf_utils.get_verifier_deployment_dir(
56 self.VERIFIER_ID, self.DEPLOYMENT_ID)
57 self.VERIFICATION_ID = None
60 def read_file(filename):
61 with open(filename) as src:
62 return [line.strip() for line in src.readlines()]
64 def generate_test_list(self, verifier_repo_dir):
65 logger.debug("Generating test case list...")
66 if self.MODE == 'defcore':
68 conf_utils.TEMPEST_DEFCORE, conf_utils.TEMPEST_RAW_LIST)
69 elif self.MODE == 'custom':
70 if os.path.isfile(conf_utils.TEMPEST_CUSTOM):
72 conf_utils.TEMPEST_CUSTOM, conf_utils.TEMPEST_RAW_LIST)
74 raise Exception("Tempest test list file %s NOT found."
75 % conf_utils.TEMPEST_CUSTOM)
77 if self.MODE == 'smoke':
79 elif self.MODE == 'full':
82 testr_mode = 'tempest.api.' + self.MODE
84 "testr list-tests {1} > {2};"
85 "cd -;".format(verifier_repo_dir,
87 conf_utils.TEMPEST_RAW_LIST))
88 ft_utils.execute_command(cmd)
90 def apply_tempest_blacklist(self):
91 logger.debug("Applying tempest blacklist...")
92 cases_file = self.read_file(conf_utils.TEMPEST_RAW_LIST)
93 result_file = open(conf_utils.TEMPEST_LIST, 'w')
96 installer_type = CONST.__getattribute__('INSTALLER_TYPE')
97 deploy_scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
98 if (bool(installer_type) * bool(deploy_scenario)):
99 # if INSTALLER_TYPE and DEPLOY_SCENARIO are set we read the
101 black_list_file = open(conf_utils.TEMPEST_BLACKLIST)
102 black_list_yaml = yaml.safe_load(black_list_file)
103 black_list_file.close()
104 for item in black_list_yaml:
105 scenarios = item['scenarios']
106 installers = item['installers']
107 if (deploy_scenario in scenarios and
108 installer_type in installers):
109 tests = item['tests']
111 black_tests.append(test)
115 logger.debug("Tempest blacklist file does not exist.")
117 for cases_line in cases_file:
118 for black_tests_line in black_tests:
119 if black_tests_line in cases_line:
122 result_file.write(str(cases_line) + '\n')
125 def run_verifier_tests(self):
126 self.OPTION += (" --load-list {} --detailed"
127 .format(conf_utils.TEMPEST_LIST))
129 cmd_line = "rally verify start " + self.OPTION
130 logger.info("Starting Tempest test suite: '%s'." % cmd_line)
132 header = ("Tempest environment:\n"
133 " SUT: %s\n Scenario: %s\n Node: %s\n Date: %s\n" %
134 (CONST.__getattribute__('INSTALLER_TYPE'),
135 CONST.__getattribute__('DEPLOY_SCENARIO'),
136 CONST.__getattribute__('NODE_NAME'),
137 time.strftime("%a %b %d %H:%M:%S %Z %Y")))
140 os.path.join(conf_utils.TEMPEST_RESULTS_DIR, "tempest.log"), 'w+')
142 os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
143 "tempest-error.log"), 'w+')
144 f_env = open(os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
145 "environment.log"), 'w+')
148 p = subprocess.Popen(
149 cmd_line, shell=True,
150 stdout=subprocess.PIPE,
155 for line in iter(p.stdout.readline, b''):
156 if re.search("\} tempest\.", line):
157 logger.info(line.replace('\n', ''))
158 elif re.search('Starting verification', line):
159 logger.info(line.replace('\n', ''))
160 first_pos = line.index("UUID=") + len("UUID=")
161 last_pos = line.index(") for deployment")
162 self.VERIFICATION_ID = line[first_pos:last_pos]
163 logger.debug('Verification UUID: %s', self.VERIFICATION_ID)
171 def parse_verifier_result(self):
172 if self.VERIFICATION_ID is None:
173 raise Exception('Verification UUID not found')
175 cmd_line = "rally verify show --uuid {}".format(self.VERIFICATION_ID)
176 logger.info("Showing result for a verification: '%s'." % cmd_line)
177 p = subprocess.Popen(cmd_line,
179 stdout=subprocess.PIPE,
180 stderr=subprocess.STDOUT)
181 for line in p.stdout:
182 new_line = line.replace(' ', '').split('|')
183 if 'Tests' in new_line:
187 if 'Testscount' in new_line:
188 num_tests = new_line[2]
189 elif 'Success' in new_line:
190 num_success = new_line[2]
191 elif 'Skipped' in new_line:
192 num_skipped = new_line[2]
193 elif 'Failures' in new_line:
194 num_failures = new_line[2]
197 num_executed = int(num_tests) - int(num_skipped)
199 self.result = 100 * int(num_success) / int(num_executed)
200 except ZeroDivisionError:
202 if int(num_tests) > 0:
203 logger.info("All tests have been skipped")
205 logger.error("No test has been executed")
208 with open(os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
209 "tempest.log"), 'r') as logfile:
210 output = logfile.read()
212 success_testcases = []
213 for match in re.findall('.*\{0\} (.*?)[. ]*success ', output):
214 success_testcases.append(match)
215 failed_testcases = []
216 for match in re.findall('.*\{0\} (.*?)[. ]*fail ', output):
217 failed_testcases.append(match)
218 skipped_testcases = []
219 for match in re.findall('.*\{0\} (.*?)[. ]*skip:', output):
220 skipped_testcases.append(match)
222 self.details = {"tests": int(num_tests),
223 "failures": int(num_failures),
224 "success": success_testcases,
225 "errors": failed_testcases,
226 "skipped": skipped_testcases}
230 logger.info("Tempest %s success_rate is %s%%"
231 % (self.case_name, self.result))
235 self.start_time = time.time()
237 if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR):
238 os.makedirs(conf_utils.TEMPEST_RESULTS_DIR)
239 resources = self.resources.create()
240 compute_cnt = snaps_utils.get_active_compute_cnt(
241 self.resources.os_creds)
242 conf_utils.configure_tempest(
244 image_id=resources.get("image_id"),
245 flavor_id=resources.get("flavor_id"),
246 compute_cnt=compute_cnt)
247 self.generate_test_list(self.VERIFIER_REPO_DIR)
248 self.apply_tempest_blacklist()
249 self.run_verifier_tests()
250 self.parse_verifier_result()
251 res = testcase.TestCase.EX_OK
252 except Exception as e:
253 logger.error('Error with run: %s' % e)
254 res = testcase.TestCase.EX_RUN_ERROR
256 self.resources.cleanup()
258 self.stop_time = time.time()
262 class TempestSmokeSerial(TempestCommon):
264 def __init__(self, **kwargs):
265 if "case_name" not in kwargs:
266 kwargs["case_name"] = 'tempest_smoke_serial'
267 TempestCommon.__init__(self, **kwargs)
269 self.OPTION = "--concurrency 1"
272 class TempestSmokeParallel(TempestCommon):
274 def __init__(self, **kwargs):
275 if "case_name" not in kwargs:
276 kwargs["case_name"] = 'tempest_smoke_parallel'
277 TempestCommon.__init__(self, **kwargs)
282 class TempestFullParallel(TempestCommon):
284 def __init__(self, **kwargs):
285 if "case_name" not in kwargs:
286 kwargs["case_name"] = 'tempest_full_parallel'
287 TempestCommon.__init__(self, **kwargs)
291 class TempestCustom(TempestCommon):
293 def __init__(self, **kwargs):
294 if "case_name" not in kwargs:
295 kwargs["case_name"] = 'tempest_custom'
296 TempestCommon.__init__(self, **kwargs)
298 self.OPTION = "--concurrency 1"
301 class TempestDefcore(TempestCommon):
303 def __init__(self, **kwargs):
304 if "case_name" not in kwargs:
305 kwargs["case_name"] = 'tempest_defcore'
306 TempestCommon.__init__(self, **kwargs)
307 self.MODE = "defcore"
308 self.OPTION = "--concurrency 1"
311 class TempestResourcesManager(object):
313 def __init__(self, **kwargs):
314 self.os_creds = kwargs.get('os_creds') or snaps_utils.get_credentials()
315 self.guid = '-' + str(uuid.uuid4())
316 self.creators = list()
318 if hasattr(CONST, 'snaps_images_cirros'):
319 self.cirros_image_config = CONST.__getattribute__(
320 'snaps_images_cirros')
322 self.cirros_image_config = None
324 def create(self, use_custom_images=False, use_custom_flavors=False,
325 create_project=False):
327 logger.debug("Creating project (tenant) for Tempest suite")
328 project_name = CONST.__getattribute__(
329 'tempest_identity_tenant_name') + self.guid
330 project_creator = deploy_utils.create_project(
331 self.os_creds, ProjectConfig(
333 description=CONST.__getattribute__(
334 'tempest_identity_tenant_description')))
335 if (project_creator is None or
336 project_creator.get_project() is None):
337 raise Exception("Failed to create tenant")
338 project_id = project_creator.get_project().id
339 self.creators.append(project_creator)
341 logger.debug("Creating user for Tempest suite")
342 user_creator = deploy_utils.create_user(
343 self.os_creds, UserConfig(
344 name=CONST.__getattribute__(
345 'tempest_identity_user_name') + self.guid,
346 password=CONST.__getattribute__(
347 'tempest_identity_user_password'),
348 project_name=project_name))
349 if user_creator is None or user_creator.get_user() is None:
350 raise Exception("Failed to create user")
351 user_id = user_creator.get_user().id
352 self.creators.append(user_creator)
358 logger.debug("Creating private network for Tempest suite")
360 tempest_network_type = None
361 tempest_physical_network = None
362 tempest_segmentation_id = None
364 if hasattr(CONST, 'tempest_network_type'):
365 tempest_network_type = CONST.__getattribute__(
366 'tempest_network_type')
367 if hasattr(CONST, 'tempest_physical_network'):
368 tempest_physical_network = CONST.__getattribute__(
369 'tempest_physical_network')
370 if hasattr(CONST, 'tempest_segmentation_id'):
371 tempest_segmentation_id = CONST.__getattribute__(
372 'tempest_segmentation_id')
374 network_creator = deploy_utils.create_network(
375 self.os_creds, NetworkConfig(
376 name=CONST.__getattribute__(
377 'tempest_private_net_name') + self.guid,
378 project_name=project_name,
379 network_type=tempest_network_type,
380 physical_network=tempest_physical_network,
381 segmentation_id=tempest_segmentation_id,
382 subnet_settings=[SubnetConfig(
383 name=CONST.__getattribute__(
384 'tempest_private_subnet_name') + self.guid,
385 project_name=project_name,
386 cidr=CONST.__getattribute__('tempest_private_subnet_cidr'))
388 if network_creator is None or network_creator.get_network() is None:
389 raise Exception("Failed to create private network")
390 self.creators.append(network_creator)
397 logger.debug("Creating image for Tempest suite")
398 image_base_name = CONST.__getattribute__(
399 'openstack_image_name') + self.guid
400 os_image_settings = openstack_tests.cirros_image_settings(
401 image_base_name, public=True,
402 image_metadata=self.cirros_image_config)
403 logger.debug("Creating image for Tempest suite")
404 image_creator = deploy_utils.create_image(
405 self.os_creds, os_image_settings)
406 if image_creator is None:
407 raise Exception('Failed to create image')
408 self.creators.append(image_creator)
409 image_id = image_creator.get_image().id
411 if use_custom_images:
412 logger.debug("Creating 2nd image for Tempest suite")
413 image_base_name_alt = CONST.__getattribute__(
414 'openstack_image_name_alt') + self.guid
415 os_image_settings_alt = openstack_tests.cirros_image_settings(
416 image_base_name_alt, public=True,
417 image_metadata=self.cirros_image_config)
418 logger.debug("Creating 2nd image for Tempest suite")
419 image_creator_alt = deploy_utils.create_image(
420 self.os_creds, os_image_settings_alt)
421 if image_creator_alt is None:
422 raise Exception('Failed to create image')
423 self.creators.append(image_creator_alt)
424 image_id_alt = image_creator_alt.get_image().id
426 if (CONST.__getattribute__('tempest_use_custom_flavors') == 'True' or
428 logger.info("Creating flavor for Tempest suite")
429 scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
430 flavor_metadata = None
431 if 'ovs' in scenario or 'fdio' in scenario:
432 flavor_metadata = create_flavor.MEM_PAGE_SIZE_LARGE
433 flavor_creator = OpenStackFlavor(
434 self.os_creds, FlavorConfig(
435 name=CONST.__getattribute__(
436 'openstack_flavor_name') + self.guid,
437 ram=CONST.__getattribute__('openstack_flavor_ram'),
438 disk=CONST.__getattribute__('openstack_flavor_disk'),
439 vcpus=CONST.__getattribute__('openstack_flavor_vcpus'),
440 metadata=flavor_metadata))
441 flavor = flavor_creator.create()
443 raise Exception('Failed to create flavor')
444 self.creators.append(flavor_creator)
445 flavor_id = flavor.id
447 if use_custom_flavors:
448 logger.info("Creating 2nd flavor for Tempest suite")
449 scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
450 flavor_metadata_alt = None
451 if 'ovs' in scenario or 'fdio' in scenario:
452 flavor_metadata_alt = create_flavor.MEM_PAGE_SIZE_LARGE
453 CONST.__setattr__('openstack_flavor_ram', 1024)
454 flavor_creator_alt = OpenStackFlavor(
455 self.os_creds, FlavorConfig(
456 name=CONST.__getattribute__(
457 'openstack_flavor_name_alt') + self.guid,
458 ram=CONST.__getattribute__('openstack_flavor_ram'),
459 disk=CONST.__getattribute__('openstack_flavor_disk'),
460 vcpus=CONST.__getattribute__('openstack_flavor_vcpus'),
461 metadata=flavor_metadata_alt))
462 flavor_alt = flavor_creator_alt.create()
463 if flavor_alt is None:
464 raise Exception('Failed to create flavor')
465 self.creators.append(flavor_creator_alt)
466 flavor_id_alt = flavor_alt.id
468 print("RESOURCES CREATE: image_id: %s, image_id_alt: %s, "
469 "flavor_id: %s, flavor_id_alt: %s" % (
470 image_id, image_id_alt, flavor_id, flavor_id_alt,))
473 'image_id': image_id,
474 'image_id_alt': image_id_alt,
475 'flavor_id': flavor_id,
476 'flavor_id_alt': flavor_id_alt
480 result['project_id'] = project_id
481 result['tenant_id'] = project_id # for compatibility
482 result['user_id'] = user_id
488 Cleanup all OpenStack objects. Should be called on completion.
490 for creator in reversed(self.creators):
493 except Exception as e:
494 logger.error('Unexpected error cleaning - %s', e)