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