PapiExecutor always verifies
[csit.git] / resources / libraries / python / InterfaceUtil.py
1 # Copyright (c) 2019 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 """Interface util library."""
15
16 from socket import AF_INET, AF_INET6, inet_ntop
17 from time import sleep
18
19 from enum import IntEnum
20 from ipaddress import ip_address
21 from robot.api import logger
22
23 from resources.libraries.python.Constants import Constants
24 from resources.libraries.python.CpuUtils import CpuUtils
25 from resources.libraries.python.DUTSetup import DUTSetup
26 from resources.libraries.python.L2Util import L2Util
27 from resources.libraries.python.PapiExecutor import PapiExecutor
28 from resources.libraries.python.parsers.JsonParser import JsonParser
29 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
30 from resources.libraries.python.topology import NodeType, Topology
31 from resources.libraries.python.VPPUtil import VPPUtil
32
33
34 class LinkBondLoadBalance(IntEnum):
35     """Link bonding load balance."""
36     L2 = 0
37     L34 = 1
38     L23 = 2
39
40
41 class LinkBondMode(IntEnum):
42     """Link bonding load balance."""
43     ROUND_ROBIN = 1
44     ACTIVE_BACKUP = 2
45     XOR = 3
46     BROADCAST = 4
47     LACP = 5
48
49
50 class InterfaceUtil(object):
51     """General utilities for managing interfaces"""
52
53     __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
54
55     @staticmethod
56     def pci_to_int(pci_str):
57         """Convert PCI address from string format (0000:18:0a.0) to
58         integer representation (169345024).
59
60         :param pci_str: PCI address in string representation.
61         :type pci_str: str
62         :returns: Integer representation of PCI address.
63         :rtype: int
64         """
65         pci = list(pci_str.split(':')[0:2])
66         pci.extend(pci_str.split(':')[2].split('.'))
67
68         return (int(pci[0], 16) | int(pci[1], 16) << 16 |
69                 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
70
71     @staticmethod
72     def get_interface_index(node, interface):
73         """Get interface sw_if_index from topology file.
74
75         :param node: Node where the interface is.
76         :param interface: Numeric index or name string of a specific interface.
77         :type node: dict
78         :type interface: str or int
79         :returns: SW interface index.
80         :rtype: int
81         """
82         try:
83             sw_if_index = int(interface)
84         except ValueError:
85             sw_if_index = Topology.get_interface_sw_index(node, interface)
86             if sw_if_index is None:
87                 sw_if_index = \
88                     Topology.get_interface_sw_index_by_name(node, interface)
89         except TypeError as err:
90             raise TypeError('Wrong interface format {ifc}: {err}'.format(
91                 ifc=interface, err=err.message))
92
93         return sw_if_index
94
95     @staticmethod
96     def set_interface_state(node, interface, state, if_type='key'):
97         """Set interface state on a node.
98
99         Function can be used for DUTs as well as for TGs.
100
101         :param node: Node where the interface is.
102         :param interface: Interface key or sw_if_index or name.
103         :param state: One of 'up' or 'down'.
104         :param if_type: Interface type
105         :type node: dict
106         :type interface: str or int
107         :type state: str
108         :type if_type: str
109         :returns: Nothing.
110         :raises ValueError: If the interface type is unknown.
111         :raises ValueError: If the state of interface is unexpected.
112         :raises ValueError: If the node has an unknown node type.
113         """
114         if if_type == 'key':
115             if isinstance(interface, basestring):
116                 sw_if_index = Topology.get_interface_sw_index(node, interface)
117                 iface_name = Topology.get_interface_name(node, interface)
118             else:
119                 sw_if_index = interface
120         elif if_type == 'name':
121             iface_key = Topology.get_interface_by_name(node, interface)
122             if iface_key is not None:
123                 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
124             iface_name = interface
125         else:
126             raise ValueError('Unknown if_type: {type}'.format(type=if_type))
127
128         if node['type'] == NodeType.DUT:
129             if state == 'up':
130                 admin_up_down = 1
131             elif state == 'down':
132                 admin_up_down = 0
133             else:
134                 raise ValueError('Unexpected interface state: {state}'.format(
135                     state=state))
136             cmd = 'sw_interface_set_flags'
137             err_msg = 'Failed to set interface state on host {host}'.format(
138                 host=node['host'])
139             args = dict(sw_if_index=sw_if_index,
140                         admin_up_down=admin_up_down)
141             with PapiExecutor(node) as papi_exec:
142                 papi_exec.add(cmd, **args).get_reply(err_msg)
143         elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
144             cmd = 'ip link set {ifc} {state}'.format(
145                 ifc=iface_name, state=state)
146             exec_cmd_no_error(node, cmd, sudo=True)
147         else:
148             raise ValueError('Node {} has unknown NodeType: "{}"'
149                              .format(node['host'], node['type']))
150
151     @staticmethod
152     def set_interface_ethernet_mtu(node, iface_key, mtu):
153         """Set Ethernet MTU for specified interface.
154
155         Function can be used only for TGs.
156
157         :param node: Node where the interface is.
158         :param iface_key: Interface key from topology file.
159         :param mtu: MTU to set.
160         :type node: dict
161         :type iface_key: str
162         :type mtu: int
163         :returns: Nothing.
164         :raises ValueError: If the node type is "DUT".
165         :raises ValueError: If the node has an unknown node type.
166         """
167         if node['type'] == NodeType.DUT:
168             raise ValueError('Node {}: Setting Ethernet MTU for interface '
169                              'on DUT nodes not supported', node['host'])
170         elif node['type'] == NodeType.TG:
171             iface_name = Topology.get_interface_name(node, iface_key)
172             cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
173             exec_cmd_no_error(node, cmd, sudo=True)
174         else:
175             raise ValueError('Node {} has unknown NodeType: "{}"'
176                              .format(node['host'], node['type']))
177
178     @staticmethod
179     def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
180         """Set default Ethernet MTU on all interfaces on node.
181
182         Function can be used only for TGs.
183
184         :param node: Node where to set default MTU.
185         :type node: dict
186         :returns: Nothing.
187         """
188         for ifc in node['interfaces']:
189             InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
190
191     @staticmethod
192     def vpp_set_interface_mtu(node, interface, mtu=9200):
193         """Set Ethernet MTU on interface.
194
195         :param node: VPP node.
196         :param interface: Interface to setup MTU. Default: 9200.
197         :param mtu: Ethernet MTU size in Bytes.
198         :type node: dict
199         :type interface: str or int
200         :type mtu: int
201         """
202         if isinstance(interface, basestring):
203             sw_if_index = Topology.get_interface_sw_index(node, interface)
204         else:
205             sw_if_index = interface
206
207         cmd = 'hw_interface_set_mtu'
208         err_msg = 'Failed to set interface MTU on host {host}'.format(
209             host=node['host'])
210         args = dict(sw_if_index=sw_if_index,
211                     mtu=int(mtu))
212         try:
213             with PapiExecutor(node) as papi_exec:
214                 papi_exec.add(cmd, **args).get_reply(err_msg)
215         except AssertionError as err:
216             # TODO: Make failure tolerance optional.
217             logger.debug("Setting MTU failed. Expected?\n{err}".format(
218                 err=err))
219
220     @staticmethod
221     def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
222         """Set Ethernet MTU on all interfaces.
223
224         :param node: VPP node.
225         :param mtu: Ethernet MTU size in Bytes. Default: 9200.
226         :type node: dict
227         :type mtu: int
228         """
229         for interface in node['interfaces']:
230             InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
231
232     @staticmethod
233     def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
234         """Set Ethernet MTU on all interfaces on all DUTs.
235
236         :param nodes: VPP nodes.
237         :param mtu: Ethernet MTU size in Bytes. Default: 9200.
238         :type nodes: dict
239         :type mtu: int
240         """
241         for node in nodes.values():
242             if node['type'] == NodeType.DUT:
243                 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
244
245     @staticmethod
246     def vpp_node_interfaces_ready_wait(node, retries=15):
247         """Wait until all interfaces with admin-up are in link-up state.
248
249         :param node: Node to wait on.
250         :param retries: Number of retries to check interface status (optional,
251             default 15).
252         :type node: dict
253         :type retries: int
254         :returns: Nothing.
255         :raises RuntimeError: If any interface is not in link-up state after
256             defined number of retries.
257         """
258         for _ in xrange(0, retries):
259             not_ready = list()
260             out = InterfaceUtil.vpp_get_interface_data(node)
261             for interface in out:
262                 if interface.get('admin_up_down') == 1:
263                     if interface.get('link_up_down') != 1:
264                         not_ready.append(interface.get('interface_name'))
265             if not not_ready:
266                 break
267             else:
268                 logger.debug('Interfaces still in link-down state:\n{ifs} '
269                              '\nWaiting...'.format(ifs=not_ready))
270                 sleep(1)
271         else:
272             err = 'Timeout, interfaces not up:\n{ifs}'.format(ifs=not_ready) \
273                 if 'not_ready' in locals() else 'No check executed!'
274             raise RuntimeError(err)
275
276     @staticmethod
277     def all_vpp_interfaces_ready_wait(nodes, retries=15):
278         """Wait until all interfaces with admin-up are in link-up state for all
279         nodes in the topology.
280
281         :param nodes: Nodes in the topology.
282         :param retries: Number of retries to check interface status (optional,
283             default 15).
284         :type nodes: dict
285         :type retries: int
286         :returns: Nothing.
287         """
288         for node in nodes.values():
289             if node['type'] == NodeType.DUT:
290                 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
291
292     @staticmethod
293     def vpp_get_interface_data(node, interface=None):
294         """Get all interface data from a VPP node. If a name or
295         sw_interface_index is provided, return only data for the matching
296         interface(s).
297
298         :param node: VPP node to get interface data from.
299         :param interface: Numeric index or name string of a specific interface.
300         :type node: dict
301         :type interface: int or str
302         :returns: List of dictionaries containing data for each interface, or a
303             single dictionary for the specified interface.
304         :rtype: list or dict
305         :raises TypeError: if the data type of interface is neither basestring
306             nor int.
307         """
308         if interface is not None:
309             if isinstance(interface, basestring):
310                 param = 'interface_name'
311             elif isinstance(interface, int):
312                 param = 'sw_if_index'
313             else:
314                 raise TypeError('Wrong interface format {ifc}'.format(
315                     ifc=interface))
316         else:
317             param = ''
318
319         cmd = 'sw_interface_dump'
320         args = dict(name_filter_valid=0,
321                     name_filter='')
322         err_msg = 'Failed to get interface dump on host {host}'.format(
323             host=node['host'])
324         with PapiExecutor(node) as papi_exec:
325             details = papi_exec.add(cmd, **args).get_details(err_msg)
326
327         def process_if_dump(if_dump):
328             """Process interface dump.
329
330             :param if_dump: Interface dump.
331             :type if_dump: dict
332             :returns: Processed interface dump.
333             :rtype: dict
334             """
335             if_dump['interface_name'] = if_dump['interface_name'].rstrip('\x00')
336             if_dump['tag'] = if_dump['tag'].rstrip('\x00')
337             if_dump['l2_address'] = L2Util.bin_to_mac(if_dump['l2_address'])
338             if_dump['b_dmac'] = L2Util.bin_to_mac(if_dump['b_dmac'])
339             if_dump['b_smac'] = L2Util.bin_to_mac(if_dump['b_smac'])
340             return if_dump
341
342         data = list() if interface is None else dict()
343         for if_dump in details:
344             if interface is None:
345                 data.append(process_if_dump(if_dump))
346             elif str(if_dump.get(param)).rstrip('\x00') == str(interface):
347                 data = process_if_dump(if_dump)
348                 break
349
350         logger.debug('Interface data:\n{if_data}'.format(if_data=data))
351         return data
352
353     @staticmethod
354     def vpp_get_interface_name(node, sw_if_index):
355         """Get interface name for the given SW interface index from actual
356         interface dump.
357
358         :param node: VPP node to get interface data from.
359         :param sw_if_index: SW interface index of the specific interface.
360         :type node: dict
361         :type sw_if_index: int
362         :returns: Name of the given interface.
363         :rtype: str
364         """
365         if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
366         if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
367             if_data = InterfaceUtil.vpp_get_interface_data(
368                 node, if_data['sup_sw_if_index'])
369
370         return if_data.get('interface_name')
371
372     @staticmethod
373     def vpp_get_interface_sw_index(node, interface_name):
374         """Get interface name for the given SW interface index from actual
375         interface dump.
376
377         :param node: VPP node to get interface data from.
378         :param interface_name: Interface name.
379         :type node: dict
380         :type interface_name: str
381         :returns: Name of the given interface.
382         :rtype: str
383         """
384         if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
385
386         return if_data.get('sw_if_index')
387
388     @staticmethod
389     def vpp_get_interface_mac(node, interface):
390         """Get MAC address for the given interface from actual interface dump.
391
392         :param node: VPP node to get interface data from.
393         :param interface: Numeric index or name string of a specific interface.
394         :type node: dict
395         :type interface: int or str
396         :returns: MAC address.
397         :rtype: str
398         """
399         if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
400         if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
401             if_data = InterfaceUtil.vpp_get_interface_data(
402                 node, if_data['sup_sw_if_index'])
403
404         return if_data.get('l2_address')
405
406     @staticmethod
407     def tg_set_interface_driver(node, pci_addr, driver):
408         """Set interface driver on the TG node.
409
410         :param node: Node to set interface driver on (must be TG node).
411         :param pci_addr: PCI address of the interface.
412         :param driver: Driver name.
413         :type node: dict
414         :type pci_addr: str
415         :type driver: str
416         :raises RuntimeError: If unbinding from the current driver fails.
417         :raises RuntimeError: If binding to the new driver fails.
418         """
419         old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
420         if old_driver == driver:
421             return
422
423         ssh = SSH()
424         ssh.connect(node)
425
426         # Unbind from current driver
427         if old_driver is not None:
428             cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
429                 .format(pci_addr, old_driver)
430             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
431             if int(ret_code) != 0:
432                 raise RuntimeError("'{0}' failed on '{1}'"
433                                    .format(cmd, node['host']))
434
435         # Bind to the new driver
436         cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
437             .format(pci_addr, driver)
438         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
439         if int(ret_code) != 0:
440             raise RuntimeError("'{0}' failed on '{1}'"
441                                .format(cmd, node['host']))
442
443     @staticmethod
444     def tg_get_interface_driver(node, pci_addr):
445         """Get interface driver from the TG node.
446
447         :param node: Node to get interface driver on (must be TG node).
448         :param pci_addr: PCI address of the interface.
449         :type node: dict
450         :type pci_addr: str
451         :returns: Interface driver or None if not found.
452         :rtype: str
453         :raises RuntimeError: If PCI rescan or lspci command execution failed.
454         """
455         return DUTSetup.get_pci_dev_driver(node, pci_addr)
456
457     @staticmethod
458     def tg_set_interfaces_udev_rules(node):
459         """Set udev rules for interfaces.
460
461         Create udev rules file in /etc/udev/rules.d where are rules for each
462         interface used by TG node, based on MAC interface has specific name.
463         So after unbind and bind again to kernel driver interface has same
464         name as before. This must be called after TG has set name for each
465         port in topology dictionary.
466         udev rule example
467         SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
468         NAME="eth1"
469
470         :param node: Node to set udev rules on (must be TG node).
471         :type node: dict
472         :raises RuntimeError: If setting of udev rules fails.
473         """
474         ssh = SSH()
475         ssh.connect(node)
476
477         cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
478         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
479         if int(ret_code) != 0:
480             raise RuntimeError("'{0}' failed on '{1}'"
481                                .format(cmd, node['host']))
482
483         for interface in node['interfaces'].values():
484             rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
485                    '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
486                    interface['name'] + '\\"'
487             cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
488                 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
489             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
490             if int(ret_code) != 0:
491                 raise RuntimeError("'{0}' failed on '{1}'"
492                                    .format(cmd, node['host']))
493
494         cmd = '/etc/init.d/udev restart'
495         ssh.exec_command_sudo(cmd)
496
497     @staticmethod
498     def tg_set_interfaces_default_driver(node):
499         """Set interfaces default driver specified in topology yaml file.
500
501         :param node: Node to setup interfaces driver on (must be TG node).
502         :type node: dict
503         """
504         for interface in node['interfaces'].values():
505             InterfaceUtil.tg_set_interface_driver(node,
506                                                   interface['pci_address'],
507                                                   interface['driver'])
508
509     @staticmethod
510     def update_vpp_interface_data_on_node(node):
511         """Update vpp generated interface data for a given node in DICT__nodes.
512
513         Updates interface names, software if index numbers and any other details
514         generated specifically by vpp that are unknown before testcase run.
515         It does this by dumping interface list from all devices using python
516         api, and pairing known information from topology (mac address) to state
517         from VPP.
518
519         :param node: Node selected from DICT__nodes.
520         :type node: dict
521         """
522         interface_list = InterfaceUtil.vpp_get_interface_data(node)
523         interface_dict = dict()
524         for ifc in interface_list:
525             interface_dict[ifc['l2_address']] = ifc
526
527         for if_name, if_data in node['interfaces'].items():
528             ifc_dict = interface_dict.get(if_data['mac_address'])
529             if ifc_dict is not None:
530                 if_data['name'] = ifc_dict['interface_name']
531                 if_data['vpp_sw_index'] = ifc_dict['sw_if_index']
532                 if_data['mtu'] = ifc_dict['mtu'][0]
533                 logger.trace('Interface {ifc} found by MAC {mac}'.format(
534                     ifc=if_name, mac=if_data['mac_address']))
535             else:
536                 logger.trace('Interface {ifc} not found by MAC {mac}'.format(
537                     ifc=if_name, mac=if_data['mac_address']))
538                 if_data['vpp_sw_index'] = None
539
540     @staticmethod
541     def update_nic_interface_names(node):
542         """Update interface names based on nic type and PCI address.
543
544         This method updates interface names in the same format as VPP does.
545
546         :param node: Node dictionary.
547         :type node: dict
548         """
549         for ifc in node['interfaces'].values():
550             if_pci = ifc['pci_address'].replace('.', ':').split(':')
551             bus = '{:x}'.format(int(if_pci[1], 16))
552             dev = '{:x}'.format(int(if_pci[2], 16))
553             fun = '{:x}'.format(int(if_pci[3], 16))
554             loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
555             if ifc['model'] == 'Intel-XL710':
556                 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
557             elif ifc['model'] == 'Intel-X710':
558                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
559             elif ifc['model'] == 'Intel-X520-DA2':
560                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
561             elif ifc['model'] == 'Cisco-VIC-1385':
562                 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
563             elif ifc['model'] == 'Cisco-VIC-1227':
564                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
565             else:
566                 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
567
568     @staticmethod
569     def update_nic_interface_names_on_all_duts(nodes):
570         """Update interface names based on nic type and PCI address on all DUTs.
571
572         This method updates interface names in the same format as VPP does.
573
574         :param nodes: Topology nodes.
575         :type nodes: dict
576         """
577         for node in nodes.values():
578             if node['type'] == NodeType.DUT:
579                 InterfaceUtil.update_nic_interface_names(node)
580
581     @staticmethod
582     def update_tg_interface_data_on_node(node, skip_tg_udev=False):
583         """Update interface name for TG/linux node in DICT__nodes.
584
585         .. note::
586             # for dev in `ls /sys/class/net/`;
587             > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
588             "52:54:00:9f:82:63": "eth0"
589             "52:54:00:77:ae:a9": "eth1"
590             "52:54:00:e1:8a:0f": "eth2"
591             "00:00:00:00:00:00": "lo"
592
593         :param node: Node selected from DICT__nodes.
594         :param skip_tg_udev: Skip udev rename on TG node.
595         :type node: dict
596         :type skip_tg_udev: bool
597         :raises RuntimeError: If getting of interface name and MAC fails.
598         """
599         # First setup interface driver specified in yaml file
600         InterfaceUtil.tg_set_interfaces_default_driver(node)
601
602         # Get interface names
603         ssh = SSH()
604         ssh.connect(node)
605
606         cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
607                '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
608
609         (ret_code, stdout, _) = ssh.exec_command(cmd)
610         if int(ret_code) != 0:
611             raise RuntimeError('Get interface name and MAC failed')
612         tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
613         interfaces = JsonParser().parse_data(tmp)
614         for interface in node['interfaces'].values():
615             name = interfaces.get(interface['mac_address'])
616             if name is None:
617                 continue
618             interface['name'] = name
619
620         # Set udev rules for interfaces
621         if not skip_tg_udev:
622             InterfaceUtil.tg_set_interfaces_udev_rules(node)
623
624     @staticmethod
625     def iface_update_numa_node(node):
626         """For all interfaces from topology file update numa node based on
627            information from the node.
628
629         :param node: Node from topology.
630         :type node: dict
631         :returns: Nothing.
632         :raises ValueError: If numa node ia less than 0.
633         :raises RuntimeError: If update of numa node failes.
634         """
635         ssh = SSH()
636         for if_key in Topology.get_node_interfaces(node):
637             if_pci = Topology.get_interface_pci_addr(node, if_key)
638             ssh.connect(node)
639             cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
640             for _ in range(3):
641                 (ret, out, _) = ssh.exec_command(cmd)
642                 if ret == 0:
643                     try:
644                         numa_node = int(out)
645                         if numa_node < 0:
646                             if CpuUtils.cpu_node_count(node) == 1:
647                                 numa_node = 0
648                             else:
649                                 raise ValueError
650                     except ValueError:
651                         logger.trace('Reading numa location failed for: {0}'
652                                      .format(if_pci))
653                     else:
654                         Topology.set_interface_numa_node(node, if_key,
655                                                          numa_node)
656                         break
657             else:
658                 raise RuntimeError('Update numa node failed for: {0}'
659                                    .format(if_pci))
660
661     @staticmethod
662     def update_all_numa_nodes(nodes, skip_tg=False):
663         """For all nodes and all their interfaces from topology file update numa
664         node information based on information from the node.
665
666         :param nodes: Nodes in the topology.
667         :param skip_tg: Skip TG node
668         :type nodes: dict
669         :type skip_tg: bool
670         :returns: Nothing.
671         """
672         for node in nodes.values():
673             if node['type'] == NodeType.DUT:
674                 InterfaceUtil.iface_update_numa_node(node)
675             elif node['type'] == NodeType.TG and not skip_tg:
676                 InterfaceUtil.iface_update_numa_node(node)
677
678     @staticmethod
679     def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
680                                                skip_tg_udev=False,
681                                                numa_node=False):
682         """Update interface names on all nodes in DICT__nodes.
683
684         This method updates the topology dictionary by querying interface lists
685         of all nodes mentioned in the topology dictionary.
686
687         :param nodes: Nodes in the topology.
688         :param skip_tg: Skip TG node.
689         :param skip_tg_udev: Skip udev rename on TG node.
690         :param numa_node: Retrieve numa_node location.
691         :type nodes: dict
692         :type skip_tg: bool
693         :type skip_tg_udev: bool
694         :type numa_node: bool
695         """
696         for node_data in nodes.values():
697             if node_data['type'] == NodeType.DUT:
698                 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
699             elif node_data['type'] == NodeType.TG and not skip_tg:
700                 InterfaceUtil.update_tg_interface_data_on_node(
701                     node_data, skip_tg_udev)
702
703             if numa_node:
704                 if node_data['type'] == NodeType.DUT:
705                     InterfaceUtil.iface_update_numa_node(node_data)
706                 elif node_data['type'] == NodeType.TG and not skip_tg:
707                     InterfaceUtil.iface_update_numa_node(node_data)
708
709     @staticmethod
710     def create_vlan_subinterface(node, interface, vlan):
711         """Create VLAN sub-interface on node.
712
713         :param node: Node to add VLAN subinterface on.
714         :param interface: Interface name on which create VLAN subinterface.
715         :param vlan: VLAN ID of the subinterface to be created.
716         :type node: dict
717         :type interface: str
718         :type vlan: int
719         :returns: Name and index of created subinterface.
720         :rtype: tuple
721         :raises RuntimeError: if it is unable to create VLAN subinterface on the
722             node.
723         """
724         iface_key = Topology.get_interface_by_name(node, interface)
725         sw_if_index = Topology.get_interface_sw_index(node, iface_key)
726
727         cmd = 'create_vlan_subif'
728         args = dict(sw_if_index=sw_if_index,
729                     vlan_id=int(vlan))
730         err_msg = 'Failed to create VLAN sub-interface on host {host}'.format(
731             host=node['host'])
732         with PapiExecutor(node) as papi_exec:
733             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
734
735         if_key = Topology.add_new_port(node, 'vlan_subif')
736         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
737         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
738         Topology.update_interface_name(node, if_key, ifc_name)
739
740         return '{ifc}.{vlan}'.format(ifc=interface, vlan=vlan), sw_if_index
741
742     @staticmethod
743     def create_vxlan_interface(node, vni, source_ip, destination_ip):
744         """Create VXLAN interface and return sw if index of created interface.
745
746         :param node: Node where to create VXLAN interface.
747         :param vni: VXLAN Network Identifier.
748         :param source_ip: Source IP of a VXLAN Tunnel End Point.
749         :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
750         :type node: dict
751         :type vni: int
752         :type source_ip: str
753         :type destination_ip: str
754         :returns: SW IF INDEX of created interface.
755         :rtype: int
756         :raises RuntimeError: if it is unable to create VxLAN interface on the
757             node.
758         """
759         src_address = ip_address(unicode(source_ip))
760         dst_address = ip_address(unicode(destination_ip))
761
762         cmd = 'vxlan_add_del_tunnel'
763         args = dict(is_add=1,
764                     is_ipv6=1 if src_address.version == 6 else 0,
765                     instance=Constants.BITWISE_NON_ZERO,
766                     src_address=src_address.packed,
767                     dst_address=dst_address.packed,
768                     mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
769                     encap_vrf_id=0,
770                     decap_next_index=Constants.BITWISE_NON_ZERO,
771                     vni=int(vni))
772         err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\
773             format(host=node['host'])
774         with PapiExecutor(node) as papi_exec:
775             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
776
777         if_key = Topology.add_new_port(node, 'vxlan_tunnel')
778         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
779         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
780         Topology.update_interface_name(node, if_key, ifc_name)
781
782         return sw_if_index
783
784     @staticmethod
785     def vxlan_dump(node, interface=None):
786         """Get VxLAN data for the given interface.
787
788         :param node: VPP node to get interface data from.
789         :param interface: Numeric index or name string of a specific interface.
790             If None, information about all VxLAN interfaces is returned.
791         :type node: dict
792         :type interface: int or str
793         :returns: Dictionary containing data for the given VxLAN interface or if
794             interface=None, the list of dictionaries with all VxLAN interfaces.
795         :rtype: dict or list
796         :raises TypeError: if the data type of interface is neither basestring
797             nor int.
798         """
799         if interface is not None:
800             sw_if_index = InterfaceUtil.get_interface_index(node, interface)
801         else:
802             sw_if_index = int(Constants.BITWISE_NON_ZERO)
803
804         cmd = 'vxlan_tunnel_dump'
805         args = dict(sw_if_index=sw_if_index)
806         err_msg = 'Failed to get VXLAN dump on host {host}'.format(
807             host=node['host'])
808         with PapiExecutor(node) as papi_exec:
809             details = papi_exec.add(cmd, **args).get_details(err_msg)
810
811         def process_vxlan_dump(vxlan_dump):
812             """Process vxlan dump.
813
814             :param vxlan_dump: Vxlan interface dump.
815             :type vxlan_dump: dict
816             :returns: Processed vxlan interface dump.
817             :rtype: dict
818             """
819             if vxlan_dump['is_ipv6']:
820                 vxlan_dump['src_address'] = \
821                     inet_ntop(AF_INET6, vxlan_dump['src_address'])
822                 vxlan_dump['dst_address'] = \
823                     inet_ntop(AF_INET6, vxlan_dump['dst_address'])
824             else:
825                 vxlan_dump['src_address'] = \
826                     inet_ntop(AF_INET, vxlan_dump['src_address'][0:4])
827                 vxlan_dump['dst_address'] = \
828                     inet_ntop(AF_INET, vxlan_dump['dst_address'][0:4])
829             return vxlan_dump
830
831         data = list() if interface is None else dict()
832         for vxlan_dump in details:
833             if interface is None:
834                 data.append(process_vxlan_dump(vxlan_dump))
835             elif vxlan_dump['sw_if_index'] == sw_if_index:
836                 data = process_vxlan_dump(vxlan_dump)
837                 break
838
839         logger.debug('VXLAN data:\n{vxlan_data}'.format(vxlan_data=data))
840         return data
841
842     @staticmethod
843     def vhost_user_dump(node):
844         """Get vhost-user data for the given node.
845
846         TODO: Move to VhostUser.py
847
848         :param node: VPP node to get interface data from.
849         :type node: dict
850         :returns: List of dictionaries with all vhost-user interfaces.
851         :rtype: list
852         """
853         cmd = 'sw_interface_vhost_user_dump'
854         err_msg = 'Failed to get vhost-user dump on host {host}'.format(
855             host=node['host'])
856         with PapiExecutor(node) as papi_exec:
857             details = papi_exec.add(cmd).get_details(err_msg)
858
859         def process_vhost_dump(vhost_dump):
860             """Process vhost dump.
861
862             :param vhost_dump: Vhost interface dump.
863             :type vhost_dump: dict
864             :returns: Processed vhost interface dump.
865             :rtype: dict
866             """
867             vhost_dump['interface_name'] = \
868                 vhost_dump['interface_name'].rstrip('\x00')
869             vhost_dump['sock_filename'] = \
870                 vhost_dump['sock_filename'].rstrip('\x00')
871             return vhost_dump
872
873         for vhost_dump in details:
874             # In-place edits.
875             process_vhost_dump(vhost_dump)
876
877         logger.debug('Vhost-user details:\n{vhost_details}'.format(
878             vhost_details=details))
879         return details
880
881     @staticmethod
882     def tap_dump(node, name=None):
883         """Get all TAP interface data from the given node, or data about
884         a specific TAP interface.
885
886         TODO: Move to Tap.py
887
888         :param node: VPP node to get data from.
889         :param name: Optional name of a specific TAP interface.
890         :type node: dict
891         :type name: str
892         :returns: Dictionary of information about a specific TAP interface, or
893             a List of dictionaries containing all TAP data for the given node.
894         :rtype: dict or list
895         """
896         cmd = 'sw_interface_tap_v2_dump'
897         err_msg = 'Failed to get TAP dump on host {host}'.format(
898             host=node['host'])
899         with PapiExecutor(node) as papi_exec:
900             details = papi_exec.add(cmd).get_details(err_msg)
901
902         def process_tap_dump(tap_dump):
903             """Process tap dump.
904
905             :param tap_dump: Tap interface dump.
906             :type tap_dump: dict
907             :returns: Processed tap interface dump.
908             :rtype: dict
909             """
910             tap_dump['dev_name'] = tap_dump['dev_name'].rstrip('\x00')
911             tap_dump['host_if_name'] = tap_dump['host_if_name'].rstrip('\x00')
912             tap_dump['host_namespace'] = \
913                 tap_dump['host_namespace'].rstrip('\x00')
914             tap_dump['host_mac_addr'] = \
915                 L2Util.bin_to_mac(tap_dump['host_mac_addr'])
916             tap_dump['host_ip4_addr'] = \
917                 inet_ntop(AF_INET, tap_dump['host_ip4_addr'])
918             tap_dump['host_ip6_addr'] = \
919                 inet_ntop(AF_INET6, tap_dump['host_ip6_addr'])
920             return tap_dump
921
922         data = list() if name is None else dict()
923         for tap_dump in details:
924             if name is None:
925                 data.append(process_tap_dump(tap_dump))
926             elif tap_dump.get('dev_name').rstrip('\x00') == name:
927                 data = process_tap_dump(tap_dump)
928                 break
929
930         logger.debug('TAP data:\n{tap_data}'.format(tap_data=data))
931         return data
932
933     @staticmethod
934     def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
935                             inner_vlan_id=None, type_subif=None):
936         """Create sub-interface on node. It is possible to set required
937         sub-interface type and VLAN tag(s).
938
939         :param node: Node to add sub-interface.
940         :param interface: Interface name on which create sub-interface.
941         :param sub_id: ID of the sub-interface to be created.
942         :param outer_vlan_id: Optional outer VLAN ID.
943         :param inner_vlan_id: Optional inner VLAN ID.
944         :param type_subif: Optional type of sub-interface. Values supported by
945             VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
946             [default_sub]
947         :type node: dict
948         :type interface: str or int
949         :type sub_id: int
950         :type outer_vlan_id: int
951         :type inner_vlan_id: int
952         :type type_subif: str
953         :returns: Name and index of created sub-interface.
954         :rtype: tuple
955         :raises RuntimeError: If it is not possible to create sub-interface.
956         """
957         subif_types = type_subif.split()
958
959         cmd = 'create_subif'
960         args = dict(
961             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
962             sub_id=int(sub_id),
963             no_tags=1 if 'no_tags' in subif_types else 0,
964             one_tag=1 if 'one_tag' in subif_types else 0,
965             two_tags=1 if 'two_tags' in subif_types else 0,
966             dot1ad=1 if 'dot1ad' in subif_types else 0,
967             exact_match=1 if 'exact_match' in subif_types else 0,
968             default_sub=1 if 'default_sub' in subif_types else 0,
969             outer_vlan_id_any=1 if type_subif == 'default_sub' else 0,
970             inner_vlan_id_any=1 if type_subif == 'default_sub' else 0,
971             outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
972             inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0)
973         err_msg = 'Failed to create sub-interface on host {host}'.format(
974             host=node['host'])
975         with PapiExecutor(node) as papi_exec:
976             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
977
978         if_key = Topology.add_new_port(node, 'subinterface')
979         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
980         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
981         Topology.update_interface_name(node, if_key, ifc_name)
982
983         return '{ifc}.{s_id}'.format(ifc=interface, s_id=sub_id), sw_if_index
984
985     @staticmethod
986     def create_gre_tunnel_interface(node, source_ip, destination_ip):
987         """Create GRE tunnel interface on node.
988
989         :param node: VPP node to add tunnel interface.
990         :param source_ip: Source of the GRE tunnel.
991         :param destination_ip: Destination of the GRE tunnel.
992         :type node: dict
993         :type source_ip: str
994         :type destination_ip: str
995         :returns: Name and index of created GRE tunnel interface.
996         :rtype: tuple
997         :raises RuntimeError: If unable to create GRE tunnel interface.
998         """
999         cmd = 'gre_tunnel_add_del'
1000         tunnel = dict(type=0,
1001                       instance=Constants.BITWISE_NON_ZERO,
1002                       src=str(source_ip),
1003                       dst=str(destination_ip),
1004                       outer_fib_id=0,
1005                       session_id=0)
1006         args = dict(is_add=1,
1007                     tunnel=tunnel)
1008         err_msg = 'Failed to create GRE tunnel interface on host {host}'.format(
1009             host=node['host'])
1010         with PapiExecutor(node) as papi_exec:
1011             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1012
1013         if_key = Topology.add_new_port(node, 'gre_tunnel')
1014         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1015         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1016         Topology.update_interface_name(node, if_key, ifc_name)
1017
1018         return ifc_name, sw_if_index
1019
1020     @staticmethod
1021     def vpp_create_loopback(node):
1022         """Create loopback interface on VPP node.
1023
1024         :param node: Node to create loopback interface on.
1025         :type node: dict
1026         :returns: SW interface index.
1027         :rtype: int
1028         :raises RuntimeError: If it is not possible to create loopback on the
1029             node.
1030         """
1031         cmd = 'create_loopback'
1032         args = dict(mac_address=0)
1033         err_msg = 'Failed to create loopback interface on host {host}'.format(
1034             host=node['host'])
1035         with PapiExecutor(node) as papi_exec:
1036             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1037
1038         if_key = Topology.add_new_port(node, 'loopback')
1039         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1040         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1041         Topology.update_interface_name(node, if_key, ifc_name)
1042
1043         return sw_if_index
1044
1045     @staticmethod
1046     def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1047         """Create bond interface on VPP node.
1048
1049         :param node: DUT node from topology.
1050         :param mode: Link bonding mode.
1051         :param load_balance: Load balance (optional, valid for xor and lacp
1052             modes, otherwise ignored).
1053         :param mac: MAC address to assign to the bond interface (optional).
1054         :type node: dict
1055         :type mode: str
1056         :type load_balance: str
1057         :type mac: str
1058         :returns: Interface key (name) in topology.
1059         :rtype: str
1060         :raises RuntimeError: If it is not possible to create bond interface on
1061             the node.
1062         """
1063         cmd = 'bond_create'
1064         args = dict(id=int(Constants.BITWISE_NON_ZERO),
1065                     use_custom_mac=0 if mac is None else 1,
1066                     mac_address=0 if mac is None else L2Util.mac_to_bin(mac),
1067                     mode=getattr(LinkBondMode, '{md}'.format(
1068                         md=mode.replace('-', '_').upper())).value,
1069                     lb=0 if load_balance is None else getattr(
1070                         LinkBondLoadBalance, '{lb}'.format(
1071                             lb=load_balance.upper())).value)
1072         err_msg = 'Failed to create bond interface on host {host}'.format(
1073             host=node['host'])
1074         with PapiExecutor(node) as papi_exec:
1075             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1076
1077         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1078                                         ifc_pfx='eth_bond')
1079         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1080
1081         return if_key
1082
1083     @staticmethod
1084     def add_eth_interface(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
1085         """Add ethernet interface to current topology.
1086
1087         :param node: DUT node from topology.
1088         :param ifc_name: Name of the interface.
1089         :param sw_if_index: SW interface index.
1090         :param ifc_pfx: Interface key prefix.
1091         :type node: dict
1092         :type ifc_name: str
1093         :type sw_if_index: int
1094         :type ifc_pfx: str
1095         """
1096         if_key = Topology.add_new_port(node, ifc_pfx)
1097
1098         if ifc_name and sw_if_index is None:
1099             sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1100                 node, ifc_name)
1101         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1102         if sw_if_index and ifc_name is None:
1103             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1104         Topology.update_interface_name(node, if_key, ifc_name)
1105         ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1106         Topology.update_interface_mac_address(node, if_key, ifc_mac)
1107
1108     @staticmethod
1109     def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
1110         """Create AVF interface on VPP node.
1111
1112         :param node: DUT node from topology.
1113         :param vf_pci_addr: Virtual Function PCI address.
1114         :param num_rx_queues: Number of RX queues.
1115         :type node: dict
1116         :type vf_pci_addr: str
1117         :type num_rx_queues: int
1118         :returns: Interface key (name) in topology.
1119         :rtype: str
1120         :raises RuntimeError: If it is not possible to create AVF interface on
1121             the node.
1122         """
1123         cmd = 'avf_create'
1124         args = dict(pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1125                     enable_elog=0,
1126                     rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1127                     rxq_size=0,
1128                     txq_size=0)
1129         err_msg = 'Failed to create AVF interface on host {host}'.format(
1130             host=node['host'])
1131         with PapiExecutor(node) as papi_exec:
1132             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1133
1134         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1135                                         ifc_pfx='eth_avf')
1136         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1137
1138         return if_key
1139
1140     @staticmethod
1141     def vpp_enslave_physical_interface(node, interface, bond_if):
1142         """Enslave physical interface to bond interface on VPP node.
1143
1144         :param node: DUT node from topology.
1145         :param interface: Physical interface key from topology file.
1146         :param bond_if: Load balance
1147         :type node: dict
1148         :type interface: str
1149         :type bond_if: str
1150         :raises RuntimeError: If it is not possible to enslave physical
1151             interface to bond interface on the node.
1152         """
1153         cmd = 'bond_enslave'
1154         args = dict(
1155             sw_if_index=Topology.get_interface_sw_index(node, interface),
1156             bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1157             is_passive=0,
1158             is_long_timeout=0)
1159         err_msg = 'Failed to enslave physical interface {ifc} to bond ' \
1160                   'interface {bond} on host {host}'.format(ifc=interface,
1161                                                            bond=bond_if,
1162                                                            host=node['host'])
1163         with PapiExecutor(node) as papi_exec:
1164             papi_exec.add(cmd, **args).get_reply(err_msg)
1165
1166     @staticmethod
1167     def vpp_show_bond_data_on_node(node, verbose=False):
1168         """Show (detailed) bond information on VPP node.
1169
1170         :param node: DUT node from topology.
1171         :param verbose: If detailed information is required or not.
1172         :type node: dict
1173         :type verbose: bool
1174         """
1175         cmd = 'sw_interface_bond_dump'
1176         err_msg = 'Failed to get bond interface dump on host {host}'.format(
1177             host=node['host'])
1178
1179         data = ('Bond data on node {host}:\n'.format(host=node['host']))
1180         with PapiExecutor(node) as papi_exec:
1181             details = papi_exec.add(cmd).get_details(err_msg)
1182
1183         for bond in details:
1184             data += ('{b}\n'.format(b=bond['interface_name'].rstrip('\x00')))
1185             data += ('  mode: {m}\n'.format(m=LinkBondMode(
1186                 bond['mode']).name.lower()))
1187             data += ('  load balance: {lb}\n'.format(lb=LinkBondLoadBalance(
1188                 bond['lb']).name.lower()))
1189             data += ('  number of active slaves: {n}\n'.format(
1190                 n=bond['active_slaves']))
1191             if verbose:
1192                 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1193                     node, Topology.get_interface_by_sw_index(
1194                         node, bond['sw_if_index']))
1195                 for slave in slave_data:
1196                     if not slave['is_passive']:
1197                         data += ('    {s}\n'.format(s=slave['interface_name']))
1198             data += ('  number of slaves: {n}\n'.format(n=bond['slaves']))
1199             if verbose:
1200                 for slave in slave_data:
1201                     data += ('    {s}\n'.format(s=slave['interface_name']))
1202             data += ('  interface id: {i}\n'.format(i=bond['id']))
1203             data += ('  sw_if_index: {i}\n'.format(i=bond['sw_if_index']))
1204         logger.info(data)
1205
1206     @staticmethod
1207     def vpp_bond_slave_dump(node, interface):
1208         """Get bond interface slave(s) data on VPP node.
1209
1210         :param node: DUT node from topology.
1211         :param interface: Physical interface key from topology file.
1212         :type node: dict
1213         :type interface: str
1214         :returns: Bond slave interface data.
1215         :rtype: dict
1216         """
1217         cmd = 'sw_interface_slave_dump'
1218         args = dict(sw_if_index=Topology.get_interface_sw_index(
1219             node, interface))
1220         err_msg = 'Failed to get slave dump on host {host}'.format(
1221             host=node['host'])
1222
1223         with PapiExecutor(node) as papi_exec:
1224             details = papi_exec.add(cmd, **args).get_details(err_msg)
1225
1226         def process_slave_dump(slave_dump):
1227             """Process slave dump.
1228
1229             :param slave_dump: Slave interface dump.
1230             :type slave_dump: dict
1231             :returns: Processed slave interface dump.
1232             :rtype: dict
1233             """
1234             slave_dump['interface_name'] = slave_dump['interface_name'].\
1235                 rstrip('\x00')
1236             return slave_dump
1237
1238         for slave_dump in details:
1239             # In-place edits.
1240             process_slave_dump(slave_dump)
1241
1242         logger.debug('Slave data:\n{slave_data}'.format(slave_data=details))
1243         return details
1244
1245     @staticmethod
1246     def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1247         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1248
1249         :param nodes: Nodes in the topology.
1250         :param verbose: If detailed information is required or not.
1251         :type nodes: dict
1252         :type verbose: bool
1253         """
1254         for node_data in nodes.values():
1255             if node_data['type'] == NodeType.DUT:
1256                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1257
1258     @staticmethod
1259     def vpp_enable_input_acl_interface(node, interface, ip_version,
1260                                        table_index):
1261         """Enable input acl on interface.
1262
1263         :param node: VPP node to setup interface for input acl.
1264         :param interface: Interface to setup input acl.
1265         :param ip_version: Version of IP protocol.
1266         :param table_index: Classify table index.
1267         :type node: dict
1268         :type interface: str or int
1269         :type ip_version: str
1270         :type table_index: int
1271         """
1272         cmd = 'input_acl_set_interface'
1273         args = dict(
1274             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1275             ip4_table_index=table_index if ip_version == 'ip4'
1276             else Constants.BITWISE_NON_ZERO,
1277             ip6_table_index=table_index if ip_version == 'ip6'
1278             else Constants.BITWISE_NON_ZERO,
1279             l2_table_index=table_index if ip_version == 'l2'
1280             else Constants.BITWISE_NON_ZERO,
1281             is_add=1)
1282         err_msg = 'Failed to enable input acl on interface {ifc}'.format(
1283             ifc=interface)
1284         with PapiExecutor(node) as papi_exec:
1285             papi_exec.add(cmd, **args).get_reply(err_msg)
1286
1287     @staticmethod
1288     def get_interface_classify_table(node, interface):
1289         """Get name of classify table for the given interface.
1290
1291         TODO: Move to Classify.py.
1292
1293         :param node: VPP node to get data from.
1294         :param interface: Name or sw_if_index of a specific interface.
1295         :type node: dict
1296         :type interface: str or int
1297         :returns: Classify table name.
1298         :rtype: str
1299         """
1300         if isinstance(interface, basestring):
1301             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1302         else:
1303             sw_if_index = interface
1304
1305         cmd = 'classify_table_by_interface'
1306         args = dict(sw_if_index=sw_if_index)
1307         err_msg = 'Failed to get classify table name by interface {ifc}'.format(
1308             ifc=interface)
1309         with PapiExecutor(node) as papi_exec:
1310             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1311
1312         return reply
1313
1314     @staticmethod
1315     def get_sw_if_index(node, interface_name):
1316         """Get sw_if_index for the given interface from actual interface dump.
1317
1318         :param node: VPP node to get interface data from.
1319         :param interface_name: Name of the specific interface.
1320         :type node: dict
1321         :type interface_name: str
1322         :returns: sw_if_index of the given interface.
1323         :rtype: str
1324         """
1325         interface_data = InterfaceUtil.vpp_get_interface_data(
1326             node, interface=interface_name)
1327         return interface_data.get('sw_if_index')
1328
1329     @staticmethod
1330     def vxlan_gpe_dump(node, interface_name=None):
1331         """Get VxLAN GPE data for the given interface.
1332
1333         :param node: VPP node to get interface data from.
1334         :param interface_name: Name of the specific interface. If None,
1335             information about all VxLAN GPE interfaces is returned.
1336         :type node: dict
1337         :type interface_name: str
1338         :returns: Dictionary containing data for the given VxLAN GPE interface
1339             or if interface=None, the list of dictionaries with all VxLAN GPE
1340             interfaces.
1341         :rtype: dict or list
1342         """
1343         if interface_name is not None:
1344             sw_if_index = InterfaceUtil.get_interface_index(
1345                 node, interface_name)
1346         else:
1347             sw_if_index = int(Constants.BITWISE_NON_ZERO)
1348
1349         cmd = 'vxlan_gpe_tunnel_dump'
1350         args = dict(sw_if_index=sw_if_index)
1351         err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
1352             host=node['host'])
1353         with PapiExecutor(node) as papi_exec:
1354             details = papi_exec.add(cmd, **args).get_details(err_msg)
1355
1356         def process_vxlan_gpe_dump(vxlan_dump):
1357             """Process vxlan_gpe dump.
1358
1359             :param vxlan_dump: Vxlan_gpe nterface dump.
1360             :type vxlan_dump: dict
1361             :returns: Processed vxlan_gpe interface dump.
1362             :rtype: dict
1363             """
1364             if vxlan_dump['is_ipv6']:
1365                 vxlan_dump['local'] = \
1366                     inet_ntop(AF_INET6, vxlan_dump['local'])
1367                 vxlan_dump['remote'] = \
1368                     inet_ntop(AF_INET6, vxlan_dump['remote'])
1369             else:
1370                 vxlan_dump['local'] = \
1371                     inet_ntop(AF_INET, vxlan_dump['local'][0:4])
1372                 vxlan_dump['remote'] = \
1373                     inet_ntop(AF_INET, vxlan_dump['remote'][0:4])
1374             return vxlan_dump
1375
1376         data = list() if interface_name is None else dict()
1377         for vxlan_dump in details:
1378             if interface_name is None:
1379                 data.append(process_vxlan_gpe_dump(vxlan_dump))
1380             elif vxlan_dump['sw_if_index'] == sw_if_index:
1381                 data = process_vxlan_gpe_dump(vxlan_dump)
1382                 break
1383
1384         logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format(
1385             vxlan_gpe_data=data))
1386         return data
1387
1388     @staticmethod
1389     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1390         """Assign VPP interface to specific VRF/FIB table.
1391
1392         :param node: VPP node where the FIB and interface are located.
1393         :param interface: Interface to be assigned to FIB.
1394         :param table_id: VRF table ID.
1395         :param ipv6: Assign to IPv6 table. Default False.
1396         :type node: dict
1397         :type interface: str or int
1398         :type table_id: int
1399         :type ipv6: bool
1400         """
1401         cmd = 'sw_interface_set_table'
1402         args = dict(
1403             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1404             is_ipv6=1 if ipv6 else 0,
1405             vrf_id=int(table_id))
1406         err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
1407             ifc=interface)
1408         with PapiExecutor(node) as papi_exec:
1409             papi_exec.add(cmd, **args).get_reply(err_msg)
1410
1411     @staticmethod
1412     def set_linux_interface_mac(node, interface, mac, namespace=None,
1413                                 vf_id=None):
1414         """Set MAC address for interface in linux.
1415
1416         :param node: Node where to execute command.
1417         :param interface: Interface in namespace.
1418         :param mac: MAC to be assigned to interface.
1419         :param namespace: Execute command in namespace. Optional
1420         :param vf_id: Virtual Function id. Optional
1421         :type node: dict
1422         :type interface: str
1423         :type mac: str
1424         :type namespace: str
1425         :type vf_id: int
1426         """
1427         mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1428             if vf_id is not None else 'address {mac}'.format(mac=mac)
1429         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1430
1431         cmd = ('{ns} ip link set {interface} {mac}'.
1432                format(ns=ns_str, interface=interface, mac=mac_str))
1433         exec_cmd_no_error(node, cmd, sudo=True)
1434
1435     @staticmethod
1436     def set_linux_interface_trust_on(node, interface, namespace=None,
1437                                      vf_id=None):
1438         """Set trust on (promisc) for interface in linux.
1439
1440         :param node: Node where to execute command.
1441         :param interface: Interface in namespace.
1442         :param namespace: Execute command in namespace. Optional
1443         :param vf_id: Virtual Function id. Optional
1444         :type node: dict
1445         :type interface: str
1446         :type namespace: str
1447         :type vf_id: int
1448         """
1449         trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1450             if vf_id is not None else 'trust on'
1451         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1452
1453         cmd = ('{ns} ip link set dev {interface} {trust}'.
1454                format(ns=ns_str, interface=interface, trust=trust_str))
1455         exec_cmd_no_error(node, cmd, sudo=True)
1456
1457     @staticmethod
1458     def set_linux_interface_spoof_off(node, interface, namespace=None,
1459                                       vf_id=None):
1460         """Set spoof off for interface in linux.
1461
1462         :param node: Node where to execute command.
1463         :param interface: Interface in namespace.
1464         :param namespace: Execute command in namespace. Optional
1465         :param vf_id: Virtual Function id. Optional
1466         :type node: dict
1467         :type interface: str
1468         :type namespace: str
1469         :type vf_id: int
1470         """
1471         spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1472             if vf_id is not None else 'spoof off'
1473         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1474
1475         cmd = ('{ns} ip link set dev {interface} {spoof}'.
1476                format(ns=ns_str, interface=interface, spoof=spoof_str))
1477         exec_cmd_no_error(node, cmd, sudo=True)
1478
1479     @staticmethod
1480     def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'):
1481         """Init PCI device by creating VFs and bind them to vfio-pci for AVF
1482         driver testing on DUT.
1483
1484         :param node: DUT node.
1485         :param ifc_key: Interface key from topology file.
1486         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
1487         :param osi_layer: OSI Layer type to initialize TG with.
1488             Default value "L2" sets linux interface spoof off.
1489         :type node: dict
1490         :type ifc_key: str
1491         :type numvfs: int
1492         :type osi_layer: str
1493         :returns: Virtual Function topology interface keys.
1494         :rtype: list
1495         :raises RuntimeError: If a reason preventing initialization is found.
1496         """
1497         # Read PCI address and driver.
1498         pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1499         pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1500         uio_driver = Topology.get_uio_driver(node)
1501         kernel_driver = Topology.get_interface_driver(node, ifc_key)
1502         if kernel_driver != "i40e":
1503             raise RuntimeError(
1504                 "AVF needs i40e driver, not {driver} at node {host} ifc {ifc}"\
1505                 .format(driver=kernel_driver, host=node["host"], ifc=ifc_key))
1506         current_driver = DUTSetup.get_pci_dev_driver(
1507             node, pf_pci_addr.replace(':', r'\:'))
1508
1509         VPPUtil.stop_vpp_service(node)
1510         if current_driver != kernel_driver:
1511             # PCI device must be re-bound to kernel driver before creating VFs.
1512             DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1513             # Stop VPP to prevent deadlock.
1514             # Unbind from current driver.
1515             DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1516             # Bind to kernel driver.
1517             DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1518
1519         # Initialize PCI VFs
1520         DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1521
1522         vf_ifc_keys = []
1523         # Set MAC address and bind each virtual function to uio driver.
1524         for vf_id in range(numvfs):
1525             vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1526                                     pf_mac_addr[3], pf_mac_addr[4],
1527                                     pf_mac_addr[5], "{:02x}".format(vf_id)])
1528
1529             pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1530                 format(pci=pf_pci_addr)
1531             InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1532                                                        vf_id=vf_id)
1533             if osi_layer == 'L2':
1534                 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1535                                                             vf_id=vf_id)
1536             InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1537                                                   vf_id=vf_id)
1538
1539             DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1540             DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1541
1542             # Add newly created ports into topology file
1543             vf_ifc_name = '{pf_if_key}_vf'.format(pf_if_key=ifc_key)
1544             vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1545             vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1546             Topology.update_interface_name(node, vf_ifc_key,
1547                                            vf_ifc_name+str(vf_id+1))
1548             Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1549             Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1550             vf_ifc_keys.append(vf_ifc_key)
1551
1552         return vf_ifc_keys
1553
1554     @staticmethod
1555     def vpp_sw_interface_rx_placement_dump(node):
1556         """Dump VPP interface RX placement on node.
1557
1558         :param node: Node to run command on.
1559         :type node: dict
1560         :returns: Thread mapping information as a list of dictionaries.
1561         :rtype: list
1562         """
1563         cmd = 'sw_interface_rx_placement_dump'
1564         err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
1565             cmd=cmd, host=node['host'])
1566         with PapiExecutor(node) as papi_exec:
1567             for ifc in node['interfaces'].values():
1568                 if ifc['vpp_sw_index'] is not None:
1569                     papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
1570             details = papi_exec.get_details(err_msg)
1571         return sorted(details, key=lambda k: k['sw_if_index'])
1572
1573     @staticmethod
1574     def vpp_sw_interface_set_rx_placement(node, sw_if_index, queue_id,
1575                                           worker_id):
1576         """Set interface RX placement to worker on node.
1577
1578         :param node: Node to run command on.
1579         :param sw_if_index: VPP SW interface index.
1580         :param queue_id: VPP interface queue ID.
1581         :param worker_id: VPP worker ID (indexing from 0).
1582         :type node: dict
1583         :type sw_if_index: int
1584         :type queue_id: int
1585         :type worker_id: int
1586         :raises RuntimeError: If failed to run command on host or if no API
1587             reply received.
1588         """
1589         cmd = 'sw_interface_set_rx_placement'
1590         err_msg = "Failed to set interface RX placement to worker on host " \
1591                   "{host}!".format(host=node['host'])
1592         args = dict(sw_if_index=sw_if_index, queue_id=queue_id,
1593                     worker_id=worker_id)
1594         with PapiExecutor(node) as papi_exec:
1595             papi_exec.add(cmd, **args).get_reply(err_msg)
1596
1597     @staticmethod
1598     def vpp_round_robin_rx_placement(node, prefix):
1599         """Set Round Robin interface RX placement on all worker threads
1600         on node.
1601
1602         :param node: Topology nodes.
1603         :param prefix: Interface name prefix.
1604         :type node: dict
1605         :type prefix: str
1606         """
1607         worker_id = 0
1608         worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1609         if not worker_cnt:
1610             return
1611         for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1612             for interface in node['interfaces'].values():
1613                 if placement['sw_if_index'] == interface['vpp_sw_index'] \
1614                     and prefix in interface['name']:
1615                     InterfaceUtil.vpp_sw_interface_set_rx_placement(
1616                         node, placement['sw_if_index'], placement['queue_id'],
1617                         worker_id % worker_cnt)
1618                     worker_id += 1
1619
1620     @staticmethod
1621     def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1622         """Set Round Robin interface RX placement on all worker threads
1623         on all DUTs.
1624
1625         :param nodes: Topology nodes.
1626         :param prefix: Interface name prefix.
1627         :type nodes: dict
1628         :type prefix: str
1629         """
1630         for node in nodes.values():
1631             if node['type'] == NodeType.DUT:
1632                 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)

©2016 FD.io a Linux Foundation Collaborative Project. All Rights Reserved.
Linux Foundation is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds.
Please see our privacy policy and terms of use.