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