vpp_config: Rework for Python2/3 compatibility.
[vpp.git] / extras / vpp_config / vpplib / VppPCIUtil.py
1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 from __future__ import print_function
15
16 """VPP PCI Utility libraries"""
17
18 import re
19 import logging
20
21 from vpplib.VPPUtil import VPPUtil
22
23 DPDK_SCRIPT = "/vpp/vpp-config/scripts/dpdk-devbind.py"
24
25 # PCI Device id regular expresssion
26 PCI_DEV_ID_REGEX = '[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+.[0-9A-Fa-f]+'
27
28
29 class VppPCIUtil(object):
30     """
31     PCI Utilities
32
33     """
34
35     @staticmethod
36     def _create_device_list(device_string):
37         """
38         Returns a list of PCI devices
39
40         :param device_string: The devices string from dpdk_devbind
41         :returns: The device list
42         :rtype: dictionary
43         """
44
45         devices = {}
46
47         ids = re.findall(PCI_DEV_ID_REGEX, device_string)
48         descriptions = re.findall(r'\'([\s\S]*?)\'', device_string)
49         unused = re.findall(r'unused=\w+|unused=', device_string)
50
51         for i, j in enumerate(ids):
52             device = {'description': descriptions[i]}
53             if unused:
54                 device['unused'] = unused[i].split('=')[1].split(',')
55
56             cmd = 'ls /sys/bus/pci/devices/{}/driver/module/drivers'. \
57                 format(ids[i])
58             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
59             if ret == 0:
60                 device['driver'] = stdout.split(':')[1].rstrip('\n')
61
62             cmd = 'cat /sys/bus/pci/devices/{}/numa_node'.format(ids[i])
63             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
64             if ret != 0:
65                 raise RuntimeError('{} failed {} {}'.
66                                    format(cmd, stderr, stdout))
67             numa_node = stdout.rstrip('\n')
68             if numa_node == '-1':
69                 device['numa_node'] = '0'
70             else:
71                 device['numa_node'] = numa_node
72
73             interfaces = []
74             device['interfaces'] = []
75             cmd = 'ls /sys/bus/pci/devices/{}/net'.format(ids[i])
76             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
77             if ret == 0:
78                 interfaces = stdout.rstrip('\n').split()
79                 device['interfaces'] = interfaces
80
81             l2_addrs = []
82             for intf in interfaces:
83                 cmd = 'cat /sys/bus/pci/devices/{}/net/{}/address'.format(
84                     ids[i], intf)
85                 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
86                 if ret != 0:
87                     raise RuntimeError('{} failed {} {}'.
88                                        format(cmd, stderr, stdout))
89
90                 l2_addrs.append(stdout.rstrip('\n'))
91
92             device['l2addr'] = l2_addrs
93
94             devices[ids[i]] = device
95
96         return devices
97
98     def __init__(self, node):
99         self._node = node
100         self._dpdk_devices = {}
101         self._kernel_devices = {}
102         self._other_devices = {}
103         self._crypto_dpdk_devices = {}
104         self._crypto_kernel_devices = {}
105         self._crypto_other_devices = {}
106         self._link_up_devices = {}
107
108     def get_all_devices(self):
109         """
110         Returns a list of all the devices
111
112         """
113
114         node = self._node
115         rootdir = node['rootdir']
116         dpdk_script = rootdir + DPDK_SCRIPT
117         cmd = dpdk_script + ' --status'
118         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
119         if ret != 0:
120             raise RuntimeError('{} failed on node {} {}'.format(
121                 cmd,
122                 node['host'],
123                 stderr))
124
125         # Get the network devices using the DPDK
126         # First get everything after using DPDK
127         stda = stdout.split('Network devices using DPDK-compatible driver')[1]
128         # Then get everything before using kernel driver
129         using_dpdk = stda.split('Network devices using kernel driver')[0]
130         self._dpdk_devices = self._create_device_list(using_dpdk)
131
132         # Get the network devices using the kernel
133         stda = stdout.split('Network devices using kernel driver')[1]
134         using_kernel = stda.split('Other network devices')[0]
135         self._kernel_devices = self._create_device_list(using_kernel)
136
137         # Get the other network devices
138         stda = stdout.split('Other network devices')[1]
139         other = stda.split('Crypto devices using DPDK-compatible driver')[0]
140         self._other_devices = self._create_device_list(other)
141
142         # Get the crypto devices using the DPDK
143         stda = stdout.split('Crypto devices using DPDK-compatible driver')[1]
144         crypto_using_dpdk = stda.split('Crypto devices using kernel driver')[0]
145         self._crypto_dpdk_devices = self._create_device_list(
146             crypto_using_dpdk)
147
148         # Get the network devices using the kernel
149         stda = stdout.split('Crypto devices using kernel driver')[1]
150         crypto_using_kernel = stda.split('Other crypto devices')[0]
151         self._crypto_kernel_devices = self._create_device_list(
152             crypto_using_kernel)
153
154         # Get the other network devices
155         crypto_other = stdout.split('Other crypto devices')[1]
156         self._crypto_other_devices = self._create_device_list(crypto_other)
157
158         # Get the devices used by the kernel
159         for devk in self._kernel_devices.items():
160             dvid = devk[0]
161             device = devk[1]
162             for i in device['interfaces']:
163                 cmd = "ip addr show " + i
164                 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
165                 if ret != 0:
166                     raise RuntimeError('{} failed on node {} {}'.format(
167                         cmd,
168                         node['host'],
169                         stderr))
170                 lstate = re.findall(r'state \w+', stdout)[0].split(' ')[1]
171
172                 # Take care of the links that are UP
173                 if lstate == 'UP':
174                     device['linkup'] = True
175                     self._link_up_devices[dvid] = device
176
177         for devl in self._link_up_devices.items():
178             dvid = devl[0]
179             del self._kernel_devices[dvid]
180
181     def get_dpdk_devices(self):
182         """
183         Returns a list the dpdk devices
184
185         """
186         return self._dpdk_devices
187
188     def get_kernel_devices(self):
189         """
190         Returns a list the kernel devices
191
192         """
193         return self._kernel_devices
194
195     def get_other_devices(self):
196         """
197         Returns a list the other devices
198
199         """
200         return self._other_devices
201
202     def get_crypto_dpdk_devices(self):
203         """
204         Returns a list the crypto dpdk devices
205
206         """
207         return self._crypto_dpdk_devices
208
209     def get_crypto_kernel_devices(self):
210         """
211         Returns a list the crypto kernel devices
212
213         """
214         return self._crypto_kernel_devices
215
216     def get_crypto_other_devices(self):
217         """
218         Returns a list the crypto other devices
219
220         """
221         return self._crypto_other_devices
222
223     def get_link_up_devices(self):
224         """
225         Returns a list the link up devices
226
227         """
228         return self._link_up_devices
229
230     @staticmethod
231     def vpp_create_interface(interfaces, device_id, device):
232         """
233         Create an interface using the device is and device
234
235         """
236
237         name = 'port' + str(len(interfaces))
238         interfaces[name] = {}
239         interfaces[name]['pci_address'] = device_id
240         interfaces[name]['numa_node'] = device['numa_node']
241         if 'l2addr' in device:
242             l2_addrs = device['l2addr']
243             for i, j in enumerate(l2_addrs):
244                 if i > 0:
245                     mname = 'mac_address' + str(i + 1)
246                     interfaces[name][mname] = l2_addrs[i]
247                 else:
248                     interfaces[name]['mac_address'] = l2_addrs[i]
249
250     @staticmethod
251     def show_vpp_devices(devices, show_interfaces=True, show_header=True):
252         """
253         show the vpp devices specified in the argument
254
255         :param devices: A list of devices
256         :param show_interfaces: show the kernel information
257         :param show_header: Display the header if true
258         :type devices: dict
259         :type show_interfaces: bool
260         :type show_header: bool
261         """
262
263         if show_interfaces:
264             header = "{:15} {:25} {:50}".format("PCI ID",
265                                                 "Kernel Interface(s)",
266                                                 "Description")
267         else:
268             header = "{:15} {:50}".format("PCI ID",
269                                           "Description")
270         dashseparator = ("-" * (len(header) - 2))
271
272         if show_header is True:
273             print (header)
274             print (dashseparator)
275         for dit in devices.items():
276             dvid = dit[0]
277             device = dit[1]
278             if show_interfaces:
279                 interfaces = device['interfaces']
280                 interface = ''
281                 for i, j in enumerate(interfaces):
282                     if i > 0:
283                         interface += ',' + interfaces[i]
284                     else:
285                         interface = interfaces[i]
286
287                 print ("{:15} {:25} {:50}".format(
288                     dvid, interface, device['description']))
289             else:
290                 print ("{:15} {:50}".format(
291                     dvid, device['description']))
292
293     @staticmethod
294     def unbind_vpp_device(node, device_id):
295         """
296         unbind the device specified
297
298         :param node: Node dictionary with cpuinfo.
299         :param device_id: The device id
300         :type node: dict
301         :type device_id: string
302         """
303
304         rootdir = node['rootdir']
305         dpdk_script = rootdir + DPDK_SCRIPT
306         cmd = dpdk_script + ' -u ' + ' ' + device_id
307         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
308         if ret != 0:
309             raise RuntimeError('{} failed on node {} {} {}'.format(
310                 cmd, node['host'],
311                 stdout, stderr))
312
313     @staticmethod
314     def bind_vpp_device(node, driver, device_id):
315         """
316         bind the device specified
317
318         :param node: Node dictionary with cpuinfo.
319         :param driver: The driver
320         :param device_id: The device id
321         :type node: dict
322         :type driver: string
323         :type device_id: string
324         :returns ret: Command return code
325         """
326
327         rootdir = node['rootdir']
328         dpdk_script = rootdir + DPDK_SCRIPT
329         cmd = dpdk_script + ' -b ' + driver + ' ' + device_id
330         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
331         if ret != 0:
332             logging.error('{} failed on node {}'.format(
333                 cmd, node['host'], stdout, stderr))
334             logging.error('{} {}'.format(
335                 stdout, stderr))
336
337         return ret