BugFix: limit the count to supported range in min/max
[yardstick.git] / yardstick / common / utils.py
index 729bc1d..6ac99a5 100644 (file)
@@ -18,6 +18,7 @@
 from __future__ import absolute_import
 from __future__ import print_function
 
+import datetime
 import errno
 import logging
 import os
@@ -29,7 +30,6 @@ import random
 import ipaddress
 from contextlib import closing
 
-import yaml
 import six
 from flask import jsonify
 from six.moves import configparser
@@ -69,43 +69,30 @@ def itersubclasses(cls, _seen=None):
                 yield sub
 
 
-def try_append_module(name, modules):
-    if name not in modules:
-        modules[name] = importutils.import_module(name)
-
-
 def import_modules_from_package(package):
     """Import modules from package and append into sys.modules
 
     :param: package - Full package name. For example: rally.deploy.engines
     """
-    path = [os.path.dirname(yardstick.__file__), ".."] + package.split(".")
-    path = os.path.join(*path)
+    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):
-        for filename in files:
-            if filename.startswith("__") or not filename.endswith(".py"):
-                continue
-            new_package = ".".join(root.split(os.sep)).split("....")[1]
-            module_name = "%s.%s" % (new_package, filename[:-3])
+        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
+        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
+        for module_name in missing_modules:
             try:
-                try_append_module(module_name, sys.modules)
-            except ImportError:
+                sys.modules[module_name] = importutils.import_module(module_name)
+            except (ImportError, SyntaxError):
                 logger.exception("unable to import %s", module_name)
 
 
-def parse_yaml(file_path):
-    try:
-        with open(file_path) as f:
-            value = yaml.safe_load(f)
-    except IOError:
-        return {}
-    except OSError as e:
-        if e.errno != errno.EEXIST:
-            raise
-    else:
-        return value
-
-
 def makedirs(d):
     try:
         os.makedirs(d)
@@ -296,7 +283,7 @@ def get_ip_version(ip_addr):
         return address.version
 
 
-def ip_to_hex(ip_addr):
+def ip_to_hex(ip_addr, separator=''):
     try:
         address = ipaddress.ip_address(six.text_type(ip_addr))
     except ValueError:
@@ -305,7 +292,11 @@ def ip_to_hex(ip_addr):
 
     if address.version != 4:
         return ip_addr
-    return '{:08x}'.format(int(address))
+
+    if not separator:
+        return '{:08x}'.format(int(address))
+
+    return separator.join('{:02x}'.format(octet) for octet in address.packed)
 
 
 def try_int(s, *args):
@@ -318,6 +309,29 @@ def try_int(s, *args):
 
 class SocketTopology(dict):
 
+    @classmethod
+    def parse_cpuinfo(cls, cpuinfo):
+        socket_map = {}
+
+        lines = cpuinfo.splitlines()
+
+        core_details = []
+        core_lines = {}
+        for line in lines:
+            if line.strip():
+                name, value = line.split(":", 1)
+                core_lines[name.strip()] = try_int(value.strip())
+            else:
+                core_details.append(core_lines)
+                core_lines = {}
+
+        for core in core_details:
+            socket_map.setdefault(core["physical id"], {}).setdefault(
+                core["core id"], {})[core["processor"]] = (
+                core["processor"], core["core id"], core["physical id"])
+
+        return cls(socket_map)
+
     def sockets(self):
         return sorted(self.keys())
 
@@ -330,29 +344,6 @@ class SocketTopology(dict):
             proc in procs)
 
 
-def parse_cpuinfo(cpuinfo):
-    socket_map = {}
-
-    lines = cpuinfo.splitlines()
-
-    core_details = []
-    core_lines = {}
-    for line in lines:
-        if line.strip():
-            name, value = line.split(":", 1)
-            core_lines[name.strip()] = try_int(value.strip())
-        else:
-            core_details.append(core_lines)
-            core_lines = {}
-
-    for core in core_details:
-        socket_map.setdefault(core["physical id"], {}).setdefault(
-            core["core id"], {})[core["processor"]] = (
-            core["processor"], core["core id"], core["physical id"])
-
-    return SocketTopology(socket_map)
-
-
 def config_to_dict(config):
     return {section: dict(config.items(section)) for section in
             config.sections()}
@@ -382,3 +373,19 @@ class ErrorClass(object):
 
     def __getattr__(self, item):
         raise AttributeError
+
+
+class Timer(object):
+    def __init__(self):
+        super(Timer, self).__init__()
+        self.start = self.delta = None
+
+    def __enter__(self):
+        self.start = datetime.datetime.now()
+        return self
+
+    def __exit__(self, *_):
+        self.delta = datetime.datetime.now() - self.start
+
+    def __getattr__(self, item):
+        return getattr(self.delta, item)