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