5d6916a7fd5745908f20a36222a1d2e9bb57c786
[samplevnf.git] / VNFs / DPPD-PROX / helper-scripts / rapid / rapid_generator_machine.py
1 #!/usr/bin/python
2
3 ##
4 ## Copyright (c) 2020 Intel Corporation
5 ##
6 ## Licensed under the Apache License, Version 2.0 (the "License");
7 ## you may not use this file except in compliance with the License.
8 ## You may obtain a copy of the License at
9 ##
10 ##     http://www.apache.org/licenses/LICENSE-2.0
11 ##
12 ## Unless required by applicable law or agreed to in writing, software
13 ## distributed under the License is distributed on an "AS IS" BASIS,
14 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 ## See the License for the specific language governing permissions and
16 ## limitations under the License.
17 ##
18
19 from rapid_log import RapidLog 
20 from prox_ctrl import prox_ctrl
21 from rapid_machine import RapidMachine
22 from math import ceil, log2
23
24
25 class RandomPortBits(object):
26     """
27     Class to generate PROX bitmaps for random bit generation
28     in source & dst UPD ports to emulate mutiple flows
29     """
30     @staticmethod
31     def get_bitmap(flow_number):
32         number_of_random_bits = ceil(log2(flow_number))
33         if number_of_random_bits > 30:
34             raise Exception("Not able to support that many flows")
35             # throw exeption since we need the first bit to be 1
36             # Otherwise, the randomization could results in all 0's
37             # and that might be an invalid UDP port and result in 
38             # packets being discarded
39         src_number_of_random_bits = number_of_random_bits // 2
40         dst_number_of_random_bits = (number_of_random_bits -
41                 src_number_of_random_bits)
42         src_port_bitmap = '1000000000000000'.replace ('0','X',
43                 src_number_of_random_bits)
44         dst_port_bitmap = '1000000000000000'.replace ('0','X',
45                 dst_number_of_random_bits)
46         return [src_port_bitmap, dst_port_bitmap, 1 << number_of_random_bits]
47
48 class RapidGeneratorMachine(RapidMachine):
49     """
50     Class to deal with a generator PROX instance (VM, bare metal, container)
51     """
52     def __init__(self, key, user, vim, rundir, resultsdir, machine_params,
53             configonly, ipv6):
54         mac_address_size = 6
55         ethertype_size = 2
56         FCS_size = 4
57         if ipv6:
58             ip_header_size = 40
59             self.ip_length_offset = 18
60             # In IPV6, the IP size is the size of the IP content
61             self.frame_size_minus_ip_size = (2 * mac_address_size +
62                     ethertype_size + ip_header_size + FCS_size)
63         else:
64             ip_header_size = 20
65             self.ip_length_offset = 16
66             # In IPV4, the IP size is the size of the IP header + IP content
67             self.frame_size_minus_ip_size = (2 * mac_address_size +
68                     ethertype_size + FCS_size)
69         self.frame_size_minus_udp_header_and_content = (2 * mac_address_size +
70                 ethertype_size + ip_header_size + FCS_size )
71         udp_header_start_offset = (2 * mac_address_size + ethertype_size +
72                 ip_header_size)
73         self.udp_source_port_offset = udp_header_start_offset 
74         self.udp_dest_port_offset = udp_header_start_offset + 2
75         self.udp_length_offset = udp_header_start_offset + 4
76         self.ipv6 = ipv6
77         if 'bucket_size_exp' in machine_params.keys():
78             self.bucket_size_exp = machine_params['bucket_size_exp']
79         else:
80             self.bucket_size_exp = 11
81         super().__init__(key, user, vim, rundir, resultsdir, machine_params,
82                 configonly)
83
84     def get_cores(self):
85         return (self.machine_params['gencores'] +
86                 self.machine_params['latcores'])
87
88     def remap_all_cpus(self):
89         """Convert relative cpu ids for different parameters (gencores, latcores)
90         """
91         super().remap_all_cpus()
92
93         if self.cpu_mapping is None:
94             return
95
96         if 'gencores' in self.machine_params.keys():
97             cpus_remapped = super().remap_cpus(self.machine_params['gencores'])
98             RapidLog.debug('{} ({}): gencores {} remapped to {}'.format(self.name, self.ip, self.machine_params['gencores'], cpus_remapped))
99             self.machine_params['gencores'] = cpus_remapped
100
101         if 'latcores' in self.machine_params.keys():
102             cpus_remapped = super().remap_cpus(self.machine_params['latcores'])
103             RapidLog.debug('{} ({}): latcores {} remapped to {}'.format(self.name, self.ip, self.machine_params['latcores'], cpus_remapped))
104             self.machine_params['latcores'] = cpus_remapped
105
106     def generate_lua(self):
107         appendix = 'gencores="%s"\n'% ','.join(map(str,
108             self.machine_params['gencores']))
109         appendix = appendix + 'latcores="%s"\n'% ','.join(map(str,
110             self.machine_params['latcores']))
111         appendix = (appendix +
112                 'bucket_size_exp="{}"\n'.format(self.bucket_size_exp))
113         if 'heartbeat' in self.machine_params.keys():
114             appendix = (appendix +
115                     'heartbeat="%s"\n'% self.machine_params['heartbeat'])
116         else:
117             appendix = appendix + 'heartbeat="60"\n'
118         super().generate_lua(appendix)
119
120     def start_prox(self):
121         # Start the generator with the -e option so that the cores don't
122         # start automatically
123         super().start_prox('-e')
124         if self.vim in ['kubernetes']:
125             self.remap_all_cpus()
126
127     def set_generator_speed(self, speed):
128         # The assumption is that we only use task 0 for generating
129         # We should check the gen.cfg file to make sure there is only task=0
130         speed_per_gen_core = speed / len(self.machine_params['gencores']) 
131         self.socket.speed(speed_per_gen_core, self.machine_params['gencores'])
132
133     def set_udp_packet_size(self, imix_frame_sizes):
134         # We should check the gen.cfg to make sure we only send UDP packets
135         # If only 1 packet size, still using the 'old' way of setting the 
136         # packet sizes in PROX. Otherwise, using the 'new' way which
137         # automatically sets IP and UDP sizes. We should switch to the new way
138         # eventually for all cases.
139         if len(imix_frame_sizes) == 1:
140             # Frame size = PROX pkt size + 4 bytes CRC
141             # The set_size function takes the PROX packet size as a parameter
142             self.socket.set_size(self.machine_params['gencores'], 0,
143                     imix_frame_sizes[0] - 4)
144             # Writing length in the ip header
145             self.socket.set_value(self.machine_params['gencores'], 0,
146                     self.ip_length_offset, imix_frame_sizes[0] - 
147                     self.frame_size_minus_ip_size, 2)
148             # Writing length in the udp header
149             self.socket.set_value(self.machine_params['gencores'], 0,
150                     self.udp_length_offset, imix_frame_sizes[0] -
151                     self.frame_size_minus_udp_header_and_content, 2)
152         else:
153             if self.ipv6:
154                 RapidLog.critical('IMIX not supported for IPV6')
155             prox_sizes = [frame_size - 4 for frame_size in imix_frame_sizes]
156             self.socket.set_imix(self.machine_params['gencores'], 0,
157                     prox_sizes)
158
159     def set_flows(self, number_of_flows):
160         source_port, destination_port, actualflows = RandomPortBits.get_bitmap(
161                 number_of_flows)
162         self.socket.set_random(self.machine_params['gencores'],0,
163                 self.udp_source_port_offset, source_port,2)
164         self.socket.set_random(self.machine_params['gencores'],0,
165                 self.udp_dest_port_offset, destination_port,2)
166         return actualflows
167
168     def start_gen_cores(self):
169         self.socket.start(self.machine_params['gencores'])
170
171     def stop_gen_cores(self):
172         self.socket.stop(self.machine_params['gencores'])
173
174     def start_latency_cores(self):
175         self.socket.start(self.machine_params['latcores'])
176
177     def stop_latency_cores(self):
178         self.socket.stop(self.machine_params['latcores'])
179
180     def lat_stats(self):
181         # Checking all tasks in the cfg file. In this way, we can have more
182         # latency tasks on the same core
183         return (self.socket.lat_stats(self.machine_params['latcores'],
184                 self.all_tasks_for_this_cfg))