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