def _validate_scenario_args(scenario, name, config): if scenario.is_classbased: # We need initialize scenario class to access instancemethods scenario = scenario().run args, _varargs, varkwargs, defaults = inspect.getargspec(scenario) hint_msg = (" Use `rally plugin show --name %s` to display " "scenario description." % name) # scenario always accepts an instance of scenario cls as a first arg missed_args = args[1:] if defaults: # do not require args with default values missed_args = missed_args[:-len(defaults)] if "args" in config: missed_args = set(missed_args) - set(config["args"]) if missed_args: msg = ("Argument(s) '%(args)s' should be specified in task config." "%(hint)s" % { "args": "', '".join(missed_args), "hint": hint_msg }) raise exceptions.InvalidArgumentsException(msg) if varkwargs is None and "args" in config: redundant_args = set(config["args"]) - set(args[1:]) if redundant_args: msg = ("Unexpected argument(s) found ['%(args)s'].%(hint)s" % { "args": "', '".join(redundant_args), "hint": hint_msg }) raise exceptions.InvalidArgumentsException(msg)
def start(self, set_name="", deployment=None, regex=None, tests_file=None, tempest_config=None, do_use=True, system_wide_install=False): """Start set of tests. :param set_name: Name of tempest test set :param deployment: UUID or name of a deployment :param regex: Regular expression of test :param tests_file: Path to a file with a list of Tempest tests :param tempest_config: User specified Tempest config file location :param do_use: Use new task as default for future operations :param system_wide_install: Use virtualenv else run tests in local environment """ if regex and set_name: raise exceptions.InvalidArgumentsException( "Arguments set_name and regex are not compatible") if tests_file and set_name: raise exceptions.InvalidArgumentsException( "Arguments tests_file and set_name are not compatible") if tests_file and regex: raise exceptions.InvalidArgumentsException( "Arguments tests_file and regex are not compatible") if not (regex or set_name or tests_file): set_name = "full" if set_name and set_name not in (list(consts.TempestTestsSets) + list(consts.TempestTestsAPI)): print("Sorry, but there are no desired Tempest test set. Please, " "choose from: %s" % ", ".join( list(consts.TempestTestsSets) + list(consts.TempestTestsAPI))) return (1) if tests_file and not os.path.exists(tests_file): print("File '%s' not found" % tests_file) return (1) verification = api.Verification.verify(deployment, set_name, regex, tests_file, tempest_config, system_wide_install) if do_use: self.use(verification["uuid"])
def delete_metadata(self, volume, keys, deletes=10, delete_size=3): """Delete volume metadata keys. Note that ``len(keys)`` must be greater than or equal to ``deletes * delete_size``. :param volume: The volume to delete metadata from :param deletes: how many operations to perform :param delete_size: number of metadata keys to delete in each operation :param keys: a list of keys to choose deletion candidates from """ if len(keys) < deletes * delete_size: raise exceptions.InvalidArgumentsException( "Not enough metadata keys to delete: " "%(num_keys)s keys, but asked to delete %(num_deletes)s" % { "num_keys": len(keys), "num_deletes": deletes * delete_size }) # make a shallow copy of the list of keys so that, when we pop # from it later, we don't modify the original list. keys = list(keys) random.shuffle(keys) action_name = ("cinder_v%s.delete_%s_metadatas_%s_times" % (self.version, delete_size, deletes)) with atomic.ActionTimer(self, action_name): for i in range(deletes): to_del = keys[i * delete_size:(i + 1) * delete_size] self._get_client().volumes.delete_metadata(volume, to_del)
def start(self, set_name="", deployment=None, regex=None, tempest_config=None, do_use=False): """Start set of tests. :param set_name: Name of tempest test set :param deployment: UUID or name of a deployment :param regex: Regular expression of test :param tempest_config: User specified Tempest config file location """ if regex and set_name: raise exceptions.InvalidArgumentsException("set_name and regex " "are not compatible") if not (regex or set_name): set_name = "full" if set_name and set_name not in (list(consts.TempestTestsSets) + list(consts.TempestTestsAPI)): print("Sorry, but there are no desired tempest test set. Please " "choose from: %s" % ", ".join(list(consts.TempestTestsSets) + list(consts.TempestTestsAPI))) return (1) verification = api.Verification.verify(deployment, set_name, regex, tempest_config) if do_use: self.use(verification["uuid"])
def modify_volume_metadata(self, sets=10, set_size=3, deletes=5, delete_size=3): """Modify a volume's metadata. This requires a volume to be created with the volumes context. Additionally, ``sets * set_size`` must be greater than or equal to ``deletes * delete_size``. :param sets: how many set_metadata operations to perform :param set_size: number of metadata keys to set in each set_metadata operation :param deletes: how many delete_metadata operations to perform :param delete_size: number of metadata keys to delete in each delete_metadata operation """ if sets * set_size < deletes * delete_size: raise exceptions.InvalidArgumentsException( "Not enough metadata keys will be created: " "Setting %(num_keys)s keys, but deleting %(num_deletes)s" % { "num_keys": sets * set_size, "num_deletes": deletes * delete_size }) volume = random.choice(self.context["tenant"]["volumes"]) keys = self._set_metadata(volume["id"], sets, set_size) self._delete_metadata(volume["id"], keys, deletes, delete_size)
def get_interpreter(python_version): """Discovers PATH to find proper python interpreter :param python_version: (major, minor) version numbers :type python_version: tuple """ if not isinstance(python_version, tuple): msg = (_LE("given format of python version `%s` is invalid") % python_version) raise exceptions.InvalidArgumentsException(msg) interpreter_name = "python%s.%s" % python_version interpreter = spawn.find_executable(interpreter_name) if interpreter: return interpreter else: interpreters = filter(os.path.isfile, [ os.path.join(p, interpreter_name) for p in os.environ.get("PATH", "").split(":") ]) cmd = "%s -c 'import sys; print(sys.version_info[:2])'" for interpreter in interpreters: try: out = sp_check_output(cmd % interpreter, shell=True) except subprocess.CalledProcessError: pass else: if out.strip() == str(python_version): return interpreter
def get_global(global_key, do_raise=False): if global_key not in os.environ: fileutils.load_env_file(os.path.expanduser(PATH_GLOBALS)) value = os.environ.get(global_key) if not value and do_raise: raise exceptions.InvalidArgumentsException("%s env is missing" % global_key) return value
def mean(values): """Find the simple average of a list of values. :parameter values: non-empty list of numbers :returns: float value """ if not values: raise exceptions.InvalidArgumentsException( "the list should be non-empty") return math.fsum(values) / len(values)
def wrap(client, owner): """Returns cinderclient wrapper based on cinder client version.""" version = client.choose_version() if version == "1": return CinderV1Wrapper(client(), owner) elif version == "2": return CinderV2Wrapper(client(), owner) else: msg = "This version of API %s could not be identified." % version LOG.warning(msg) raise exceptions.InvalidArgumentsException(msg)
def wrap(client, owner): """Returns glanceclient wrapper based on glance client version.""" version = client.choose_version() if version == "1": return GlanceV1Wrapper(client(), owner) elif version == "2": return GlanceV2Wrapper(client(), owner) else: msg = "Version %s of the glance API could not be identified." % version LOG.warning(msg) raise exceptions.InvalidArgumentsException(msg)
def wrap(client, owner): """Returns cinderclient wrapper based on cinder client version.""" LOG.warning("Method wrap from %s and whole Cinder wrappers are " "deprecated since Rally 0.10.0 and will be removed soon. Use " "rally.plugins.openstack.services.storage.block.BlockStorage " "instead." % __file__) version = client.choose_version() if version == "1": return CinderV1Wrapper(client(), owner) elif version == "2": return CinderV2Wrapper(client(), owner) else: msg = "This version of API %s could not be identified." % version LOG.warning(msg) raise exceptions.InvalidArgumentsException(msg)
def _delete_metadata(self, share, keys, delete_size=3): """Deletes share metadata. :param share: The share to delete metadata from. :param delete_size: number of metadata keys to delete using one single call. :param keys: a list or tuple of keys to choose deletion candidates from :raises exceptions.InvalidArgumentsException: if invalid arguments were provided. """ if not (isinstance(keys, list) and keys): raise exceptions.InvalidArgumentsException( "Param 'keys' should be non-empty 'list'. keys = '%s'" % keys) for i in range(0, len(keys), delete_size): self.clients("manila").shares.delete_metadata( share["id"], keys[i:i + delete_size])
def wrap(client, owner): """Returns glanceclient wrapper based on glance client version.""" LOG.warning("Method wrap from %s and whole Glance wrappers are " "deprecated since Rally 0.10.0 and will be removed soon. Use " "rally.plugins.openstack.services.image.image.Image " "instead." % __file__) version = client.choose_version() if version == "1": return GlanceV1Wrapper(client(), owner) elif version == "2": return GlanceV2Wrapper(client(), owner) else: msg = "Version %s of the glance API could not be identified." % version LOG.warning(msg) raise exceptions.InvalidArgumentsException(msg)
def _make_timestamp_query(self, start_time=None, end_time=None): """Create ceilometer query for timestamp range. :param start_time: start datetime in isoformat :param end_time: end datetime in isoformat :returns: query with timestamp range """ query = [] if end_time and start_time and end_time < start_time: msg = "End time should be great or equal than start time" raise exceptions.InvalidArgumentsException(msg) if start_time: query.append(self._make_query_item("timestamp", ">=", start_time)) if end_time: query.append(self._make_query_item("timestamp", "<=", end_time)) return query
def _set_metadata(self, share, sets=1, set_size=1, key_min_length=1, key_max_length=256, value_min_length=1, value_max_length=1024): """Sets share metadata. :param share: the share to set metadata on :param sets: how many operations to perform :param set_size: number of metadata keys to set in each operation :param key_min_length: minimal size of metadata key to set :param key_max_length: maximum size of metadata key to set :param value_min_length: minimal size of metadata value to set :param value_max_length: maximum size of metadata value to set :returns: A list of keys that were set :raises exceptions.InvalidArgumentsException: if invalid arguments were provided. """ if not (key_min_length <= key_max_length and value_min_length <= value_max_length): raise exceptions.InvalidArgumentsException( "Min length for keys and values of metadata can not be bigger " "than maximum length.") keys = [] for i in range(sets): metadata = {} for j in range(set_size): if key_min_length == key_max_length: key_length = key_min_length else: key_length = random.choice( range(key_min_length, key_max_length)) if value_min_length == value_max_length: value_length = value_min_length else: value_length = random.choice( range(value_min_length, value_max_length)) key = self._generate_random_part(length=key_length) keys.append(key) metadata[key] = self._generate_random_part(length=value_length) self.clients("manila").shares.set_metadata(share["id"], metadata) return keys
def sleep_between(self, min_sleep, max_sleep): """Performs a time.sleep() call for a random amount of seconds. The exact time is chosen uniformly randomly from the interval [min_sleep; max_sleep). The method also updates the idle_duration variable to take into account the overall time spent on sleeping. :param min_sleep: Minimum sleep time in seconds (non-negative) :param max_sleep: Maximum sleep time in seconds (non-negative) """ if not 0 <= min_sleep <= max_sleep: raise exceptions.InvalidArgumentsException( message="0 <= min_sleep <= max_sleep") sleep_time = random.uniform(min_sleep, max_sleep) time.sleep(sleep_time) self._idle_duration += sleep_time
def sleep_between(self, min_sleep, max_sleep, atomic_delay=0.1): """Call an interruptable_sleep() for a random amount of seconds. The exact time is chosen uniformly randomly from the interval [min_sleep; max_sleep). The method also updates the idle_duration variable to take into account the overall time spent on sleeping. :param min_sleep: Minimum sleep time in seconds (non-negative) :param max_sleep: Maximum sleep time in seconds (non-negative) :param atomic_delay: parameter with which time.sleep would be called int(sleep_time / atomic_delay) times. """ if not 0 <= min_sleep <= max_sleep: raise exceptions.InvalidArgumentsException( "0 <= min_sleep <= max_sleep") sleep_time = random.uniform(min_sleep, max_sleep) utils.interruptable_sleep(sleep_time, atomic_delay) self._idle_duration += sleep_time
def results(self, uuids=None, output_file=None, output_html=False, output_json=False, output_csv=False): """Display results of verifications. :param verification: UUID of a verification :param output_file: Path to a file to save results :param output_json: Display results in JSON format (Default) :param output_html: Display results in HTML format :param output_csv: Display results in CSV format """ if not uuids: uuid = envutils.get_global(envutils.ENV_VERIFICATION) if not uuid: raise exceptions.InvalidArgumentsException( "Verification UUID is missing") uuids = [uuid] data = [] for uuid in uuids: try: verification = api.Verification.get(uuid) except exceptions.NotFoundException as e: print(six.text_type(e)) return 1 data.append(verification) if output_json + output_html + output_csv > 1: print(_("Please specify only one format option from %s.") % "--json, --html, --csv") return 1 verifications = {} for ver in data: uuid = ver.db_object["uuid"] res = ver.get_results() or {} tests = {} for test in list(res.get("test_cases", {}).values()): name = test["name"] if name in tests: mesg = ("Duplicated test in verification " "%(uuid)s: %(test)s" % {"uuid": uuid, "test": name}) raise exceptions.RallyException(mesg) reason = test.get("reason", "") traceback = test.get("traceback", "") sep = "\n\n" if reason and traceback else "" tests[name] = {"tags": test["tags"], "status": test["status"], "duration": test["time"], "details": (reason + sep + traceback.strip()) or None} verifications[uuid] = { "tests": tests, "duration": res.get("time", 0), "total": res.get("tests", 0), "skipped": res.get("skipped", 0), "success": res.get("success", 0), "expected_failures": res.get("expected_failures", 0), "unexpected_success": res.get("unexpected_success", 0), "failures": res.get("failures", 0), "started_at": ver.db_object[ "created_at"].strftime("%Y-%d-%m %H:%M:%S"), "finished_at": ver.db_object[ "updated_at"].strftime("%Y-%d-%m %H:%M:%S"), "status": ver.db_object["status"], "set_name": ver.db_object["set_name"] } if output_html: result = report.VerificationReport(verifications).to_html() elif output_csv: result = report.VerificationReport(verifications).to_csv() else: result = report.VerificationReport(verifications).to_json() if output_file: output_file = os.path.expanduser(output_file) with open(output_file, "wb") as f: f.write(result) else: print(result)