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