Initial code drop from Cisco
[nfvbench.git] / nfvbench / utils.py
1 # Copyright 2016 Cisco Systems, Inc.  All rights reserved.
2 #
3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 #    not use this file except in compliance with the License. You may obtain
5 #    a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 #    Unless required by applicable law or agreed to in writing, software
10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 #    License for the specific language governing permissions and limitations
13 #    under the License.
14
15 import errno
16 import fcntl
17 from functools import wraps
18 import json
19 from log import LOG
20 import os
21 import re
22 import signal
23 import subprocess
24
25
26 class TimeoutError(Exception):
27     pass
28
29
30 def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
31     def decorator(func):
32         def _handle_timeout(signum, frame):
33             raise TimeoutError(error_message)
34
35         def wrapper(*args, **kwargs):
36             signal.signal(signal.SIGALRM, _handle_timeout)
37             signal.alarm(seconds)
38             try:
39                 result = func(*args, **kwargs)
40             finally:
41                 signal.alarm(0)
42             return result
43
44         return wraps(func)(wrapper)
45
46     return decorator
47
48
49 def save_json_result(result, json_file, std_json_path, service_chain, service_chain_count,
50                      flow_count, frame_sizes):
51     """Save results in json format file."""
52     filepaths = []
53     if json_file:
54         filepaths.append(json_file)
55     if std_json_path:
56         name_parts = [service_chain, str(service_chain_count), str(flow_count)] + list(frame_sizes)
57         filename = '-'.join(name_parts) + '.json'
58         filepaths.append(os.path.join(std_json_path, filename))
59
60     if filepaths:
61         for file_path in filepaths:
62             LOG.info('Saving results in json file: ' + file_path + "...")
63             with open(file_path, 'w') as jfp:
64                 json.dump(result,
65                           jfp,
66                           indent=4,
67                           sort_keys=True,
68                           separators=(',', ': '),
69                           default=lambda obj: obj.to_json())
70
71
72 def byteify(data, ignore_dicts=False):
73     # if this is a unicode string, return its string representation
74     if isinstance(data, unicode):
75         return data.encode('utf-8')
76     # if this is a list of values, return list of byteified values
77     if isinstance(data, list):
78         return [byteify(item, ignore_dicts=ignore_dicts) for item in data]
79     # if this is a dictionary, return dictionary of byteified keys and values
80     # but only if we haven't already byteified it
81     if isinstance(data, dict) and not ignore_dicts:
82         return {byteify(key, ignore_dicts=ignore_dicts): byteify(value, ignore_dicts=ignore_dicts)
83                 for key, value in data.iteritems()}
84     # if it's anything else, return it in its original form
85     return data
86
87
88 def dict_to_json_dict(record):
89     return json.loads(json.dumps(record, default=lambda obj: obj.to_json()))
90
91
92 def get_intel_pci(nic_ports):
93     """Returns the first two PCI addresses of sorted PCI list for Intel NIC (i40e, ixgbe)"""
94     hx = r'[0-9a-fA-F]'
95     regex = r'{hx}{{4}}:({hx}{{2}}:{hx}{{2}}\.{hx}{{1}}).*(drv={driver}|.*unused=.*{driver})'
96
97     try:
98         trex_base_dir = '/opt/trex'
99         contents = os.listdir(trex_base_dir)
100         trex_dir = os.path.join(trex_base_dir, contents[0])
101         process = subprocess.Popen(['python', 'dpdk_setup_ports.py', '-s'],
102                                    cwd=trex_dir,
103                                    stdout=subprocess.PIPE,
104                                    stderr=subprocess.PIPE)
105         devices, _ = process.communicate()
106     except Exception:
107         devices = ''
108
109     for driver in ['i40e', 'ixgbe']:
110         matches = re.findall(regex.format(hx=hx, driver=driver), devices)
111         if matches:
112             pcis = map(lambda x: x[0], matches)
113             if len(pcis) < 2:
114                 continue
115             pcis.sort()
116             return [pcis[port_index] for port_index in nic_ports]
117
118     return []
119
120
121 multiplier_map = {
122     'K': 1000,
123     'M': 1000000,
124     'G': 1000000000
125 }
126
127
128 def parse_flow_count(flow_count):
129     flow_count = str(flow_count)
130     input_fc = flow_count
131     multiplier = 1
132     if flow_count[-1].upper() in multiplier_map:
133         multiplier = multiplier_map[flow_count[-1].upper()]
134         flow_count = flow_count[:-1]
135
136     try:
137         flow_count = int(flow_count)
138     except ValueError:
139         raise Exception("Unknown flow count format '{}'".format(input_fc))
140
141     return flow_count * multiplier
142
143
144 class RunLock(object):
145     """
146     Attempts to lock file and run current instance of NFVbench as the first,
147     otherwise raises exception.
148     """
149
150     def __init__(self, path='/tmp/nfvbench.lock'):
151         self._path = path
152         self._fd = None
153
154     def __enter__(self):
155         try:
156             self._fd = os.open(self._path, os.O_CREAT)
157             fcntl.flock(self._fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
158         except (OSError, IOError):
159             raise Exception('Other NFVbench process is running. Please wait')
160
161     def __exit__(self, *args):
162         fcntl.flock(self._fd, fcntl.LOCK_UN)
163         os.close(self._fd)
164         self._fd = None
165
166         # Try to remove the lock file, but don't try too hard because it is unnecessary.
167         try:
168             os.unlink(self._path)
169         except (OSError, IOError):
170             pass