FIX: Interface up handling
[csit.git] / resources / libraries / python / InterfaceUtil.py
1 # Copyright (c) 2018 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.ssh import SSH
21 from resources.libraries.python.IPUtil import convert_ipv4_netmask_prefix
22 from resources.libraries.python.DUTSetup import DUTSetup
23 from resources.libraries.python.ssh import exec_cmd_no_error
24 from resources.libraries.python.topology import NodeType, Topology
25 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
26 from resources.libraries.python.VatJsonUtil import VatJsonUtil
27 from resources.libraries.python.parsers.JsonParser import JsonParser
28
29
30 class InterfaceUtil(object):
31     """General utilities for managing interfaces"""
32
33     __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
34
35     @staticmethod
36     def set_interface_state(node, interface, state, if_type="key"):
37         """Set interface state on a node.
38
39         Function can be used for DUTs as well as for TGs.
40
41         :param node: Node where the interface is.
42         :param interface: Interface key or sw_if_index or name.
43         :param state: One of 'up' or 'down'.
44         :param if_type: Interface type
45         :type node: dict
46         :type interface: str or int
47         :type state: str
48         :type if_type: str
49         :returns: Nothing.
50         :raises ValueError: If the interface type is unknown.
51         :raises ValueError: If the state of interface is unexpected.
52         :raises ValueError: If the node has an unknown node type.
53         """
54
55         if if_type == "key":
56             if isinstance(interface, basestring):
57                 sw_if_index = Topology.get_interface_sw_index(node, interface)
58                 iface_name = Topology.get_interface_name(node, interface)
59             else:
60                 sw_if_index = interface
61         elif if_type == "name":
62             iface_key = Topology.get_interface_by_name(node, interface)
63             if iface_key is not None:
64                 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
65             iface_name = interface
66         else:
67             raise ValueError("if_type unknown: {}".format(if_type))
68
69         if node['type'] == NodeType.DUT:
70             if state == 'up':
71                 state = 'admin-up link-up'
72             elif state == 'down':
73                 state = 'admin-down link-down'
74             else:
75                 raise ValueError('Unexpected interface state: {}'.format(state))
76             VatExecutor.cmd_from_template(node, 'set_if_state.vat',
77                                           sw_if_index=sw_if_index, state=state)
78         elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
79             cmd = 'ip link set {} {}'.format(iface_name, state)
80             exec_cmd_no_error(node, cmd, sudo=True)
81         else:
82             raise ValueError('Node {} has unknown NodeType: "{}"'
83                              .format(node['host'], node['type']))
84
85     @staticmethod
86     def set_interface_ethernet_mtu(node, iface_key, mtu):
87         """Set Ethernet MTU for specified interface.
88
89         Function can be used only for TGs.
90
91         :param node: Node where the interface is.
92         :param iface_key: Interface key from topology file.
93         :param mtu: MTU to set.
94         :type node: dict
95         :type iface_key: str
96         :type mtu: int
97         :returns: Nothing.
98         :raises ValueError: If the node type is "DUT".
99         :raises ValueError: If the node has an unknown node type.
100         """
101         if node['type'] == NodeType.DUT:
102             raise ValueError('Node {}: Setting Ethernet MTU for interface '
103                              'on DUT nodes not supported', node['host'])
104         elif node['type'] == NodeType.TG:
105             iface_name = Topology.get_interface_name(node, iface_key)
106             cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
107             exec_cmd_no_error(node, cmd, sudo=True)
108         else:
109             raise ValueError('Node {} has unknown NodeType: "{}"'
110                              .format(node['host'], node['type']))
111
112     @staticmethod
113     def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
114         """Set default Ethernet MTU on all interfaces on node.
115
116         Function can be used only for TGs.
117
118         :param node: Node where to set default MTU.
119         :type node: dict
120         :returns: Nothing.
121         """
122         for ifc in node['interfaces']:
123             InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
124
125     @staticmethod
126     def vpp_set_interface_mtu(node, interface, mtu=9200):
127         """Set Ethernet MTU on interface.
128
129         :param node: VPP node.
130         :param interface: Interface to setup MTU. Default: 9200.
131         :param mtu: Ethernet MTU size in Bytes.
132         :type node: dict
133         :type interface: str or int
134         :type mtu: int
135         """
136         if isinstance(interface, basestring):
137             sw_if_index = Topology.get_interface_sw_index(node, interface)
138         else:
139             sw_if_index = interface
140
141         if sw_if_index:
142             with VatTerminal(node, json_param=False) as vat:
143                 vat.vat_terminal_exec_cmd_from_template(
144                     "hw_interface_set_mtu.vat", sw_if_index=sw_if_index,
145                     mtu=mtu)
146
147     @staticmethod
148     def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
149         """Set Ethernet MTU on all interfaces.
150
151         :param node: VPP node.
152         :param mtu: Ethernet MTU size in Bytes. Default: 9200.
153         :type node: dict
154         :type mtu: int
155         """
156         for interface in node['interfaces']:
157             InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
158
159     @staticmethod
160     def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
161         """Set Ethernet MTU on all interfaces on all DUTs.
162
163         :param nodes: VPP nodes.
164         :param mtu: Ethernet MTU size in Bytes. Default: 9200.
165         :type nodes: dict
166         :type mtu: int
167         """
168         for node in nodes.values():
169             if node['type'] == NodeType.DUT:
170                 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
171
172     @staticmethod
173     def vpp_node_interfaces_ready_wait(node, timeout=30):
174         """Wait until all interfaces with admin-up are in link-up state.
175
176         :param node: Node to wait on.
177         :param timeout: Waiting timeout in seconds (optional, default 10s).
178         :type node: dict
179         :type timeout: int
180         :returns: Nothing.
181         :raises RuntimeError: If the timeout period value has elapsed.
182         """
183         if_ready = False
184         not_ready = []
185         start = time()
186         while not if_ready:
187             out = InterfaceUtil.vpp_get_interface_data(node)
188             if time() - start > timeout:
189                 for interface in out:
190                     if interface.get('admin_up_down') == 1:
191                         if interface.get('link_up_down') != 1:
192                             logger.debug('{0} link-down'.format(
193                                 interface.get('interface_name')))
194                 raise RuntimeError('timeout, not up {0}'.format(not_ready))
195             not_ready = []
196             for interface in out:
197                 if interface.get('admin_up_down') == 1:
198                     if interface.get('link_up_down') != 1:
199                         not_ready.append(interface.get('interface_name'))
200             if not not_ready:
201                 if_ready = True
202             else:
203                 logger.debug('Interfaces still in link-down state: {0}, '
204                              'waiting...'.format(not_ready))
205                 sleep(1)
206
207     @staticmethod
208     def vpp_nodes_interfaces_ready_wait(nodes, timeout=30):
209         """Wait until all interfaces with admin-up are in link-up state for
210         listed nodes.
211
212         :param nodes: List of nodes to wait on.
213         :param timeout: Seconds to wait per node for all interfaces to come up.
214         :type nodes: list
215         :type timeout: int
216         :returns: Nothing.
217         """
218         for node in nodes:
219             InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
220
221     @staticmethod
222     def all_vpp_interfaces_ready_wait(nodes, timeout=30):
223         """Wait until all interfaces with admin-up are in link-up state for all
224         nodes in the topology.
225
226         :param nodes: Nodes in the topology.
227         :param timeout: Seconds to wait per node for all interfaces to come up.
228         :type nodes: dict
229         :type timeout: int
230         :returns: Nothing.
231         """
232         for node in nodes.values():
233             if node['type'] == NodeType.DUT:
234                 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
235
236     @staticmethod
237     def vpp_get_interface_data(node, interface=None):
238         """Get all interface data from a VPP node. If a name or
239         sw_interface_index is provided, return only data for the matching
240         interface.
241
242         :param node: VPP node to get interface data from.
243         :param interface: Numeric index or name string of a specific interface.
244         :type node: dict
245         :type interface: int or str
246         :returns: List of dictionaries containing data for each interface, or a
247             single dictionary for the specified interface.
248         :rtype: list or dict
249         :raises TypeError: if the data type of interface is neither basestring
250             nor int.
251         """
252         with VatTerminal(node) as vat:
253             response = vat.vat_terminal_exec_cmd_from_template(
254                 "interface_dump.vat")
255
256         data = response[0]
257
258         if interface is not None:
259             if isinstance(interface, basestring):
260                 param = "interface_name"
261             elif isinstance(interface, int):
262                 param = "sw_if_index"
263             else:
264                 raise TypeError
265             for data_if in data:
266                 if data_if[param] == interface:
267                     return data_if
268             return dict()
269         return data
270
271     @staticmethod
272     def vpp_get_interface_name(node, sw_if_index):
273         """Get interface name for the given SW interface index from actual
274         interface dump.
275
276         :param node: VPP node to get interface data from.
277         :param sw_if_index: SW interface index of the specific interface.
278         :type node: dict
279         :type sw_if_index: int
280         :returns: Name of the given interface.
281         :rtype: str
282         """
283
284         if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
285         if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
286             if_data = InterfaceUtil.vpp_get_interface_data(
287                 node, if_data['sup_sw_if_index'])
288         try:
289             if_name = if_data["interface_name"]
290         except KeyError:
291             if_name = None
292         return if_name
293
294     @staticmethod
295     def vpp_get_interface_mac(node, interface=None):
296         """Get MAC address for the given interface from actual interface dump.
297
298         :param node: VPP node to get interface data from.
299         :param interface: Numeric index or name string of a specific interface.
300         :type node: dict
301         :type interface: int or str
302         :returns: MAC address.
303         :rtype: str
304         """
305
306         if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
307         if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
308             if_data = InterfaceUtil.vpp_get_interface_data(
309                 node, if_data['sup_sw_if_index'])
310         mac_data = [str(hex(item))[2:] for item in if_data['l2_address'][:6]]
311         mac_data_nice = []
312         for item in mac_data:
313             if len(item) == 1:
314                 item = '0' + item
315             mac_data_nice.append(item)
316         mac = ":".join(mac_data_nice)
317         return mac
318
319     @staticmethod
320     def vpp_get_interface_ip_addresses(node, interface, ip_version):
321         """Get list of IP addresses from an interface on a VPP node.
322
323          :param node: VPP node to get data from.
324          :param interface: Name of an interface on the VPP node.
325          :param ip_version: IP protocol version (ipv4 or ipv6).
326          :type node: dict
327          :type interface: str
328          :type ip_version: str
329          :returns: List of dictionaries, each containing IP address, subnet
330             prefix length and also the subnet mask for ipv4 addresses.
331             Note: A single interface may have multiple IP addresses assigned.
332          :rtype: list
333         """
334
335         try:
336             sw_if_index = Topology.convert_interface_reference(
337                 node, interface, "sw_if_index")
338         except RuntimeError:
339             if isinstance(interface, basestring):
340                 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
341             else:
342                 raise
343
344         with VatTerminal(node) as vat:
345             response = vat.vat_terminal_exec_cmd_from_template(
346                 "ip_address_dump.vat", ip_version=ip_version,
347                 sw_if_index=sw_if_index)
348
349         data = response[0]
350
351         if ip_version == "ipv4":
352             for item in data:
353                 item["netmask"] = convert_ipv4_netmask_prefix(
354                     item["prefix_length"])
355         return data
356
357     @staticmethod
358     def tg_set_interface_driver(node, pci_addr, driver):
359         """Set interface driver on the TG node.
360
361         :param node: Node to set interface driver on (must be TG node).
362         :param pci_addr: PCI address of the interface.
363         :param driver: Driver name.
364         :type node: dict
365         :type pci_addr: str
366         :type driver: str
367         :raises RuntimeError: If unbinding from the current driver fails.
368         :raises RuntimeError: If binding to the new driver fails.
369         """
370         old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
371         if old_driver == driver:
372             return
373
374         ssh = SSH()
375         ssh.connect(node)
376
377         # Unbind from current driver
378         if old_driver is not None:
379             cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
380                 .format(pci_addr, old_driver)
381             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
382             if int(ret_code) != 0:
383                 raise RuntimeError("'{0}' failed on '{1}'"
384                                    .format(cmd, node['host']))
385
386         # Bind to the new driver
387         cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
388             .format(pci_addr, driver)
389         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
390         if int(ret_code) != 0:
391             raise RuntimeError("'{0}' failed on '{1}'"
392                                .format(cmd, node['host']))
393
394     @staticmethod
395     def tg_get_interface_driver(node, pci_addr):
396         """Get interface driver from the TG node.
397
398         :param node: Node to get interface driver on (must be TG node).
399         :param pci_addr: PCI address of the interface.
400         :type node: dict
401         :type pci_addr: str
402         :returns: Interface driver or None if not found.
403         :rtype: str
404         :raises RuntimeError: If PCI rescan or lspci command execution failed.
405         """
406         return DUTSetup.get_pci_dev_driver(node, pci_addr)
407
408     @staticmethod
409     def tg_set_interfaces_udev_rules(node):
410         """Set udev rules for interfaces.
411
412         Create udev rules file in /etc/udev/rules.d where are rules for each
413         interface used by TG node, based on MAC interface has specific name.
414         So after unbind and bind again to kernel driver interface has same
415         name as before. This must be called after TG has set name for each
416         port in topology dictionary.
417         udev rule example
418         SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
419         NAME="eth1"
420
421         :param node: Node to set udev rules on (must be TG node).
422         :type node: dict
423         :raises RuntimeError: If setting of udev rules fails.
424         """
425         ssh = SSH()
426         ssh.connect(node)
427
428         cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
429         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
430         if int(ret_code) != 0:
431             raise RuntimeError("'{0}' failed on '{1}'"
432                                .format(cmd, node['host']))
433
434         for interface in node['interfaces'].values():
435             rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
436                    '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
437                    interface['name'] + '\\"'
438             cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
439                 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
440             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
441             if int(ret_code) != 0:
442                 raise RuntimeError("'{0}' failed on '{1}'"
443                                    .format(cmd, node['host']))
444
445         cmd = '/etc/init.d/udev restart'
446         ssh.exec_command_sudo(cmd)
447
448     @staticmethod
449     def tg_set_interfaces_default_driver(node):
450         """Set interfaces default driver specified in topology yaml file.
451
452         :param node: Node to setup interfaces driver on (must be TG node).
453         :type node: dict
454         """
455         for interface in node['interfaces'].values():
456             InterfaceUtil.tg_set_interface_driver(node,
457                                                   interface['pci_address'],
458                                                   interface['driver'])
459
460     @staticmethod
461     def update_vpp_interface_data_on_node(node):
462         """Update vpp generated interface data for a given node in DICT__nodes.
463
464         Updates interface names, software if index numbers and any other details
465         generated specifically by vpp that are unknown before testcase run.
466         It does this by dumping interface list to JSON output from all
467         devices using vpp_api_test, and pairing known information from topology
468         (mac address/pci address of interface) to state from VPP.
469
470         :param node: Node selected from DICT__nodes.
471         :type node: dict
472         """
473         vat_executor = VatExecutor()
474         vat_executor.execute_script_json_out("dump_interfaces.vat", node)
475         interface_dump_json = vat_executor.get_script_stdout()
476         VatJsonUtil.update_vpp_interface_data_from_json(node,
477                                                         interface_dump_json)
478
479     @staticmethod
480     def update_nic_interface_names(node):
481         """Update interface names based on nic type and PCI address.
482
483         This method updates interface names in the same format as VPP does.
484
485         :param node: Node dictionary.
486         :type node: dict
487         """
488         for ifc in node['interfaces'].values():
489             if_pci = ifc['pci_address'].replace('.', ':').split(':')
490             bus = '{:x}'.format(int(if_pci[1], 16))
491             dev = '{:x}'.format(int(if_pci[2], 16))
492             fun = '{:x}'.format(int(if_pci[3], 16))
493             loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
494             if ifc['model'] == 'Intel-XL710':
495                 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
496             elif ifc['model'] == 'Intel-X710':
497                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
498             elif ifc['model'] == 'Intel-X520-DA2':
499                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
500             elif ifc['model'] == 'Cisco-VIC-1385':
501                 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
502             elif ifc['model'] == 'Cisco-VIC-1227':
503                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
504             else:
505                 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
506
507     @staticmethod
508     def update_nic_interface_names_on_all_duts(nodes):
509         """Update interface names based on nic type and PCI address on all DUTs.
510
511         This method updates interface names in the same format as VPP does.
512
513         :param nodes: Topology nodes.
514         :type nodes: dict
515         """
516         for node in nodes.values():
517             if node['type'] == NodeType.DUT:
518                 InterfaceUtil.update_nic_interface_names(node)
519
520     @staticmethod
521     def update_tg_interface_data_on_node(node):
522         """Update interface name for TG/linux node in DICT__nodes.
523
524         .. note::
525             # for dev in `ls /sys/class/net/`;
526             > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
527             "52:54:00:9f:82:63": "eth0"
528             "52:54:00:77:ae:a9": "eth1"
529             "52:54:00:e1:8a:0f": "eth2"
530             "00:00:00:00:00:00": "lo"
531
532         .. note:: TODO: parse lshw -json instead
533
534         :param node: Node selected from DICT__nodes.
535         :type node: dict
536         :raises RuntimeError: If getting of interface name and MAC fails.
537         """
538         # First setup interface driver specified in yaml file
539         InterfaceUtil.tg_set_interfaces_default_driver(node)
540
541         # Get interface names
542         ssh = SSH()
543         ssh.connect(node)
544
545         cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
546                '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
547
548         (ret_code, stdout, _) = ssh.exec_command(cmd)
549         if int(ret_code) != 0:
550             raise RuntimeError('Get interface name and MAC failed')
551         tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
552         interfaces = JsonParser().parse_data(tmp)
553         for interface in node['interfaces'].values():
554             name = interfaces.get(interface['mac_address'])
555             if name is None:
556                 continue
557             interface['name'] = name
558
559         # Set udev rules for interfaces
560         InterfaceUtil.tg_set_interfaces_udev_rules(node)
561
562     @staticmethod
563     def iface_update_numa_node(node):
564         """For all interfaces from topology file update numa node based on
565            information from the node.
566
567         :param node: Node from topology.
568         :type node: dict
569         :returns: Nothing.
570         :raises ValueError: If numa node ia less than 0.
571         :raises RuntimeError: If update of numa node failes.
572         """
573         ssh = SSH()
574         for if_key in Topology.get_node_interfaces(node):
575             if_pci = Topology.get_interface_pci_addr(node, if_key)
576             ssh.connect(node)
577             cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
578             for _ in range(3):
579                 (ret, out, _) = ssh.exec_command(cmd)
580                 if ret == 0:
581                     try:
582                         numa_node = int(out)
583                         if numa_node < 0:
584                             raise ValueError
585                     except ValueError:
586                         logger.trace('Reading numa location failed for: {0}'\
587                             .format(if_pci))
588                     else:
589                         Topology.set_interface_numa_node(node, if_key,
590                                                          numa_node)
591                         break
592             else:
593                 raise RuntimeError('Update numa node failed for: {0}'\
594                     .format(if_pci))
595
596     @staticmethod
597     def update_all_numa_nodes(nodes, skip_tg=False):
598         """For all nodes and all their interfaces from topology file update numa
599         node information based on information from the node.
600
601         :param nodes: Nodes in the topology.
602         :param skip_tg: Skip TG node
603         :type nodes: dict
604         :type skip_tg: bool
605         :returns: Nothing.
606         """
607         for node in nodes.values():
608             if node['type'] == NodeType.DUT:
609                 InterfaceUtil.iface_update_numa_node(node)
610             elif node['type'] == NodeType.TG and not skip_tg:
611                 InterfaceUtil.iface_update_numa_node(node)
612
613     @staticmethod
614     def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
615                                                numa_node=False):
616         """Update interface names on all nodes in DICT__nodes.
617
618         This method updates the topology dictionary by querying interface lists
619         of all nodes mentioned in the topology dictionary.
620
621         :param nodes: Nodes in the topology.
622         :param skip_tg: Skip TG node
623         :param numa_node: Retrieve numa_node location.
624         :type nodes: dict
625         :type skip_tg: bool
626         :type numa_node: bool
627         """
628         for node_data in nodes.values():
629             if node_data['type'] == NodeType.DUT:
630                 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
631             elif node_data['type'] == NodeType.TG and not skip_tg:
632                 InterfaceUtil.update_tg_interface_data_on_node(node_data)
633
634             if numa_node:
635                 if node_data['type'] == NodeType.DUT:
636                     InterfaceUtil.iface_update_numa_node(node_data)
637                 elif node_data['type'] == NodeType.TG and not skip_tg:
638                     InterfaceUtil.iface_update_numa_node(node_data)
639
640     @staticmethod
641     def create_vlan_subinterface(node, interface, vlan):
642         """Create VLAN subinterface on node.
643
644         :param node: Node to add VLAN subinterface on.
645         :param interface: Interface name on which create VLAN subinterface.
646         :param vlan: VLAN ID of the subinterface to be created.
647         :type node: dict
648         :type interface: str
649         :type vlan: int
650         :returns: Name and index of created subinterface.
651         :rtype: tuple
652         :raises RuntimeError: if it is unable to create VLAN subinterface on the
653             node.
654         """
655         iface_key = Topology.get_interface_by_name(node, interface)
656         sw_if_index = Topology.get_interface_sw_index(node, iface_key)
657
658         output = VatExecutor.cmd_from_template(node, "create_vlan_subif.vat",
659                                                sw_if_index=sw_if_index,
660                                                vlan=vlan)
661         if output[0]["retval"] == 0:
662             sw_subif_idx = output[0]["sw_if_index"]
663             logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} '
664                          'created on node {}'.format(sw_subif_idx,
665                                                      vlan, node['host']))
666             if_key = Topology.add_new_port(node, "vlan_subif")
667             Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
668             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
669             Topology.update_interface_name(node, if_key, ifc_name)
670         else:
671             raise RuntimeError('Unable to create VLAN subinterface on node {}'
672                                .format(node['host']))
673
674         with VatTerminal(node, False) as vat:
675             vat.vat_terminal_exec_cmd('exec show interfaces')
676
677         return '{}.{}'.format(interface, vlan), sw_subif_idx
678
679     @staticmethod
680     def create_vxlan_interface(node, vni, source_ip, destination_ip):
681         """Create VXLAN interface and return sw if index of created interface.
682
683         Executes "vxlan_add_del_tunnel src {src} dst {dst} vni {vni}" VAT
684         command on the node.
685
686         :param node: Node where to create VXLAN interface.
687         :param vni: VXLAN Network Identifier.
688         :param source_ip: Source IP of a VXLAN Tunnel End Point.
689         :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
690         :type node: dict
691         :type vni: int
692         :type source_ip: str
693         :type destination_ip: str
694         :returns: SW IF INDEX of created interface.
695         :rtype: int
696         :raises RuntimeError: if it is unable to create VxLAN interface on the
697             node.
698         """
699         output = VatExecutor.cmd_from_template(node, "vxlan_create.vat",
700                                                src=source_ip,
701                                                dst=destination_ip,
702                                                vni=vni)
703         output = output[0]
704
705         if output["retval"] == 0:
706             sw_if_idx = output["sw_if_index"]
707             if_key = Topology.add_new_port(node, "vxlan_tunnel")
708             Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
709             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
710             Topology.update_interface_name(node, if_key, ifc_name)
711             return sw_if_idx
712         else:
713             raise RuntimeError("Unable to create VXLAN interface on node {0}"
714                                .format(node))
715
716     @staticmethod
717     def vxlan_dump(node, interface=None):
718         """Get VxLAN data for the given interface.
719
720         :param node: VPP node to get interface data from.
721         :param interface: Numeric index or name string of a specific interface.
722             If None, information about all VxLAN interfaces is returned.
723         :type node: dict
724         :type interface: int or str
725         :returns: Dictionary containing data for the given VxLAN interface or if
726             interface=None, the list of dictionaries with all VxLAN interfaces.
727         :rtype: dict or list
728         :raises TypeError: if the data type of interface is neither basestring
729             nor int.
730         """
731         param = "sw_if_index"
732         if interface is None:
733             param = ''
734             sw_if_index = ''
735         elif isinstance(interface, basestring):
736             sw_if_index = Topology.get_interface_sw_index(node, interface)
737         elif isinstance(interface, int):
738             sw_if_index = interface
739         else:
740             raise TypeError("Wrong interface format {0}".format(interface))
741
742         with VatTerminal(node) as vat:
743             response = vat.vat_terminal_exec_cmd_from_template(
744                 "vxlan_dump.vat", param=param, sw_if_index=sw_if_index)
745
746         if sw_if_index:
747             for vxlan in response[0]:
748                 if vxlan["sw_if_index"] == sw_if_index:
749                     return vxlan
750             return {}
751         return response[0]
752
753     @staticmethod
754     def vhost_user_dump(node):
755         """Get vhost-user data for the given node.
756
757         :param node: VPP node to get interface data from.
758         :type node: dict
759         :returns: List of dictionaries with all vhost-user interfaces.
760         :rtype: list
761         """
762         with VatTerminal(node) as vat:
763             response = vat.vat_terminal_exec_cmd_from_template(
764                 "vhost_user_dump.vat")
765
766         return response[0]
767
768     @staticmethod
769     def tap_dump(node, name=None):
770         """Get all TAP interface data from the given node, or data about
771         a specific TAP interface.
772
773         :param node: VPP node to get data from.
774         :param name: Optional name of a specific TAP interface.
775         :type node: dict
776         :type name: str
777         :returns: Dictionary of information about a specific TAP interface, or
778             a List of dictionaries containing all TAP data for the given node.
779         :rtype: dict or list
780         """
781         with VatTerminal(node) as vat:
782             response = vat.vat_terminal_exec_cmd_from_template(
783                 "tap_dump.vat")
784         if name is None:
785             return response[0]
786         else:
787             for item in response[0]:
788                 if name == item['dev_name']:
789                     return item
790             return {}
791
792     @staticmethod
793     def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
794                             inner_vlan_id=None, type_subif=None):
795         """Create sub-interface on node. It is possible to set required
796         sub-interface type and VLAN tag(s).
797
798         :param node: Node to add sub-interface.
799         :param interface: Interface name on which create sub-interface.
800         :param sub_id: ID of the sub-interface to be created.
801         :param outer_vlan_id: Optional outer VLAN ID.
802         :param inner_vlan_id: Optional inner VLAN ID.
803         :param type_subif: Optional type of sub-interface. Values supported by
804             VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
805             [default_sub]
806         :type node: dict
807         :type interface: str or int
808         :type sub_id: int
809         :type outer_vlan_id: int
810         :type inner_vlan_id: int
811         :type type_subif: str
812         :returns: Name and index of created sub-interface.
813         :rtype: tuple
814         :raises RuntimeError: If it is not possible to create sub-interface.
815         """
816
817         outer_vlan_id = 'outer_vlan_id {0}'.format(outer_vlan_id)\
818             if outer_vlan_id else ''
819
820         inner_vlan_id = 'inner_vlan_id {0}'.format(inner_vlan_id)\
821             if inner_vlan_id else ''
822
823         if type_subif is None:
824             type_subif = ''
825
826         if isinstance(interface, basestring):
827             iface_key = Topology.get_interface_by_name(node, interface)
828             sw_if_index = Topology.get_interface_sw_index(node, iface_key)
829         else:
830             sw_if_index = interface
831
832         output = VatExecutor.cmd_from_template(node, "create_sub_interface.vat",
833                                                sw_if_index=sw_if_index,
834                                                sub_id=sub_id,
835                                                outer_vlan_id=outer_vlan_id,
836                                                inner_vlan_id=inner_vlan_id,
837                                                type_subif=type_subif)
838
839         if output[0]["retval"] == 0:
840             sw_subif_idx = output[0]["sw_if_index"]
841             logger.trace('Created subinterface with index {}'
842                          .format(sw_subif_idx))
843             if_key = Topology.add_new_port(node, "subinterface")
844             Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
845             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
846             Topology.update_interface_name(node, if_key, ifc_name)
847         else:
848             raise RuntimeError('Unable to create sub-interface on node {}'
849                                .format(node['host']))
850
851         with VatTerminal(node, json_param=False) as vat:
852             vat.vat_terminal_exec_cmd('exec show interfaces')
853
854         name = '{}.{}'.format(interface, sub_id)
855         return name, sw_subif_idx
856
857     @staticmethod
858     def create_gre_tunnel_interface(node, source_ip, destination_ip):
859         """Create GRE tunnel interface on node.
860
861         :param node: VPP node to add tunnel interface.
862         :param source_ip: Source of the GRE tunnel.
863         :param destination_ip: Destination of the GRE tunnel.
864         :type node: dict
865         :type source_ip: str
866         :type destination_ip: str
867         :returns: Name and index of created GRE tunnel interface.
868         :rtype: tuple
869         :raises RuntimeError: If unable to create GRE tunnel interface.
870         """
871         output = VatExecutor.cmd_from_template(node, "create_gre.vat",
872                                                src=source_ip,
873                                                dst=destination_ip)
874         output = output[0]
875
876         if output["retval"] == 0:
877             sw_if_idx = output["sw_if_index"]
878
879             vat_executor = VatExecutor()
880             vat_executor.execute_script_json_out("dump_interfaces.vat", node)
881             interface_dump_json = vat_executor.get_script_stdout()
882             name = VatJsonUtil.get_interface_name_from_json(
883                 interface_dump_json, sw_if_idx)
884
885             if_key = Topology.add_new_port(node, "gre_tunnel")
886             Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
887             Topology.update_interface_name(node, if_key, name)
888
889             return name, sw_if_idx
890         else:
891             raise RuntimeError('Unable to create GRE tunnel on node {}.'
892                                .format(node))
893
894     @staticmethod
895     def vpp_create_loopback(node):
896         """Create loopback interface on VPP node.
897
898         :param node: Node to create loopback interface on.
899         :type node: dict
900         :returns: SW interface index.
901         :rtype: int
902         :raises RuntimeError: If it is not possible to create loopback on the
903             node.
904         """
905         out = VatExecutor.cmd_from_template(node, "create_loopback.vat")
906         if out[0].get('retval') == 0:
907             sw_if_idx = out[0].get('sw_if_index')
908             if_key = Topology.add_new_port(node, "loopback")
909             Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
910             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
911             Topology.update_interface_name(node, if_key, ifc_name)
912             return sw_if_idx
913         else:
914             raise RuntimeError('Create loopback failed on node "{}"'
915                                .format(node['host']))
916
917     @staticmethod
918     def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
919         """Create bond interface on VPP node.
920
921         :param node: DUT node from topology.
922         :param mode: Link bonding mode.
923         :param load_balance: Load balance (optional, valid for xor and lacp
924             modes, otherwise ignored).
925         :param mac: MAC address to assign to the bond interface (optional).
926         :type node: dict
927         :type mode: str
928         :type load_balance: str
929         :type mac: str
930         :returns: Interface key (name) in topology.
931         :rtype: str
932         :raises RuntimeError: If it is not possible to create bond interface on
933             the node.
934         """
935         hw_addr = '' if mac is None else 'hw-addr {mac}'.format(mac=mac)
936         lb = '' if load_balance is None \
937             else 'lb {lb}'.format(lb=load_balance)
938
939         output = VatExecutor.cmd_from_template(
940             node, 'create_bond_interface.vat', mode=mode, lb=lb, mac=hw_addr)
941
942         if output[0].get('retval') == 0:
943             sw_if_idx = output[0].get('sw_if_index')
944             InterfaceUtil.add_bond_eth_interface(node, sw_if_idx=sw_if_idx)
945             if_key = Topology.get_interface_by_sw_index(node, sw_if_idx)
946             return if_key
947         else:
948             raise RuntimeError('Create bond interface failed on node "{n}"'
949                                .format(n=node['host']))
950
951     @staticmethod
952     def add_bond_eth_interface(node, ifc_name=None, sw_if_idx=None):
953         """Add BondEthernet interface to current topology.
954
955         :param node: DUT node from topology.
956         :param ifc_name: Name of the BondEthernet interface.
957         :param sw_if_idx: SW interface index.
958         :type node: dict
959         :type ifc_name: str
960         :type sw_if_idx: int
961         """
962         if_key = Topology.add_new_port(node, 'eth_bond')
963
964         vat_executor = VatExecutor()
965         vat_executor.execute_script_json_out("dump_interfaces.vat", node)
966         interface_dump_json = vat_executor.get_script_stdout()
967
968         if ifc_name and sw_if_idx is None:
969             sw_if_idx = VatJsonUtil.get_interface_sw_index_from_json(
970                 interface_dump_json, ifc_name)
971         Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
972         if sw_if_idx and ifc_name is None:
973             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
974         Topology.update_interface_name(node, if_key, ifc_name)
975         ifc_mac = VatJsonUtil.get_interface_mac_from_json(
976             interface_dump_json, sw_if_idx)
977         Topology.update_interface_mac_address(node, if_key, ifc_mac)
978
979     @staticmethod
980     def vpp_enslave_physical_interface(node, interface, bond_interface):
981         """Enslave physical interface to bond interface on VPP node.
982
983         :param node: DUT node from topology.
984         :param interface: Physical interface key from topology file.
985         :param bond_interface: Load balance
986         :type node: dict
987         :type interface: str
988         :type bond_interface: str
989         :raises RuntimeError: If it is not possible to enslave physical
990             interface to bond interface on the node.
991         """
992         ifc = Topology.get_interface_sw_index(node, interface)
993         bond_ifc = Topology.get_interface_sw_index(node, bond_interface)
994
995         output = VatExecutor.cmd_from_template(
996             node, 'enslave_physical_interface.vat', p_int=ifc, b_int=bond_ifc)
997
998         retval = output[0].get('retval', None)
999         if retval is None or int(retval) != 0:
1000             raise RuntimeError('Enslave physical interface {ifc} to bond '
1001                                'interface {bond} failed on node "{n}"'
1002                                .format(ifc=interface, bond=bond_interface,
1003                                        n=node['host']))
1004
1005     @staticmethod
1006     def vpp_show_bond_data_on_node(node, details=False):
1007         """Show (detailed) bond information on VPP node.
1008
1009         :param node: DUT node from topology.
1010         :param details: If detailed information is required or not.
1011         :type node: dict
1012         :type details: bool
1013         """
1014         cmd = 'exec show bond details' if details else 'exec show bond'
1015         with VatTerminal(node, json_param=False) as vat:
1016             vat.vat_terminal_exec_cmd(cmd)
1017
1018     @staticmethod
1019     def vpp_show_bond_data_on_all_nodes(nodes, details=False):
1020         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1021
1022         :param nodes: Nodes in the topology.
1023         :param details: If detailed information is required or not.
1024         :type nodes: dict
1025         :type details: bool
1026         """
1027         for node_data in nodes.values():
1028             if node_data['type'] == NodeType.DUT:
1029                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, details)
1030
1031     @staticmethod
1032     def vpp_enable_input_acl_interface(node, interface, ip_version,
1033                                        table_index):
1034         """Enable input acl on interface.
1035
1036         :param node: VPP node to setup interface for input acl.
1037         :param interface: Interface to setup input acl.
1038         :param ip_version: Version of IP protocol.
1039         :param table_index: Classify table index.
1040         :type node: dict
1041         :type interface: str or int
1042         :type ip_version: str
1043         :type table_index: int
1044         """
1045         if isinstance(interface, basestring):
1046             sw_if_index = Topology.get_interface_sw_index(node, interface)
1047         else:
1048             sw_if_index = interface
1049
1050         with VatTerminal(node) as vat:
1051             vat.vat_terminal_exec_cmd_from_template("input_acl_int.vat",
1052                                                     sw_if_index=sw_if_index,
1053                                                     ip_version=ip_version,
1054                                                     table_index=table_index)
1055
1056     @staticmethod
1057     def get_interface_classify_table(node, interface):
1058         """Get name of classify table for the given interface.
1059
1060         :param node: VPP node to get data from.
1061         :param interface: Name or sw_if_index of a specific interface.
1062         :type node: dict
1063         :type interface: str or int
1064         :returns: Classify table name.
1065         :rtype: str
1066         """
1067         if isinstance(interface, basestring):
1068             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1069         else:
1070             sw_if_index = interface
1071
1072         with VatTerminal(node) as vat:
1073             data = vat.vat_terminal_exec_cmd_from_template(
1074                 "classify_interface_table.vat",
1075                 sw_if_index=sw_if_index)
1076         return data[0]
1077
1078     @staticmethod
1079     def get_interface_vrf_table(node, interface):
1080         """Get vrf ID for the given interface.
1081
1082         :param node: VPP node.
1083         :param interface: Name or sw_if_index of a specific interface.
1084         :type node: dict
1085         :type interface: str or int
1086         :returns: vrf ID of the specified interface.
1087         :rtype: int
1088         """
1089
1090         if isinstance(interface, basestring):
1091             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1092         else:
1093             sw_if_index = interface
1094
1095         with VatTerminal(node) as vat:
1096             data = vat.vat_terminal_exec_cmd_from_template(
1097                 "interface_vrf_dump.vat",
1098                 sw_if_index=sw_if_index)
1099         return data[0]["vrf_id"]
1100
1101     @staticmethod
1102     def get_sw_if_index(node, interface_name):
1103         """Get sw_if_index for the given interface from actual interface dump.
1104
1105         :param node: VPP node to get interface data from.
1106         :param interface_name: Name of the specific interface.
1107         :type node: dict
1108         :type interface_name: str
1109         :returns: sw_if_index of the given interface.
1110         :rtype: str
1111         """
1112
1113         with VatTerminal(node) as vat:
1114             if_data = vat.vat_terminal_exec_cmd_from_template(
1115                 "interface_dump.vat")
1116         for interface in if_data[0]:
1117             if interface["interface_name"] == interface_name:
1118                 return interface["sw_if_index"]
1119
1120         return None
1121
1122     @staticmethod
1123     def vxlan_gpe_dump(node, interface_name=None):
1124         """Get VxLAN GPE data for the given interface.
1125
1126         :param node: VPP node to get interface data from.
1127         :param interface_name: Name of the specific interface. If None,
1128             information about all VxLAN GPE interfaces is returned.
1129         :type node: dict
1130         :type interface_name: str
1131         :returns: Dictionary containing data for the given VxLAN GPE interface
1132             or if interface=None, the list of dictionaries with all VxLAN GPE
1133             interfaces.
1134         :rtype: dict or list
1135         """
1136
1137         with VatTerminal(node) as vat:
1138             vxlan_gpe_data = vat.vat_terminal_exec_cmd_from_template(
1139                 "vxlan_gpe_dump.vat")
1140
1141         if interface_name:
1142             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface_name)
1143             if sw_if_index:
1144                 for vxlan_gpe in vxlan_gpe_data[0]:
1145                     if vxlan_gpe["sw_if_index"] == sw_if_index:
1146                         return vxlan_gpe
1147             return {}
1148
1149         return vxlan_gpe_data[0]
1150
1151     @staticmethod
1152     def vpp_proxy_arp_interface_enable(node, interface):
1153         """Enable proxy ARP on interface.
1154
1155         :param node: VPP node to enable proxy ARP on interface.
1156         :param interface: Interface to enable proxy ARP.
1157         :type node: dict
1158         :type interface: str or int
1159         """
1160         if isinstance(interface, basestring):
1161             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1162         else:
1163             sw_if_index = interface
1164
1165         with VatTerminal(node) as vat:
1166             vat.vat_terminal_exec_cmd_from_template(
1167                 "proxy_arp_intfc_enable.vat",
1168                 sw_if_index=sw_if_index)
1169
1170     @staticmethod
1171     def vpp_ip_source_check_setup(node, interface):
1172         """Setup Reverse Path Forwarding source check on interface.
1173
1174         :param node: Node to setup RPF source check.
1175         :param interface: Interface name to setup RPF source check.
1176         :type node: dict
1177         :type interface: str
1178         """
1179         with VatTerminal(node) as vat:
1180             vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat",
1181                                                     interface_name=interface)
1182
1183     @staticmethod
1184     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1185         """Assign VPP interface to specific VRF/FIB table.
1186
1187         :param node: VPP node where the FIB and interface are located.
1188         :param interface: Interface to be assigned to FIB.
1189         :param table_id: VRF table ID.
1190         :param ipv6: Assign to IPv6 table. Default False.
1191         :type node: dict
1192         :type interface: str or int
1193         :type table_id: int
1194         :type ipv6: bool
1195         """
1196         if isinstance(interface, basestring):
1197             sw_if_index = Topology.get_interface_sw_index(node, interface)
1198         else:
1199             sw_if_index = interface
1200
1201         ipv6 = 'ipv6' if ipv6 else ''
1202
1203         with VatTerminal(node) as vat:
1204             ret = vat.vat_terminal_exec_cmd_from_template(
1205                 "set_fib_to_interface.vat",
1206                 sw_index=sw_if_index, vrf=table_id, ipv6=ipv6)
1207
1208         if ret[0]["retval"] != 0:
1209             raise RuntimeError('Unable to assign interface to FIB node {}.'
1210                                .format(node))
1211
1212     @staticmethod
1213     def set_linux_interface_mac(node, interface, mac, namespace=None):
1214         """Set MAC address for interface in linux.
1215
1216         :param node: Node where to execute command.
1217         :param interface: Interface in namespace.
1218         :param mac: MAC to be assigned to interface.
1219         :param namespace: Execute command in namespace. Optional
1220         :type node: dict
1221         :type interface: str
1222         :type mac: str
1223         :type namespace: str
1224         """
1225         if namespace is not None:
1226             cmd = 'ip netns exec {} ip link set {} address {}'.format(
1227                 namespace, interface, mac)
1228         else:
1229             cmd = 'ip link set {} address {}'.format(interface, mac)
1230         exec_cmd_no_error(node, cmd, sudo=True)