Merge "Add test case sample to enable mixed network"
[yardstick.git] / yardstick / common / utils.py
index 8604e90..357f66b 100644 (file)
@@ -22,6 +22,7 @@ import ipaddress
 import logging
 import os
 import random
+import re
 import socket
 import subprocess
 import sys
@@ -30,6 +31,7 @@ import six
 from flask import jsonify
 from six.moves import configparser
 from oslo_serialization import jsonutils
+from oslo_utils import encodeutils
 
 import yardstick
 
@@ -64,7 +66,7 @@ def itersubclasses(cls, _seen=None):
                 yield sub
 
 
-def import_modules_from_package(package):
+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
@@ -85,10 +87,27 @@ def import_modules_from_package(package):
         for module_name in missing_modules:
             try:
                 importlib.import_module(module_name)
-            except (ImportError, SyntaxError):
+            except (ImportError, SyntaxError) as exc:
+                if raise_exception:
+                    raise exc
                 logger.exception('Unable to import module %s', module_name)
 
 
+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):
     try:
         os.makedirs(d)
@@ -105,13 +124,12 @@ 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):
@@ -395,3 +413,45 @@ class Timer(object):
 
     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