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
28 from six.moves import configparser
29 from xtesting.core import testcase
32 from functest.core import singlevm
33 from functest.utils import config
34 from functest.utils import env
36 LOGGER = logging.getLogger(__name__)
39 class RallyBase(singlevm.VmReady2):
40 """Base class form Rally testcases implementation."""
42 # pylint: disable=too-many-instance-attributes, too-many-public-methods
43 stests = ['authenticate', 'glance', 'cinder', 'gnocchi', 'heat',
44 'keystone', 'neutron', 'nova', 'quotas', 'swift', 'barbican']
46 rally_conf_path = "/etc/rally/rally.conf"
47 rally_aar4_patch_path = pkg_resources.resource_filename(
48 'functest', 'ci/rally_aarch64_patch.conf')
49 rally_dir = pkg_resources.resource_filename(
50 'functest', 'opnfv_tests/openstack/rally')
51 rally_scenario_dir = pkg_resources.resource_filename(
52 'functest', 'opnfv_tests/openstack/rally/scenario')
53 template_dir = pkg_resources.resource_filename(
54 'functest', 'opnfv_tests/openstack/rally/scenario/templates')
55 support_dir = pkg_resources.resource_filename(
56 'functest', 'opnfv_tests/openstack/rally/scenario/support')
59 iterations_amount = 10
62 volume_service_type = "volumev3"
63 blacklist_file = os.path.join(rally_dir, "blacklist.yaml")
64 task_dir = os.path.join(getattr(config.CONF, 'dir_rally_data'), 'task')
65 temp_dir = os.path.join(task_dir, 'var')
72 def __init__(self, **kwargs):
73 """Initialize RallyBase object."""
74 super(RallyBase, self).__init__(**kwargs)
75 assert self.orig_cloud
77 if self.orig_cloud.get_role("admin"):
79 elif self.orig_cloud.get_role("Admin"):
82 raise Exception("Cannot detect neither admin nor Admin")
83 self.orig_cloud.grant_role(
84 role_name, user=self.project.user.id,
85 project=self.project.project.id,
86 domain=self.project.domain.id)
87 self.results_dir = os.path.join(
88 getattr(config.CONF, 'dir_results'), self.case_name)
92 self.scenario_dir = ''
94 self.start_time = None
98 self.flavor_alt = None
101 self.network_extensions = []
104 def build_task_args(self, test_name):
105 """Build arguments for the Rally task."""
106 task_args = {'service_list': [test_name]}
107 task_args['image_name'] = str(self.image.name)
108 task_args['flavor_name'] = str(self.flavor.name)
109 task_args['flavor_alt_name'] = str(self.flavor_alt.name)
110 task_args['glance_image_location'] = str(self.filename)
111 task_args['glance_image_format'] = str(self.image_format)
112 task_args['tmpl_dir'] = str(self.template_dir)
113 task_args['sup_dir'] = str(self.support_dir)
114 task_args['users_amount'] = self.users_amount
115 task_args['tenants_amount'] = self.tenants_amount
116 task_args['use_existing_users'] = False
117 task_args['iterations'] = self.iterations_amount
118 task_args['concurrency'] = self.concurrency
119 task_args['smoke'] = self.smoke
120 task_args['volume_version'] = self.volume_version
121 task_args['volume_service_type'] = self.volume_service_type
122 task_args['block_migration'] = env.get("BLOCK_MIGRATION").lower()
125 task_args['floating_network'] = str(self.ext_net.name)
127 task_args['floating_network'] = ''
130 task_args['netid'] = str(self.network.id)
132 task_args['netid'] = ''
136 def _prepare_test_list(self, test_name):
137 """Build the list of test cases to be executed."""
138 test_yaml_file_name = 'opnfv-{}.yaml'.format(test_name)
139 scenario_file_name = os.path.join(self.rally_scenario_dir,
142 if not os.path.exists(scenario_file_name):
143 scenario_file_name = os.path.join(self.scenario_dir,
146 if not os.path.exists(scenario_file_name):
147 raise Exception("The scenario '%s' does not exist."
148 % scenario_file_name)
150 LOGGER.debug('Scenario fetched from : %s', scenario_file_name)
151 test_file_name = os.path.join(self.temp_dir, test_yaml_file_name)
153 if not os.path.exists(self.temp_dir):
154 os.makedirs(self.temp_dir)
156 self.apply_blacklist(scenario_file_name, test_file_name)
157 return test_file_name
160 def get_verifier_deployment_id():
162 Returns deployment id for active Rally deployment
164 cmd = ("rally deployment list | awk '/" +
165 getattr(config.CONF, 'rally_deployment_name') +
167 proc = subprocess.Popen(cmd, shell=True,
168 stdout=subprocess.PIPE,
169 stderr=subprocess.STDOUT)
170 deployment_uuid = proc.stdout.readline().rstrip()
171 return deployment_uuid.decode("utf-8")
174 def create_rally_deployment(environ=None):
175 """Create new rally deployment"""
176 # set the architecture to default
177 pod_arch = env.get("POD_ARCH")
178 arch_filter = ['aarch64']
180 if pod_arch and pod_arch in arch_filter:
181 LOGGER.info("Apply aarch64 specific to rally config...")
182 with open(RallyBase.rally_aar4_patch_path, "r") as pfile:
183 rally_patch_conf = pfile.read()
185 for line in fileinput.input(RallyBase.rally_conf_path):
187 if "cirros|testvm" in line:
188 print(rally_patch_conf)
190 LOGGER.info("Creating Rally environment...")
192 cmd = ['rally', 'deployment', 'destroy',
194 str(getattr(config.CONF, 'rally_deployment_name'))]
195 output = subprocess.check_output(cmd)
196 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
197 except subprocess.CalledProcessError:
200 cmd = ['rally', 'deployment', 'create', '--fromenv',
201 '--name', str(getattr(config.CONF, 'rally_deployment_name'))]
202 output = subprocess.check_output(cmd, env=environ)
203 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
205 cmd = ['rally', 'deployment', 'check']
206 output = subprocess.check_output(cmd)
207 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
208 return RallyBase.get_verifier_deployment_id()
211 def update_keystone_default_role(rally_conf='/etc/rally/rally.conf'):
212 """Set keystone_default_role in rally.conf"""
213 if env.get("NEW_USER_ROLE").lower() != "member":
214 rconfig = configparser.RawConfigParser()
215 rconfig.read(rally_conf)
216 if not rconfig.has_section('openstack'):
217 rconfig.add_section('openstack')
219 'openstack', 'keystone_default_role', env.get("NEW_USER_ROLE"))
220 with open(rally_conf, 'w') as config_file:
221 rconfig.write(config_file)
224 def clean_rally_conf(rally_conf='/etc/rally/rally.conf'):
225 """Clean Rally config"""
226 if env.get("NEW_USER_ROLE").lower() != "member":
227 rconfig = configparser.RawConfigParser()
228 rconfig.read(rally_conf)
229 if rconfig.has_option('openstack', 'keystone_default_role'):
230 rconfig.remove_option('openstack', 'keystone_default_role')
231 with open(rally_conf, 'w') as config_file:
232 rconfig.write(config_file)
235 def get_task_id(cmd_raw):
237 Get task id from command rally result.
240 :return: task_id as string
242 taskid_re = re.compile('^Task +(.*): started$')
243 for line in cmd_raw.splitlines(True):
245 match = taskid_re.match(line.decode("utf-8"))
247 return match.group(1)
251 def task_succeed(json_raw):
253 Parse JSON from rally JSON results.
258 rally_report = json.loads(json_raw)
259 tasks = rally_report.get('tasks')
262 if task.get('status') != 'finished' or \
263 task.get('pass_sla') is not True:
269 def _migration_supported(self):
270 """Determine if migration is supported."""
271 if self.compute_cnt > 1:
275 def _network_trunk_supported(self):
276 """Determine if network trunk service is available"""
277 if 'trunk' in self.network_extensions:
283 """Exclude scenario."""
286 with open(RallyBase.blacklist_file, 'r') as black_list_file:
287 black_list_yaml = yaml.safe_load(black_list_file)
289 deploy_scenario = env.get('DEPLOY_SCENARIO')
290 if (bool(deploy_scenario) and
291 'scenario' in black_list_yaml.keys()):
292 for item in black_list_yaml['scenario']:
293 scenarios = item['scenarios']
294 in_it = RallyBase.in_iterable_re
295 if in_it(deploy_scenario, scenarios):
296 tests = item['tests']
297 black_tests.extend(tests)
298 except Exception: # pylint: disable=broad-except
299 LOGGER.debug("Scenario exclusion not applied.")
304 def in_iterable_re(needle, haystack):
306 Check if given needle is in the iterable haystack, using regex.
308 :param needle: string to be matched
309 :param haystack: iterable of strings (optionally regex patterns)
310 :return: True if needle is eqial to any of the elements in haystack,
311 or if a nonempty regex pattern in haystack is found in needle.
313 # match without regex
314 if needle in haystack:
317 for pattern in haystack:
318 # match if regex pattern is set and found in the needle
319 if pattern and re.search(pattern, needle) is not None:
325 """Exclude functionalities."""
330 with open(RallyBase.blacklist_file, 'r') as black_list_file:
331 black_list_yaml = yaml.safe_load(black_list_file)
333 if env.get('BLOCK_MIGRATION').lower() == 'true':
334 func_list.append("block_migration")
335 if not self._migration_supported():
336 func_list.append("no_migration")
337 if not self._network_trunk_supported():
338 func_list.append("no_net_trunk_service")
340 func_list.append("no_floating_ip")
342 if 'functionality' in black_list_yaml.keys():
343 for item in black_list_yaml['functionality']:
344 functions = item['functions']
345 for func in func_list:
346 if func in functions:
347 tests = item['tests']
348 black_tests.extend(tests)
349 except Exception: # pylint: disable=broad-except
350 LOGGER.debug("Functionality exclusion not applied.")
354 def apply_blacklist(self, case_file_name, result_file_name):
355 """Apply blacklist."""
356 LOGGER.debug("Applying blacklist...")
357 cases_file = open(case_file_name, 'r')
358 result_file = open(result_file_name, 'w')
360 black_tests = list(set(self.excl_func() +
361 self.excl_scenario()))
364 LOGGER.debug("Blacklisted tests: %s", str(black_tests))
367 for cases_line in cases_file:
369 for black_tests_line in black_tests:
370 if re.search(black_tests_line,
371 cases_line.strip().rstrip(':')):
375 result_file.write(str(cases_line))
377 if cases_line.isspace():
384 def file_is_empty(file_name):
385 """Determine is a file is empty."""
387 if os.stat(file_name).st_size > 0:
389 except Exception: # pylint: disable=broad-except
394 def _save_results(self, test_name, task_id):
395 """ Generate and save task execution results"""
396 # check for result directory and create it otherwise
397 if not os.path.exists(self.results_dir):
398 LOGGER.debug('%s does not exist, we create it.',
400 os.makedirs(self.results_dir)
402 # put detailed result to log
403 cmd = (["rally", "task", "detailed", "--uuid", task_id])
404 LOGGER.debug('running command: %s', cmd)
405 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
406 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
408 # save report as JSON
409 report_json_name = '{}.json'.format(test_name)
410 report_json_dir = os.path.join(self.results_dir, report_json_name)
411 cmd = (["rally", "task", "report", "--json", "--uuid", task_id,
412 "--out", report_json_dir])
413 LOGGER.debug('running command: %s', cmd)
414 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
415 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
417 json_results = open(report_json_dir).read()
418 self._append_summary(json_results, test_name)
420 # parse JSON operation result
421 if self.task_succeed(json_results):
422 LOGGER.info('Test scenario: "%s" OK.', test_name)
424 LOGGER.info('Test scenario: "%s" Failed.', test_name)
426 def run_task(self, test_name):
428 LOGGER.info('Starting test scenario "%s" ...', test_name)
429 LOGGER.debug('running command: %s', self.run_cmd)
430 proc = subprocess.Popen(self.run_cmd, stdout=subprocess.PIPE,
431 stderr=subprocess.STDOUT)
433 output = proc.communicate(timeout=self.task_timeout)[0]
434 except subprocess.TimeoutExpired:
437 LOGGER.error("Failed to complete run task")
438 raise Exception("Failed to complete run task")
439 task_id = self.get_task_id(output)
440 LOGGER.debug('task_id : %s', task_id)
442 LOGGER.error("Failed to retrieve task_id")
443 LOGGER.error("Result:\n%s", output.decode("utf-8"))
444 raise Exception("Failed to retrieve task id")
445 self._save_results(test_name, task_id)
447 def _append_summary(self, json_raw, test_name):
448 # pylint: disable=too-many-locals
449 """Update statistics summary info."""
452 overall_duration = 0.0
456 rally_report = json.loads(json_raw)
457 for task in rally_report.get('tasks'):
458 for subtask in task.get('subtasks'):
460 for workload in subtask.get('workloads'):
461 if workload.get('full_duration'):
462 overall_duration += workload.get('full_duration')
464 if workload.get('data'):
465 nb_tests += len(workload.get('data'))
467 for result in workload.get('data'):
468 if not result.get('error'):
474 failures.append(subtask['title'])
476 success.append(subtask['title'])
478 scenario_summary = {'test_name': test_name,
479 'overall_duration': overall_duration,
480 'nb_tests': nb_tests,
481 'nb_success': nb_success,
483 'failures': failures,
484 'task_status': self.task_succeed(json_raw)}
485 self.summary.append(scenario_summary)
487 def prepare_run(self, **kwargs):
488 """Prepare resources needed by test scenarios."""
490 LOGGER.debug('Validating run tests...')
491 for test in kwargs.get('tests', self.stests):
492 if test in self.stests:
493 self.tests.append(test)
495 raise Exception("Test name '%s' is invalid" % test)
497 if not os.path.exists(self.task_dir):
498 os.makedirs(self.task_dir)
500 task = os.path.join(self.rally_dir, 'task.yaml')
501 if not os.path.exists(task):
502 LOGGER.error("Task file '%s' does not exist.", task)
503 raise Exception("Task file '{}' does not exist.".
505 self.task_file = os.path.join(self.task_dir, 'task.yaml')
506 shutil.copyfile(task, self.task_file)
508 task_macro = os.path.join(self.rally_dir, 'macro')
509 if not os.path.exists(task_macro):
510 LOGGER.error("Task macro dir '%s' does not exist.", task_macro)
511 raise Exception("Task macro dir '{}' does not exist.".
513 macro_dir = os.path.join(self.task_dir, 'macro')
514 if os.path.exists(macro_dir):
515 shutil.rmtree(macro_dir)
516 shutil.copytree(task_macro, macro_dir)
518 self.update_keystone_default_role()
519 self.compute_cnt = len(self.cloud.list_hypervisors())
520 self.network_extensions = self.cloud.get_network_extensions()
521 self.flavor_alt = self.create_flavor_alt()
522 self.services = [service.name for service in
523 self.cloud.list_services()]
525 LOGGER.debug("flavor: %s", self.flavor_alt)
527 def prepare_task(self, test_name):
528 """Prepare resources for test run."""
529 file_name = self._prepare_test_list(test_name)
530 if self.file_is_empty(file_name):
531 LOGGER.info('No tests for scenario "%s"', test_name)
533 self.run_cmd = (["rally", "task", "start", "--abort-on-sla-failure",
534 "--task", self.task_file, "--task-args",
535 str(self.build_task_args(test_name))])
538 def run_tests(self, **kwargs):
540 optional = kwargs.get('optional', [])
541 for test in self.tests:
542 if test in self.services or test not in optional:
543 if self.prepare_task(test):
546 def _generate_report(self):
547 """Generate test execution summary report."""
554 res_table = prettytable.PrettyTable(
556 field_names=['Module', 'Duration', 'nb. Test Run', 'Success'])
557 res_table.align['Module'] = "l"
558 res_table.align['Duration'] = "r"
559 res_table.align['Success'] = "r"
561 # for each scenario we draw a row for the table
562 for item in self.summary:
563 if item['task_status'] is True:
565 total_duration += item['overall_duration']
566 total_nb_tests += item['nb_tests']
567 total_nb_success += item['nb_success']
569 success_avg = 100 * item['nb_success'] / item['nb_tests']
570 except ZeroDivisionError:
572 success_str = str("{:0.2f}".format(success_avg)) + '%'
573 duration_str = time.strftime("%H:%M:%S",
574 time.gmtime(item['overall_duration']))
575 res_table.add_row([item['test_name'], duration_str,
576 item['nb_tests'], success_str])
577 payload.append({'module': item['test_name'],
578 'details': {'duration': item['overall_duration'],
579 'nb tests': item['nb_tests'],
580 'success rate': success_str,
581 'success': item['success'],
582 'failures': item['failures']}})
584 total_duration_str = time.strftime("%H:%M:%S",
585 time.gmtime(total_duration))
587 self.result = 100 * total_nb_success / total_nb_tests
588 except ZeroDivisionError:
590 success_rate = "{:0.2f}".format(self.result)
591 success_rate_str = str(success_rate) + '%'
592 res_table.add_row(["", "", "", ""])
593 res_table.add_row(["TOTAL:", total_duration_str, total_nb_tests,
596 LOGGER.info("Rally Summary Report:\n\n%s\n", res_table.get_string())
597 LOGGER.info("Rally '%s' success_rate is %s%% in %s/%s modules",
598 self.case_name, success_rate, nb_modules,
600 payload.append({'summary': {'duration': total_duration,
601 'nb tests': total_nb_tests,
602 'nb success': success_rate}})
603 self.details = payload
606 def export_task(file_name, export_type="html"):
607 """Export all task results (e.g. html or xunit report)
610 subprocess.CalledProcessError: if Rally doesn't return 0
615 cmd = ["rally", "task", "export", "--type", export_type,
617 str(getattr(config.CONF, 'rally_deployment_name')),
619 LOGGER.debug('running command: %s', cmd)
620 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
621 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
624 def verify_report(file_name, uuid, export_type="html"):
625 """Generate the verifier report (e.g. html or xunit report)
628 subprocess.CalledProcessError: if Rally doesn't return 0
633 cmd = ["rally", "verify", "report", "--type", export_type,
634 "--uuid", uuid, "--to", file_name]
635 LOGGER.debug('running command: %s', cmd)
636 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
637 LOGGER.info("%s\n%s", " ".join(cmd), output.decode("utf-8"))
640 """Cleanup of OpenStack resources. Should be called on completion."""
641 self.clean_rally_conf()
642 self.clean_rally_logs()
644 self.orig_cloud.delete_flavor(self.flavor_alt.id)
645 super(RallyBase, self).clean()
647 def is_successful(self):
648 """The overall result of the test."""
649 for item in self.summary:
650 if item['task_status'] is False:
651 return testcase.TestCase.EX_TESTCASE_FAILED
653 return super(RallyBase, self).is_successful()
656 def update_rally_logs(res_dir, rally_conf='/etc/rally/rally.conf'):
657 """Print rally logs in res dir"""
658 if not os.path.exists(res_dir):
660 rconfig = configparser.RawConfigParser()
661 rconfig.read(rally_conf)
662 rconfig.set('DEFAULT', 'debug', True)
663 rconfig.set('DEFAULT', 'use_stderr', False)
664 rconfig.set('DEFAULT', 'log-file', 'rally.log')
665 rconfig.set('DEFAULT', 'log_dir', res_dir)
666 with open(rally_conf, 'w') as config_file:
667 rconfig.write(config_file)
670 def clean_rally_logs(rally_conf='/etc/rally/rally.conf'):
671 """Clean Rally config"""
672 rconfig = configparser.RawConfigParser()
673 rconfig.read(rally_conf)
674 if rconfig.has_option('DEFAULT', 'use_stderr'):
675 rconfig.remove_option('DEFAULT', 'use_stderr')
676 if rconfig.has_option('DEFAULT', 'debug'):
677 rconfig.remove_option('DEFAULT', 'debug')
678 if rconfig.has_option('DEFAULT', 'log-file'):
679 rconfig.remove_option('DEFAULT', 'log-file')
680 if rconfig.has_option('DEFAULT', 'log_dir'):
681 rconfig.remove_option('DEFAULT', 'log_dir')
682 with open(rally_conf, 'w') as config_file:
683 rconfig.write(config_file)
685 def run(self, **kwargs):
687 self.start_time = time.time()
689 assert super(RallyBase, self).run(
690 **kwargs) == testcase.TestCase.EX_OK
691 self.update_rally_logs(self.res_dir)
692 self.create_rally_deployment(environ=self.project.get_environ())
693 self.prepare_run(**kwargs)
694 self.run_tests(**kwargs)
695 self._generate_report()
697 "{}/{}.html".format(self.results_dir, self.case_name))
699 "{}/{}.xml".format(self.results_dir, self.case_name),
700 export_type="junit-xml")
701 res = testcase.TestCase.EX_OK
702 except Exception: # pylint: disable=broad-except
703 LOGGER.exception('Error with run:')
705 res = testcase.TestCase.EX_RUN_ERROR
706 self.stop_time = time.time()
710 class RallySanity(RallyBase):
711 """Rally sanity testcase implementation."""
713 def __init__(self, **kwargs):
714 """Initialize RallySanity object."""
715 if "case_name" not in kwargs:
716 kwargs["case_name"] = "rally_sanity"
717 super(RallySanity, self).__init__(**kwargs)
719 self.scenario_dir = os.path.join(self.rally_scenario_dir, 'sanity')
722 class RallyFull(RallyBase):
723 """Rally full testcase implementation."""
727 def __init__(self, **kwargs):
728 """Initialize RallyFull object."""
729 if "case_name" not in kwargs:
730 kwargs["case_name"] = "rally_full"
731 super(RallyFull, self).__init__(**kwargs)
733 self.scenario_dir = os.path.join(self.rally_scenario_dir, 'full')
736 class RallyJobs(RallyBase):
737 """Rally OpenStack CI testcase implementation."""
742 def __init__(self, **kwargs):
743 """Initialize RallyJobs object."""
744 if "case_name" not in kwargs:
745 kwargs["case_name"] = "rally_jobs"
746 super(RallyJobs, self).__init__(**kwargs)
747 self.task_file = os.path.join(self.rally_dir, 'rally_jobs.yaml')
748 self.task_yaml = None
750 def prepare_run(self, **kwargs):
751 """Create resources needed by test scenarios."""
752 super(RallyJobs, self).prepare_run(**kwargs)
753 with open(os.path.join(self.rally_dir,
754 'rally_jobs.yaml'), 'r') as task_file:
755 self.task_yaml = yaml.safe_load(task_file)
757 for task in self.task_yaml:
758 if task not in self.tests:
759 raise Exception("Test '%s' not in '%s'" %
762 def apply_blacklist(self, case_file_name, result_file_name):
763 # pylint: disable=too-many-branches
764 """Apply blacklist."""
765 LOGGER.debug("Applying blacklist...")
766 black_tests = list(set(self.excl_func() +
767 self.excl_scenario()))
769 LOGGER.debug("Blacklisted tests: %s", str(black_tests))
771 template = YAML(typ='jinja2')
772 with open(case_file_name, 'r') as fname:
773 cases = template.load(fname)
774 if cases.get("version", 1) == 1:
775 # scenarios in dictionary
776 for name in cases.keys():
777 if self.in_iterable_re(name, black_tests):
780 # workloads in subtasks
781 for sind, subtask in reversed(list(
782 enumerate(cases.get('subtasks', [])))):
783 for wind, workload in reversed(list(
784 enumerate(subtask.get('workloads', [])))):
785 scenario = workload.get('scenario', {})
786 for name in scenario.keys():
787 if self.in_iterable_re(name, black_tests):
788 cases['subtasks'][sind]['workloads'].pop(wind)
790 if 'workloads' in cases['subtasks'][sind]:
791 if not cases['subtasks'][sind]['workloads']:
792 cases['subtasks'].pop(sind)
793 # scenarios in subtasks
794 for sind, subtask in reversed(list(
795 enumerate(cases.get('subtasks', [])))):
796 scenario = subtask.get('scenario', {})
797 for name in scenario.keys():
798 if self.in_iterable_re(name, black_tests):
799 cases['subtasks'].pop(sind)
802 with open(result_file_name, 'w') as fname:
803 template.dump(cases, fname)
805 def build_task_args(self, test_name):
806 """Build arguments for the Rally task."""
809 task_args['floating_network'] = str(self.ext_net.name)
811 task_args['floating_network'] = ''
812 task_args['image_name'] = str(self.image.name)
813 task_args['flavor_name'] = str(self.flavor.name)
817 def _remove_plugins_extra():
818 inst_dir = getattr(config.CONF, 'dir_rally_inst')
820 shutil.rmtree(os.path.join(inst_dir, 'plugins'))
821 shutil.rmtree(os.path.join(inst_dir, 'extra'))
822 except Exception: # pylint: disable=broad-except
825 def prepare_task(self, test_name):
826 """Prepare resources for test run."""
827 self._remove_plugins_extra()
828 jobs_dir = os.path.join(
829 getattr(config.CONF, 'dir_rally_data'), test_name, 'rally-jobs')
830 inst_dir = getattr(config.CONF, 'dir_rally_inst')
831 shutil.copytree(os.path.join(jobs_dir, 'plugins'),
832 os.path.join(inst_dir, 'plugins'))
833 shutil.copytree(os.path.join(jobs_dir, 'extra'),
834 os.path.join(inst_dir, 'extra'))
836 task_name = self.task_yaml.get(test_name).get("task")
837 task = os.path.join(jobs_dir, task_name)
838 if not os.path.exists(task):
839 raise Exception("The scenario '%s' does not exist." % task)
840 LOGGER.debug('Scenario fetched from : %s', task)
842 if not os.path.exists(self.temp_dir):
843 os.makedirs(self.temp_dir)
844 task_file_name = os.path.join(self.temp_dir, task_name)
845 self.apply_blacklist(task, task_file_name)
846 self.run_cmd = (["rally", "task", "start", "--task", task_file_name,
847 "--task-args", str(self.build_task_args(test_name))])
851 self._remove_plugins_extra()
852 super(RallyJobs, self).clean()