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