-# Copyright (c) 2019 Cisco and/or its affiliates.
+# Copyright (c) 2020 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
from robot.api import logger
from resources.libraries.python.Constants import Constants
-from resources.libraries.python.CpuUtils import CpuUtils
from resources.libraries.python.DUTSetup import DUTSetup
+from resources.libraries.python.IPAddress import IPAddress
from resources.libraries.python.L2Util import L2Util
from resources.libraries.python.PapiExecutor import PapiSocketExecutor
from resources.libraries.python.parsers.JsonParser import JsonParser
-from resources.libraries.python.ssh import SSH, exec_cmd_no_error
+from resources.libraries.python.ssh import SSH, exec_cmd, exec_cmd_no_error
from resources.libraries.python.topology import NodeType, Topology
from resources.libraries.python.VPPUtil import VPPUtil
BOND_API_MODE_LACP = 5
+class RdmaMode(IntEnum):
+ """RDMA interface mode."""
+ RDMA_API_MODE_AUTO = 0
+ RDMA_API_MODE_IBV = 1
+ RDMA_API_MODE_DV = 2
+
+
class InterfaceUtil:
"""General utilities for managing interfaces"""
- __UDEV_IF_RULES_FILE = u"/etc/udev/rules.d/10-network.rules"
-
@staticmethod
def pci_to_int(pci_str):
"""Convert PCI address from string format (0000:18:0a.0) to
)
@staticmethod
- def set_interface_ethernet_mtu(node, iface_key, mtu):
- """Set Ethernet MTU for specified interface.
+ def set_interface_mtu(node, pf_pcis, mtu=9200):
+ """Set Ethernet MTU for specified interfaces.
- Function can be used only for TGs.
-
- :param node: Node where the interface is.
- :param iface_key: Interface key from topology file.
- :param mtu: MTU to set.
- :type node: dict
- :type iface_key: str
+ :param node: Topology node.
+ :param pf_pcis: List of node's interfaces PCI addresses.
+ :param mtu: MTU to set. Default: 9200.
+ :type nodes: dict
+ :type pf_pcis: list
:type mtu: int
- :returns: Nothing.
- :raises ValueError: If the node type is "DUT".
- :raises ValueError: If the node has an unknown node type.
+ :raises RuntimeError: If failed to set MTU on interface.
"""
- if node[u"type"] == NodeType.DUT:
- msg = f"Node {node[u'host']}: Setting Ethernet MTU for interface " \
- f"on DUT nodes not supported"
- elif node[u"type"] != NodeType.TG:
- msg = f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
- else:
- iface_name = Topology.get_interface_name(node, iface_key)
- cmd = f"ip link set {iface_name} mtu {mtu}"
+ for pf_pci in pf_pcis:
+ pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
+ cmd = f"ip link set {pf_eth} mtu {mtu}"
exec_cmd_no_error(node, cmd, sudo=True)
- return
- raise ValueError(msg)
@staticmethod
- def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
- """Set default Ethernet MTU on all interfaces on node.
+ def set_interface_flow_control(node, pf_pcis, rx=u"off", tx=u"off"):
+ """Set Ethernet flow control for specified interfaces.
- Function can be used only for TGs.
+ :param node: Topology node.
+ :param pf_pcis: List of node's interfaces PCI addresses.
+ :param rx: RX flow. Default: off.
+ :param tx: TX flow. Default: off.
+ :type nodes: dict
+ :type pf_pcis: list
+ :type rx: str
+ :type tx: str
+ """
+ for pf_pci in pf_pcis:
+ pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
+ cmd = f"ethtool -A {pf_eth} rx off tx off"
+ ret_code, _, _ = exec_cmd(node, cmd, sudo=True)
+ if int(ret_code) not in (0, 78):
+ raise RuntimeError("Failed to set MTU on {pf_eth}!")
- :param node: Node where to set default MTU.
- :type node: dict
- :returns: Nothing.
+
+ @staticmethod
+ def set_pci_parameter(node, pf_pcis, key, value):
+ """Set PCI parameter for specified interfaces.
+
+ :param node: Topology node.
+ :param pf_pcis: List of node's interfaces PCI addresses.
+ :param key: Key to set.
+ :param value: Value to set.
+ :type nodes: dict
+ :type pf_pcis: list
+ :type key: str
+ :type value: str
"""
- for ifc in node[u"interfaces"]:
- InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
+ for pf_pci in pf_pcis:
+ cmd = f"setpci -s {pf_pci} {key}={value}"
+ exec_cmd_no_error(node, cmd, sudo=True)
@staticmethod
def vpp_set_interface_mtu(node, interface, mtu=9200):
return if_data.get(u"l2_address")
+ @staticmethod
+ def vpp_set_interface_mac(node, interface, mac):
+ """Set MAC address for the given interface.
+
+ :param node: VPP node to set interface MAC.
+ :param interface: Numeric index or name string of a specific interface.
+ :param mac: Required MAC address.
+ :type node: dict
+ :type interface: int or str
+ :type mac: str
+ """
+ cmd = u"sw_interface_set_mac_address"
+ args = dict(
+ sw_if_index=InterfaceUtil.get_interface_index(node, interface),
+ mac_address=L2Util.mac_to_bin(mac)
+ )
+ err_msg = f"Failed to set MAC address of interface {interface}" \
+ f"on host {node[u'host']}"
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args).get_reply(err_msg)
+
@staticmethod
def tg_set_interface_driver(node, pci_addr, driver):
"""Set interface driver on the TG node.
"""
return DUTSetup.get_pci_dev_driver(node, pci_addr)
- @staticmethod
- def tg_set_interfaces_udev_rules(node):
- """Set udev rules for interfaces.
-
- Create udev rules file in /etc/udev/rules.d where are rules for each
- interface used by TG node, based on MAC interface has specific name.
- So after unbind and bind again to kernel driver interface has same
- name as before. This must be called after TG has set name for each
- port in topology dictionary.
- udev rule example
- SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
- NAME="eth1"
-
- :param node: Node to set udev rules on (must be TG node).
- :type node: dict
- :raises RuntimeError: If setting of udev rules fails.
- """
- ssh = SSH()
- ssh.connect(node)
-
- cmd = f"rm -f {InterfaceUtil.__UDEV_IF_RULES_FILE}"
- ret_code, _, _ = ssh.exec_command_sudo(cmd)
- if int(ret_code) != 0:
- raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
-
- for interface in node[u"interfaces"].values():
- rule = u'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
- u'==\\"' + interface[u"mac_address"] + u'\\", NAME=\\"' + \
- interface[u"name"] + u'\\"'
- cmd = f"sh -c \"echo '{rule}'\" >> " \
- f"{InterfaceUtil.__UDEV_IF_RULES_FILE}'"
-
- ret_code, _, _ = ssh.exec_command_sudo(cmd)
- if int(ret_code) != 0:
- raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
-
- cmd = u"/etc/init.d/udev restart"
- ssh.exec_command_sudo(cmd)
-
@staticmethod
def tg_set_interfaces_default_driver(node):
"""Set interfaces default driver specified in topology yaml file.
InterfaceUtil.update_nic_interface_names(node)
@staticmethod
- def update_tg_interface_data_on_node(node, skip_tg_udev=False):
+ def update_tg_interface_data_on_node(node):
"""Update interface name for TG/linux node in DICT__nodes.
.. note::
"00:00:00:00:00:00": "lo"
:param node: Node selected from DICT__nodes.
- :param skip_tg_udev: Skip udev rename on TG node.
:type node: dict
- :type skip_tg_udev: bool
:raises RuntimeError: If getting of interface name and MAC fails.
"""
# First setup interface driver specified in yaml file
continue
interface[u"name"] = name
- # Set udev rules for interfaces
- if not skip_tg_udev:
- InterfaceUtil.tg_set_interfaces_udev_rules(node)
-
@staticmethod
def iface_update_numa_node(node):
"""For all interfaces from topology file update numa node based on
:raises ValueError: If numa node ia less than 0.
:raises RuntimeError: If update of numa node failed.
"""
- def check_cpu_node_count(node_n, val):
- val = int(val)
- if val < 0:
- if CpuUtils.cpu_node_count(node_n) == 1:
- val = 0
- else:
- raise ValueError
- return val
ssh = SSH()
for if_key in Topology.get_node_interfaces(node):
if_pci = Topology.get_interface_pci_addr(node, if_key)
ret, out, _ = ssh.exec_command(cmd)
if ret == 0:
try:
- numa_node = check_cpu_node_count(node, out)
+ numa_node = 0 if int(out) < 0 else int(out)
except ValueError:
logger.trace(
f"Reading numa location failed for: {if_pci}"
else:
raise RuntimeError(f"Update numa node failed for: {if_pci}")
- @staticmethod
- def update_all_numa_nodes(nodes, skip_tg=False):
- """For all nodes and all their interfaces from topology file update numa
- node information based on information from the node.
-
- :param nodes: Nodes in the topology.
- :param skip_tg: Skip TG node
- :type nodes: dict
- :type skip_tg: bool
- :returns: Nothing.
- """
- for node in nodes.values():
- if node[u"type"] == NodeType.DUT:
- InterfaceUtil.iface_update_numa_node(node)
- elif node[u"type"] == NodeType.TG and not skip_tg:
- InterfaceUtil.iface_update_numa_node(node)
-
@staticmethod
def update_all_interface_data_on_all_nodes(
- nodes, skip_tg=False, skip_tg_udev=False, numa_node=False):
+ nodes, skip_tg=False, skip_vpp=False):
"""Update interface names on all nodes in DICT__nodes.
This method updates the topology dictionary by querying interface lists
:param nodes: Nodes in the topology.
:param skip_tg: Skip TG node.
- :param skip_tg_udev: Skip udev rename on TG node.
- :param numa_node: Retrieve numa_node location.
+ :param skip_vpp: Skip VPP node.
:type nodes: dict
:type skip_tg: bool
- :type skip_tg_udev: bool
- :type numa_node: bool
+ :type skip_vpp: bool
"""
- for node_data in nodes.values():
- if node_data[u"type"] == NodeType.DUT:
- InterfaceUtil.update_vpp_interface_data_on_node(node_data)
- elif node_data[u"type"] == NodeType.TG and not skip_tg:
- InterfaceUtil.update_tg_interface_data_on_node(
- node_data, skip_tg_udev)
-
- if numa_node:
- if node_data[u"type"] == NodeType.DUT:
- InterfaceUtil.iface_update_numa_node(node_data)
- elif node_data[u"type"] == NodeType.TG and not skip_tg:
- InterfaceUtil.iface_update_numa_node(node_data)
+ for node in nodes.values():
+ if node[u"type"] == NodeType.DUT and not skip_vpp:
+ InterfaceUtil.update_vpp_interface_data_on_node(node)
+ elif node[u"type"] == NodeType.TG and not skip_tg:
+ InterfaceUtil.update_tg_interface_data_on_node(node)
+ InterfaceUtil.iface_update_numa_node(node)
@staticmethod
def create_vlan_subinterface(node, interface, vlan):
:raises RuntimeError: if it is unable to create VxLAN interface on the
node.
"""
- src_address = ip_address(source_ip)
- dst_address = ip_address(destination_ip)
-
cmd = u"vxlan_add_del_tunnel"
args = dict(
- is_add=1,
- is_ipv6=1 if src_address.version == 6 else 0,
+ is_add=True,
instance=Constants.BITWISE_NON_ZERO,
- src_address=src_address.packed,
- dst_address=dst_address.packed,
+ src_address=IPAddress.create_ip_address_object(
+ ip_address(source_ip)
+ ),
+ dst_address=IPAddress.create_ip_address_object(
+ ip_address(destination_ip)
+ ),
mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
encap_vrf_id=0,
decap_next_index=Constants.BITWISE_NON_ZERO,
cmd = u"sw_interface_set_vxlan_bypass"
args = dict(
- is_ipv6=0,
+ is_ipv6=False,
sw_if_index=sw_if_index,
- enable=1
+ enable=True
)
err_msg = f"Failed to set VXLAN bypass on interface " \
f"on host {node[u'host']}"
:returns: Processed vxlan interface dump.
:rtype: dict
"""
- if vxlan_dump[u"is_ipv6"]:
- vxlan_dump[u"src_address"] = \
- ip_address(vxlan_dump[u"src_address"])
- vxlan_dump[u"dst_address"] = \
- ip_address(vxlan_dump[u"dst_address"])
- else:
- vxlan_dump[u"src_address"] = \
- ip_address(vxlan_dump[u"src_address"][0:4])
- vxlan_dump[u"dst_address"] = \
- ip_address(vxlan_dump[u"dst_address"][0:4])
+ vxlan_dump[u"src_address"] = str(vxlan_dump[u"src_address"])
+ vxlan_dump[u"dst_address"] = str(vxlan_dump[u"dst_address"])
return vxlan_dump
if interface is not None:
return if_key
@staticmethod
- def add_eth_interface(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
+ def add_eth_interface(
+ node, ifc_name=None, sw_if_index=None, ifc_pfx=None,
+ host_if_key=None):
"""Add ethernet interface to current topology.
:param node: DUT node from topology.
:param ifc_name: Name of the interface.
:param sw_if_index: SW interface index.
:param ifc_pfx: Interface key prefix.
+ :param host_if_key: Host interface key from topology file.
:type node: dict
:type ifc_name: str
:type sw_if_index: int
:type ifc_pfx: str
+ :type host_if_key: str
"""
if_key = Topology.add_new_port(node, ifc_pfx)
Topology.update_interface_name(node, if_key, ifc_name)
ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
Topology.update_interface_mac_address(node, if_key, ifc_mac)
+ if host_if_key is not None:
+ Topology.set_interface_numa_node(
+ node, if_key, Topology.get_interface_numa_node(
+ node, host_if_key
+ )
+ )
+ Topology.update_interface_pci_address(
+ node, if_key, Topology.get_interface_pci_addr(node, host_if_key)
+ )
@staticmethod
- def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
+ def vpp_create_avf_interface(
+ node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0):
"""Create AVF interface on VPP node.
:param node: DUT node from topology.
- :param vf_pci_addr: PCI address binded to i40evf driver.
+ :param if_key: Interface key from topology file of interface
+ to be bound to i40evf driver.
:param num_rx_queues: Number of RX queues.
+ :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
+ :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
:type node: dict
- :type vf_pci_addr: str
+ :type if_key: str
:type num_rx_queues: int
- :returns: Interface key (name) in topology.
+ :type rxq_size: int
+ :type txq_size: int
+ :returns: AVF interface key (name) in topology.
:rtype: str
:raises RuntimeError: If it is not possible to create AVF interface on
the node.
)
cmd = u"avf_create"
+ vf_pci_addr = Topology.get_interface_pci_addr(node, if_key)
args = dict(
pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
enable_elog=0,
rxq_num=int(num_rx_queues) if num_rx_queues else 0,
- rxq_size=0,
- txq_size=0
+ rxq_size=rxq_size,
+ txq_size=txq_size
)
err_msg = f"Failed to create AVF interface on host {node[u'host']}"
with PapiSocketExecutor(node) as papi_exec:
sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
InterfaceUtil.add_eth_interface(
- node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf"
+ node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf",
+ host_if_key=if_key
)
- if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
- return if_key
+ return Topology.get_interface_by_sw_index(node, sw_if_index)
@staticmethod
- def vpp_create_rdma_interface(node, pci_addr, num_rx_queues=None):
+ def vpp_create_rdma_interface(
+ node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0,
+ mode=u"auto"):
"""Create RDMA interface on VPP node.
:param node: DUT node from topology.
- :param pci_addr: PCI address binded to rdma-core driver.
+ :param if_key: Physical interface key from topology file of interface
+ to be bound to rdma-core driver.
:param num_rx_queues: Number of RX queues.
+ :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
+ :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
+ :param mode: RDMA interface mode - auto/ibv/dv.
:type node: dict
- :type pci_addr: str
+ :type if_key: str
:type num_rx_queues: int
- :returns: Interface key (name) in topology.
+ :type rxq_size: int
+ :type txq_size: int
+ :type mode: str
+ :returns: Interface key (name) in topology file.
:rtype: str
:raises RuntimeError: If it is not possible to create RDMA interface on
the node.
"""
+ PapiSocketExecutor.run_cli_cmd(
+ node, u"set logging class rdma level debug"
+ )
+
cmd = u"rdma_create"
+ pci_addr = Topology.get_interface_pci_addr(node, if_key)
args = dict(
name=InterfaceUtil.pci_to_eth(node, pci_addr),
host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
rxq_num=int(num_rx_queues) if num_rx_queues else 0,
- rxq_size=0,
- txq_size=0
+ rxq_size=rxq_size,
+ txq_size=txq_size,
+ mode=getattr(RdmaMode, f"RDMA_API_MODE_{mode.upper()}").value,
)
err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
with PapiSocketExecutor(node) as papi_exec:
sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
+ InterfaceUtil.vpp_set_interface_mac(
+ node, sw_if_index, Topology.get_interface_mac(node, if_key)
+ )
InterfaceUtil.add_eth_interface(
- node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma"
+ node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma",
+ host_if_key=if_key
)
- if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
- return if_key
+ return Topology.get_interface_by_sw_index(node, sw_if_index)
@staticmethod
def vpp_enslave_physical_interface(node, interface, bond_if):
def get_sw_if_index(node, interface_name):
"""Get sw_if_index for the given interface from actual interface dump.
+ FIXME: Delete and redirect callers to vpp_get_interface_sw_index.
+
:param node: VPP node to get interface data from.
:param interface_name: Name of the specific interface.
:type node: dict
)
pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
- InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
- vf_id=vf_id)
+ InterfaceUtil.set_linux_interface_trust_on(
+ node, pf_dev, vf_id=vf_id
+ )
if osi_layer == u"L2":
InterfaceUtil.set_linux_interface_spoof_off(
node, pf_dev, vf_id=vf_id
)
Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
+ Topology.set_interface_numa_node(
+ node, vf_ifc_key, Topology.get_interface_numa_node(
+ node, ifc_key
+ )
+ )
vf_ifc_keys.append(vf_ifc_key)
return vf_ifc_keys