Fix various pylint violations
[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         for item in response[0]:
787             if name == item['dev_name']:
788                 return item
789         return {}
790
791     @staticmethod
792     def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
793                             inner_vlan_id=None, type_subif=None):
794         """Create sub-interface on node. It is possible to set required
795         sub-interface type and VLAN tag(s).
796
797         :param node: Node to add sub-interface.
798         :param interface: Interface name on which create sub-interface.
799         :param sub_id: ID of the sub-interface to be created.
800         :param outer_vlan_id: Optional outer VLAN ID.
801         :param inner_vlan_id: Optional inner VLAN ID.
802         :param type_subif: Optional type of sub-interface. Values supported by
803             VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
804             [default_sub]
805         :type node: dict
806         :type interface: str or int
807         :type sub_id: int
808         :type outer_vlan_id: int
809         :type inner_vlan_id: int
810         :type type_subif: str
811         :returns: Name and index of created sub-interface.
812         :rtype: tuple
813         :raises RuntimeError: If it is not possible to create sub-interface.
814         """
815
816         outer_vlan_id = 'outer_vlan_id {0}'.format(outer_vlan_id)\
817             if outer_vlan_id else ''
818
819         inner_vlan_id = 'inner_vlan_id {0}'.format(inner_vlan_id)\
820             if inner_vlan_id else ''
821
822         if type_subif is None:
823             type_subif = ''
824
825         if isinstance(interface, basestring):
826             iface_key = Topology.get_interface_by_name(node, interface)
827             sw_if_index = Topology.get_interface_sw_index(node, iface_key)
828         else:
829             sw_if_index = interface
830
831         output = VatExecutor.cmd_from_template(node, "create_sub_interface.vat",
832                                                sw_if_index=sw_if_index,
833                                                sub_id=sub_id,
834                                                outer_vlan_id=outer_vlan_id,
835                                                inner_vlan_id=inner_vlan_id,
836                                                type_subif=type_subif)
837
838         if output[0]["retval"] == 0:
839             sw_subif_idx = output[0]["sw_if_index"]
840             logger.trace('Created subinterface with index {}'
841                          .format(sw_subif_idx))
842             if_key = Topology.add_new_port(node, "subinterface")
843             Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
844             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
845             Topology.update_interface_name(node, if_key, ifc_name)
846         else:
847             raise RuntimeError('Unable to create sub-interface on node {}'
848                                .format(node['host']))
849
850         with VatTerminal(node, json_param=False) as vat:
851             vat.vat_terminal_exec_cmd('exec show interfaces')
852
853         name = '{}.{}'.format(interface, sub_id)
854         return name, sw_subif_idx
855
856     @staticmethod
857     def create_gre_tunnel_interface(node, source_ip, destination_ip):
858         """Create GRE tunnel interface on node.
859
860         :param node: VPP node to add tunnel interface.
861         :param source_ip: Source of the GRE tunnel.
862         :param destination_ip: Destination of the GRE tunnel.
863         :type node: dict
864         :type source_ip: str
865         :type destination_ip: str
866         :returns: Name and index of created GRE tunnel interface.
867         :rtype: tuple
868         :raises RuntimeError: If unable to create GRE tunnel interface.
869         """
870         output = VatExecutor.cmd_from_template(node, "create_gre.vat",
871                                                src=source_ip,
872                                                dst=destination_ip)
873         output = output[0]
874
875         if output["retval"] == 0:
876             sw_if_idx = output["sw_if_index"]
877
878             vat_executor = VatExecutor()
879             vat_executor.execute_script_json_out("dump_interfaces.vat", node)
880             interface_dump_json = vat_executor.get_script_stdout()
881             name = VatJsonUtil.get_interface_name_from_json(
882                 interface_dump_json, sw_if_idx)
883
884             if_key = Topology.add_new_port(node, "gre_tunnel")
885             Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
886             Topology.update_interface_name(node, if_key, name)
887
888             return name, sw_if_idx
889         else:
890             raise RuntimeError('Unable to create GRE tunnel on node {}.'
891                                .format(node))
892
893     @staticmethod
894     def vpp_create_loopback(node):
895         """Create loopback interface on VPP node.
896
897         :param node: Node to create loopback interface on.
898         :type node: dict
899         :returns: SW interface index.
900         :rtype: int
901         :raises RuntimeError: If it is not possible to create loopback on the
902             node.
903         """
904         out = VatExecutor.cmd_from_template(node, "create_loopback.vat")
905         if out[0].get('retval') == 0:
906             sw_if_idx = out[0].get('sw_if_index')
907             if_key = Topology.add_new_port(node, "loopback")
908             Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
909             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
910             Topology.update_interface_name(node, if_key, ifc_name)
911             return sw_if_idx
912         else:
913             raise RuntimeError('Create loopback failed on node "{}"'
914                                .format(node['host']))
915
916     @staticmethod
917     def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
918         """Create bond interface on VPP node.
919
920         :param node: DUT node from topology.
921         :param mode: Link bonding mode.
922         :param load_balance: Load balance (optional, valid for xor and lacp
923             modes, otherwise ignored).
924         :param mac: MAC address to assign to the bond interface (optional).
925         :type node: dict
926         :type mode: str
927         :type load_balance: str
928         :type mac: str
929         :returns: Interface key (name) in topology.
930         :rtype: str
931         :raises RuntimeError: If it is not possible to create bond interface on
932             the node.
933         """
934         hw_addr = '' if mac is None else 'hw-addr {mac}'.format(mac=mac)
935         ldb = '' if load_balance is None \
936             else 'lb {ldb}'.format(ldb=load_balance)
937
938         output = VatExecutor.cmd_from_template(
939             node, 'create_bond_interface.vat', mode=mode, lb=ldb, mac=hw_addr)
940
941         if output[0].get('retval') == 0:
942             sw_if_idx = output[0].get('sw_if_index')
943             InterfaceUtil.add_bond_eth_interface(node, sw_if_idx=sw_if_idx)
944             if_key = Topology.get_interface_by_sw_index(node, sw_if_idx)
945             return if_key
946         else:
947             raise RuntimeError('Create bond interface failed on "{host}"'
948                                .format(host=node['host']))
949
950     @staticmethod
951     def add_bond_eth_interface(node, ifc_name=None, sw_if_idx=None):
952         """Add BondEthernet interface to current topology.
953
954         :param node: DUT node from topology.
955         :param ifc_name: Name of the BondEthernet interface.
956         :param sw_if_idx: SW interface index.
957         :type node: dict
958         :type ifc_name: str
959         :type sw_if_idx: int
960         """
961         if_key = Topology.add_new_port(node, 'eth_bond')
962
963         vat_executor = VatExecutor()
964         vat_executor.execute_script_json_out("dump_interfaces.vat", node)
965         interface_dump_json = vat_executor.get_script_stdout()
966
967         if ifc_name and sw_if_idx is None:
968             sw_if_idx = VatJsonUtil.get_interface_sw_index_from_json(
969                 interface_dump_json, ifc_name)
970         Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
971         if sw_if_idx and ifc_name is None:
972             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
973         Topology.update_interface_name(node, if_key, ifc_name)
974         ifc_mac = VatJsonUtil.get_interface_mac_from_json(
975             interface_dump_json, sw_if_idx)
976         Topology.update_interface_mac_address(node, if_key, ifc_mac)
977
978     @staticmethod
979     def vpp_enslave_physical_interface(node, interface, bond_interface):
980         """Enslave physical interface to bond interface on VPP node.
981
982         :param node: DUT node from topology.
983         :param interface: Physical interface key from topology file.
984         :param bond_interface: Load balance
985         :type node: dict
986         :type interface: str
987         :type bond_interface: str
988         :raises RuntimeError: If it is not possible to enslave physical
989             interface to bond interface on the node.
990         """
991         ifc = Topology.get_interface_sw_index(node, interface)
992         bond_ifc = Topology.get_interface_sw_index(node, bond_interface)
993
994         output = VatExecutor.cmd_from_template(
995             node, 'enslave_physical_interface.vat', p_int=ifc, b_int=bond_ifc)
996
997         retval = output[0].get('retval', None)
998         if retval is None or int(retval) != 0:
999             raise RuntimeError('Enslave physical interface {ifc} to bond '
1000                                'interface {bond} failed on node "{n}"'
1001                                .format(ifc=interface, bond=bond_interface,
1002                                        n=node['host']))
1003
1004     @staticmethod
1005     def vpp_show_bond_data_on_node(node, details=False):
1006         """Show (detailed) bond information on VPP node.
1007
1008         :param node: DUT node from topology.
1009         :param details: If detailed information is required or not.
1010         :type node: dict
1011         :type details: bool
1012         """
1013         cmd = 'exec show bond details' if details else 'exec show bond'
1014         with VatTerminal(node, json_param=False) as vat:
1015             vat.vat_terminal_exec_cmd(cmd)
1016
1017     @staticmethod
1018     def vpp_show_bond_data_on_all_nodes(nodes, details=False):
1019         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1020
1021         :param nodes: Nodes in the topology.
1022         :param details: If detailed information is required or not.
1023         :type nodes: dict
1024         :type details: bool
1025         """
1026         for node_data in nodes.values():
1027             if node_data['type'] == NodeType.DUT:
1028                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, details)
1029
1030     @staticmethod
1031     def vpp_enable_input_acl_interface(node, interface, ip_version,
1032                                        table_index):
1033         """Enable input acl on interface.
1034
1035         :param node: VPP node to setup interface for input acl.
1036         :param interface: Interface to setup input acl.
1037         :param ip_version: Version of IP protocol.
1038         :param table_index: Classify table index.
1039         :type node: dict
1040         :type interface: str or int
1041         :type ip_version: str
1042         :type table_index: int
1043         """
1044         if isinstance(interface, basestring):
1045             sw_if_index = Topology.get_interface_sw_index(node, interface)
1046         else:
1047             sw_if_index = interface
1048
1049         with VatTerminal(node) as vat:
1050             vat.vat_terminal_exec_cmd_from_template("input_acl_int.vat",
1051                                                     sw_if_index=sw_if_index,
1052                                                     ip_version=ip_version,
1053                                                     table_index=table_index)
1054
1055     @staticmethod
1056     def get_interface_classify_table(node, interface):
1057         """Get name of classify table for the given interface.
1058
1059         :param node: VPP node to get data from.
1060         :param interface: Name or sw_if_index of a specific interface.
1061         :type node: dict
1062         :type interface: str or int
1063         :returns: Classify table name.
1064         :rtype: str
1065         """
1066         if isinstance(interface, basestring):
1067             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1068         else:
1069             sw_if_index = interface
1070
1071         with VatTerminal(node) as vat:
1072             data = vat.vat_terminal_exec_cmd_from_template(
1073                 "classify_interface_table.vat",
1074                 sw_if_index=sw_if_index)
1075         return data[0]
1076
1077     @staticmethod
1078     def get_interface_vrf_table(node, interface):
1079         """Get vrf ID for the given interface.
1080
1081         :param node: VPP node.
1082         :param interface: Name or sw_if_index of a specific interface.
1083         :type node: dict
1084         :type interface: str or int
1085         :returns: vrf ID of the specified interface.
1086         :rtype: int
1087         """
1088
1089         if isinstance(interface, basestring):
1090             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1091         else:
1092             sw_if_index = interface
1093
1094         with VatTerminal(node) as vat:
1095             data = vat.vat_terminal_exec_cmd_from_template(
1096                 "interface_vrf_dump.vat",
1097                 sw_if_index=sw_if_index)
1098         return data[0]["vrf_id"]
1099
1100     @staticmethod
1101     def get_sw_if_index(node, interface_name):
1102         """Get sw_if_index for the given interface from actual interface dump.
1103
1104         :param node: VPP node to get interface data from.
1105         :param interface_name: Name of the specific interface.
1106         :type node: dict
1107         :type interface_name: str
1108         :returns: sw_if_index of the given interface.
1109         :rtype: str
1110         """
1111
1112         with VatTerminal(node) as vat:
1113             if_data = vat.vat_terminal_exec_cmd_from_template(
1114                 "interface_dump.vat")
1115         for interface in if_data[0]:
1116             if interface["interface_name"] == interface_name:
1117                 return interface["sw_if_index"]
1118
1119         return None
1120
1121     @staticmethod
1122     def vxlan_gpe_dump(node, interface_name=None):
1123         """Get VxLAN GPE data for the given interface.
1124
1125         :param node: VPP node to get interface data from.
1126         :param interface_name: Name of the specific interface. If None,
1127             information about all VxLAN GPE interfaces is returned.
1128         :type node: dict
1129         :type interface_name: str
1130         :returns: Dictionary containing data for the given VxLAN GPE interface
1131             or if interface=None, the list of dictionaries with all VxLAN GPE
1132             interfaces.
1133         :rtype: dict or list
1134         """
1135
1136         with VatTerminal(node) as vat:
1137             vxlan_gpe_data = vat.vat_terminal_exec_cmd_from_template(
1138                 "vxlan_gpe_dump.vat")
1139
1140         if interface_name:
1141             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface_name)
1142             if sw_if_index:
1143                 for vxlan_gpe in vxlan_gpe_data[0]:
1144                     if vxlan_gpe["sw_if_index"] == sw_if_index:
1145                         return vxlan_gpe
1146             return {}
1147
1148         return vxlan_gpe_data[0]
1149
1150     @staticmethod
1151     def vpp_proxy_arp_interface_enable(node, interface):
1152         """Enable proxy ARP on interface.
1153
1154         :param node: VPP node to enable proxy ARP on interface.
1155         :param interface: Interface to enable proxy ARP.
1156         :type node: dict
1157         :type interface: str or int
1158         """
1159         if isinstance(interface, basestring):
1160             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1161         else:
1162             sw_if_index = interface
1163
1164         with VatTerminal(node) as vat:
1165             vat.vat_terminal_exec_cmd_from_template(
1166                 "proxy_arp_intfc_enable.vat",
1167                 sw_if_index=sw_if_index)
1168
1169     @staticmethod
1170     def vpp_ip_source_check_setup(node, interface):
1171         """Setup Reverse Path Forwarding source check on interface.
1172
1173         :param node: Node to setup RPF source check.
1174         :param interface: Interface name to setup RPF source check.
1175         :type node: dict
1176         :type interface: str
1177         """
1178         with VatTerminal(node) as vat:
1179             vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat",
1180                                                     interface_name=interface)
1181
1182     @staticmethod
1183     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1184         """Assign VPP interface to specific VRF/FIB table.
1185
1186         :param node: VPP node where the FIB and interface are located.
1187         :param interface: Interface to be assigned to FIB.
1188         :param table_id: VRF table ID.
1189         :param ipv6: Assign to IPv6 table. Default False.
1190         :type node: dict
1191         :type interface: str or int
1192         :type table_id: int
1193         :type ipv6: bool
1194         """
1195         if isinstance(interface, basestring):
1196             sw_if_index = Topology.get_interface_sw_index(node, interface)
1197         else:
1198             sw_if_index = interface
1199
1200         ipv6 = 'ipv6' if ipv6 else ''
1201
1202         with VatTerminal(node) as vat:
1203             ret = vat.vat_terminal_exec_cmd_from_template(
1204                 "set_fib_to_interface.vat",
1205                 sw_index=sw_if_index, vrf=table_id, ipv6=ipv6)
1206
1207         if ret[0]["retval"] != 0:
1208             raise RuntimeError('Unable to assign interface to FIB node {}.'
1209                                .format(node))
1210
1211     @staticmethod
1212     def set_linux_interface_mac(node, interface, mac, namespace=None):
1213         """Set MAC address for interface in linux.
1214
1215         :param node: Node where to execute command.
1216         :param interface: Interface in namespace.
1217         :param mac: MAC to be assigned to interface.
1218         :param namespace: Execute command in namespace. Optional
1219         :type node: dict
1220         :type interface: str
1221         :type mac: str
1222         :type namespace: str
1223         """
1224         if namespace is not None:
1225             cmd = 'ip netns exec {} ip link set {} address {}'.format(
1226                 namespace, interface, mac)
1227         else:
1228             cmd = 'ip link set {} address {}'.format(interface, mac)
1229         exec_cmd_no_error(node, cmd, sudo=True)