From 04a7de082bd221eae3c7004f4e0b99dfa4f8be91 Mon Sep 17 00:00:00 2001 From: ahothan Date: Fri, 28 Jul 2017 17:08:46 -0700 Subject: Initial code drop from Cisco Change-Id: Ie2993886dc8e95c5f73ccdb871add8b96ffcc849 Signed-off-by: ahothan --- nfvbench/utils.py | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 nfvbench/utils.py (limited to 'nfvbench/utils.py') diff --git a/nfvbench/utils.py b/nfvbench/utils.py new file mode 100644 index 0000000..4d9749c --- /dev/null +++ b/nfvbench/utils.py @@ -0,0 +1,170 @@ +# Copyright 2016 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import errno +import fcntl +from functools import wraps +import json +from log import LOG +import os +import re +import signal +import subprocess + + +class TimeoutError(Exception): + pass + + +def timeout(seconds=10, error_message=os.strerror(errno.ETIME)): + def decorator(func): + def _handle_timeout(signum, frame): + raise TimeoutError(error_message) + + def wrapper(*args, **kwargs): + signal.signal(signal.SIGALRM, _handle_timeout) + signal.alarm(seconds) + try: + result = func(*args, **kwargs) + finally: + signal.alarm(0) + return result + + return wraps(func)(wrapper) + + return decorator + + +def save_json_result(result, json_file, std_json_path, service_chain, service_chain_count, + flow_count, frame_sizes): + """Save results in json format file.""" + filepaths = [] + if json_file: + filepaths.append(json_file) + if std_json_path: + name_parts = [service_chain, str(service_chain_count), str(flow_count)] + list(frame_sizes) + filename = '-'.join(name_parts) + '.json' + filepaths.append(os.path.join(std_json_path, filename)) + + if filepaths: + for file_path in filepaths: + LOG.info('Saving results in json file: ' + file_path + "...") + with open(file_path, 'w') as jfp: + json.dump(result, + jfp, + indent=4, + 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 + + +def dict_to_json_dict(record): + return json.loads(json.dumps(record, default=lambda obj: obj.to_json())) + + +def get_intel_pci(nic_ports): + """Returns the first two PCI addresses of sorted PCI list for Intel NIC (i40e, ixgbe)""" + hx = r'[0-9a-fA-F]' + regex = r'{hx}{{4}}:({hx}{{2}}:{hx}{{2}}\.{hx}{{1}}).*(drv={driver}|.*unused=.*{driver})' + + try: + trex_base_dir = '/opt/trex' + contents = os.listdir(trex_base_dir) + trex_dir = os.path.join(trex_base_dir, contents[0]) + process = subprocess.Popen(['python', 'dpdk_setup_ports.py', '-s'], + cwd=trex_dir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + devices, _ = process.communicate() + except Exception: + devices = '' + + for driver in ['i40e', 'ixgbe']: + matches = re.findall(regex.format(hx=hx, driver=driver), devices) + if matches: + pcis = map(lambda x: x[0], matches) + if len(pcis) < 2: + continue + pcis.sort() + return [pcis[port_index] for port_index in nic_ports] + + return [] + + +multiplier_map = { + 'K': 1000, + 'M': 1000000, + 'G': 1000000000 +} + + +def parse_flow_count(flow_count): + flow_count = str(flow_count) + input_fc = flow_count + multiplier = 1 + if flow_count[-1].upper() in multiplier_map: + multiplier = multiplier_map[flow_count[-1].upper()] + flow_count = flow_count[:-1] + + try: + flow_count = int(flow_count) + except ValueError: + raise Exception("Unknown flow count format '{}'".format(input_fc)) + + return flow_count * multiplier + + +class RunLock(object): + """ + Attempts to lock file and run current instance of NFVbench as the first, + otherwise raises exception. + """ + + def __init__(self, path='/tmp/nfvbench.lock'): + self._path = path + self._fd = None + + def __enter__(self): + 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') + + def __exit__(self, *args): + fcntl.flock(self._fd, fcntl.LOCK_UN) + os.close(self._fd) + self._fd = None + + # Try to remove the lock file, but don't try too hard because it is unnecessary. + try: + os.unlink(self._path) + except (OSError, IOError): + pass -- cgit 1.2.3-korg