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