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