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