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 cmd = ["rally", "verify", "start", "--load-list",
127 conf_utils.TEMPEST_LIST]
128 cmd.extend(self.OPTION)
129 logger.info("Starting Tempest test suite: '%s'." % cmd)
131 header = ("Tempest environment:\n"
132 " SUT: %s\n Scenario: %s\n Node: %s\n Date: %s\n" %
133 (CONST.__getattribute__('INSTALLER_TYPE'),
134 CONST.__getattribute__('DEPLOY_SCENARIO'),
135 CONST.__getattribute__('NODE_NAME'),
136 time.strftime("%a %b %d %H:%M:%S %Z %Y")))
139 os.path.join(conf_utils.TEMPEST_RESULTS_DIR, "tempest.log"), 'w+')
141 os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
142 "tempest-error.log"), 'w+')
143 f_env = open(os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
144 "environment.log"), 'w+')
147 p = subprocess.Popen(
149 stdout=subprocess.PIPE,
154 for line in iter(p.stdout.readline, b''):
155 if re.search("\} tempest\.", line):
156 logger.info(line.replace('\n', ''))
157 elif re.search('Starting verification', line):
158 logger.info(line.replace('\n', ''))
159 first_pos = line.index("UUID=") + len("UUID=")
160 last_pos = line.index(") for deployment")
161 self.VERIFICATION_ID = line[first_pos:last_pos]
162 logger.debug('Verification UUID: %s', self.VERIFICATION_ID)
170 def parse_verifier_result(self):
171 if self.VERIFICATION_ID is None:
172 raise Exception('Verification UUID not found')
174 cmd = ["rally", "verify", "show", "--uuid", self.VERIFICATION_ID]
175 logger.info("Showing result for a verification: '%s'." % cmd)
176 p = subprocess.Popen(cmd,
177 stdout=subprocess.PIPE,
178 stderr=subprocess.STDOUT)
179 for line in p.stdout:
180 new_line = line.replace(' ', '').split('|')
181 if 'Tests' in new_line:
185 if 'Testscount' in new_line:
186 num_tests = new_line[2]
187 elif 'Success' in new_line:
188 num_success = new_line[2]
189 elif 'Skipped' in new_line:
190 num_skipped = new_line[2]
191 elif 'Failures' in new_line:
192 num_failures = new_line[2]
195 num_executed = int(num_tests) - int(num_skipped)
197 self.result = 100 * int(num_success) / int(num_executed)
198 except ZeroDivisionError:
200 if int(num_tests) > 0:
201 logger.info("All tests have been skipped")
203 logger.error("No test has been executed")
206 with open(os.path.join(conf_utils.TEMPEST_RESULTS_DIR,
207 "tempest.log"), 'r') as logfile:
208 output = logfile.read()
210 success_testcases = []
211 for match in re.findall('.*\{0\} (.*?)[. ]*success ', output):
212 success_testcases.append(match)
213 failed_testcases = []
214 for match in re.findall('.*\{0\} (.*?)[. ]*fail ', output):
215 failed_testcases.append(match)
216 skipped_testcases = []
217 for match in re.findall('.*\{0\} (.*?)[. ]*skip:', output):
218 skipped_testcases.append(match)
220 self.details = {"tests": int(num_tests),
221 "failures": int(num_failures),
222 "success": success_testcases,
223 "errors": failed_testcases,
224 "skipped": skipped_testcases}
228 logger.info("Tempest %s success_rate is %s%%"
229 % (self.case_name, self.result))
233 self.start_time = time.time()
235 if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR):
236 os.makedirs(conf_utils.TEMPEST_RESULTS_DIR)
237 resources = self.resources.create()
238 compute_cnt = snaps_utils.get_active_compute_cnt(
239 self.resources.os_creds)
240 conf_utils.configure_tempest(
242 image_id=resources.get("image_id"),
243 flavor_id=resources.get("flavor_id"),
244 compute_cnt=compute_cnt)
245 self.generate_test_list(self.VERIFIER_REPO_DIR)
246 self.apply_tempest_blacklist()
247 self.run_verifier_tests()
248 self.parse_verifier_result()
249 res = testcase.TestCase.EX_OK
250 except Exception as e:
251 logger.error('Error with run: %s' % e)
252 res = testcase.TestCase.EX_RUN_ERROR
254 self.resources.cleanup()
256 self.stop_time = time.time()
260 class TempestSmokeSerial(TempestCommon):
262 def __init__(self, **kwargs):
263 if "case_name" not in kwargs:
264 kwargs["case_name"] = 'tempest_smoke_serial'
265 TempestCommon.__init__(self, **kwargs)
267 self.OPTION = ["--concurrency", "1"]
270 class TempestSmokeParallel(TempestCommon):
272 def __init__(self, **kwargs):
273 if "case_name" not in kwargs:
274 kwargs["case_name"] = 'tempest_smoke_parallel'
275 TempestCommon.__init__(self, **kwargs)
279 class TempestFullParallel(TempestCommon):
281 def __init__(self, **kwargs):
282 if "case_name" not in kwargs:
283 kwargs["case_name"] = 'tempest_full_parallel'
284 TempestCommon.__init__(self, **kwargs)
288 class TempestCustom(TempestCommon):
290 def __init__(self, **kwargs):
291 if "case_name" not in kwargs:
292 kwargs["case_name"] = 'tempest_custom'
293 TempestCommon.__init__(self, **kwargs)
295 self.OPTION = ["--concurrency", "1"]
298 class TempestDefcore(TempestCommon):
300 def __init__(self, **kwargs):
301 if "case_name" not in kwargs:
302 kwargs["case_name"] = 'tempest_defcore'
303 TempestCommon.__init__(self, **kwargs)
304 self.MODE = "defcore"
305 self.OPTION = ["--concurrency", "1"]
308 class TempestResourcesManager(object):
310 def __init__(self, **kwargs):
311 self.os_creds = kwargs.get('os_creds') or snaps_utils.get_credentials()
312 self.guid = '-' + str(uuid.uuid4())
313 self.creators = list()
315 if hasattr(CONST, 'snaps_images_cirros'):
316 self.cirros_image_config = CONST.__getattribute__(
317 'snaps_images_cirros')
319 self.cirros_image_config = None
321 def _create_project(self):
322 """Create project for tests."""
323 project_creator = deploy_utils.create_project(
324 self.os_creds, ProjectConfig(
325 name=CONST.__getattribute__(
326 'tempest_identity_tenant_name') + self.guid,
327 description=CONST.__getattribute__(
328 'tempest_identity_tenant_description')))
329 if project_creator is None or project_creator.get_project() is None:
330 raise Exception("Failed to create tenant")
331 self.creators.append(project_creator)
332 return project_creator.get_project().id
334 def _create_user(self):
335 """Create user for tests."""
336 user_creator = deploy_utils.create_user(
337 self.os_creds, UserConfig(
338 name=CONST.__getattribute__(
339 'tempest_identity_user_name') + self.guid,
340 password=CONST.__getattribute__(
341 'tempest_identity_user_password'),
342 project_name=CONST.__getattribute__(
343 'tempest_identity_tenant_name') + self.guid))
344 if user_creator is None or user_creator.get_user() is None:
345 raise Exception("Failed to create user")
346 self.creators.append(user_creator)
347 return user_creator.get_user().id
349 def _create_network(self, project_name):
350 """Create network for tests."""
351 tempest_network_type = None
352 tempest_physical_network = None
353 tempest_segmentation_id = None
355 if hasattr(CONST, 'tempest_network_type'):
356 tempest_network_type = CONST.__getattribute__(
357 'tempest_network_type')
358 if hasattr(CONST, 'tempest_physical_network'):
359 tempest_physical_network = CONST.__getattribute__(
360 'tempest_physical_network')
361 if hasattr(CONST, 'tempest_segmentation_id'):
362 tempest_segmentation_id = CONST.__getattribute__(
363 'tempest_segmentation_id')
365 network_creator = deploy_utils.create_network(
366 self.os_creds, NetworkConfig(
367 name=CONST.__getattribute__(
368 'tempest_private_net_name') + self.guid,
369 project_name=project_name,
370 network_type=tempest_network_type,
371 physical_network=tempest_physical_network,
372 segmentation_id=tempest_segmentation_id,
373 subnet_settings=[SubnetConfig(
374 name=CONST.__getattribute__(
375 'tempest_private_subnet_name') + self.guid,
376 project_name=project_name,
377 cidr=CONST.__getattribute__(
378 'tempest_private_subnet_cidr'))]))
379 if network_creator is None or network_creator.get_network() is None:
380 raise Exception("Failed to create private network")
381 self.creators.append(network_creator)
383 def _create_image(self, name):
384 """Create image for tests"""
385 os_image_settings = openstack_tests.cirros_image_settings(
387 image_metadata=self.cirros_image_config)
388 image_creator = deploy_utils.create_image(
389 self.os_creds, os_image_settings)
390 if image_creator is None:
391 raise Exception('Failed to create image')
392 self.creators.append(image_creator)
393 return image_creator.get_image().id
395 def _create_flavor(self, name):
396 """Create flavor for tests."""
397 scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
398 flavor_metadata = None
399 if 'ovs' in scenario or 'fdio' in scenario:
400 flavor_metadata = create_flavor.MEM_PAGE_SIZE_LARGE
401 flavor_creator = OpenStackFlavor(
402 self.os_creds, FlavorConfig(
404 ram=CONST.__getattribute__('openstack_flavor_ram'),
405 disk=CONST.__getattribute__('openstack_flavor_disk'),
406 vcpus=CONST.__getattribute__('openstack_flavor_vcpus'),
407 metadata=flavor_metadata))
408 flavor = flavor_creator.create()
410 raise Exception('Failed to create flavor')
411 self.creators.append(flavor_creator)
414 def create(self, use_custom_images=False, use_custom_flavors=False,
415 create_project=False):
416 """Create resources for Tempest test suite."""
419 'image_id_alt': None,
421 'flavor_id_alt': None
426 logger.debug("Creating project and user for Tempest suite")
427 project_name = CONST.__getattribute__(
428 'tempest_identity_tenant_name') + self.guid
429 result['project_id'] = self._create_project()
430 result['user_id'] = self._create_user()
431 result['tenant_id'] = result['project_id'] # for compatibility
433 logger.debug("Creating private network for Tempest suite")
434 self._create_network(project_name)
436 logger.debug("Creating image for Tempest suite")
437 image_name = CONST.__getattribute__('openstack_image_name') + self.guid
438 result['image_id'] = self._create_image(image_name)
440 if use_custom_images:
441 logger.debug("Creating 2nd image for Tempest suite")
442 image_name = CONST.__getattribute__(
443 'openstack_image_name_alt') + self.guid
444 result['image_id_alt'] = self._create_image(image_name)
446 if (CONST.__getattribute__('tempest_use_custom_flavors') == 'True' or
448 logger.info("Creating flavor for Tempest suite")
449 name = CONST.__getattribute__('openstack_flavor_name') + self.guid
450 result['flavor_id'] = self._create_flavor(name)
452 if use_custom_flavors:
453 logger.info("Creating 2nd flavor for Tempest suite")
454 scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
455 if 'ovs' in scenario or 'fdio' in scenario:
456 CONST.__setattr__('openstack_flavor_ram', 1024)
457 name = CONST.__getattribute__(
458 'openstack_flavor_name_alt') + self.guid
459 result['flavor_id_alt'] = self._create_flavor(name)
465 Cleanup all OpenStack objects. Should be called on completion.
467 for creator in reversed(self.creators):
470 except Exception as e:
471 logger.error('Unexpected error cleaning - %s', e)