summaryrefslogtreecommitdiffstats
path: root/nfvbench/traffic_gen/dummy.py
blob: ef7f272e024e3ce385bab4a5cd1ffed54305ed58 (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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# 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.

from nfvbench.log import LOG
from traffic_base import AbstractTrafficGenerator
import traffic_utils as utils


class DummyTG(AbstractTrafficGenerator):
    """Experimental dummy traffic generator.

    This traffic generator will pretend to generate traffic and return fake data.
    Useful for unit testing without actually generating any traffic.
    """

    def __init__(self, traffic_client):
        AbstractTrafficGenerator.__init__(self, traffic_client)
        self.port_handle = []
        self.rates = []
        self.l2_frame_size = 0
        self.duration_sec = traffic_client.config.duration_sec
        self.intf_speed = traffic_client.generator_config.intf_speed
        self.set_response_curve()
        self.packet_list = None

    def get_version(self):
        return "0.1"

    def get_tx_pps_dropped_pps(self, tx_rate):
        """Get actual tx packets based on requested tx rate.

        :param tx_rate: requested TX rate with unit ('40%', '1Mbps', '1000pps')

        :return: the actual TX pps and the dropped pps corresponding to the requested TX rate
        """
        dr, tx = self.__get_dr_actual_tx(tx_rate)
        actual_tx_bps = utils.load_to_bps(tx, self.intf_speed)
        avg_packet_size = utils.get_average_packet_size(self.l2_frame_size)
        tx_packets = utils.bps_to_pps(actual_tx_bps, avg_packet_size)

        dropped = tx_packets * dr / 100
        # print '===get_tx_pkts_dropped_pkts req tex=', tx_rate, 'dr=', dr,
        # 'actual tx rate=', tx, 'actual tx pkts=', tx_packets, 'dropped=', dropped
        return int(tx_packets), int(dropped)

    def set_response_curve(self, lr_dr=0, ndr=100, max_actual_tx=100, max_11_tx=100):
        """Set traffic gen response characteristics.

        Specifies the drop rate curve and the actual TX curve
        :param float lr_dr: The actual drop rate at TX line rate (in %, 0..100)
        :param float ndr: The true NDR  (0 packet drop) in % (0..100) of line rate"
        :param float max_actual_tx: highest actual TX when requested TX is 100%
        :param float max_11_tx: highest requested TX that results in same actual TX
        """
        self.target_ndr = ndr
        if ndr < 100:
            self.dr_slope = float(lr_dr) / (100 - ndr)
        else:
            self.dr_slope = 0
        self.max_11_tx = max_11_tx
        self.max_actual_tx = max_actual_tx
        if max_11_tx < 100:
            self.tx_slope = float(max_actual_tx - max_11_tx) / (100 - max_11_tx)
        else:
            self.tx_slope = 0

    def __get_dr_actual_tx(self, requested_tx_rate):
        """Get drop rate at given requested tx rate.

        :param float requested_tx_rate: requested tx rate in % (0..100)
        :return: the drop rate and actual tx rate at that requested_tx_rate in % (0..100)
        """
        if requested_tx_rate <= self.max_11_tx:
            actual_tx = requested_tx_rate
        else:
            actual_tx = self.max_11_tx + (requested_tx_rate - self.max_11_tx) * self.tx_slope
        if actual_tx <= self.target_ndr:
            dr = 0.0
        else:
            dr = (actual_tx - self.target_ndr) * self.dr_slope
        return dr, actual_tx

    def connect(self):
        ports = list(self.traffic_client.generator_config.ports)
        self.port_handle = ports

    def create_traffic(self, l2frame_size, rates, bidirectional, latency=True, e2e=False):
        self.rates = [utils.to_rate_str(rate) for rate in rates]
        self.l2_frame_size = l2frame_size

    def clear_streamblock(self):
        pass

    def get_stats(self):
        """Get stats from current run.

        The binary search mainly looks at 2 results to make the decision:
            actual tx packets
            actual rx dropped packets
        From the Requested TX rate - we get the Actual TX rate and the RX drop rate
        From the Run duration and actual TX rate - we get the actual total tx packets
        From the Actual tx packets and RX drop rate - we get the RX dropped packets
        """
        result = {}
        total_tx_pps = 0

        # use dummy values for all other result field as the goal is to
        # test the ndr/pdr convergence code
        for idx, ph in enumerate(self.port_handle):
            requested_tx_rate = utils.get_load_from_rate(self.rates[idx])
            tx_pps, dropped_pps = self.get_tx_pps_dropped_pps(requested_tx_rate)

            # total packets sent per direction - used by binary search
            total_pkts = tx_pps * self.duration_sec
            dropped_pkts = dropped_pps * self.duration_sec
            _, tx_pkt_rate = self.__get_dr_actual_tx(requested_tx_rate)
            result[ph] = {
                'tx': {
                    'total_pkts': total_pkts,
                    'total_pkt_bytes': 100000,
                    'pkt_rate': tx_pkt_rate,
                    'pkt_bit_rate': 1000000
                },
                'rx': {
                    # total packets received
                    'total_pkts': total_pkts - dropped_pkts,
                    'total_pkt_bytes': 100000,
                    'pkt_rate': 100,
                    'pkt_bit_rate': 1000000,
                    'dropped_pkts': dropped_pkts
                }
            }
            result[ph]['rx']['max_delay_usec'] = 10.0
            result[ph]['rx']['min_delay_usec'] = 1.0
            result[ph]['rx']['avg_delay_usec'] = 2.0
            total_tx_pps += tx_pps
        # actual total tx rate in pps
        result['total_tx_rate'] = total_tx_pps
        return result

    def get_stream_stats(self, tg_stats, if_stats, latencies, chain_idx):
        for port in range(2):
            if_stats[port].tx = 1000
            if_stats[port].rx = 1000
            latencies[port].min_usec = 10
            latencies[port].max_usec = 100
            latencies[port].avg_usec = 50

    def get_macs(self):
        return ['00:00:00:00:00:01', '00:00:00:00:00:02']

    def get_port_speed_gbps(self):
        """Return the local port speeds.

        return: a list of speed in Gbps indexed by the port#
        """
        return [10, 10]

    def clear_stats(self):
        pass

    def start_traffic(self):
        pass

    def fetch_capture_packets(self):
        def _get_packet_capture(mac):
            # convert text to binary
            src_mac = mac.replace(':', '').decode('hex')
            return {'binary': 'SSSSSS' + src_mac}

        # for packet capture, generate 2*scc random packets
        # normally we should generate packets coming from the right dest macs
        self.packet_list = []
        for dest_macs in self.traffic_client.generator_config.get_dest_macs():
            for mac in dest_macs:
                self.packet_list.append(_get_packet_capture(mac))

    def stop_traffic(self):
        pass

    def start_capture(self):
        pass

    def stop_capture(self):
        pass

    def cleanup(self):
        pass

    def set_mode(self):
        pass

    def set_service_mode(self, enabled=True):
        pass

    def resolve_arp(self):
        """Resolve ARP sucessfully."""
        def get_macs(port, scc):
            return ['00:00:00:00:%02x:%02x' % (port, chain) for chain in range(scc)]
        scc = self.traffic_client.generator_config.service_chain_count
        res = [get_macs(port, scc) for port in range(2)]
        LOG.info('Dummy TG ARP: %s', str(res))
        return res