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