summaryrefslogtreecommitdiffstats
path: root/nfvbench/traffic_gen/traffic_utils.py
blob: 4a7f8557eaa9c0d57760bee6910946b7dff7efa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# 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 bitmath
from nfvbench.utils import multiplier_map

imix_avg_l2_size = None


def convert_rates(l2frame_size, rate, intf_speed):
    avg_packet_size = get_average_packet_size(l2frame_size)
    if 'rate_pps' in rate:
        initial_rate_type = 'rate_pps'
        pps = rate['rate_pps']
        bps = pps_to_bps(pps, avg_packet_size)
        load = bps_to_load(bps, intf_speed)
    elif 'rate_bps' in rate:
        initial_rate_type = 'rate_bps'
        bps = rate['rate_bps']
        load = bps_to_load(bps, intf_speed)
        pps = bps_to_pps(bps, avg_packet_size)
    elif 'rate_percent' in rate:
        initial_rate_type = 'rate_percent'
        load = rate['rate_percent']
        bps = load_to_bps(load, intf_speed)
        pps = bps_to_pps(bps, avg_packet_size)
    else:
        raise Exception('Traffic config needs to have a rate type key')

    return {
        'initial_rate_type': initial_rate_type,
        'rate_pps': int(pps),
        'rate_percent': load,
        'rate_bps': int(bps)
    }


def get_average_packet_size(l2frame_size):
    if l2frame_size.upper() == 'IMIX':
        return imix_avg_l2_size
    return float(l2frame_size)


def load_to_bps(load_percentage, intf_speed):
    return float(load_percentage) / 100.0 * intf_speed


def bps_to_load(bps, intf_speed):
    return float(bps) / intf_speed * 100.0


def bps_to_pps(bps, avg_packet_size):
    return float(bps) / (avg_packet_size + 20.0) / 8


def pps_to_bps(pps, avg_packet_size):
    return float(pps) * (avg_packet_size + 20.0) * 8


def weighted_avg(weight, count):
    if sum(weight):

        return sum([x[0] * x[1] for x in zip(weight, count)]) / sum(weight)
    return float('nan')

def _get_bitmath_rate(rate_bps):
    rate = rate_bps.replace('ps', '').strip()
    bitmath_rate = bitmath.parse_string(rate)
    if bitmath_rate.bits <= 0:
        raise Exception('%s is out of valid range' % rate_bps)
    return bitmath_rate

def parse_rate_str(rate_str):
    if rate_str.endswith('pps'):
        rate_pps = rate_str[:-3]
        if not rate_pps:
            raise Exception('%s is missing a numeric value' % rate_str)
        try:
            multiplier = multiplier_map[rate_pps[-1].upper()]
            rate_pps = rate_pps[:-1]
        except KeyError:
            multiplier = 1
        rate_pps = int(rate_pps.strip()) * multiplier
        if rate_pps <= 0:
            raise Exception('%s is out of valid range' % rate_str)
        return {'rate_pps': str(rate_pps)}
    elif rate_str.endswith('ps'):
        rate = rate_str.replace('ps', '').strip()
        bit_rate = bitmath.parse_string(rate).bits
        if bit_rate <= 0:
            raise Exception('%s is out of valid range' % rate_str)
        return {'rate_bps': str(int(bit_rate))}
    elif rate_str.endswith('%'):
        rate_percent = float(rate_str.replace('%', '').strip())
        if rate_percent <= 0 or rate_percent > 100.0:
            raise Exception('%s is out of valid range (must be 1-100%%)' % rate_str)
        return {'rate_percent': str(rate_percent)}
    else:
        raise Exception('Unknown rate string format %s' % rate_str)

def get_load_from_rate(rate_str, avg_frame_size=64, line_rate='10Gbps'):
    '''From any rate string (with unit) return the corresponding load (in % unit)

    :param str rate_str: the rate to convert - must end with a unit (e.g. 1Mpps, 30%, 1Gbps)
    :param int avg_frame_size: average frame size in bytes (needed only if pps is given)
    :param str line_rate: line rate ending with bps unit (e.g. 1Mbps, 10Gbps) is the rate that
                      corresponds to 100% rate
    :return float: the corresponding rate in % of line rate
    '''
    rate_dict = parse_rate_str(rate_str)
    if 'rate_percent' in rate_dict:
        return float(rate_dict['rate_percent'])
    lr_bps = _get_bitmath_rate(line_rate).bits
    if 'rate_bps' in rate_dict:
        bps = int(rate_dict['rate_bps'])
    else:
        # must be rate_pps
        pps = rate_dict['rate_pps']
        bps = pps_to_bps(pps, avg_frame_size)
    return bps_to_load(bps, lr_bps)

def divide_rate(rate, divisor):
    if 'rate_pps' in rate:
        key = 'rate_pps'
        value = int(rate[key])
    elif 'rate_bps' in rate:
        key = 'rate_bps'
        value = int(rate[key])
    else:
        key = 'rate_percent'
        value = float(rate[key])
    value /= divisor
    rate = dict(rate)
    rate[key] = str(value) if value else str(1)
    return rate


def to_rate_str(rate):
    if 'rate_pps' in rate:
        pps = rate['rate_pps']
        return '{}pps'.format(pps)
    elif 'rate_bps' in rate:
        bps = rate['rate_bps']
        return '{}bps'.format(bps)
    elif 'rate_percent' in rate:
        load = rate['rate_percent']
        return '{}%'.format(load)
    assert False
    # avert pylint warning
    return None


def nan_replace(d):
    """Replaces every occurence of 'N/A' with float nan."""
    for k, v in d.iteritems():
        if isinstance(v, dict):
            nan_replace(v)
        elif v == 'N/A':
            d[k] = float('nan')


def mac_to_int(mac):
    """Converts MAC address to integer representation."""
    return int(mac.translate(None, ":.- "), 16)


def int_to_mac(i):
    """Converts integer representation of MAC address to hex string."""
    mac = format(i, 'x').zfill(12)
    blocks = [mac[x:x + 2] for x in xrange(0, len(mac), 2)]
    return ':'.join(blocks)