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