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