NFVBENCH-184: Add a feature (+ options) allowing to change ownership of shared log...
[nfvbench.git] / nfvbench / utils.py
index ecbb55a..6da14ed 100644 (file)
@@ -13,6 +13,7 @@
 #    under the License.
 
 import glob
+from math import gcd
 from math import isnan
 import os
 import re
@@ -23,8 +24,8 @@ import errno
 import fcntl
 from functools import wraps
 import json
-from log import LOG
-
+from .log import LOG
+from nfvbench.traffic_gen.traffic_utils import multiplier_map
 
 class TimeoutError(Exception):
     pass
@@ -50,7 +51,7 @@ def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
 
 
 def save_json_result(result, json_file, std_json_path, service_chain, service_chain_count,
-                     flow_count, frame_sizes):
+                     flow_count, frame_sizes, user_id=None, group_id=None):
     """Save results in json format file."""
     filepaths = []
     if json_file:
@@ -70,22 +71,11 @@ def save_json_result(result, json_file, std_json_path, service_chain, service_ch
                           sort_keys=True,
                           separators=(',', ': '),
                           default=lambda obj: obj.to_json())
-
-
-def byteify(data, ignore_dicts=False):
-    # if this is a unicode string, return its string representation
-    if isinstance(data, unicode):
-        return data.encode('utf-8')
-    # if this is a list of values, return list of byteified values
-    if isinstance(data, list):
-        return [byteify(item, ignore_dicts=ignore_dicts) for item in data]
-    # if this is a dictionary, return dictionary of byteified keys and values
-    # but only if we haven't already byteified it
-    if isinstance(data, dict) and not ignore_dicts:
-        return {byteify(key, ignore_dicts=ignore_dicts): byteify(value, ignore_dicts=ignore_dicts)
-                for key, value in data.iteritems()}
-    # if it's anything else, return it in its original form
-    return data
+                # possibly change file ownership
+                if group_id is None:
+                    group_id = user_id
+                if user_id is not None:
+                    os.chown(file_path, user_id, group_id)
 
 
 def dict_to_json_dict(record):
@@ -113,8 +103,8 @@ def get_intel_pci(nic_slot=None, nic_ports=None):
 
     if nic_slot and nic_ports:
         dmidecode = subprocess.check_output(['dmidecode', '-t', 'slot'])
-        regex = r"(?<=SlotID:%s).*?(....:..:..\..)" % nic_slot
-        match = re.search(regex, dmidecode, flags=re.DOTALL)
+        regex = r"(?<=SlotID:{}).*?(....:..:..\..)".format(nic_slot)
+        match = re.search(regex, dmidecode.decode('utf-8'), flags=re.DOTALL)
         if not match:
             return None
 
@@ -144,38 +134,32 @@ def get_intel_pci(nic_slot=None, nic_ports=None):
         devices = ''
 
     for driver in ['i40e', 'ixgbe']:
-        matches = re.findall(regex.format(hx=hx, driver=driver), devices)
+        matches = re.findall(regex.format(hx=hx, driver=driver), devices.decode("utf-8"))
         if not matches:
             continue
 
         matches.sort()
+        device_list = list(x[0].split('.')[0] for x in matches)
+        device_ports_list = {i: {'ports': device_list.count(i)} for i in device_list}
         for port in matches:
             intf_name = glob.glob("/sys/bus/pci/devices/%s/net/*" % port[0])
-            if not intf_name:
-                # Interface is not bind to kernel driver, so take it
-                pcis.append(port[1])
-            else:
+            if intf_name:
                 intf_name = intf_name[0][intf_name[0].rfind('/') + 1:]
                 process = subprocess.Popen(['ip', '-o', '-d', 'link', 'show', intf_name],
                                            stdout=subprocess.PIPE,
                                            stderr=subprocess.PIPE)
                 intf_info, _ = process.communicate()
-                if not re.search('team_slave|bond_slave', intf_info):
-                    pcis.append(port[1])
-
+                if re.search('team_slave|bond_slave', intf_info.decode("utf-8")):
+                    device_ports_list[port[0].split('.')[0]]['busy'] = True
+        for port in matches:
+            if not device_ports_list[port[0].split('.')[0]].get('busy'):
+                pcis.append(port[1])
             if len(pcis) == 2:
                 break
 
     return pcis
 
 
-multiplier_map = {
-    'K': 1000,
-    'M': 1000000,
-    'G': 1000000000
-}
-
-
 def parse_flow_count(flow_count):
     flow_count = str(flow_count)
     input_fc = flow_count
@@ -187,13 +171,14 @@ def parse_flow_count(flow_count):
     try:
         flow_count = int(flow_count)
     except ValueError:
-        raise Exception("Unknown flow count format '{}'".format(input_fc))
+        raise Exception("Unknown flow count format '{}'".format(input_fc)) from ValueError
 
     return flow_count * multiplier
 
 
 def cast_integer(value):
-    return int(value) if not isnan(value) else value
+    # force 0 value if NaN value from TRex to avoid error in JSON result parsing
+    return int(value) if not isnan(value) else 0
 
 
 class RunLock(object):
@@ -210,8 +195,8 @@ class RunLock(object):
         try:
             self._fd = os.open(self._path, os.O_CREAT)
             fcntl.flock(self._fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
-        except (OSError, IOError):
-            raise Exception('Other NFVbench process is running. Please wait')
+        except (OSError, IOError) as e:
+            raise Exception('Other NFVbench process is running. Please wait') from e
 
     def __exit__(self, *args):
         fcntl.flock(self._fd, fcntl.LOCK_UN)
@@ -223,3 +208,46 @@ class RunLock(object):
             os.unlink(self._path)
         except (OSError, IOError):
             pass
+
+
+def get_divisors(n):
+    for i in range(1, int(n / 2) + 1):
+        if n % i == 0:
+            yield i
+    yield n
+
+
+def lcm(a, b):
+    """
+    Calculate the maximum possible value for both IP and ports,
+    eventually for maximum possible flow.
+    """
+    if a != 0 and b != 0:
+        lcm_value = a * b // gcd(a, b)
+        return lcm_value
+    raise TypeError(" IP size or port range can't be zero !")
+
+
+def find_tuples_equal_to_lcm_value(a, b, lcm_value):
+    """
+    Find numbers from two list matching a LCM value.
+    """
+    for x in a:
+        for y in b:
+            if lcm(x, y) == lcm_value:
+                yield (x, y)
+
+
+def find_max_size(max_size, tuples, flow):
+    if tuples:
+        if max_size > tuples[-1][0]:
+            max_size = tuples[-1][0]
+            return int(max_size)
+        if max_size > tuples[-1][1]:
+            max_size = tuples[-1][1]
+            return int(max_size)
+
+    for i in range(max_size, 1, -1):
+        if flow % i == 0:
+            return int(i)
+    return 1