558e99b4e730c8a7d7188ce7f647011524546f3e
[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         sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
264
265         with VatTerminal(node) as vat:
266             response = vat.vat_terminal_exec_cmd_from_template(
267                 "ip_address_dump.vat", ip_version=ip_version,
268                 sw_if_index=sw_if_index)
269
270         data = response[0]
271
272         if ip_version == "ipv4":
273             for item in data:
274                 item["netmask"] = convert_ipv4_netmask_prefix(
275                     item["prefix_length"])
276         return data
277
278     @staticmethod
279     def tg_set_interface_driver(node, pci_addr, driver):
280         """Set interface driver on the TG node.
281
282         :param node: Node to set interface driver on (must be TG node).
283         :param pci_addr: PCI address of the interface.
284         :param driver: Driver name.
285         :type node: dict
286         :type pci_addr: str
287         :type driver: str
288         :returns: None.
289         :raises RuntimeError: If unbinding from the current driver fails.
290         :raises RuntimeError: If binding to the new driver fails.
291         """
292         old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
293         if old_driver == driver:
294             return
295
296         ssh = SSH()
297         ssh.connect(node)
298
299         # Unbind from current driver
300         if old_driver is not None:
301             cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
302                 .format(pci_addr, old_driver)
303             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
304             if int(ret_code) != 0:
305                 raise RuntimeError("'{0}' failed on '{1}'"
306                                    .format(cmd, node['host']))
307
308         # Bind to the new driver
309         cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
310             .format(pci_addr, 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     @staticmethod
317     def tg_get_interface_driver(node, pci_addr):
318         """Get interface driver from the TG node.
319
320         :param node: Node to get interface driver on (must be TG node).
321         :param pci_addr: PCI address of the interface.
322         :type node: dict
323         :type pci_addr: str
324         :returns: Interface driver or None if not found.
325         :rtype: str
326         :raises RuntimeError: If it is not possible to get the interface driver
327         information from the node.
328
329         .. note::
330             # lspci -vmmks 0000:00:05.0
331             Slot:   00:05.0
332             Class:  Ethernet controller
333             Vendor: Red Hat, Inc
334             Device: Virtio network device
335             SVendor:        Red Hat, Inc
336             SDevice:        Device 0001
337             PhySlot:        5
338             Driver: virtio-pci
339         """
340         ssh = SSH()
341         ssh.connect(node)
342
343         cmd = 'lspci -vmmks {0}'.format(pci_addr)
344
345         (ret_code, stdout, _) = ssh.exec_command(cmd)
346         if int(ret_code) != 0:
347             raise RuntimeError("'{0}' failed on '{1}'"
348                                .format(cmd, node['host']))
349
350         for line in stdout.splitlines():
351             if len(line) == 0:
352                 continue
353             (name, value) = line.split("\t", 1)
354             if name == 'Driver:':
355                 return value
356
357         return None
358
359     @staticmethod
360     def tg_set_interfaces_udev_rules(node):
361         """Set udev rules for interfaces.
362
363         Create udev rules file in /etc/udev/rules.d where are rules for each
364         interface used by TG node, based on MAC interface has specific name.
365         So after unbind and bind again to kernel driver interface has same
366         name as before. This must be called after TG has set name for each
367         port in topology dictionary.
368         udev rule example
369         SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
370         NAME="eth1"
371
372         :param node: Node to set udev rules on (must be TG node).
373         :type node: dict
374         :raises RuntimeError: If setting of udev rules fails.
375         """
376         ssh = SSH()
377         ssh.connect(node)
378
379         cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
380         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
381         if int(ret_code) != 0:
382             raise RuntimeError("'{0}' failed on '{1}'"
383                                .format(cmd, node['host']))
384
385         for interface in node['interfaces'].values():
386             rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
387                    '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
388                    interface['name'] + '\\"'
389             cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
390                 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
391             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
392             if int(ret_code) != 0:
393                 raise RuntimeError("'{0}' failed on '{1}'"
394                                    .format(cmd, node['host']))
395
396         cmd = '/etc/init.d/udev restart'
397         ssh.exec_command_sudo(cmd)
398
399     @staticmethod
400     def tg_set_interfaces_default_driver(node):
401         """Set interfaces default driver specified in topology yaml file.
402
403         :param node: Node to setup interfaces driver on (must be TG node).
404         :type node: dict
405         """
406         for interface in node['interfaces'].values():
407             InterfaceUtil.tg_set_interface_driver(node,
408                                                   interface['pci_address'],
409                                                   interface['driver'])
410
411     @staticmethod
412     def update_vpp_interface_data_on_node(node):
413         """Update vpp generated interface data for a given node in DICT__nodes.
414
415         Updates interface names, software if index numbers and any other details
416         generated specifically by vpp that are unknown before testcase run.
417         It does this by dumping interface list to JSON output from all
418         devices using vpp_api_test, and pairing known information from topology
419         (mac address/pci address of interface) to state from VPP.
420
421         :param node: Node selected from DICT__nodes.
422         :type node: dict
423         """
424         vat_executor = VatExecutor()
425         vat_executor.execute_script_json_out("dump_interfaces.vat", node)
426         interface_dump_json = vat_executor.get_script_stdout()
427         VatJsonUtil.update_vpp_interface_data_from_json(node,
428                                                         interface_dump_json)
429
430     @staticmethod
431     def update_tg_interface_data_on_node(node):
432         """Update interface name for TG/linux node in DICT__nodes.
433
434         :param node: Node selected from DICT__nodes.
435         :type node: dict
436         :raises RuntimeError: If getting of interface name and MAC fails.
437
438         .. note::
439             # for dev in `ls /sys/class/net/`;
440             > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
441             "52:54:00:9f:82:63": "eth0"
442             "52:54:00:77:ae:a9": "eth1"
443             "52:54:00:e1:8a:0f": "eth2"
444             "00:00:00:00:00:00": "lo"
445
446         .. todo:: parse lshw -json instead
447         """
448         # First setup interface driver specified in yaml file
449         InterfaceUtil.tg_set_interfaces_default_driver(node)
450
451         # Get interface names
452         ssh = SSH()
453         ssh.connect(node)
454
455         cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
456                '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
457
458         (ret_code, stdout, _) = ssh.exec_command(cmd)
459         if int(ret_code) != 0:
460             raise RuntimeError('Get interface name and MAC failed')
461         tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
462         interfaces = JsonParser().parse_data(tmp)
463         for interface in node['interfaces'].values():
464             name = interfaces.get(interface['mac_address'])
465             if name is None:
466                 continue
467             interface['name'] = name
468
469         # Set udev rules for interfaces
470         InterfaceUtil.tg_set_interfaces_udev_rules(node)
471
472     @staticmethod
473     def iface_update_numa_node(node):
474         """For all interfaces from topology file update numa node based on
475            information from the node.
476
477         :param node: Node from topology.
478         :type node: dict
479         :returns: Nothing.
480         :raises ValueError: If numa node ia less than 0.
481         :raises RuntimeError: If update of numa node failes.
482         """
483         ssh = SSH()
484         for if_key in Topology.get_node_interfaces(node):
485             if_pci = Topology.get_interface_pci_addr(node, if_key)
486             ssh.connect(node)
487             cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
488             for _ in range(3):
489                 (ret, out, _) = ssh.exec_command(cmd)
490                 if ret == 0:
491                     try:
492                         numa_node = int(out)
493                         if numa_node < 0:
494                             raise ValueError
495                     except ValueError:
496                         logger.trace('Reading numa location failed for: {0}'\
497                             .format(if_pci))
498                     else:
499                         Topology.set_interface_numa_node(node, if_key,
500                                                          numa_node)
501                         break
502             else:
503                 raise RuntimeError('Update numa node failed for: {0}'\
504                     .format(if_pci))
505
506     @staticmethod
507     def update_all_numa_nodes(nodes, skip_tg=False):
508         """For all nodes and all their interfaces from topology file update numa
509         node information based on information from the node.
510
511         :param nodes: Nodes in the topology.
512         :param skip_tg: Skip TG node
513         :type nodes: dict
514         :type skip_tg: bool
515         :returns: Nothing.
516         """
517         for node in nodes.values():
518             if node['type'] == NodeType.DUT:
519                 InterfaceUtil.iface_update_numa_node(node)
520             elif node['type'] == NodeType.TG and not skip_tg:
521                 InterfaceUtil.iface_update_numa_node(node)
522
523     @staticmethod
524     def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
525                                                numa_node=False):
526         """Update interface names on all nodes in DICT__nodes.
527
528         This method updates the topology dictionary by querying interface lists
529         of all nodes mentioned in the topology dictionary.
530
531         :param nodes: Nodes in the topology.
532         :param skip_tg: Skip TG node
533         :param numa_node: Retrieve numa_node location.
534         :type nodes: dict
535         :type skip_tg: bool
536         :type numa_node: bool
537         """
538         for node_data in nodes.values():
539             if node_data['type'] == NodeType.DUT:
540                 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
541             elif node_data['type'] == NodeType.TG and not skip_tg:
542                 InterfaceUtil.update_tg_interface_data_on_node(node_data)
543
544             if numa_node:
545                 if node_data['type'] == NodeType.DUT:
546                     InterfaceUtil.iface_update_numa_node(node_data)
547                 elif node_data['type'] == NodeType.TG and not skip_tg:
548                     InterfaceUtil.iface_update_numa_node(node_data)
549
550     @staticmethod
551     def create_vlan_subinterface(node, interface, vlan):
552         """Create VLAN subinterface on node.
553
554         :param node: Node to add VLAN subinterface on.
555         :param interface: Interface name on which create VLAN subinterface.
556         :param vlan: VLAN ID of the subinterface to be created.
557         :type node: dict
558         :type interface: str
559         :type vlan: int
560         :returns: Name and index of created subinterface.
561         :rtype: tuple
562         :raises RuntimeError: if it is unable to create VLAN subinterface on the
563         node.
564         """
565         iface_key = Topology.get_interface_by_name(node, interface)
566         sw_if_index = Topology.get_interface_sw_index(node, iface_key)
567
568         output = VatExecutor.cmd_from_template(node, "create_vlan_subif.vat",
569                                                sw_if_index=sw_if_index,
570                                                vlan=vlan)
571         if output[0]["retval"] == 0:
572             sw_subif_index = output[0]["sw_if_index"]
573             logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} '
574                          'created on node {}'.format(sw_subif_index,
575                                                      vlan, node['host']))
576         else:
577             raise RuntimeError('Unable to create VLAN subinterface on node {}'
578                                .format(node['host']))
579
580         with VatTerminal(node, False) as vat:
581             vat.vat_terminal_exec_cmd('exec show interfaces')
582
583         return '{}.{}'.format(interface, vlan), sw_subif_index
584
585     @staticmethod
586     def create_vxlan_interface(node, vni, source_ip, destination_ip):
587         """Create VXLAN interface and return sw if index of created interface.
588
589         Executes "vxlan_add_del_tunnel src {src} dst {dst} vni {vni}" VAT
590         command on the node.
591
592         :param node: Node where to create VXLAN interface.
593         :param vni: VXLAN Network Identifier.
594         :param source_ip: Source IP of a VXLAN Tunnel End Point.
595         :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
596         :type node: dict
597         :type vni: int
598         :type source_ip: str
599         :type destination_ip: str
600         :returns: SW IF INDEX of created interface.
601         :rtype: int
602         :raises RuntimeError: if it is unable to create VxLAN interface on the
603         node.
604         """
605         output = VatExecutor.cmd_from_template(node, "vxlan_create.vat",
606                                                src=source_ip,
607                                                dst=destination_ip,
608                                                vni=vni)
609         output = output[0]
610
611         if output["retval"] == 0:
612             return output["sw_if_index"]
613         else:
614             raise RuntimeError('Unable to create VXLAN interface on node {0}'
615                                .format(node))
616
617     @staticmethod
618     def vxlan_dump(node, interface=None):
619         """Get VxLAN data for the given interface.
620
621         :param node: VPP node to get interface data from.
622         :param interface: Numeric index or name string of a specific interface.
623         If None, information about all VxLAN interfaces is returned.
624         :type node: dict
625         :type interface: int or str
626         :returns: Dictionary containing data for the given VxLAN interface or if
627         interface=None, the list of dictionaries with all VxLAN interfaces.
628         :rtype: dict or list
629         :raises TypeError: if the data type of interface is neither basestring
630         nor int.
631         """
632         param = "sw_if_index"
633         if interface is None:
634             param = ''
635             sw_if_index = ''
636         elif isinstance(interface, basestring):
637             sw_if_index = Topology.get_interface_sw_index(node, interface)
638         elif isinstance(interface, int):
639             sw_if_index = interface
640         else:
641             raise TypeError("Wrong interface format {0}".format(interface))
642
643         with VatTerminal(node) as vat:
644             response = vat.vat_terminal_exec_cmd_from_template(
645                 "vxlan_dump.vat", param=param, sw_if_index=sw_if_index)
646
647         if sw_if_index:
648             for vxlan in response[0]:
649                 if vxlan["sw_if_index"] == sw_if_index:
650                     return vxlan
651             return {}
652         return response[0]
653
654     @staticmethod
655     def vhost_user_dump(node):
656         """Get vhost-user data for the given node.
657
658         :param node: VPP node to get interface data from.
659         :type node: dict
660         :returns: List of dictionaries with all vhost-user interfaces.
661         :rtype: list
662         """
663         with VatTerminal(node) as vat:
664             response = vat.vat_terminal_exec_cmd_from_template(
665                 "vhost_user_dump.vat")
666
667         return response[0]
668
669     @staticmethod
670     def tap_dump(node, name=None):
671         """Get all TAP interface data from the given node, or data about
672         a specific TAP interface.
673
674         :param node: VPP node to get data from.
675         :param name: Optional name of a specific TAP interface.
676         :type node: dict
677         :type name: str
678         :returns: Dictionary of information about a specific TAP interface, or
679         a List of dictionaries containing all TAP data for the given node.
680         :rtype: dict or list
681         """
682         with VatTerminal(node) as vat:
683             response = vat.vat_terminal_exec_cmd_from_template(
684                 "tap_dump.vat")
685         if name is None:
686             return response[0]
687         else:
688             for item in response[0]:
689                 if name == item['dev_name']:
690                     return item
691             return {}
692
693     @staticmethod
694     def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
695                             inner_vlan_id=None, type_subif=None):
696         """Create sub-interface on node. It is possible to set required
697         sub-interface type and VLAN tag(s).
698
699         :param node: Node to add sub-interface.
700         :param interface: Interface name on which create sub-interface.
701         :param sub_id: ID of the sub-interface to be created.
702         :param outer_vlan_id: Optional outer VLAN ID.
703         :param inner_vlan_id: Optional inner VLAN ID.
704         :param type_subif: Optional type of sub-interface. Values supported by
705         VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match] [default_sub]
706         :type node: dict
707         :type interface: str or int
708         :type sub_id: int
709         :type outer_vlan_id: int
710         :type inner_vlan_id: int
711         :type type_subif: str
712         :returns: Name and index of created sub-interface.
713         :rtype: tuple
714         :raises RuntimeError: If it is not possible to create sub-interface.
715         """
716
717         outer_vlan_id = 'outer_vlan_id {0}'.format(outer_vlan_id)\
718             if outer_vlan_id else ''
719
720         inner_vlan_id = 'inner_vlan_id {0}'.format(inner_vlan_id)\
721             if inner_vlan_id else ''
722
723         if type_subif is None:
724             type_subif = ''
725
726         if isinstance(interface, basestring):
727             iface_key = Topology.get_interface_by_name(node, interface)
728             sw_if_index = Topology.get_interface_sw_index(node, iface_key)
729         else:
730             sw_if_index = interface
731
732         output = VatExecutor.cmd_from_template(node, "create_sub_interface.vat",
733                                                sw_if_index=sw_if_index,
734                                                sub_id=sub_id,
735                                                outer_vlan_id=outer_vlan_id,
736                                                inner_vlan_id=inner_vlan_id,
737                                                type_subif=type_subif)
738
739         if output[0]["retval"] == 0:
740             sw_subif_index = output[0]["sw_if_index"]
741             logger.trace('Created subinterface with index {}'
742                          .format(sw_subif_index))
743         else:
744             raise RuntimeError('Unable to create sub-interface on node {}'
745                                .format(node['host']))
746
747         with VatTerminal(node, json_param=False) as vat:
748             vat.vat_terminal_exec_cmd('exec show interfaces')
749
750         name = '{}.{}'.format(interface, sub_id)
751         return name, sw_subif_index
752
753     @staticmethod
754     def create_gre_tunnel_interface(node, source_ip, destination_ip):
755         """Create GRE tunnel interface on node.
756
757         :param node: VPP node to add tunnel interface.
758         :param source_ip: Source of the GRE tunnel.
759         :param destination_ip: Destination of the GRE tunnel.
760         :type node: dict
761         :type source_ip: str
762         :type destination_ip: str
763         :returns: Name and index of created GRE tunnel interface.
764         :rtype: tuple
765         :raises RuntimeError: If unable to create GRE tunnel interface.
766         """
767         output = VatExecutor.cmd_from_template(node, "create_gre.vat",
768                                                src=source_ip,
769                                                dst=destination_ip)
770         output = output[0]
771
772         if output["retval"] == 0:
773             sw_if_index = output["sw_if_index"]
774
775             vat_executor = VatExecutor()
776             vat_executor.execute_script_json_out("dump_interfaces.vat", node)
777             interface_dump_json = vat_executor.get_script_stdout()
778             name = VatJsonUtil.get_interface_name_from_json(
779                 interface_dump_json, sw_if_index)
780             return name, sw_if_index
781         else:
782             raise RuntimeError('Unable to create GRE tunnel on node {}.'
783                                .format(node))
784
785     @staticmethod
786     def vpp_create_loopback(node):
787         """Create loopback interface on VPP node.
788
789         :param node: Node to create loopback interface on.
790         :type node: dict
791         :returns: SW interface index.
792         :rtype: int
793         :raises RuntimeError: If it is not possible to create loopback on the
794         node.
795         """
796         out = VatExecutor.cmd_from_template(node, "create_loopback.vat")
797         if out[0].get('retval') == 0:
798             return out[0].get('sw_if_index')
799         else:
800             raise RuntimeError('Create loopback failed on node "{}"'
801                                .format(node['host']))
802
803     @staticmethod
804     def vpp_enable_input_acl_interface(node, interface, ip_version,
805                                        table_index):
806         """Enable input acl on interface.
807
808         :param node: VPP node to setup interface for input acl.
809         :param interface: Interface to setup input acl.
810         :param ip_version: Version of IP protocol.
811         :param table_index: Classify table index.
812         :type node: dict
813         :type interface: str or int
814         :type ip_version: str
815         :type table_index: int
816         """
817         if isinstance(interface, basestring):
818             sw_if_index = Topology.get_interface_sw_index(node, interface)
819         else:
820             sw_if_index = interface
821
822         with VatTerminal(node) as vat:
823             vat.vat_terminal_exec_cmd_from_template("input_acl_int.vat",
824                                                     sw_if_index=sw_if_index,
825                                                     ip_version=ip_version,
826                                                     table_index=table_index)
827
828     @staticmethod
829     def get_interface_classify_table(node, interface):
830         """Get name of classify table for the given interface.
831
832         :param node: VPP node to get data from.
833         :param interface: Name or sw_if_index of a specific interface.
834         :type node: dict
835         :type interface: str or int
836         :returns: Classify table name.
837         :rtype: str
838         """
839         if isinstance(interface, basestring):
840             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
841         else:
842             sw_if_index = interface
843
844         with VatTerminal(node) as vat:
845             data = vat.vat_terminal_exec_cmd_from_template(
846                 "classify_interface_table.vat",
847                 sw_if_index=sw_if_index)
848         return data[0]
849
850     @staticmethod
851     def get_interface_vrf_table(node, interface):
852         """Get vrf ID for the given interface.
853
854         :param node: VPP node.
855         :param interface: Name or sw_if_index of a specific interface.
856         :type node: dict
857         :type interface: str or int
858         :returns: vrf ID of the specified interface.
859         :rtype: int
860         """
861
862         if isinstance(interface, basestring):
863             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
864         else:
865             sw_if_index = interface
866
867         with VatTerminal(node) as vat:
868             data = vat.vat_terminal_exec_cmd_from_template(
869                 "interface_vrf_dump.vat",
870                 sw_if_index=sw_if_index)
871         return data[0]["vrf_id"]
872
873     @staticmethod
874     def get_sw_if_index(node, interface_name):
875         """Get sw_if_index for the given interface from actual interface dump.
876
877         :param node: VPP node to get interface data from.
878         :param interface_name: Name of the specific interface.
879         :type node: dict
880         :type interface_name: str
881         :returns: sw_if_index of the given interface.
882         :rtype: str
883         """
884
885         with VatTerminal(node) as vat:
886             if_data = vat.vat_terminal_exec_cmd_from_template(
887                 "interface_dump.vat")
888         for interface in if_data[0]:
889             if interface["interface_name"] == interface_name:
890                 return interface["sw_if_index"]
891
892         return None
893
894     @staticmethod
895     def vxlan_gpe_dump(node, interface_name=None):
896         """Get VxLAN GPE data for the given interface.
897
898         :param node: VPP node to get interface data from.
899         :param interface_name: Name of the specific interface. If None,
900         information about all VxLAN GPE interfaces is returned.
901         :type node: dict
902         :type interface_name: str
903         :returns: Dictionary containing data for the given VxLAN GPE interface
904         or if interface=None, the list of dictionaries with all VxLAN GPE
905         interfaces.
906         :rtype: dict or list
907         """
908
909         with VatTerminal(node) as vat:
910             vxlan_gpe_data = vat.vat_terminal_exec_cmd_from_template(
911                 "vxlan_gpe_dump.vat")
912
913         if interface_name:
914             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface_name)
915             if sw_if_index:
916                 for vxlan_gpe in vxlan_gpe_data[0]:
917                     if vxlan_gpe["sw_if_index"] == sw_if_index:
918                         return vxlan_gpe
919             return {}
920
921         return vxlan_gpe_data[0]
922
923     @staticmethod
924     def vpp_proxy_arp_interface_enable(node, interface):
925         """Enable proxy ARP on interface.
926
927         :param node: VPP node to enable proxy ARP on interface.
928         :param interface: Interface to enable proxy ARP.
929         :type node: dict
930         :type interface: str or int
931         """
932         if isinstance(interface, basestring):
933             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
934         else:
935             sw_if_index = interface
936
937         with VatTerminal(node) as vat:
938             vat.vat_terminal_exec_cmd_from_template(
939                 "proxy_arp_intfc_enable.vat",
940                 sw_if_index=sw_if_index)
941
942     @staticmethod
943     def vpp_ip_source_check_setup(node, interface):
944         """Setup Reverse Path Forwarding source check on interface.
945
946         :param node: Node to setup RPF source check.
947         :param interface: Interface name to setup RPF source check.
948         :type node: dict
949         :type interface: str
950         """
951         with VatTerminal(node) as vat:
952             vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat",
953                                                     interface_name=interface)
954
955     @staticmethod
956     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
957         """Assign VPP interface to specific VRF/FIB table.
958
959         :param node: VPP node where the FIB and interface are located.
960         :param interface: Interface to be assigned to FIB.
961         :param table_id: VRF table ID.
962         :param ipv6: Assign to IPv6 table. Default False.
963         :type node: dict
964         :type interface: str or int
965         :type table_id: int
966         :type ipv6: bool
967         """
968         if isinstance(interface, basestring):
969             sw_if_index = Topology.get_interface_sw_index(node, interface)
970         else:
971             sw_if_index = interface
972
973         ipv6 = 'ipv6' if ipv6 else ''
974
975         with VatTerminal(node) as vat:
976             ret = vat.vat_terminal_exec_cmd_from_template(
977                 "set_fib_to_interface.vat",
978                 sw_index=sw_if_index, vrf=table_id, ipv6=ipv6)
979
980         if ret[0]["retval"] != 0:
981             raise RuntimeError('Unable to assign interface to FIB node {}.'
982                                .format(node))
983
984
985     @staticmethod
986     def set_linux_interface_mac(node, interface, mac, namespace=None):
987         """Set MAC address for interface in linux.
988
989         :param node: Node where to execute command.
990         :param interface: Interface in namespace.
991         :param mac: MAC to be assigned to interface.
992         :param namespace: Execute command in namespace. Optional
993         :type node: dict
994         :type interface: str
995         :type mac: str
996         :type namespace: str
997         """
998         if namespace is not None:
999             cmd = 'ip netns exec {} ip link set {} address {}'.format(
1000                 namespace, interface, mac)
1001         else:
1002             cmd = 'ip link set {} address {}'.format(interface, mac)
1003         exec_cmd_no_error(node, cmd, sudo=True)