Merge "Add test suite for scenario "opnfv_os-ovn-nofeature-ha""
[yardstick.git] / yardstick / common / utils.py
index 251e5cc..51313ef 100644 (file)
@@ -21,6 +21,7 @@ import importlib
 import ipaddress
 import logging
 import os
+import pydoc
 import random
 import re
 import signal
@@ -28,6 +29,8 @@ import socket
 import subprocess
 import sys
 import time
+import threading
+import math
 
 import six
 from flask import jsonify
@@ -37,7 +40,6 @@ from oslo_utils import encodeutils
 
 import yardstick
 from yardstick.common import exceptions
-from yardstick.common.yaml_loader import yaml_load
 
 
 logger = logging.getLogger(__name__)
@@ -194,20 +196,16 @@ def parse_ini_file(path):
 
 def get_port_mac(sshclient, port):
     cmd = "ifconfig |grep HWaddr |grep %s |awk '{print $5}' " % port
-    status, stdout, stderr = sshclient.execute(cmd)
+    _, stdout, _ = sshclient.execute(cmd, raise_on_error=True)
 
-    if status:
-        raise RuntimeError(stderr)
     return stdout.rstrip()
 
 
 def get_port_ip(sshclient, port):
     cmd = "ifconfig %s |grep 'inet addr' |awk '{print $2}' " \
         "|cut -d ':' -f2 " % port
-    status, stdout, stderr = sshclient.execute(cmd)
+    _, stdout, _ = sshclient.execute(cmd, raise_on_error=True)
 
-    if status:
-        raise RuntimeError(stderr)
     return stdout.rstrip()
 
 
@@ -282,11 +280,19 @@ def get_free_port(ip):
 
 
 def mac_address_to_hex_list(mac):
-    octets = ["0x{:02x}".format(int(elem, 16)) for elem in mac.split(':')]
-    assert len(octets) == 6 and all(len(octet) == 4 for octet in octets)
+    try:
+        octets = ["0x{:02x}".format(int(elem, 16)) for elem in mac.split(':')]
+    except ValueError:
+        raise exceptions.InvalidMacAddress(mac_address=mac)
+    if len(octets) != 6 or all(len(octet) != 4 for octet in octets):
+        raise exceptions.InvalidMacAddress(mac_address=mac)
     return octets
 
 
+def make_ipv4_address(ip_addr):
+    return ipaddress.IPv4Address(six.text_type(ip_addr))
+
+
 def safe_ip_address(ip_addr):
     """ get ip address version v6 or v4 """
     try:
@@ -336,6 +342,14 @@ def ip_to_hex(ip_addr, separator=''):
     return separator.join('{:02x}'.format(octet) for octet in address.packed)
 
 
+def get_mask_from_ip_range(ip_low, ip_high):
+    _ip_low = ipaddress.ip_address(ip_low)
+    _ip_high = ipaddress.ip_address(ip_high)
+    _ip_low_int = int(_ip_low)
+    _ip_high_int = int(_ip_high)
+    return _ip_high.max_prefixlen - (_ip_high_int ^ _ip_low_int).bit_length()
+
+
 def try_int(s, *args):
     """Convert to integer if possible."""
     try:
@@ -468,6 +482,9 @@ class Timer(object):
     def __del__(self):  # pragma: no cover
         signal.alarm(0)
 
+    def delta_time_sec(self):
+        return (datetime.datetime.now() - self.start).total_seconds()
+
 
 def read_meminfo(ssh_client):
     """Read "/proc/meminfo" file and parse all keys and values"""
@@ -483,6 +500,23 @@ def read_meminfo(ssh_client):
     return output
 
 
+def setup_hugepages(ssh_client, size_kb):
+    """Setup needed number of hugepages for the size specified"""
+
+    NR_HUGEPAGES_PATH = '/proc/sys/vm/nr_hugepages'
+    meminfo = read_meminfo(ssh_client)
+    hp_size_kb = int(meminfo['Hugepagesize'])
+    hp_number = int(math.ceil(size_kb / float(hp_size_kb)))
+    ssh_client.execute(
+        'echo %s | sudo tee %s' % (hp_number, NR_HUGEPAGES_PATH))
+    hp = six.BytesIO()
+    ssh_client.get_file_obj(NR_HUGEPAGES_PATH, hp)
+    hp_number_set = int(hp.getvalue().decode('utf-8').splitlines()[0])
+    logger.info('Hugepages size (kB): %s, number claimed: %s, number set: %s',
+                hp_size_kb, hp_number, hp_number_set)
+    return hp_size_kb, hp_number, hp_number_set
+
+
 def find_relative_file(path, task_path):
     """
     Find file in one of places: in abs of path or relative to a directory path,
@@ -514,25 +548,73 @@ def open_relative_file(path, task_path):
 def wait_until_true(predicate, timeout=60, sleep=1, exception=None):
     """Wait until callable predicate is evaluated as True
 
+    When in a thread different from the main one, Timer(timeout) will fail
+    because signal is not handled. In this case
+
     :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():
+    if isinstance(threading.current_thread(), threading._MainThread):
+        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
+    else:
+        with Timer() as timer:
+            while timer.delta_time_sec() < timeout:
+                if predicate():
+                    return
                 time.sleep(sleep)
-    except exceptions.TimerTimeout:
         if exception and issubclass(exception, Exception):
             raise exception  # pylint: disable=raising-bad-type
         raise exceptions.WaitTimeout
 
 
-def read_yaml_file(path):
-    """Read yaml file"""
+def send_socket_command(host, port, command):
+    """Send a string command to a specific port in a host
+
+    :param host: (str) ip or hostname of the host
+    :param port: (int) port number
+    :param command: (str) command to send
+    :return: 0 if success, error number if error
+    """
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    ret = 0
+    try:
+        err_number = sock.connect_ex((host, int(port)))
+        if err_number != 0:
+            return err_number
+        sock.sendall(six.b(command))
+    except Exception:  # pylint: disable=broad-except
+        ret = 1
+    finally:
+        sock.close()
+    return ret
+
+
+def safe_cast(value, type_to_convert, default_value):
+    """Convert value to type, in case of error return default_value
+
+    :param value: value to convert
+    :param type_to_convert: type to convert, could be "type" or "string"
+    :param default_value: default value to return
+    :return: converted value or default_value
+    """
+    if isinstance(type_to_convert, type):
+        _type = type_to_convert
+    else:
+        _type = pydoc.locate(type_to_convert)
+        if not _type:
+            raise exceptions.InvalidType(type_to_convert=type_to_convert)
 
-    with open(path) as stream:
-        data = yaml_load(stream)
-    return data
+    try:
+        return _type(value)
+    except ValueError:
+        return default_value