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