NFVBENCH-172: Add quartiles and 99 percentile latency values
[nfvbench.git] / nfvbench / traffic_gen / dummy.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 from nfvbench.log import LOG
16 from .traffic_base import AbstractTrafficGenerator
17 from . import traffic_utils as utils
18
19
20 class DummyTG(AbstractTrafficGenerator):
21     """Experimental dummy traffic generator.
22
23     This traffic generator will pretend to generate traffic and return fake data.
24     Useful for unit testing without actually generating any traffic.
25     """
26
27     def __init__(self, traffic_client):
28         AbstractTrafficGenerator.__init__(self, traffic_client)
29         self.port_handle = []
30         self.rates = []
31         self.l2_frame_size = 0
32         self.duration_sec = traffic_client.config.duration_sec
33         self.intf_speed = traffic_client.generator_config.intf_speed
34         self.set_response_curve()
35         self.packet_list = None
36
37     def get_version(self):
38         return "0.1"
39
40     def get_tx_pps_dropped_pps(self, tx_rate):
41         """Get actual tx packets based on requested tx rate.
42
43         :param tx_rate: requested TX rate with unit ('40%', '1Mbps', '1000pps')
44
45         :return: the actual TX pps and the dropped pps corresponding to the requested TX rate
46         """
47         dr, tx = self.__get_dr_actual_tx(tx_rate)
48         actual_tx_bps = utils.load_to_bps(tx, self.intf_speed)
49         avg_packet_size = utils.get_average_packet_size(self.l2_frame_size)
50         tx_packets = utils.bps_to_pps(actual_tx_bps, avg_packet_size)
51
52         dropped = tx_packets * dr / 100
53         # print '===get_tx_pkts_dropped_pkts req tex=', tx_rate, 'dr=', dr,
54         # 'actual tx rate=', tx, 'actual tx pkts=', tx_packets, 'dropped=', dropped
55         return int(tx_packets), int(dropped)
56
57     def set_response_curve(self, lr_dr=0, ndr=100, max_actual_tx=100, max_11_tx=100):
58         """Set traffic gen response characteristics.
59
60         Specifies the drop rate curve and the actual TX curve
61         :param float lr_dr: The actual drop rate at TX line rate (in %, 0..100)
62         :param float ndr: The true NDR  (0 packet drop) in % (0..100) of line rate"
63         :param float max_actual_tx: highest actual TX when requested TX is 100%
64         :param float max_11_tx: highest requested TX that results in same actual TX
65         """
66         self.target_ndr = ndr
67         if ndr < 100:
68             self.dr_slope = float(lr_dr) / (100 - ndr)
69         else:
70             self.dr_slope = 0
71         self.max_11_tx = max_11_tx
72         self.max_actual_tx = max_actual_tx
73         if max_11_tx < 100:
74             self.tx_slope = float(max_actual_tx - max_11_tx) / (100 - max_11_tx)
75         else:
76             self.tx_slope = 0
77
78     def __get_dr_actual_tx(self, requested_tx_rate):
79         """Get drop rate at given requested tx rate.
80
81         :param float requested_tx_rate: requested tx rate in % (0..100)
82         :return: the drop rate and actual tx rate at that requested_tx_rate in % (0..100)
83         """
84         if requested_tx_rate <= self.max_11_tx:
85             actual_tx = requested_tx_rate
86         else:
87             actual_tx = self.max_11_tx + (requested_tx_rate - self.max_11_tx) * self.tx_slope
88         if actual_tx <= self.target_ndr:
89             dr = 0.0
90         else:
91             dr = (actual_tx - self.target_ndr) * self.dr_slope
92         return dr, actual_tx
93
94     def connect(self):
95         ports = list(self.traffic_client.generator_config.ports)
96         self.port_handle = ports
97
98     def create_traffic(self, l2frame_size, rates, bidirectional, latency=True, e2e=False):
99         self.rates = [utils.to_rate_str(rate) for rate in rates]
100         self.l2_frame_size = l2frame_size
101
102     def clear_streamblock(self):
103         pass
104
105     def get_stats(self, ifstats):
106         """Get stats from current run.
107
108         The binary search mainly looks at 2 results to make the decision:
109             actual tx packets
110             actual rx dropped packets
111         From the Requested TX rate - we get the Actual TX rate and the RX drop rate
112         From the Run duration and actual TX rate - we get the actual total tx packets
113         From the Actual tx packets and RX drop rate - we get the RX dropped packets
114         """
115         result = {}
116         total_tx_pps = 0
117
118         # use dummy values for all other result field as the goal is to
119         # test the ndr/pdr convergence code
120         for idx, ph in enumerate(self.port_handle):
121             requested_tx_rate = utils.get_load_from_rate(self.rates[idx])
122             tx_pps, dropped_pps = self.get_tx_pps_dropped_pps(requested_tx_rate)
123
124             # total packets sent per direction - used by binary search
125             total_pkts = tx_pps * self.duration_sec
126             dropped_pkts = dropped_pps * self.duration_sec
127             _, tx_pkt_rate = self.__get_dr_actual_tx(requested_tx_rate)
128             result[ph] = {
129                 'tx': {
130                     'total_pkts': total_pkts,
131                     'total_pkt_bytes': 100000,
132                     'pkt_rate': tx_pkt_rate,
133                     'pkt_bit_rate': 1000000
134                 },
135                 'rx': {
136                     # total packets received
137                     'total_pkts': total_pkts - dropped_pkts,
138                     'total_pkt_bytes': 100000,
139                     'pkt_rate': 100,
140                     'pkt_bit_rate': 1000000,
141                     'dropped_pkts': dropped_pkts
142                 }
143             }
144             result[ph]['rx']['max_delay_usec'] = 10.0
145             result[ph]['rx']['min_delay_usec'] = 1.0
146             result[ph]['rx']['avg_delay_usec'] = 2.0
147             total_tx_pps += tx_pps
148         # actual total tx rate in pps
149         result['total_tx_rate'] = total_tx_pps
150         # actual offered tx rate in bps
151         avg_packet_size = utils.get_average_packet_size(self.l2_frame_size)
152         total_tx_bps = utils.pps_to_bps(total_tx_pps, avg_packet_size)
153         result['offered_tx_rate_bps'] = total_tx_bps
154         return result
155
156     def get_stream_stats(self, tg_stats, if_stats, latencies, chain_idx):
157         for port in range(2):
158             if_stats[port].tx = 1000
159             if_stats[port].rx = 1000
160             latencies[port].min_usec = 10
161             latencies[port].max_usec = 100
162             latencies[port].avg_usec = 50
163
164     def get_macs(self):
165         return ['00:00:00:00:00:01', '00:00:00:00:00:02']
166
167     def get_port_speed_gbps(self):
168         """Return the local port speeds.
169
170         return: a list of speed in Gbps indexed by the port#
171         """
172         return [10, 10]
173
174     def clear_stats(self):
175         pass
176
177     def start_traffic(self):
178         pass
179
180     def fetch_capture_packets(self):
181         def _get_packet_capture(mac):
182             # convert text to binary
183             src_mac = bytearray.fromhex(mac.replace(':', '')).decode()
184             return {'binary': bytes('SSSSSS' + src_mac, 'ascii')}
185
186         # for packet capture, generate 2*scc random packets
187         # normally we should generate packets coming from the right dest macs
188         self.packet_list = []
189         for dest_macs in self.traffic_client.generator_config.get_dest_macs():
190             for mac in dest_macs:
191                 self.packet_list.append(_get_packet_capture(mac))
192
193     def stop_traffic(self):
194         pass
195
196     def start_capture(self):
197         pass
198
199     def stop_capture(self):
200         pass
201
202     def cleanup(self):
203         pass
204
205     def set_mode(self):
206         pass
207
208     def set_service_mode(self, enabled=True):
209         pass
210
211     def resolve_arp(self):
212         """Resolve ARP sucessfully."""
213         def get_macs(port, scc):
214             return ['00:00:00:00:%02x:%02x' % (port, chain) for chain in range(scc)]
215         scc = self.traffic_client.generator_config.service_chain_count
216         res = [get_macs(port, scc) for port in range(2)]
217         LOG.info('Dummy TG ARP: %s', str(res))
218         return res