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 TESTS = ['authenticate', 'glance', 'cinder', 'gnocchi', 'heat',
44 'keystone', 'neutron', 'nova', 'quotas']
46 RALLY_CONF_PATH = "/etc/rally/rally.conf"
47 RALLY_AARCH64_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')
70 def __init__(self, **kwargs):
71 """Initialize RallyBase object."""
72 super(RallyBase, self).__init__(**kwargs)
73 assert self.orig_cloud
75 if self.orig_cloud.get_role("admin"):
77 elif self.orig_cloud.get_role("Admin"):
80 raise Exception("Cannot detect neither admin nor Admin")
81 self.orig_cloud.grant_role(
82 role_name, user=self.project.user.id,
83 project=self.project.project.id,
84 domain=self.project.domain.id)
85 self.results_dir = os.path.join(
86 getattr(config.CONF, 'dir_results'), self.case_name)
90 self.scenario_dir = ''
92 self.start_time = None
96 self.flavor_alt = None
99 self.network_extensions = []
102 def build_task_args(self, test_name):
103 """Build arguments for the Rally task."""
104 task_args = {'service_list': [test_name]}
105 task_args['image_name'] = str(self.image.name)
106 task_args['flavor_name'] = str(self.flavor.name)
107 task_args['flavor_alt_name'] = str(self.flavor_alt.name)
108 task_args['glance_image_location'] = str(self.filename)
109 task_args['glance_image_format'] = str(self.image_format)
110 task_args['tmpl_dir'] = str(self.TEMPLATE_DIR)
111 task_args['sup_dir'] = str(self.SUPPORT_DIR)
112 task_args['users_amount'] = self.USERS_AMOUNT
113 task_args['tenants_amount'] = self.TENANTS_AMOUNT
114 task_args['use_existing_users'] = False
115 task_args['iterations'] = self.ITERATIONS_AMOUNT
116 task_args['concurrency'] = self.CONCURRENCY
117 task_args['smoke'] = self.smoke
118 task_args['volume_version'] = self.VOLUME_VERSION
119 task_args['volume_service_type'] = self.VOLUME_SERVICE_TYPE
120 task_args['block_migration'] = env.get("BLOCK_MIGRATION").lower()
123 task_args['floating_network'] = str(self.ext_net.name)
125 task_args['floating_network'] = ''
128 task_args['netid'] = str(self.network.id)
130 task_args['netid'] = ''
134 def _prepare_test_list(self, test_name):
135 """Build the list of test cases to be executed."""
136 test_yaml_file_name = 'opnfv-{}.yaml'.format(test_name)
137 scenario_file_name = os.path.join(self.RALLY_SCENARIO_DIR,
140 if not os.path.exists(scenario_file_name):
141 scenario_file_name = os.path.join(self.scenario_dir,
144 if not os.path.exists(scenario_file_name):
145 raise Exception("The scenario '%s' does not exist."
146 % scenario_file_name)
148 LOGGER.debug('Scenario fetched from : %s', scenario_file_name)
149 test_file_name = os.path.join(self.TEMP_DIR, test_yaml_file_name)
151 if not os.path.exists(self.TEMP_DIR):
152 os.makedirs(self.TEMP_DIR)
154 self.apply_blacklist(scenario_file_name, test_file_name)
155 return test_file_name
158 def get_verifier_deployment_id():
160 Returns deployment id for active Rally deployment
162 cmd = ("rally deployment list | awk '/" +
163 getattr(config.CONF, 'rally_deployment_name') +
165 proc = subprocess.Popen(cmd, shell=True,
166 stdout=subprocess.PIPE,
167 stderr=subprocess.STDOUT)
168 deployment_uuid = proc.stdout.readline().rstrip()
169 return deployment_uuid.decode()
172 def create_rally_deployment(environ=None):
173 """Create new rally deployment"""
174 # set the architecture to default
175 pod_arch = env.get("POD_ARCH")
176 arch_filter = ['aarch64']
178 if pod_arch and pod_arch in arch_filter:
179 LOGGER.info("Apply aarch64 specific to rally config...")
180 with open(RallyBase.RALLY_AARCH64_PATCH_PATH, "r") as pfile:
181 rally_patch_conf = pfile.read()
183 for line in fileinput.input(RallyBase.RALLY_CONF_PATH):
185 if "cirros|testvm" in line:
186 print(rally_patch_conf)
188 LOGGER.info("Creating Rally environment...")
190 cmd = ['rally', 'deployment', 'destroy',
192 str(getattr(config.CONF, 'rally_deployment_name'))]
193 output = subprocess.check_output(cmd)
194 LOGGER.info("%s\n%s", " ".join(cmd), output)
195 except subprocess.CalledProcessError:
198 cmd = ['rally', 'deployment', 'create', '--fromenv',
199 '--name', str(getattr(config.CONF, 'rally_deployment_name'))]
200 output = subprocess.check_output(cmd, env=environ)
201 LOGGER.info("%s\n%s", " ".join(cmd), output)
203 cmd = ['rally', 'deployment', 'check']
204 output = subprocess.check_output(cmd)
205 LOGGER.info("%s\n%s", " ".join(cmd), output)
206 return RallyBase.get_verifier_deployment_id()
209 def update_keystone_default_role(rally_conf='/etc/rally/rally.conf'):
210 """Set keystone_default_role in rally.conf"""
211 if env.get("NEW_USER_ROLE").lower() != "member":
212 rconfig = configparser.RawConfigParser()
213 rconfig.read(rally_conf)
214 if not rconfig.has_section('openstack'):
215 rconfig.add_section('openstack')
217 'openstack', 'keystone_default_role', env.get("NEW_USER_ROLE"))
218 with open(rally_conf, 'w') as config_file:
219 rconfig.write(config_file)
222 def clean_rally_conf(rally_conf='/etc/rally/rally.conf'):
223 """Clean Rally config"""
224 if env.get("NEW_USER_ROLE").lower() != "member":
225 rconfig = configparser.RawConfigParser()
226 rconfig.read(rally_conf)
227 if rconfig.has_option('openstack', 'keystone_default_role'):
228 rconfig.remove_option('openstack', 'keystone_default_role')
229 with open(rally_conf, 'w') as config_file:
230 rconfig.write(config_file)
233 def get_task_id(cmd_raw):
235 Get task id from command rally result.
238 :return: task_id as string
240 taskid_re = re.compile('^Task +(.*): started$')
241 for line in cmd_raw.splitlines(True):
243 match = taskid_re.match(line.decode())
245 return match.group(1)
249 def task_succeed(json_raw):
251 Parse JSON from rally JSON results.
256 rally_report = json.loads(json_raw)
257 tasks = rally_report.get('tasks')
260 if task.get('status') != 'finished' or \
261 task.get('pass_sla') is not True:
267 def _migration_supported(self):
268 """Determine if migration is supported."""
269 if self.compute_cnt > 1:
273 def _network_trunk_supported(self):
274 """Determine if network trunk service is available"""
275 if 'trunk' in self.network_extensions:
281 """Exclude scenario."""
284 with open(RallyBase.BLACKLIST_FILE, 'r') as black_list_file:
285 black_list_yaml = yaml.safe_load(black_list_file)
287 deploy_scenario = env.get('DEPLOY_SCENARIO')
288 if (bool(deploy_scenario) and
289 'scenario' in black_list_yaml.keys()):
290 for item in black_list_yaml['scenario']:
291 scenarios = item['scenarios']
292 in_it = RallyBase.in_iterable_re
293 if in_it(deploy_scenario, scenarios):
294 tests = item['tests']
295 black_tests.extend(tests)
296 except Exception: # pylint: disable=broad-except
297 LOGGER.debug("Scenario exclusion not applied.")
302 def in_iterable_re(needle, haystack):
304 Check if given needle is in the iterable haystack, using regex.
306 :param needle: string to be matched
307 :param haystack: iterable of strings (optionally regex patterns)
308 :return: True if needle is eqial to any of the elements in haystack,
309 or if a nonempty regex pattern in haystack is found in needle.
311 # match without regex
312 if needle in haystack:
315 for pattern in haystack:
316 # match if regex pattern is set and found in the needle
317 if pattern and re.search(pattern, needle) is not None:
323 """Exclude functionalities."""
328 with open(RallyBase.BLACKLIST_FILE, 'r') as black_list_file:
329 black_list_yaml = yaml.safe_load(black_list_file)
331 if env.get('BLOCK_MIGRATION').lower() == 'true':
332 func_list.append("block_migration")
333 if not self._migration_supported():
334 func_list.append("no_migration")
335 if not self._network_trunk_supported():
336 func_list.append("no_net_trunk_service")
338 if 'functionality' in black_list_yaml.keys():
339 for item in black_list_yaml['functionality']:
340 functions = item['functions']
341 for func in func_list:
342 if func in functions:
343 tests = item['tests']
344 black_tests.extend(tests)
345 except Exception: # pylint: disable=broad-except
346 LOGGER.debug("Functionality exclusion not applied.")
350 def apply_blacklist(self, case_file_name, result_file_name):
351 """Apply blacklist."""
352 LOGGER.debug("Applying blacklist...")
353 cases_file = open(case_file_name, 'r')
354 result_file = open(result_file_name, 'w')
356 black_tests = list(set(self.excl_func() +
357 self.excl_scenario()))
360 LOGGER.debug("Blacklisted tests: %s", str(black_tests))
363 for cases_line in cases_file:
365 for black_tests_line in black_tests:
366 if re.search(black_tests_line,
367 cases_line.strip().rstrip(':')):
371 result_file.write(str(cases_line))
373 if cases_line.isspace():
380 def file_is_empty(file_name):
381 """Determine is a file is empty."""
383 if os.stat(file_name).st_size > 0:
385 except Exception: # pylint: disable=broad-except
390 def _save_results(self, test_name, task_id):
391 """ Generate and save task execution results"""
392 # check for result directory and create it otherwise
393 if not os.path.exists(self.results_dir):
394 LOGGER.debug('%s does not exist, we create it.',
396 os.makedirs(self.results_dir)
398 # put detailed result to log
399 cmd = (["rally", "task", "detailed", "--uuid", task_id])
400 LOGGER.debug('running command: %s', cmd)
401 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
402 LOGGER.info("%s\n%s", " ".join(cmd), output)
404 # save report as JSON
405 report_json_name = '{}.json'.format(test_name)
406 report_json_dir = os.path.join(self.results_dir, report_json_name)
407 cmd = (["rally", "task", "report", "--json", "--uuid", task_id,
408 "--out", report_json_dir])
409 LOGGER.debug('running command: %s', cmd)
410 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
411 LOGGER.info("%s\n%s", " ".join(cmd), output)
413 json_results = open(report_json_dir).read()
414 self._append_summary(json_results, test_name)
416 # parse JSON operation result
417 if self.task_succeed(json_results):
418 LOGGER.info('Test scenario: "%s" OK.', test_name)
420 LOGGER.info('Test scenario: "%s" Failed.', test_name)
422 def run_task(self, test_name):
424 LOGGER.info('Starting test scenario "%s" ...', test_name)
425 LOGGER.debug('running command: %s', self.run_cmd)
426 proc = subprocess.Popen(self.run_cmd, stdout=subprocess.PIPE,
427 stderr=subprocess.STDOUT)
428 output = proc.communicate()[0]
430 task_id = self.get_task_id(output)
431 LOGGER.debug('task_id : %s', task_id)
433 LOGGER.error("Failed to retrieve task_id")
434 LOGGER.error("Result:\n%s", output)
435 raise Exception("Failed to retrieve task id")
436 self._save_results(test_name, task_id)
438 def _append_summary(self, json_raw, test_name):
439 """Update statistics summary info."""
442 overall_duration = 0.0
446 rally_report = json.loads(json_raw)
447 for task in rally_report.get('tasks'):
448 for subtask in task.get('subtasks'):
450 for workload in subtask.get('workloads'):
451 if workload.get('full_duration'):
452 overall_duration += workload.get('full_duration')
454 if workload.get('data'):
455 nb_tests += len(workload.get('data'))
457 for result in workload.get('data'):
458 if not result.get('error'):
464 failures.append(subtask['title'])
466 success.append(subtask['title'])
468 scenario_summary = {'test_name': test_name,
469 'overall_duration': overall_duration,
470 'nb_tests': nb_tests,
471 'nb_success': nb_success,
473 'failures': failures,
474 'task_status': self.task_succeed(json_raw)}
475 self.summary.append(scenario_summary)
477 def prepare_run(self, **kwargs):
478 """Prepare resources needed by test scenarios."""
480 LOGGER.debug('Validating run tests...')
481 for test in kwargs.get('tests', self.TESTS):
482 if test in self.TESTS:
483 self.tests.append(test)
485 raise Exception("Test name '%s' is invalid" % test)
487 if not os.path.exists(self.TASK_DIR):
488 os.makedirs(self.TASK_DIR)
490 task = os.path.join(self.RALLY_DIR, 'task.yaml')
491 if not os.path.exists(task):
492 LOGGER.error("Task file '%s' does not exist.", task)
493 raise Exception("Task file '{}' does not exist.".
495 self.task_file = os.path.join(self.TASK_DIR, 'task.yaml')
496 shutil.copyfile(task, self.task_file)
498 task_macro = os.path.join(self.RALLY_DIR, 'macro')
499 if not os.path.exists(task_macro):
500 LOGGER.error("Task macro dir '%s' does not exist.", task_macro)
501 raise Exception("Task macro dir '{}' does not exist.".
503 macro_dir = os.path.join(self.TASK_DIR, 'macro')
504 if os.path.exists(macro_dir):
505 shutil.rmtree(macro_dir)
506 shutil.copytree(task_macro, macro_dir)
508 self.update_keystone_default_role()
509 self.compute_cnt = len(self.cloud.list_hypervisors())
510 self.network_extensions = self.cloud.get_network_extensions()
511 self.flavor_alt = self.create_flavor_alt()
512 self.services = [service.name for service in
513 self.cloud.list_services()]
515 LOGGER.debug("flavor: %s", self.flavor_alt)
517 def prepare_task(self, test_name):
518 """Prepare resources for test run."""
519 file_name = self._prepare_test_list(test_name)
520 if self.file_is_empty(file_name):
521 LOGGER.info('No tests for scenario "%s"', test_name)
523 self.run_cmd = (["rally", "task", "start", "--abort-on-sla-failure",
524 "--task", self.task_file, "--task-args",
525 str(self.build_task_args(test_name))])
528 def run_tests(self, **kwargs):
530 optional = kwargs.get('optional', [])
531 for test in self.tests:
532 if test in self.services or test not in optional:
533 if self.prepare_task(test):
536 def _generate_report(self):
537 """Generate test execution summary report."""
544 res_table = prettytable.PrettyTable(
546 field_names=['Module', 'Duration', 'nb. Test Run', 'Success'])
547 res_table.align['Module'] = "l"
548 res_table.align['Duration'] = "r"
549 res_table.align['Success'] = "r"
551 # for each scenario we draw a row for the table
552 for item in self.summary:
553 if item['task_status'] is True:
555 total_duration += item['overall_duration']
556 total_nb_tests += item['nb_tests']
557 total_nb_success += item['nb_success']
559 success_avg = 100 * item['nb_success'] / item['nb_tests']
560 except ZeroDivisionError:
562 success_str = str("{:0.2f}".format(success_avg)) + '%'
563 duration_str = time.strftime("%H:%M:%S",
564 time.gmtime(item['overall_duration']))
565 res_table.add_row([item['test_name'], duration_str,
566 item['nb_tests'], success_str])
567 payload.append({'module': item['test_name'],
568 'details': {'duration': item['overall_duration'],
569 'nb tests': item['nb_tests'],
570 'success rate': success_str,
571 'success': item['success'],
572 'failures': item['failures']}})
574 total_duration_str = time.strftime("%H:%M:%S",
575 time.gmtime(total_duration))
577 self.result = 100 * total_nb_success / total_nb_tests
578 except ZeroDivisionError:
580 success_rate = "{:0.2f}".format(self.result)
581 success_rate_str = str(success_rate) + '%'
582 res_table.add_row(["", "", "", ""])
583 res_table.add_row(["TOTAL:", total_duration_str, total_nb_tests,
586 LOGGER.info("Rally Summary Report:\n\n%s\n", res_table.get_string())
587 LOGGER.info("Rally '%s' success_rate is %s%% in %s/%s modules",
588 self.case_name, success_rate, nb_modules,
590 payload.append({'summary': {'duration': total_duration,
591 'nb tests': total_nb_tests,
592 'nb success': success_rate}})
593 self.details = payload
596 def export_task(file_name, export_type="html"):
597 """Export all task results (e.g. html or xunit report)
600 subprocess.CalledProcessError: if Rally doesn't return 0
605 cmd = ["rally", "task", "export", "--type", export_type,
607 str(getattr(config.CONF, 'rally_deployment_name')),
609 LOGGER.debug('running command: %s', cmd)
610 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
611 LOGGER.info("%s\n%s", " ".join(cmd), output)
614 def verify_report(file_name, uuid, export_type="html"):
615 """Generate the verifier report (e.g. html or xunit report)
618 subprocess.CalledProcessError: if Rally doesn't return 0
623 cmd = ["rally", "verify", "report", "--type", export_type,
624 "--uuid", uuid, "--to", file_name]
625 LOGGER.debug('running command: %s', cmd)
626 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
627 LOGGER.info("%s\n%s", " ".join(cmd), output)
630 """Cleanup of OpenStack resources. Should be called on completion."""
631 self.clean_rally_conf()
633 self.orig_cloud.delete_flavor(self.flavor_alt.id)
634 super(RallyBase, self).clean()
636 def is_successful(self):
637 """The overall result of the test."""
638 for item in self.summary:
639 if item['task_status'] is False:
640 return testcase.TestCase.EX_TESTCASE_FAILED
642 return super(RallyBase, self).is_successful()
644 def run(self, **kwargs):
646 self.start_time = time.time()
648 assert super(RallyBase, self).run(
649 **kwargs) == testcase.TestCase.EX_OK
650 self.create_rally_deployment(environ=self.project.get_environ())
651 self.prepare_run(**kwargs)
652 self.run_tests(**kwargs)
653 self._generate_report()
655 "{}/{}.html".format(self.results_dir, self.case_name))
657 "{}/{}.xml".format(self.results_dir, self.case_name),
658 export_type="junit-xml")
659 res = testcase.TestCase.EX_OK
660 except Exception: # pylint: disable=broad-except
661 LOGGER.exception('Error with run:')
663 res = testcase.TestCase.EX_RUN_ERROR
664 self.stop_time = time.time()
668 class RallySanity(RallyBase):
669 """Rally sanity testcase implementation."""
671 def __init__(self, **kwargs):
672 """Initialize RallySanity object."""
673 if "case_name" not in kwargs:
674 kwargs["case_name"] = "rally_sanity"
675 super(RallySanity, self).__init__(**kwargs)
677 self.scenario_dir = os.path.join(self.RALLY_SCENARIO_DIR, 'sanity')
680 class RallyFull(RallyBase):
681 """Rally full testcase implementation."""
683 def __init__(self, **kwargs):
684 """Initialize RallyFull object."""
685 if "case_name" not in kwargs:
686 kwargs["case_name"] = "rally_full"
687 super(RallyFull, self).__init__(**kwargs)
689 self.scenario_dir = os.path.join(self.RALLY_SCENARIO_DIR, 'full')
692 class RallyJobs(RallyBase):
693 """Rally OpenStack CI testcase implementation."""
697 def __init__(self, **kwargs):
698 """Initialize RallyJobs object."""
699 if "case_name" not in kwargs:
700 kwargs["case_name"] = "rally_jobs"
701 super(RallyJobs, self).__init__(**kwargs)
702 self.task_file = os.path.join(self.RALLY_DIR, 'rally_jobs.yaml')
703 self.task_yaml = None
705 def prepare_run(self, **kwargs):
706 """Create resources needed by test scenarios."""
707 super(RallyJobs, self).prepare_run(**kwargs)
708 with open(os.path.join(self.RALLY_DIR,
709 'rally_jobs.yaml'), 'r') as task_file:
710 self.task_yaml = yaml.safe_load(task_file)
712 for task in self.task_yaml:
713 if task not in self.tests:
714 raise Exception("Test '%s' not in '%s'" %
717 def apply_blacklist(self, case_file_name, result_file_name):
718 # pylint: disable=too-many-branches
719 """Apply blacklist."""
720 LOGGER.debug("Applying blacklist...")
721 black_tests = list(set(self.excl_func() +
722 self.excl_scenario()))
724 LOGGER.debug("Blacklisted tests: %s", str(black_tests))
726 template = YAML(typ='jinja2')
727 with open(case_file_name, 'r') as fname:
728 cases = template.load(fname)
729 if cases.get("version", 1) == 1:
730 # scenarios in dictionary
731 for name in cases.keys():
732 if self.in_iterable_re(name, black_tests):
735 # workloads in subtasks
736 for sind, subtask in enumerate(cases.get('subtasks', [])):
738 for wind, workload in enumerate(subtask.get('workloads', [])):
739 scenario = workload.get('scenario', {})
740 for name in scenario.keys():
741 if self.in_iterable_re(name, black_tests):
744 for wind in reversed(idx):
745 cases['subtasks'][sind]['workloads'].pop(wind)
746 # scenarios in subtasks
748 for sind, subtask in enumerate(cases.get('subtasks', [])):
749 scenario = subtask.get('scenario', {})
750 for name in scenario.keys():
751 if self.in_iterable_re(name, black_tests):
754 for sind in reversed(idx):
755 cases['subtasks'].pop(sind)
757 with open(result_file_name, 'w') as fname:
758 template.dump(cases, fname)
760 def build_task_args(self, test_name):
761 """Build arguments for the Rally task."""
764 task_args['floating_network'] = str(self.ext_net.name)
766 task_args['floating_network'] = ''
770 def _remove_plugins_extra():
771 inst_dir = getattr(config.CONF, 'dir_rally_inst')
773 shutil.rmtree(os.path.join(inst_dir, 'plugins'))
774 shutil.rmtree(os.path.join(inst_dir, 'extra'))
775 except Exception: # pylint: disable=broad-except
778 def prepare_task(self, test_name):
779 """Prepare resources for test run."""
780 self._remove_plugins_extra()
781 jobs_dir = os.path.join(
782 getattr(config.CONF, 'dir_rally_data'), test_name, 'rally-jobs')
783 inst_dir = getattr(config.CONF, 'dir_rally_inst')
784 shutil.copytree(os.path.join(jobs_dir, 'plugins'),
785 os.path.join(inst_dir, 'plugins'))
786 shutil.copytree(os.path.join(jobs_dir, 'extra'),
787 os.path.join(inst_dir, 'extra'))
789 task_name = self.task_yaml.get(test_name).get("task")
790 task = os.path.join(jobs_dir, task_name)
791 if not os.path.exists(task):
792 raise Exception("The scenario '%s' does not exist." % task)
793 LOGGER.debug('Scenario fetched from : %s', task)
795 if not os.path.exists(self.TEMP_DIR):
796 os.makedirs(self.TEMP_DIR)
797 task_file_name = os.path.join(self.TEMP_DIR, task_name)
798 self.apply_blacklist(task, task_file_name)
799 self.run_cmd = (["rally", "task", "start", "--task", task_file_name,
800 "--task-args", str(self.build_task_args(test_name))])
804 self._remove_plugins_extra()
805 super(RallyJobs, self).clean()