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