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