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 """Rally testcases implementation."""
13 from __future__ import division
14 from __future__ import print_function
27 from ruamel.yaml import YAML
29 from six.moves import configparser
30 from xtesting.core import testcase
33 from functest.core import singlevm
34 from functest.utils import config
35 from functest.utils import env
36 from functest.utils import functest_utils
38 LOGGER = logging.getLogger(__name__)
41 class RallyBase(singlevm.VmReady2):
42 """Base class form Rally testcases implementation."""
44 # pylint: disable=too-many-instance-attributes, too-many-public-methods
45 stests = ['authenticate', 'glance', 'cinder', 'gnocchi', 'heat',
46 'keystone', 'neutron', 'nova', 'quotas', 'swift', 'barbican']
48 rally_conf_path = "/etc/rally/rally.conf"
49 rally_aar4_patch_path = pkg_resources.resource_filename(
50 'functest', 'ci/rally_aarch64_patch.conf')
51 rally_dir = pkg_resources.resource_filename(
52 'functest', 'opnfv_tests/openstack/rally')
53 rally_scenario_dir = pkg_resources.resource_filename(
54 'functest', 'opnfv_tests/openstack/rally/scenario')
55 template_dir = pkg_resources.resource_filename(
56 'functest', 'opnfv_tests/openstack/rally/scenario/templates')
57 support_dir = pkg_resources.resource_filename(
58 'functest', 'opnfv_tests/openstack/rally/scenario/support')
61 iterations_amount = 10
64 volume_service_type = "volumev3"
65 blacklist_file = os.path.join(rally_dir, "blacklist.yaml")
66 task_dir = os.path.join(getattr(config.CONF, 'dir_rally_data'), 'task')
67 temp_dir = os.path.join(task_dir, 'var')
74 def __init__(self, **kwargs):
75 """Initialize RallyBase object."""
76 super(RallyBase, self).__init__(**kwargs)
77 assert self.orig_cloud
79 if self.orig_cloud.get_role("admin"):
81 elif self.orig_cloud.get_role("Admin"):
84 raise Exception("Cannot detect neither admin nor Admin")
85 self.orig_cloud.grant_role(
86 role_name, user=self.project.user.id,
87 project=self.project.project.id,
88 domain=self.project.domain.id)
89 self.results_dir = os.path.join(
90 getattr(config.CONF, 'dir_results'), self.case_name)
94 self.scenario_dir = ''
96 self.start_time = None
99 self.flavor_alt = None
102 self.network_extensions = []
105 def build_task_args(self, test_name):
106 """Build arguments for the Rally task."""
107 task_args = {'service_list': [test_name]}
108 task_args['image_name'] = str(self.image.name)
109 task_args['flavor_name'] = str(self.flavor.name)
110 task_args['flavor_alt_name'] = str(self.flavor_alt.name)
111 task_args['glance_image_location'] = str(self.filename)
112 task_args['glance_image_format'] = str(self.image_format)
113 task_args['tmpl_dir'] = str(self.template_dir)
114 task_args['sup_dir'] = str(self.support_dir)
115 task_args['users_amount'] = self.users_amount
116 task_args['tenants_amount'] = self.tenants_amount
117 task_args['use_existing_users'] = False
118 task_args['iterations'] = self.iterations_amount
119 task_args['concurrency'] = self.concurrency
120 task_args['smoke'] = self.smoke
121 task_args['volume_version'] = self.volume_version
122 task_args['volume_service_type'] = self.volume_service_type
123 task_args['block_migration'] = env.get("BLOCK_MIGRATION").lower()
126 task_args['floating_network'] = str(self.ext_net.name)
128 task_args['floating_network'] = ''
131 task_args['netid'] = str(self.network.id)
133 task_args['netid'] = ''
137 def _prepare_test_list(self, test_name):
138 """Build the list of test cases to be executed."""
139 test_yaml_file_name = 'opnfv-{}.yaml'.format(test_name)
140 scenario_file_name = os.path.join(self.rally_scenario_dir,
143 if not os.path.exists(scenario_file_name):
144 scenario_file_name = os.path.join(self.scenario_dir,
147 if not os.path.exists(scenario_file_name):
148 raise Exception("The scenario '%s' does not exist."
149 % scenario_file_name)
151 LOGGER.debug('Scenario fetched from : %s', scenario_file_name)
152 test_file_name = os.path.join(self.temp_dir, test_yaml_file_name)
154 if not os.path.exists(self.temp_dir):
155 os.makedirs(self.temp_dir)
157 self.apply_blacklist(scenario_file_name, test_file_name)
158 return test_file_name
161 def get_verifier_deployment_id():
163 Returns deployment id for active Rally deployment
165 cmd = ("rally deployment list | awk '/" +
166 getattr(config.CONF, 'rally_deployment_name') +
168 proc = subprocess.Popen(cmd, shell=True,
169 stdout=subprocess.PIPE,
170 stderr=subprocess.STDOUT)
171 deployment_uuid = proc.stdout.readline().rstrip()
172 return deployment_uuid.decode("utf-8")
175 def create_rally_deployment(environ=None):
176 # pylint: disable=unexpected-keyword-arg
177 """Create new rally deployment"""
178 # set the architecture to default
179 pod_arch = env.get("POD_ARCH")
180 arch_filter = ['aarch64']
182 if pod_arch and pod_arch in arch_filter:
183 LOGGER.info("Apply aarch64 specific to rally config...")
184 with open(RallyBase.rally_aar4_patch_path, "r") as pfile:
185 rally_patch_conf = pfile.read()
187 for line in fileinput.input(RallyBase.rally_conf_path):
189 if "cirros|testvm" in line:
190 print(rally_patch_conf)
192 LOGGER.info("Creating Rally environment...")
194 cmd = ['rally', 'deployment', 'destroy',
196 str(getattr(config.CONF, 'rally_deployment_name'))]
197 output = subprocess.check_output(cmd)
198 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
199 except subprocess.CalledProcessError:
202 cmd = ['rally', 'deployment', 'create', '--fromenv',
203 '--name', str(getattr(config.CONF, 'rally_deployment_name'))]
204 output = subprocess.check_output(cmd, env=environ)
205 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
207 cmd = ['rally', 'deployment', 'check']
208 output = subprocess.check_output(cmd)
209 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
210 return RallyBase.get_verifier_deployment_id()
213 def update_keystone_default_role(rally_conf='/etc/rally/rally.conf'):
214 """Set keystone_default_role in rally.conf"""
215 if env.get("NEW_USER_ROLE").lower() != "member":
216 rconfig = configparser.RawConfigParser()
217 rconfig.read(rally_conf)
218 if not rconfig.has_section('openstack'):
219 rconfig.add_section('openstack')
221 'openstack', 'keystone_default_role', env.get("NEW_USER_ROLE"))
222 with open(rally_conf, 'w') as config_file:
223 rconfig.write(config_file)
226 def clean_rally_conf(rally_conf='/etc/rally/rally.conf'):
227 """Clean Rally config"""
228 if env.get("NEW_USER_ROLE").lower() != "member":
229 rconfig = configparser.RawConfigParser()
230 rconfig.read(rally_conf)
231 if rconfig.has_option('openstack', 'keystone_default_role'):
232 rconfig.remove_option('openstack', 'keystone_default_role')
233 with open(rally_conf, 'w') as config_file:
234 rconfig.write(config_file)
237 def get_task_id(tag):
239 Get task id from command rally result.
242 :return: task_id as string
244 cmd = ["rally", "task", "list", "--tag", tag, "--uuids-only"]
245 output = subprocess.check_output(cmd).decode("utf-8").rstrip()
246 LOGGER.info("%s: %s", " ".join(cmd), output)
250 def task_succeed(json_raw):
252 Parse JSON from rally JSON results.
257 rally_report = json.loads(json_raw)
258 tasks = rally_report.get('tasks')
261 if task.get('status') != 'finished' or \
262 task.get('pass_sla') is not True:
268 def _migration_supported(self):
269 """Determine if migration is supported."""
270 if self.compute_cnt > 1:
274 def _network_trunk_supported(self):
275 """Determine if network trunk service is available"""
276 if 'trunk' in self.network_extensions:
282 """Exclude scenario."""
285 with open(RallyBase.blacklist_file, 'r') as black_list_file:
286 black_list_yaml = yaml.safe_load(black_list_file)
288 deploy_scenario = env.get('DEPLOY_SCENARIO')
289 if (bool(deploy_scenario) and
290 'scenario' in black_list_yaml.keys()):
291 for item in black_list_yaml['scenario']:
292 scenarios = item['scenarios']
293 in_it = RallyBase.in_iterable_re
294 if in_it(deploy_scenario, scenarios):
295 tests = item['tests']
296 black_tests.extend(tests)
297 except Exception: # pylint: disable=broad-except
298 LOGGER.debug("Scenario exclusion not applied.")
303 def in_iterable_re(needle, haystack):
305 Check if given needle is in the iterable haystack, using regex.
307 :param needle: string to be matched
308 :param haystack: iterable of strings (optionally regex patterns)
309 :return: True if needle is eqial to any of the elements in haystack,
310 or if a nonempty regex pattern in haystack is found in needle.
312 # match without regex
313 if needle in haystack:
316 for pattern in haystack:
317 # match if regex pattern is set and found in the needle
318 if pattern and re.search(pattern, needle) is not None:
324 """Exclude functionalities."""
329 with open(RallyBase.blacklist_file, 'r') as black_list_file:
330 black_list_yaml = yaml.safe_load(black_list_file)
332 if env.get('BLOCK_MIGRATION').lower() == 'true':
333 func_list.append("block_migration")
334 if not self._migration_supported():
335 func_list.append("no_migration")
336 if not self._network_trunk_supported():
337 func_list.append("no_net_trunk_service")
339 func_list.append("no_floating_ip")
341 if 'functionality' in black_list_yaml.keys():
342 for item in black_list_yaml['functionality']:
343 functions = item['functions']
344 for func in func_list:
345 if func in functions:
346 tests = item['tests']
347 black_tests.extend(tests)
348 except Exception: # pylint: disable=broad-except
349 LOGGER.debug("Functionality exclusion not applied.")
353 def apply_blacklist(self, case_file_name, result_file_name):
354 """Apply blacklist."""
355 LOGGER.debug("Applying blacklist...")
356 cases_file = open(case_file_name, 'r')
357 result_file = open(result_file_name, 'w')
359 black_tests = list(set(self.excl_func() +
360 self.excl_scenario()))
363 LOGGER.debug("Blacklisted tests: %s", str(black_tests))
366 for cases_line in cases_file:
368 for black_tests_line in black_tests:
369 if re.search(black_tests_line,
370 cases_line.strip().rstrip(':')):
374 result_file.write(str(cases_line))
376 if cases_line.isspace():
383 def file_is_empty(file_name):
384 """Determine is a file is empty."""
386 if os.stat(file_name).st_size > 0:
388 except Exception: # pylint: disable=broad-except
393 def _save_results(self, test_name, task_id):
394 """ Generate and save task execution results"""
395 # check for result directory and create it otherwise
396 if not os.path.exists(self.results_dir):
397 LOGGER.debug('%s does not exist, we create it.',
399 os.makedirs(self.results_dir)
401 # put detailed result to log
402 cmd = (["rally", "task", "detailed", "--uuid", task_id])
403 LOGGER.debug('running command: %s', cmd)
404 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
405 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
407 # save report as JSON
408 report_json_name = '{}.json'.format(test_name)
409 report_json_dir = os.path.join(self.results_dir, report_json_name)
410 cmd = (["rally", "task", "report", "--json", "--uuid", task_id,
411 "--out", report_json_dir])
412 LOGGER.debug('running command: %s', cmd)
413 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
414 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
416 json_results = open(report_json_dir).read()
417 self._append_summary(json_results, test_name)
419 # parse JSON operation result
420 if self.task_succeed(json_results):
421 LOGGER.info('Test scenario: "%s" OK.', test_name)
423 LOGGER.info('Test scenario: "%s" Failed.', test_name)
425 def run_task(self, test_name):
427 LOGGER.info('Starting test scenario "%s" ...', test_name)
428 LOGGER.debug('running command: %s', self.run_cmd)
431 self.run_cmd, timeout=self.task_timeout,
432 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
434 with open(os.devnull, 'wb') as devnull:
435 subprocess.call(self.run_cmd, stdout=devnull, stderr=devnull)
436 task_id = self.get_task_id(test_name)
437 LOGGER.debug('task_id : %s', task_id)
439 LOGGER.error("Failed to retrieve task_id")
440 raise Exception("Failed to retrieve task id")
441 self._save_results(test_name, task_id)
443 def _append_summary(self, json_raw, test_name):
444 # pylint: disable=too-many-locals
445 """Update statistics summary info."""
448 overall_duration = 0.0
452 rally_report = json.loads(json_raw)
453 for task in rally_report.get('tasks'):
454 for subtask in task.get('subtasks'):
456 for workload in subtask.get('workloads'):
457 if workload.get('full_duration'):
458 overall_duration += workload.get('full_duration')
460 if workload.get('data'):
461 nb_tests += len(workload.get('data'))
463 for result in workload.get('data'):
464 if not result.get('error'):
470 failures.append(subtask['title'])
472 success.append(subtask['title'])
474 scenario_summary = {'test_name': test_name,
475 'overall_duration': overall_duration,
476 'nb_tests': nb_tests,
477 'nb_success': nb_success,
479 'failures': failures,
480 'task_status': self.task_succeed(json_raw)}
481 self.summary.append(scenario_summary)
483 def prepare_run(self, **kwargs):
484 """Prepare resources needed by test scenarios."""
486 LOGGER.debug('Validating run tests...')
487 for test in kwargs.get('tests', self.stests):
488 if test in self.stests:
489 self.tests.append(test)
491 raise Exception("Test name '%s' is invalid" % test)
493 if not os.path.exists(self.task_dir):
494 os.makedirs(self.task_dir)
496 task = os.path.join(self.rally_dir, 'task.yaml')
497 if not os.path.exists(task):
498 LOGGER.error("Task file '%s' does not exist.", task)
499 raise Exception("Task file '{}' does not exist.".
501 self.task_file = os.path.join(self.task_dir, 'task.yaml')
502 shutil.copyfile(task, self.task_file)
504 task_macro = os.path.join(self.rally_dir, 'macro')
505 if not os.path.exists(task_macro):
506 LOGGER.error("Task macro dir '%s' does not exist.", task_macro)
507 raise Exception("Task macro dir '{}' does not exist.".
509 macro_dir = os.path.join(self.task_dir, 'macro')
510 if os.path.exists(macro_dir):
511 shutil.rmtree(macro_dir)
512 shutil.copytree(task_macro, macro_dir)
514 self.update_keystone_default_role()
515 self.compute_cnt = self.count_hypervisors()
516 self.network_extensions = self.cloud.get_network_extensions()
517 self.flavor_alt = self.create_flavor_alt()
518 self.services = [service.name for service in
519 functest_utils.list_services(self.cloud)]
521 LOGGER.debug("flavor: %s", self.flavor_alt)
523 def prepare_task(self, test_name):
524 """Prepare resources for test run."""
525 file_name = self._prepare_test_list(test_name)
526 if self.file_is_empty(file_name):
527 LOGGER.info('No tests for scenario "%s"', test_name)
529 self.run_cmd = (["rally", "task", "start", "--tag", test_name,
530 "--abort-on-sla-failure",
531 "--task", self.task_file, "--task-args",
532 str(self.build_task_args(test_name))])
535 def run_tests(self, **kwargs):
537 optional = kwargs.get('optional', [])
538 for test in self.tests:
539 if test in self.services or test not in optional:
540 if self.prepare_task(test):
543 def _generate_report(self):
544 """Generate test execution summary report."""
551 res_table = prettytable.PrettyTable(
553 field_names=['Module', 'Duration', 'nb. Test Run', 'Success'])
554 res_table.align['Module'] = "l"
555 res_table.align['Duration'] = "r"
556 res_table.align['Success'] = "r"
558 # for each scenario we draw a row for the table
559 for item in self.summary:
560 if item['task_status'] is True:
562 total_duration += item['overall_duration']
563 total_nb_tests += item['nb_tests']
564 total_nb_success += item['nb_success']
566 success_avg = 100 * item['nb_success'] / item['nb_tests']
567 except ZeroDivisionError:
569 success_str = str("{:0.2f}".format(success_avg)) + '%'
570 duration_str = time.strftime("%H:%M:%S",
571 time.gmtime(item['overall_duration']))
572 res_table.add_row([item['test_name'], duration_str,
573 item['nb_tests'], success_str])
574 payload.append({'module': item['test_name'],
575 'details': {'duration': item['overall_duration'],
576 'nb tests': item['nb_tests'],
577 'success rate': success_str,
578 'success': item['success'],
579 'failures': item['failures']}})
581 total_duration_str = time.strftime("%H:%M:%S",
582 time.gmtime(total_duration))
584 self.result = 100 * total_nb_success / total_nb_tests
585 except ZeroDivisionError:
587 success_rate = "{:0.2f}".format(self.result)
588 success_rate_str = str(success_rate) + '%'
589 res_table.add_row(["", "", "", ""])
590 res_table.add_row(["TOTAL:", total_duration_str, total_nb_tests,
593 LOGGER.info("Rally Summary Report:\n\n%s\n", res_table.get_string())
594 LOGGER.info("Rally '%s' success_rate is %s%% in %s/%s modules",
595 self.case_name, success_rate, nb_modules,
597 self.details['summary'] = {'duration': total_duration,
598 'nb tests': total_nb_tests,
599 'nb success': success_rate}
600 self.details["modules"] = payload
603 def export_task(file_name, export_type="html"):
604 """Export all task results (e.g. html or xunit report)
607 subprocess.CalledProcessError: if Rally doesn't return 0
612 cmd = ["rally", "task", "export", "--type", export_type,
614 str(getattr(config.CONF, 'rally_deployment_name')),
616 LOGGER.debug('running command: %s', cmd)
617 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
618 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
621 def verify_report(file_name, uuid, export_type="html"):
622 """Generate the verifier report (e.g. html or xunit report)
625 subprocess.CalledProcessError: if Rally doesn't return 0
630 cmd = ["rally", "verify", "report", "--type", export_type,
631 "--uuid", uuid, "--to", file_name]
632 LOGGER.debug('running command: %s', cmd)
633 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
634 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
637 """Cleanup of OpenStack resources. Should be called on completion."""
638 self.clean_rally_conf()
639 self.clean_rally_logs()
641 self.orig_cloud.delete_flavor(self.flavor_alt.id)
642 super(RallyBase, self).clean()
644 def is_successful(self):
645 """The overall result of the test."""
646 for item in self.summary:
647 if item['task_status'] is False:
648 return testcase.TestCase.EX_TESTCASE_FAILED
650 return super(RallyBase, self).is_successful()
653 def update_rally_logs(res_dir, rally_conf='/etc/rally/rally.conf'):
654 """Print rally logs in res dir"""
655 if not os.path.exists(res_dir):
657 rconfig = configparser.RawConfigParser()
658 rconfig.read(rally_conf)
659 rconfig.set('DEFAULT', 'debug', True)
660 rconfig.set('DEFAULT', 'use_stderr', False)
661 rconfig.set('DEFAULT', 'log-file', 'rally.log')
662 rconfig.set('DEFAULT', 'log_dir', res_dir)
663 with open(rally_conf, 'w') as config_file:
664 rconfig.write(config_file)
667 def clean_rally_logs(rally_conf='/etc/rally/rally.conf'):
668 """Clean Rally config"""
669 rconfig = configparser.RawConfigParser()
670 rconfig.read(rally_conf)
671 if rconfig.has_option('DEFAULT', 'use_stderr'):
672 rconfig.remove_option('DEFAULT', 'use_stderr')
673 if rconfig.has_option('DEFAULT', 'debug'):
674 rconfig.remove_option('DEFAULT', 'debug')
675 if rconfig.has_option('DEFAULT', 'log-file'):
676 rconfig.remove_option('DEFAULT', 'log-file')
677 if rconfig.has_option('DEFAULT', 'log_dir'):
678 rconfig.remove_option('DEFAULT', 'log_dir')
679 with open(rally_conf, 'w') as config_file:
680 rconfig.write(config_file)
682 def run(self, **kwargs):
684 self.start_time = time.time()
686 assert super(RallyBase, self).run(
687 **kwargs) == testcase.TestCase.EX_OK
688 self.update_rally_logs(self.res_dir)
689 self.create_rally_deployment(environ=self.project.get_environ())
690 self.prepare_run(**kwargs)
691 self.run_tests(**kwargs)
692 self._generate_report()
694 "{}/{}.html".format(self.results_dir, self.case_name))
696 "{}/{}.xml".format(self.results_dir, self.case_name),
697 export_type="junit-xml")
698 res = testcase.TestCase.EX_OK
699 except Exception: # pylint: disable=broad-except
700 LOGGER.exception('Error with run:')
702 res = testcase.TestCase.EX_RUN_ERROR
703 self.stop_time = time.time()
707 class RallySanity(RallyBase):
708 """Rally sanity testcase implementation."""
710 def __init__(self, **kwargs):
711 """Initialize RallySanity object."""
712 if "case_name" not in kwargs:
713 kwargs["case_name"] = "rally_sanity"
714 super(RallySanity, self).__init__(**kwargs)
716 self.scenario_dir = os.path.join(self.rally_scenario_dir, 'sanity')
719 class RallyFull(RallyBase):
720 """Rally full testcase implementation."""
724 def __init__(self, **kwargs):
725 """Initialize RallyFull object."""
726 if "case_name" not in kwargs:
727 kwargs["case_name"] = "rally_full"
728 super(RallyFull, self).__init__(**kwargs)
730 self.scenario_dir = os.path.join(self.rally_scenario_dir, 'full')
733 class RallyJobs(RallyBase):
734 """Rally OpenStack CI testcase implementation."""
739 def __init__(self, **kwargs):
740 """Initialize RallyJobs object."""
741 if "case_name" not in kwargs:
742 kwargs["case_name"] = "rally_jobs"
743 super(RallyJobs, self).__init__(**kwargs)
744 self.task_file = os.path.join(self.rally_dir, 'rally_jobs.yaml')
745 self.task_yaml = None
747 def prepare_run(self, **kwargs):
748 """Create resources needed by test scenarios."""
749 super(RallyJobs, self).prepare_run(**kwargs)
750 with open(os.path.join(self.rally_dir,
751 'rally_jobs.yaml'), 'r') as task_file:
752 self.task_yaml = yaml.safe_load(task_file)
754 for task in self.task_yaml:
755 if task not in self.tests:
756 raise Exception("Test '%s' not in '%s'" %
759 def apply_blacklist(self, case_file_name, result_file_name):
760 # pylint: disable=too-many-branches
761 """Apply blacklist."""
762 LOGGER.debug("Applying blacklist...")
763 black_tests = list(set(self.excl_func() +
764 self.excl_scenario()))
766 LOGGER.debug("Blacklisted tests: %s", str(black_tests))
768 template = YAML(typ='jinja2')
769 with open(case_file_name, 'r') as fname:
770 cases = template.load(fname)
771 if cases.get("version", 1) == 1:
772 # scenarios in dictionary
773 for name in cases.keys():
774 if self.in_iterable_re(name, black_tests):
777 # workloads in subtasks
778 for sind, subtask in reversed(list(
779 enumerate(cases.get('subtasks', [])))):
780 for wind, workload in reversed(list(
781 enumerate(subtask.get('workloads', [])))):
782 scenario = workload.get('scenario', {})
783 for name in scenario.keys():
784 if self.in_iterable_re(name, black_tests):
785 cases['subtasks'][sind]['workloads'].pop(wind)
787 if 'workloads' in cases['subtasks'][sind]:
788 if not cases['subtasks'][sind]['workloads']:
789 cases['subtasks'].pop(sind)
790 # scenarios in subtasks
791 for sind, subtask in reversed(list(
792 enumerate(cases.get('subtasks', [])))):
793 scenario = subtask.get('scenario', {})
794 for name in scenario.keys():
795 if self.in_iterable_re(name, black_tests):
796 cases['subtasks'].pop(sind)
799 with open(result_file_name, 'w') as fname:
800 template.dump(cases, fname)
802 def build_task_args(self, test_name):
803 """Build arguments for the Rally task."""
806 task_args['floating_network'] = str(self.ext_net.name)
808 task_args['floating_network'] = ''
809 task_args['image_name'] = str(self.image.name)
810 task_args['flavor_name'] = str(self.flavor.name)
814 def _remove_plugins_extra():
815 inst_dir = getattr(config.CONF, 'dir_rally_inst')
817 shutil.rmtree(os.path.join(inst_dir, 'plugins'))
818 shutil.rmtree(os.path.join(inst_dir, 'extra'))
819 except Exception: # pylint: disable=broad-except
822 def prepare_task(self, test_name):
823 """Prepare resources for test run."""
824 self._remove_plugins_extra()
825 jobs_dir = os.path.join(
826 getattr(config.CONF, 'dir_rally_data'), test_name, 'rally-jobs')
827 inst_dir = getattr(config.CONF, 'dir_rally_inst')
828 shutil.copytree(os.path.join(jobs_dir, 'plugins'),
829 os.path.join(inst_dir, 'plugins'))
830 shutil.copytree(os.path.join(jobs_dir, 'extra'),
831 os.path.join(inst_dir, 'extra'))
833 task_name = self.task_yaml.get(test_name).get("task")
834 task = os.path.join(jobs_dir, task_name)
835 if not os.path.exists(task):
836 raise Exception("The scenario '%s' does not exist." % task)
837 LOGGER.debug('Scenario fetched from : %s', task)
839 if not os.path.exists(self.temp_dir):
840 os.makedirs(self.temp_dir)
841 task_file_name = os.path.join(self.temp_dir, task_name)
842 self.apply_blacklist(task, task_file_name)
843 self.run_cmd = (["rally", "task", "start", "--tag", test_name,
844 "--task", task_file_name,
845 "--task-args", str(self.build_task_args(test_name))])
849 self._remove_plugins_extra()
850 super(RallyJobs, self).clean()