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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """Interface util library"""
16 from time import time, sleep
18 from robot.api import logger
20 from resources.libraries.python.ssh import SSH
21 from resources.libraries.python.ssh import exec_cmd_no_error
22 from resources.libraries.python.topology import NodeType, Topology
23 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
24 from resources.libraries.python.VatJsonUtil import VatJsonUtil
25 from resources.libraries.python.parsers.JsonParser import JsonParser
28 class InterfaceUtil(object):
29 """General utilities for managing interfaces"""
31 __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
34 def set_interface_state(node, interface, state):
35 """Set interface state on a node.
37 Function can be used for DUTs as well as for TGs.
39 :param node: node where the interface is
40 :param interface: interface name or sw_if_index
41 :param state: one of 'up' or 'down'
43 :type interface: str or int
47 if node['type'] == NodeType.DUT:
53 raise ValueError('Unexpected interface state: {}'.format(state))
55 if isinstance(interface, basestring):
56 sw_if_index = Topology.get_interface_sw_index(node, interface)
58 sw_if_index = interface
60 VatExecutor.cmd_from_template(node, 'set_if_state.vat',
61 sw_if_index=sw_if_index, state=state)
63 elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
64 cmd = 'ip link set {} {}'.format(interface, state)
65 exec_cmd_no_error(node, cmd, sudo=True)
67 raise Exception('Node {} has unknown NodeType: "{}"'.
68 format(node['host'], node['type']))
71 def set_interface_ethernet_mtu(node, interface, mtu):
72 """Set Ethernet MTU for specified interface.
74 Function can be used only for TGs.
76 :param node: node where the interface is
77 :param interface: interface name
78 :param mtu: MTU to set
84 if node['type'] == NodeType.DUT:
85 ValueError('Node {}: Setting Ethernet MTU for interface '
86 'on DUT nodes not supported', node['host'])
87 elif node['type'] == NodeType.TG:
88 cmd = 'ip link set {} mtu {}'.format(interface, mtu)
89 exec_cmd_no_error(node, cmd, sudo=True)
91 raise ValueError('Node {} has unknown NodeType: "{}"'.
92 format(node['host'], node['type']))
95 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
96 """Set default Ethernet MTU on all interfaces on node.
98 Function can be used only for TGs.
100 :param node: node where to set default MTU
104 for ifc in node['interfaces'].values():
105 InterfaceUtil.set_interface_ethernet_mtu(node, ifc['name'], 1500)
108 def vpp_node_interfaces_ready_wait(node, timeout=10):
109 """Wait until all interfaces with admin-up are in link-up state.
111 :param node: Node to wait on.
112 :param timeout: Waiting timeout in seconds (optional, default 10s)
115 :raises: RuntimeError if the timeout period value has elapsed.
121 out = InterfaceUtil.vpp_get_interface_data(node)
122 if time() - start > timeout:
123 for interface in out:
124 if interface.get('admin_up_down') == 1:
125 if interface.get('link_up_down') != 1:
126 logger.debug('{0} link-down'.format(
127 interface.get('interface_name')))
128 raise RuntimeError('timeout, not up {0}'.format(not_ready))
130 for interface in out:
131 if interface.get('admin_up_down') == 1:
132 if interface.get('link_up_down') != 1:
133 not_ready.append(interface.get('interface_name'))
137 logger.debug('Interfaces still in link-down state: {0}, '
138 'waiting...'.format(not_ready))
142 def vpp_nodes_interfaces_ready_wait(nodes, timeout=10):
143 """Wait until all interfaces with admin-up are in link-up state for
146 :param nodes: List of nodes to wait on.
147 :param timeout: Seconds to wait per node for all interfaces to come up.
150 :raises: RuntimeError if the timeout period value has elapsed.
153 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
156 def all_vpp_interfaces_ready_wait(nodes, timeout=10):
157 """Wait until all interfaces with admin-up are in link-up state for all
158 nodes in the topology.
160 :param nodes: Nodes in the topology.
161 :param timeout: Seconds to wait per node for all interfaces to come up.
164 :raises: RuntimeError if the timeout period value has elapsed.
166 for node in nodes.values():
167 if node['type'] == NodeType.DUT:
168 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
171 def vpp_get_interface_data(node, interface=None):
172 """Get all interface data from a VPP node. If a name or
173 sw_interface_index is provided, return only data for the matching
175 :param node: VPP node to get interface data from.
176 :param interface: Numeric index or name string of a specific interface.
178 :type interface: int or str
179 :return: List of dictionaries containing data for each interface, or a
180 single dictionary for the specified interface.
183 with VatTerminal(node) as vat:
184 response = vat.vat_terminal_exec_cmd_from_template(
185 "interface_dump.vat")
189 if interface is not None:
190 if isinstance(interface, basestring):
191 sw_if_index = Topology.get_interface_sw_index(node, interface)
193 sw_if_index = interface
196 if data_if["sw_if_index"] == sw_if_index:
203 def tg_set_interface_driver(node, pci_addr, driver):
204 """Set interface driver on the TG node.
206 :param node: Node to set interface driver on (must be TG node).
207 :param pci_addr: PCI address of the interface.
208 :param driver: Driver name.
213 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
214 if old_driver == driver:
220 # Unbind from current driver
221 if old_driver is not None:
222 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'.format(
223 pci_addr, old_driver)
224 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
225 if int(ret_code) != 0:
226 raise Exception("'{0}' failed on '{1}'".format(cmd,
229 # Bind to the new driver
230 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'.format(
232 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
233 if int(ret_code) != 0:
234 raise Exception("'{0}' failed on '{1}'".format(cmd, node['host']))
237 def tg_get_interface_driver(node, pci_addr):
238 """Get interface driver from the TG node.
240 :param node: Node to get interface driver on (must be TG node).
241 :param pci_addr: PCI address of the interface.
244 :return: Interface driver or None if not found.
248 # lspci -vmmks 0000:00:05.0
250 Class: Ethernet controller
252 Device: Virtio network device
253 SVendor: Red Hat, Inc
261 cmd = 'lspci -vmmks {0}'.format(pci_addr)
263 (ret_code, stdout, _) = ssh.exec_command(cmd)
264 if int(ret_code) != 0:
265 raise Exception("'{0}' failed on '{1}'".format(cmd, node['host']))
267 for line in stdout.splitlines():
270 (name, value) = line.split("\t", 1)
271 if name == 'Driver:':
277 def tg_set_interfaces_udev_rules(node):
278 """Set udev rules for interfaces.
280 Create udev rules file in /etc/udev/rules.d where are rules for each
281 interface used by TG node, based on MAC interface has specific name.
282 So after unbind and bind again to kernel driver interface has same
283 name as before. This must be called after TG has set name for each
284 port in topology dictionary.
286 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
289 :param node: Node to set udev rules on (must be TG node).
295 cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
296 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
297 if int(ret_code) != 0:
298 raise Exception("'{0}' failed on '{1}'".format(cmd, node['host']))
300 for interface in node['interfaces'].values():
301 rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
302 '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
303 interface['name'] + '\\"'
304 cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
305 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
306 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
307 if int(ret_code) != 0:
308 raise Exception("'{0}' failed on '{1}'".format(cmd,
311 cmd = '/etc/init.d/udev restart'
312 ssh.exec_command_sudo(cmd)
315 def tg_set_interfaces_default_driver(node):
316 """Set interfaces default driver specified in topology yaml file.
318 :param node: Node to setup interfaces driver on (must be TG node).
321 for interface in node['interfaces'].values():
322 InterfaceUtil.tg_set_interface_driver(node,
323 interface['pci_address'],
327 def update_vpp_interface_data_on_node(node):
328 """Update vpp generated interface data for a given node in DICT__nodes
330 Updates interface names, software if index numbers and any other details
331 generated specifically by vpp that are unknown before testcase run.
332 It does this by dumping interface list to JSON output from all
333 devices using vpp_api_test, and pairing known information from topology
334 (mac address/pci address of interface) to state from VPP.
336 :param node: Node selected from DICT__nodes
339 vat_executor = VatExecutor()
340 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
341 interface_dump_json = vat_executor.get_script_stdout()
342 VatJsonUtil.update_vpp_interface_data_from_json(node,
346 def update_tg_interface_data_on_node(node):
347 """Update interface name for TG/linux node in DICT__nodes.
349 :param node: Node selected from DICT__nodes.
353 # for dev in `ls /sys/class/net/`;
354 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
355 "52:54:00:9f:82:63": "eth0"
356 "52:54:00:77:ae:a9": "eth1"
357 "52:54:00:e1:8a:0f": "eth2"
358 "00:00:00:00:00:00": "lo"
360 .. todo:: parse lshw -json instead
362 # First setup interface driver specified in yaml file
363 InterfaceUtil.tg_set_interfaces_default_driver(node)
365 # Get interface names
369 cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
370 '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
372 (ret_code, stdout, _) = ssh.exec_command(cmd)
373 if int(ret_code) != 0:
374 raise Exception('Get interface name and MAC failed')
375 tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
376 interfaces = JsonParser().parse_data(tmp)
377 for interface in node['interfaces'].values():
378 name = interfaces.get(interface['mac_address'])
381 interface['name'] = name
383 # Set udev rules for interfaces
384 InterfaceUtil.tg_set_interfaces_udev_rules(node)
387 def update_all_interface_data_on_all_nodes(nodes):
388 """Update interface names on all nodes in DICT__nodes.
390 This method updates the topology dictionary by querying interface lists
391 of all nodes mentioned in the topology dictionary.
393 :param nodes: Nodes in the topology.
396 for node_data in nodes.values():
397 if node_data['type'] == NodeType.DUT:
398 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
399 elif node_data['type'] == NodeType.TG:
400 InterfaceUtil.update_tg_interface_data_on_node(node_data)
403 def create_vlan_subinterface(node, interface, vlan):
404 """Create VLAN subinterface on node.
406 :param node: Node to add VLAN subinterface on.
407 :param interface: Interface name on which create VLAN subinterface.
408 :param vlan: VLAN ID of the subinterface to be created.
412 :return: Name and index of created subinterface.
415 sw_if_index = Topology.get_interface_sw_index(node, interface)
417 output = VatExecutor.cmd_from_template(node, "create_vlan_subif.vat",
418 sw_if_index=sw_if_index,
420 if output[0]["retval"] == 0:
421 sw_subif_index = output[0]["sw_if_index"]
422 logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} '
423 'created on node {}'.format(sw_subif_index,
426 raise RuntimeError('Unable to create VLAN subinterface on node {}'
427 .format(node['host']))
429 with VatTerminal(node, False) as vat:
430 vat.vat_terminal_exec_cmd('exec show interfaces')
432 return '{}.{}'.format(interface, vlan), sw_subif_index
435 def create_vxlan_interface(node, vni, source_ip, destination_ip):
436 """Create VXLAN interface and return sw if index of created interface.
438 Executes "vxlan_add_del_tunnel src {src} dst {dst} vni {vni}" VAT
441 :param node: Node where to create VXLAN interface.
442 :param vni: VXLAN Network Identifier.
443 :param source_ip: Source IP of a VXLAN Tunnel End Point.
444 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
448 :type destination_ip: str
449 :return: SW IF INDEX of created interface.
452 output = VatExecutor.cmd_from_template(node, "vxlan_create.vat",
458 if output["retval"] == 0:
459 return output["sw_if_index"]
461 raise RuntimeError('Unable to create VXLAN interface on node {}'