X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=yardstick%2Fcommon%2Futils.py;h=869db469b2675cd124f0f2f0f7d3ecd46a1b999d;hb=745db76a4cc99a1597a3570ecdc063d549839a15;hp=1d7ea071cf7325d48d99449b5f14a6c226f8d221;hpb=b39fe5457e846e734e980666bc209fb7c07a6bdc;p=yardstick.git diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index 1d7ea071c..869db469b 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -13,31 +13,31 @@ # License for the specific language governing permissions and limitations # under the License. -# yardstick comment: this is a modified copy of rally/rally/common/utils.py - -from __future__ import absolute_import -from __future__ import print_function - +import collections +from contextlib import closing import datetime import errno +import importlib +import ipaddress import logging import os +import random +import re +import signal +import socket import subprocess import sys -import collections -import socket -import random -import ipaddress -from contextlib import closing +import time import six from flask import jsonify from six.moves import configparser -from oslo_utils import importutils from oslo_serialization import jsonutils +from oslo_utils import encodeutils import yardstick -from yardstick.common.yaml_loader import yaml_load +from yardstick.common import exceptions + logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -70,41 +70,46 @@ def itersubclasses(cls, _seen=None): yield sub -def import_modules_from_package(package): - """Import modules from package and append into sys.modules +def import_modules_from_package(package, raise_exception=False): + """Import modules given a package name :param: package - Full package name. For example: rally.deploy.engines """ yardstick_root = os.path.dirname(os.path.dirname(yardstick.__file__)) - path = os.path.join(yardstick_root, *package.split(".")) - for root, dirs, files in os.walk(path): - matches = (filename for filename in files if filename.endswith(".py") and - not filename.startswith("__")) - new_package = os.path.relpath(root, yardstick_root).replace(os.sep, ".") + path = os.path.join(yardstick_root, *package.split('.')) + for root, _, files in os.walk(path): + matches = (filename for filename in files if filename.endswith('.py') + and not filename.startswith('__')) + new_package = os.path.relpath(root, yardstick_root).replace(os.sep, + '.') module_names = set( - ("{}.{}".format(new_package, filename.rsplit(".py", 1)[0]) for filename in matches)) - # find modules which haven't already been imported + '{}.{}'.format(new_package, filename.rsplit('.py', 1)[0]) + for filename in matches) + # Find modules which haven't already been imported missing_modules = module_names.difference(sys.modules) - logger.debug("importing %s", missing_modules) - # we have already checked for already imported modules, so we don't need to check again + logger.debug('Importing modules: %s', missing_modules) for module_name in missing_modules: try: - sys.modules[module_name] = importutils.import_module(module_name) - except ImportError: - logger.exception("unable to import %s", module_name) + importlib.import_module(module_name) + except (ImportError, SyntaxError) as exc: + if raise_exception: + raise exc + logger.exception('Unable to import module %s', module_name) -def parse_yaml(file_path): - try: - with open(file_path) as f: - value = yaml_load(f) - except IOError: - return {} - except OSError as e: - if e.errno != errno.EEXIST: - raise - else: - return value +NON_NONE_DEFAULT = object() + + +def get_key_with_default(data, key, default=NON_NONE_DEFAULT): + value = data.get(key, default) + if value is NON_NONE_DEFAULT: + raise KeyError(key) + return value + + +def make_dict_from_map(data, key_map): + return {dest_key: get_key_with_default(data, src_key, default) + for dest_key, (src_key, default) in key_map.items()} def makedirs(d): @@ -123,19 +128,23 @@ def remove_file(path): raise -def execute_command(cmd): +def execute_command(cmd, **kwargs): exec_msg = "Executing command: '%s'" % cmd logger.debug(exec_msg) - output = subprocess.check_output(cmd.split()).split(os.linesep) - - return output + output = subprocess.check_output(cmd.split(), **kwargs) + return encodeutils.safe_decode(output, incoming='utf-8').split(os.linesep) def source_env(env_file): p = subprocess.Popen(". %s; env" % env_file, stdout=subprocess.PIPE, shell=True) output = p.communicate()[0] + + # sometimes output type would be binary_type, and it don't have splitlines + # method, so we need to decode + if isinstance(output, six.binary_type): + output = encodeutils.safe_decode(output) env = dict(line.split('=', 1) for line in output.splitlines() if '=' in line) os.environ.update(env) return env @@ -265,10 +274,10 @@ def set_dict_value(dic, keys, value): def get_free_port(ip): with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: - while True: + port = random.randint(5000, 10000) + while s.connect_ex((ip, port)) == 0: port = random.randint(5000, 10000) - if s.connect_ex((ip, port)) != 0: - return port + return port def mac_address_to_hex_list(mac): @@ -297,6 +306,19 @@ def get_ip_version(ip_addr): return address.version +def make_ip_addr(ip, mask): + """ + :param ip[str]: ip adddress + :param mask[str]: /24 prefix of 255.255.255.0 netmask + :return: IPv4Interface object + """ + try: + return ipaddress.ip_interface(six.text_type('/'.join([ip, mask]))) + except (TypeError, ValueError): + # None so we can skip later + return None + + def ip_to_hex(ip_addr, separator=''): try: address = ipaddress.ip_address(six.text_type(ip_addr)) @@ -364,10 +386,13 @@ def config_to_dict(config): def validate_non_string_sequence(value, default=None, raise_exc=None): - if isinstance(value, collections.Sequence) and not isinstance(value, str): + # NOTE(ralonsoh): refactor this function to check if raise_exc is an + # Exception. Remove duplicate code, this function is duplicated in this + # repository. + if isinstance(value, collections.Sequence) and not isinstance(value, six.string_types): return value if raise_exc: - raise raise_exc + raise raise_exc # pylint: disable=raising-bad-type return default @@ -379,6 +404,13 @@ def join_non_strings(separator, *non_strings): return str(separator).join(str(non_string) for non_string in non_strings) +def safe_decode_utf8(s): + """Safe decode a str from UTF""" + if six.PY3 and isinstance(s, bytes): + return s.decode('utf-8', 'surrogateescape') + return s + + class ErrorClass(object): def __init__(self, *args, **kwargs): @@ -390,16 +422,86 @@ class ErrorClass(object): class Timer(object): - def __init__(self): + def __init__(self, timeout=None): super(Timer, self).__init__() self.start = self.delta = None + self._timeout = int(timeout) if timeout else None + + def _timeout_handler(self, *args): + raise exceptions.TimerTimeout(timeout=self._timeout) def __enter__(self): self.start = datetime.datetime.now() + if self._timeout: + signal.signal(signal.SIGALRM, self._timeout_handler) + signal.alarm(self._timeout) return self def __exit__(self, *_): + if self._timeout: + signal.alarm(0) self.delta = datetime.datetime.now() - self.start def __getattr__(self, item): return getattr(self.delta, item) + + +def read_meminfo(ssh_client): + """Read "/proc/meminfo" file and parse all keys and values""" + + cpuinfo = six.BytesIO() + ssh_client.get_file_obj('/proc/meminfo', cpuinfo) + lines = cpuinfo.getvalue().decode('utf-8') + matches = re.findall(r"([\w\(\)]+):\s+(\d+)( kB)*", lines) + output = {} + for match in matches: + output[match[0]] = match[1] + + return output + + +def find_relative_file(path, task_path): + """ + Find file in one of places: in abs of path or relative to a directory path, + in this order. + + :param path: + :param task_path: + :return str: full path to file + """ + # fixme: create schema to validate all fields have been provided + for lookup in [os.path.abspath(path), os.path.join(task_path, path)]: + try: + with open(lookup): + return lookup + except IOError: + pass + raise IOError(errno.ENOENT, 'Unable to find {} file'.format(path)) + + +def open_relative_file(path, task_path): + try: + return open(path) + except IOError as e: + if e.errno == errno.ENOENT: + return open(os.path.join(task_path, path)) + raise + + +def wait_until_true(predicate, timeout=60, sleep=1, exception=None): + """Wait until callable predicate is evaluated as True + + :param predicate: (func) callable deciding whether waiting should continue + :param timeout: (int) timeout in seconds how long should function wait + :param sleep: (int) polling interval for results in seconds + :param exception: exception instance to raise on timeout. If None is passed + (default) then WaitTimeout exception is raised. + """ + try: + with Timer(timeout=timeout): + while not predicate(): + time.sleep(sleep) + except exceptions.TimerTimeout: + if exception and issubclass(exception, Exception): + raise exception # pylint: disable=raising-bad-type + raise exceptions.WaitTimeout