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