Merge "Local Documentation Builds"
[nfvbench.git] / nfvbench / traffic_gen / traffic_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
16 import bitmath
17 from nfvbench.utils import multiplier_map
18
19 imix_avg_l2_size = None
20
21
22 def convert_rates(l2frame_size, rate, intf_speed):
23     """Convert a given rate unit into the other rate units.
24
25     l2frame_size: size of the L2 frame in bytes or 'IMIX'
26     rate: a dict that has at least one of the following key:
27           'rate_pps', 'rate_bps', 'rate_percent'
28           with the corresponding input value
29     intf_speed: the line rate speed in bits per second
30     """
31     avg_packet_size = get_average_packet_size(l2frame_size)
32     if 'rate_pps' in rate:
33         # input = packets/sec
34         initial_rate_type = 'rate_pps'
35         pps = rate['rate_pps']
36         bps = pps_to_bps(pps, avg_packet_size)
37         load = bps_to_load(bps, intf_speed)
38     elif 'rate_bps' in rate:
39         # input = bits per second
40         initial_rate_type = 'rate_bps'
41         bps = rate['rate_bps']
42         load = bps_to_load(bps, intf_speed)
43         pps = bps_to_pps(bps, avg_packet_size)
44     elif 'rate_percent' in rate:
45         # input = percentage of the line rate (between 0.0 and 100.0)
46         initial_rate_type = 'rate_percent'
47         load = rate['rate_percent']
48         bps = load_to_bps(load, intf_speed)
49         pps = bps_to_pps(bps, avg_packet_size)
50     else:
51         raise Exception('Traffic config needs to have a rate type key')
52
53     return {
54         'initial_rate_type': initial_rate_type,
55         'rate_pps': int(pps),
56         'rate_percent': load,
57         'rate_bps': int(bps)
58     }
59
60
61 def get_average_packet_size(l2frame_size):
62     if l2frame_size.upper() == 'IMIX':
63         return imix_avg_l2_size
64     return float(l2frame_size)
65
66
67 def load_to_bps(load_percentage, intf_speed):
68     return float(load_percentage) / 100.0 * intf_speed
69
70
71 def bps_to_load(bps, intf_speed):
72     return float(bps) / intf_speed * 100.0
73
74
75 def bps_to_pps(bps, avg_packet_size):
76     return float(bps) / (avg_packet_size + 20.0) / 8
77
78
79 def pps_to_bps(pps, avg_packet_size):
80     return float(pps) * (avg_packet_size + 20.0) * 8
81
82
83 def weighted_avg(weight, count):
84     if sum(weight):
85
86         return sum([x[0] * x[1] for x in zip(weight, count)]) / sum(weight)
87     return float('nan')
88
89 def _get_bitmath_rate(rate_bps):
90     rate = rate_bps.replace('ps', '').strip()
91     bitmath_rate = bitmath.parse_string(rate)
92     if bitmath_rate.bits <= 0:
93         raise Exception('%s is out of valid range' % rate_bps)
94     return bitmath_rate
95
96 def parse_rate_str(rate_str):
97     if rate_str.endswith('pps'):
98         rate_pps = rate_str[:-3]
99         if not rate_pps:
100             raise Exception('%s is missing a numeric value' % rate_str)
101         try:
102             multiplier = multiplier_map[rate_pps[-1].upper()]
103             rate_pps = rate_pps[:-1]
104         except KeyError:
105             multiplier = 1
106         rate_pps = int(rate_pps.strip()) * multiplier
107         if rate_pps <= 0:
108             raise Exception('%s is out of valid range' % rate_str)
109         return {'rate_pps': str(rate_pps)}
110     elif rate_str.endswith('ps'):
111         rate = rate_str.replace('ps', '').strip()
112         bit_rate = bitmath.parse_string(rate).bits
113         if bit_rate <= 0:
114             raise Exception('%s is out of valid range' % rate_str)
115         return {'rate_bps': str(int(bit_rate))}
116     elif rate_str.endswith('%'):
117         rate_percent = float(rate_str.replace('%', '').strip())
118         if rate_percent <= 0 or rate_percent > 100.0:
119             raise Exception('%s is out of valid range (must be 1-100%%)' % rate_str)
120         return {'rate_percent': str(rate_percent)}
121     else:
122         raise Exception('Unknown rate string format %s' % rate_str)
123
124 def get_load_from_rate(rate_str, avg_frame_size=64, line_rate='10Gbps'):
125     '''From any rate string (with unit) return the corresponding load (in % unit)
126
127     :param str rate_str: the rate to convert - must end with a unit (e.g. 1Mpps, 30%, 1Gbps)
128     :param int avg_frame_size: average frame size in bytes (needed only if pps is given)
129     :param str line_rate: line rate ending with bps unit (e.g. 1Mbps, 10Gbps) is the rate that
130                       corresponds to 100% rate
131     :return float: the corresponding rate in % of line rate
132     '''
133     rate_dict = parse_rate_str(rate_str)
134     if 'rate_percent' in rate_dict:
135         return float(rate_dict['rate_percent'])
136     lr_bps = _get_bitmath_rate(line_rate).bits
137     if 'rate_bps' in rate_dict:
138         bps = int(rate_dict['rate_bps'])
139     else:
140         # must be rate_pps
141         pps = rate_dict['rate_pps']
142         bps = pps_to_bps(pps, avg_frame_size)
143     return bps_to_load(bps, lr_bps)
144
145 def divide_rate(rate, divisor):
146     if 'rate_pps' in rate:
147         key = 'rate_pps'
148         value = int(rate[key])
149     elif 'rate_bps' in rate:
150         key = 'rate_bps'
151         value = int(rate[key])
152     else:
153         key = 'rate_percent'
154         value = float(rate[key])
155     value /= divisor
156     rate = dict(rate)
157     rate[key] = str(value) if value else str(1)
158     return rate
159
160
161 def to_rate_str(rate):
162     if 'rate_pps' in rate:
163         pps = rate['rate_pps']
164         return '{}pps'.format(pps)
165     elif 'rate_bps' in rate:
166         bps = rate['rate_bps']
167         return '{}bps'.format(bps)
168     elif 'rate_percent' in rate:
169         load = rate['rate_percent']
170         return '{}%'.format(load)
171     assert False
172     # avert pylint warning
173     return None
174
175
176 def nan_replace(d):
177     """Replaces every occurence of 'N/A' with float nan."""
178     for k, v in d.iteritems():
179         if isinstance(v, dict):
180             nan_replace(v)
181         elif v == 'N/A':
182             d[k] = float('nan')
183
184
185 def mac_to_int(mac):
186     """Converts MAC address to integer representation."""
187     return int(mac.translate(None, ":.- "), 16)
188
189
190 def int_to_mac(i):
191     """Converts integer representation of MAC address to hex string."""
192     mac = format(i, 'x').zfill(12)
193     blocks = [mac[x:x + 2] for x in xrange(0, len(mac), 2)]
194     return ':'.join(blocks)