Changes from ViNePerf
[samplevnf.git] / VNFs / DPPD-PROX / helper-scripts / rapid / rapid_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 import os
22 import re
23
24 class RapidMachine(object):
25     """
26     Class to deal with a PROX instance (VM, bare metal, container)
27     """
28     def __init__(self, key, user, password, vim, rundir, resultsdir,
29             machine_params, configonly):
30         self.name = machine_params['name']
31         self.ip = machine_params['admin_ip']
32         self.key = key
33         self.user = user
34         self.password = password
35         self.rundir = rundir
36         self.resultsdir = resultsdir
37         self.dp_ports = []
38         self.dpdk_port_index = []
39         self.configonly = configonly
40         index = 1
41         while True:
42             ip_key = 'dp_ip{}'.format(index)
43             mac_key = 'dp_mac{}'.format(index)
44             if ip_key in machine_params.keys() and mac_key in machine_params.keys():
45                 dp_port = {'ip': machine_params[ip_key], 'mac' : machine_params[mac_key]}
46                 self.dp_ports.append(dict(dp_port))
47                 self.dpdk_port_index.append(index - 1)
48                 index += 1
49             else:
50                 break
51         self.machine_params = machine_params
52         self.vim = vim
53         self.cpu_mapping = None
54         self.numa_nodes = [ 0 ]
55         self.socket_mem_mb = '512'
56         if 'config_file' in self.machine_params.keys():
57             PROXConfigfile =  open (self.machine_params['config_file'], 'r')
58             PROXConfig = PROXConfigfile.read()
59             PROXConfigfile.close()
60             self.all_tasks_for_this_cfg = set(re.findall("task\s*=\s*(\d+)",PROXConfig))
61
62     def get_cores(self):
63         return (self.machine_params['cores'])
64
65     def expand_list_format(self, list):
66         """Expand cpuset list format provided as comma-separated list of
67         numbers and ranges of numbers. For more information please see
68         https://man7.org/linux/man-pages/man7/cpuset.7.html
69         """
70         list_expanded = []
71         for num in list.split(','):
72             if '-' in num:
73                 num_range = num.split('-')
74                 list_expanded += range(int(num_range[0]), int(num_range[1]) + 1)
75             else:
76                 list_expanded.append(int(num))
77         return list_expanded
78
79     def read_cpuset(self):
80         """Read list of cpus on which we allowed to execute
81         """
82         cmd = 'cat /sys/fs/cgroup/cpuset/cpuset.cpus'
83         cpuset_cpus = self._client.run_cmd(cmd).decode().rstrip()
84         RapidLog.debug('{} ({}): Allocated cpuset: {}'.format(self.name, self.ip, cpuset_cpus))
85         self.cpu_mapping = self.expand_list_format(cpuset_cpus)
86         RapidLog.debug('{} ({}): Expanded cpuset: {}'.format(self.name, self.ip, self.cpu_mapping))
87
88         # Log CPU core mapping for user information
89         cpu_mapping_str = ''
90         for i in range(len(self.cpu_mapping)):
91             cpu_mapping_str = cpu_mapping_str + '[' + str(i) + '->' + str(self.cpu_mapping[i]) + '], '
92         cpu_mapping_str = cpu_mapping_str[:-2]
93         RapidLog.debug('{} ({}): CPU mapping: {}'.format(self.name, self.ip, cpu_mapping_str))
94
95     def remap_cpus(self, cpus):
96         """Convert relative cpu ids provided as function parameter to match
97         cpu ids from allocated list
98         """
99         cpus_remapped = []
100         for cpu in cpus:
101             cpus_remapped.append(self.cpu_mapping[cpu])
102         return cpus_remapped
103
104     def remap_all_cpus(self):
105         """Convert relative cpu ids for different parameters (mcore, cores)
106         """
107         if self.cpu_mapping is None:
108             RapidLog.debug('{} ({}): cpu mapping is not defined! Please check the configuration!'.format(self.name, self.ip))
109             return
110
111         if 'mcore' in self.machine_params.keys():
112             cpus_remapped = self.remap_cpus(self.machine_params['mcore'])
113             RapidLog.debug('{} ({}): mcore {} remapped to {}'.format(self.name, self.ip, self.machine_params['mcore'], cpus_remapped))
114             self.machine_params['mcore'] = cpus_remapped
115
116         if 'cores' in self.machine_params.keys():
117             cpus_remapped = self.remap_cpus(self.machine_params['cores'])
118             RapidLog.debug('{} ({}): cores {} remapped to {}'.format(self.name, self.ip, self.machine_params['cores'], cpus_remapped))
119             self.machine_params['cores'] = cpus_remapped
120
121     def read_cpuset_mems(self):
122         """Read list of NUMA nodes on which we allowed to allocate memory
123         """
124         cmd = 'cat /sys/fs/cgroup/cpuset/cpuset.mems'
125         cpuset_mems = self._client.run_cmd(cmd).decode().rstrip()
126         RapidLog.debug('{} ({}): Allowed NUMA nodes: {}'.format(self.name, self.ip, cpuset_mems))
127         self.numa_nodes = self.expand_list_format(cpuset_mems)
128         RapidLog.debug('{} ({}): Expanded allowed NUMA nodes: {}'.format(self.name, self.ip, self.numa_nodes))
129
130     def get_prox_socket_mem_str(self):
131         socket_mem_str = ''
132         for node in range(self.numa_nodes[-1] + 1):
133             if node in self.numa_nodes:
134                 socket_mem_str += self.socket_mem_mb + ','
135             else:
136                 socket_mem_str += '0,'
137         socket_mem_str = socket_mem_str[:-1]
138         return socket_mem_str
139
140     def devbind(self):
141         # Script to bind the right network interface to the poll mode driver
142         for index, dp_port in enumerate(self.dp_ports, start = 1):
143             DevBindFileName = self.rundir + '/devbind-{}-port{}.sh'.format(self.ip, index)
144             self._client.scp_put('./devbind.sh', DevBindFileName)
145             cmd =  'sed -i \'s/MACADDRESS/' + dp_port['mac'] + '/\' ' + DevBindFileName 
146             result = self._client.run_cmd(cmd)
147             RapidLog.debug('devbind.sh MAC updated for port {} on {} {}'.format(index, self.name, result))
148             if ((not self.configonly) and self.machine_params['prox_launch_exit']):
149                 result = self._client.run_cmd(DevBindFileName)
150                 RapidLog.debug('devbind.sh running for port {} on {} {}'.format(index, self.name, result))
151
152     def generate_lua(self, appendix = ''):
153         self.LuaFileName = 'parameters-{}.lua'.format(self.ip)
154         with open(self.LuaFileName, "w") as LuaFile:
155             LuaFile.write('require "helper"\n')
156             LuaFile.write('name="%s"\n'% self.name)
157             for index, dp_port in enumerate(self.dp_ports, start = 1):
158                 LuaFile.write('local_ip{}="{}"\n'.format(index, dp_port['ip']))
159                 LuaFile.write('local_hex_ip{}=convertIPToHex(local_ip{})\n'.format(index, index))
160             if self.vim in ['kubernetes']:
161                 socket_mem_str = self.get_prox_socket_mem_str()
162                 RapidLog.debug('{} ({}): PROX socket mem str: {}'.format(self.name, self.ip, socket_mem_str))
163                 LuaFile.write("eal=\"--socket-mem=%s --file-prefix %s --pci-whitelist %s\"\n" % (socket_mem_str, self.name, self.machine_params['dp_pci_dev']))
164             else:
165                 LuaFile.write("eal=\"\"\n")
166             if 'mcore' in self.machine_params.keys():
167                 LuaFile.write('mcore="%s"\n'% ','.join(map(str, self.machine_params['mcore'])))
168             if 'cores' in self.machine_params.keys():
169                 LuaFile.write('cores="%s"\n'% ','.join(map(str, self.machine_params['cores'])))
170             if 'ports' in self.machine_params.keys():
171                 LuaFile.write('ports="%s"\n'% ','.join(map(str, self.machine_params['ports'])))
172             if 'dest_ports' in self.machine_params.keys():
173                 for index, dest_port in enumerate(self.machine_params['dest_ports'], start = 1):
174                     LuaFile.write('dest_ip{}="{}"\n'.format(index, dest_port['ip']))
175                     LuaFile.write('dest_hex_ip{}=convertIPToHex(dest_ip{})\n'.format(index, index))
176                     LuaFile.write('dest_hex_mac{}="{}"\n'.format(index , dest_port['mac'].replace(':',' ')))
177             if 'gw_vm' in self.machine_params.keys():
178                 for index, gw_ip in enumerate(self.machine_params['gw_ips'],
179                         start = 1):
180                     LuaFile.write('gw_ip{}="{}"\n'.format(index, gw_ip))
181                     LuaFile.write('gw_hex_ip{}=convertIPToHex(gw_ip{})\n'.
182                             format(index, index))
183             LuaFile.write(appendix)
184         self._client.scp_put(self.LuaFileName, self.rundir + '/parameters.lua')
185         self._client.scp_put('helper.lua', self.rundir + '/helper.lua')
186
187     def start_prox(self, autostart=''):
188         if self.machine_params['prox_socket']:
189             self._client = prox_ctrl(self.ip, self.key, self.user,
190                     self.password)
191             self._client.test_connection()
192             if self.vim in ['OpenStack']:
193                 self.devbind()
194             if self.vim in ['kubernetes']:
195                 self.read_cpuset()
196                 self.read_cpuset_mems()
197                 self.remap_all_cpus()
198             _, prox_config_file_name = os.path.split(self.
199                     machine_params['config_file'])
200             if self.machine_params['prox_launch_exit']:
201                 self.generate_lua()
202                 self._client.scp_put(self.machine_params['config_file'], '{}/{}'.
203                         format(self.rundir, prox_config_file_name))
204                 if not self.configonly:
205                     cmd = 'sudo {}/prox {} -t -o cli -f {}/{}'.format(self.rundir,
206                             autostart, self.rundir, prox_config_file_name)
207                     RapidLog.debug("Starting PROX on {}: {}".format(self.name,
208                         cmd))
209                     result = self._client.run_cmd(cmd)
210                     RapidLog.debug("Finished PROX on {}: {}".format(self.name,
211                         cmd))
212
213     def close_prox(self):
214         if (not self.configonly) and self.machine_params[
215                 'prox_socket'] and self.machine_params['prox_launch_exit']:
216             self.socket.quit_prox()
217             self._client.scp_get('/prox.log', '{}/{}.prox.log'.format(
218                 self.resultsdir, self.name))
219
220     def connect_prox(self):
221         if self.machine_params['prox_socket']:
222            self.socket = self._client.connect_socket()
223
224     def start(self):
225         self.socket.start(self.get_cores())
226
227     def stop(self):
228         self.socket.stop(self.get_cores())
229
230     def reset_stats(self):
231         self.socket.reset_stats()
232
233     def core_stats(self):
234         return (self.socket.core_stats(self.get_cores(), self.all_tasks_for_this_cfg))
235
236     def multi_port_stats(self):
237         return (self.socket.multi_port_stats(self.dpdk_port_index))