-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2018 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:
"""Defines nodes and topology structure."""
-from resources.libraries.python.parsers.JsonParser import JsonParser
-from resources.libraries.python.VatExecutor import VatExecutor
-from resources.libraries.python.ssh import SSH
-from resources.libraries.python.InterfaceSetup import InterfaceSetup
+from collections import Counter
+
+from yaml import load
+
from robot.api import logger
-from robot.libraries.BuiltIn import BuiltIn
+from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError
from robot.api.deco import keyword
-from yaml import load
-__all__ = ["DICT__nodes", 'Topology']
+__all__ = ["DICT__nodes", 'Topology', 'NodeType']
def load_topo_from_yaml():
- """Load topology from file defined in "${TOPOLOGY_PATH}" variable
+ """Load topology from file defined in "${TOPOLOGY_PATH}" variable.
- :return: nodes from loaded topology
+ :returns: Nodes from loaded topology.
"""
- topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
+ try:
+ topo_path = BuiltIn().get_variable_value("${TOPOLOGY_PATH}")
+ except RobotNotRunningError:
+ return ''
with open(topo_path) as work_file:
return load(work_file.read())['nodes']
+# pylint: disable=invalid-name
+
class NodeType(object):
- """Defines node types used in topology dictionaries"""
+ """Defines node types used in topology dictionaries."""
# Device Under Test (this node has VPP running on it)
DUT = 'DUT'
# Traffic Generator (this node has traffic generator on it)
TG = 'TG'
+ # Virtual Machine (this node running on DUT node)
+ VM = 'VM'
class NodeSubTypeTG(object):
- #T-Rex traffic generator
+ """Defines node sub-type TG - traffic generator."""
+ # T-Rex traffic generator
TREX = 'TREX'
# Moongen
MOONGEN = 'MOONGEN'
class Topology(object):
- """Topology data manipulation and extraction methods
+ """Topology data manipulation and extraction methods.
Defines methods used for manipulation and extraction of data from
- the used topology.
+ the active topology.
+
+ "Active topology" contains initially data from the topology file and can be
+ extended with additional data from the DUTs like internal interface indexes
+ or names. Additional data which can be filled to the active topology are
+
+ - additional internal representation (index, name, ...)
+ - operational data (dynamic ports)
+
+ To access the port data it is recommended to use a port key because the key
+ does not rely on the data retrieved from nodes, this allows to call most of
+ the methods without having filled active topology with internal nodes data.
"""
+ @staticmethod
+ def add_new_port(node, ptype):
+ """Add new port to the node to active topology.
+
+ :param node: Node to add new port on.
+ :param ptype: Port type, used as key prefix.
+ :type node: dict
+ :type ptype: str
+ :returns: Port key or None
+ :rtype: string or None
+ """
+ max_ports = 1000000
+ iface = None
+ for i in range(1, max_ports):
+ if node['interfaces'].get(str(ptype) + str(i)) is None:
+ iface = str(ptype) + str(i)
+ node['interfaces'][iface] = dict()
+ break
+ return iface
+
+ @staticmethod
+ def remove_port(node, iface_key):
+ """Remove required port from active topology.
+
+ :param node: Node to remove port on.
+ :param: iface_key: Topology key of the interface.
+ :type node: dict
+ :type iface_key: str
+ :returns: Nothing
+ """
+ try:
+ node['interfaces'].pop(iface_key)
+ except KeyError:
+ pass
+
+ @staticmethod
+ def remove_all_ports(node, ptype):
+ """Remove all ports with ptype as prefix.
+
+ :param node: Node to remove ports on.
+ :param: ptype: Port type, used as key prefix.
+ :type node: dict
+ :type ptype: str
+ :returns: Nothing
+ """
+ for if_key in list(node['interfaces']):
+ if if_key.startswith(str(ptype)):
+ node['interfaces'].pop(if_key)
+
+ @staticmethod
+ def remove_all_added_ports_on_all_duts_from_topology(nodes):
+ """Remove all added ports on all DUT nodes in the topology.
+
+ :param nodes: Nodes in the topology.
+ :type nodes: dict
+ :returns: Nothing
+ """
+ port_types = ('subinterface', 'vlan_subif', 'memif', 'tap', 'vhost',
+ 'loopback', 'gre_tunnel', 'vxlan_tunnel')
+
+ for node_data in nodes.values():
+ if node_data['type'] == NodeType.DUT:
+ for ptype in port_types:
+ Topology.remove_all_ports(node_data, ptype)
+
+ @staticmethod
+ def update_interface_sw_if_index(node, iface_key, sw_if_index):
+ """Update sw_if_index on the interface from the node.
+
+ :param node: Node to update sw_if_index on.
+ :param iface_key: Topology key of the interface.
+ :param sw_if_index: Internal index to store.
+ :type node: dict
+ :type iface_key: str
+ :type sw_if_index: int
+ """
+ node['interfaces'][iface_key]['vpp_sw_index'] = int(sw_if_index)
+
+ @staticmethod
+ def update_interface_name(node, iface_key, name):
+ """Update name on the interface from the node.
+
+ :param node: Node to update name on.
+ :param iface_key: Topology key of the interface.
+ :param name: Interface name to store.
+ :type node: dict
+ :type iface_key: str
+ :type name: str
+ """
+ node['interfaces'][iface_key]['name'] = str(name)
+
+ @staticmethod
+ def update_interface_mac_address(node, iface_key, mac_address):
+ """Update mac_address on the interface from the node.
+
+ :param node: Node to update MAC on.
+ :param iface_key: Topology key of the interface.
+ :param mac_address: MAC address.
+ :type node: dict
+ :type iface_key: str
+ :type mac_address: str
+ """
+ node['interfaces'][iface_key]['mac_address'] = str(mac_address)
+
+ @staticmethod
+ def update_interface_vhost_socket(node, iface_key, vhost_socket):
+ """Update vhost socket name on the interface from the node.
+
+ :param node: Node to update socket name on.
+ :param iface_key: Topology key of the interface.
+ :param vhost_socket: Path to named socket on node.
+ :type node: dict
+ :type iface_key: str
+ :type vhost_socket: str
+ """
+ node['interfaces'][iface_key]['vhost_socket'] = str(vhost_socket)
+
+ @staticmethod
+ def update_interface_memif_socket(node, iface_key, memif_socket):
+ """Update memif socket name on the interface from the node.
+
+ :param node: Node to update socket name on.
+ :param iface_key: Topology key of the interface.
+ :param memif_socket: Path to named socket on node.
+ :type node: dict
+ :type iface_key: str
+ :type memif_socket: str
+ """
+ node['interfaces'][iface_key]['memif_socket'] = str(memif_socket)
+
+ @staticmethod
+ def update_interface_memif_id(node, iface_key, memif_id):
+ """Update memif ID on the interface from the node.
+
+ :param node: Node to update memif ID on.
+ :param iface_key: Topology key of the interface.
+ :param memif_id: Memif interface ID.
+ :type node: dict
+ :type iface_key: str
+ :type memif_id: str
+ """
+ node['interfaces'][iface_key]['memif_id'] = str(memif_id)
+
+ @staticmethod
+ def update_interface_memif_role(node, iface_key, memif_role):
+ """Update memif role on the interface from the node.
+
+ :param node: Node to update memif role on.
+ :param iface_key: Topology key of the interface.
+ :param memif_role: Memif role.
+ :type node: dict
+ :type iface_key: str
+ :type memif_role: str
+ """
+ node['interfaces'][iface_key]['memif_role'] = str(memif_role)
+
+ @staticmethod
+ def update_interface_tap_dev_name(node, iface_key, dev_name):
+ """Update device name on the tap interface from the node.
+
+ :param node: Node to update tap device name on.
+ :param iface_key: Topology key of the interface.
+ :param dev_name: Device name of the tap interface.
+ :type node: dict
+ :type iface_key: str
+ :type dev_name: str
+ :returns: Nothing
+ """
+ node['interfaces'][iface_key]['dev_name'] = str(dev_name)
+
@staticmethod
def get_node_by_hostname(nodes, hostname):
"""Get node from nodes of the topology by hostname.
:param hostname: Host name.
:type nodes: dict
:type hostname: str
- :return: Node dictionary or None if not found.
+ :returns: Node dictionary or None if not found.
"""
for node in nodes.values():
if node['host'] == hostname:
:param nodes: Nodes of the test topology.
:type nodes: dict
- :return: Links in the topology.
+ :returns: Links in the topology.
:rtype: list
"""
links = []
@staticmethod
def _get_interface_by_key_value(node, key, value):
- """Return node interface name according to key and value
+ """Return node interface key from topology file
+ according to key and value.
- :param node: :param node: the node dictionary
- :param key: key by which to select the interface.
- :param value: value that should be found using the key.
- :return:
+ :param node: The node dictionary.
+ :param key: Key by which to select the interface.
+ :param value: Value that should be found using the key.
+ :type node: dict
+ :type key: string
+ :type value: string
+ :returns: Interface key from topology file
+ :rtype: string
"""
-
interfaces = node['interfaces']
retval = None
- for interface in interfaces.values():
- k_val = interface.get(key)
+ for if_key, if_val in interfaces.iteritems():
+ k_val = if_val.get(key)
if k_val is not None:
if k_val == value:
- retval = interface['name']
+ retval = if_key
break
return retval
- def get_interface_by_link_name(self, node, link_name):
- """Return interface name of link on node.
+ @staticmethod
+ def get_interface_by_name(node, iface_name):
+ """Return interface key based on name from DUT/TG.
- This method returns the interface name asociated with a given link
- for a given node.
- :param link_name: name of the link that a interface is connected to.
- :param node: the node topology dictionary
- :return: interface name of the interface connected to the given link
+ This method returns interface key based on interface name
+ retrieved from the DUT, or TG.
+
+ :param node: The node topology dictionary.
+ :param iface_name: Interface name (string form).
+ :type node: dict
+ :type iface_name: string
+ :returns: Interface key.
+ :rtype: str
"""
+ return Topology._get_interface_by_key_value(node, "name", iface_name)
- return self._get_interface_by_key_value(node, "link", link_name)
+ @staticmethod
+ def get_interface_by_link_name(node, link_name):
+ """Return interface key of link on node.
+
+ This method returns the interface name associated with a given link
+ for a given node.
+
+ :param node: The node topology dictionary.
+ :param link_name: Name of the link that a interface is connected to.
+ :type node: dict
+ :type link_name: string
+ :returns: Interface key of the interface connected to the given link.
+ :rtype: str
+ """
+ return Topology._get_interface_by_key_value(node, "link", link_name)
def get_interfaces_by_link_names(self, node, link_names):
- """Return dictionary of dicitonaries {"interfaceN", interface name}.
+ """Return dictionary of dictionaries {"interfaceN", interface name}.
- This method returns the interface names asociated with given links
+ This method returns the interface names associated with given links
for a given node.
- The resulting dictionary can be then used to with VatConfigGenerator
- to generate a VAT script with proper interface names.
- :param link_names: list of names of the link that a interface is
- connected to.
- :param node: the node topology directory
- :return: dictionary of interface names that are connected to the given
- links
- """
+ :param node: The node topology directory.
+ :param link_names: List of names of the link that a interface is
+ connected to.
+ :type node: dict
+ :type link_names: list
+ :returns: Dictionary of interface names that are connected to the given
+ links.
+ :rtype: dict
+ """
retval = {}
interface_key_tpl = "interface{}"
interface_number = 1
for link_name in link_names:
- interface_name = self.get_interface_by_link_name(node, link_name)
+ interface = self.get_interface_by_link_name(node, link_name)
+ interface_name = self.get_interface_name(node, interface)
interface_key = interface_key_tpl.format(str(interface_number))
retval[interface_key] = interface_name
interface_number += 1
return retval
- def get_interface_by_sw_index(self, node, sw_index):
+ @staticmethod
+ def get_interface_by_sw_index(node, sw_index):
"""Return interface name of link on node.
- This method returns the interface name asociated with a software index
- assigned to the interface by vpp for a given node.
- :param sw_index: sw_index of the link that a interface is connected to.
- :param node: the node topology dictionary
- :return: interface name of the interface connected to the given link
- """
-
- return self._get_interface_by_key_value(node, "vpp_sw_index", sw_index)
-
- @staticmethod
- def convert_mac_to_number_list(mac_address):
- """Convert mac address string to list of decimal numbers.
-
- Converts a : separated mac address to decimal number list as used
- in json interface dump.
- :param mac_address: string mac address
- :return: list representation of mac address
- """
-
- list_mac = []
- for num in mac_address.split(":"):
- list_mac.append(int(num, 16))
- return list_mac
-
- def _extract_vpp_interface_by_mac(self, interfaces_list, mac_address):
- """Return interface dictionary from interface_list by mac address.
-
- Extracts interface dictionary from all of the interfaces in interfaces
- list parsed from json according to mac_address of the interface
- :param interfaces_list: dictionary of all interfaces parsed from json
- :param mac_address: string mac address of interface we are looking for
- :return: interface dictionary from json
- """
-
- interface_dict = {}
- list_mac_address = self.convert_mac_to_number_list(mac_address)
- logger.trace(str(list_mac_address))
- for interface in interfaces_list:
- # TODO: create vat json integrity checking and move there
- if "l2_address" not in interface:
- raise KeyError(
- "key l2_address not found in interface dict."
- "Probably input list is not parsed from correct VAT "
- "json output.")
- if "l2_address_length" not in interface:
- raise KeyError(
- "key l2_address_length not found in interface "
- "dict. Probably input list is not parsed from correct "
- "VAT json output.")
- mac_from_json = interface["l2_address"][:6]
- if mac_from_json == list_mac_address:
- if interface["l2_address_length"] != 6:
- raise ValueError("l2_address_length value is not 6.")
- interface_dict = interface
- break
- return interface_dict
-
- def vpp_interface_name_from_json_by_mac(self, json_data, mac_address):
- """Return vpp interface name string from VAT interface dump json output
-
- Extracts the name given to an interface by VPP.
- These interface names differ from what you would see if you
- used the ipconfig or similar command.
- Required json data can be obtained by calling :
- VatExecutor.execute_script_json_out("dump_interfaces.vat", node)
- :param json_data: string json data from sw_interface_dump VAT command
- :param mac_address: string containing mac address of interface
- whose vpp name we wish to discover.
- :return: string vpp interface name
- """
-
- interfaces_list = JsonParser().parse_data(json_data)
- # TODO: checking if json data is parsed correctly
- interface_dict = self._extract_vpp_interface_by_mac(interfaces_list,
- mac_address)
- interface_name = interface_dict["interface_name"]
- return interface_name
-
- def _update_node_interface_data_from_json(self, node, interface_dump_json):
- """Update node vpp data in node__DICT from json interface dump.
-
- This method updates vpp interface names and sw indexexs according to
- interface mac addresses found in interface_dump_json
- :param node: node dictionary
- :param interface_dump_json: json output from dump_interface_list VAT
- command
- """
-
- interface_list = JsonParser().parse_data(interface_dump_json)
- for ifc in node['interfaces'].values():
- if 'link' not in ifc:
- continue
- if_mac = ifc['mac_address']
- interface_dict = self._extract_vpp_interface_by_mac(interface_list,
- if_mac)
- if not interface_dict:
- raise Exception('Interface {0} not found by MAC {1}'.
- format(ifc, if_mac))
- ifc['name'] = interface_dict["interface_name"]
- ifc['vpp_sw_index'] = interface_dict["sw_if_index"]
- ifc['mtu'] = interface_dict["mtu"]
+ This method returns the interface name associated with a software
+ interface index assigned to the interface by vpp for a given node.
- def update_vpp_interface_data_on_node(self, node):
- """Update vpp generated interface data for a given node in DICT__nodes
-
- Updates interface names, software index numbers and any other details
- generated specifically by vpp that are unknown before testcase run.
- :param node: Node selected from DICT__nodes
+ :param node: The node topology dictionary.
+ :param sw_index: Sw_index of the link that a interface is connected to.
+ :type node: dict
+ :type sw_index: int
+ :returns: Interface name of the interface connected to the given link.
+ :rtype: str
"""
-
- vat_executor = VatExecutor()
- vat_executor.execute_script_json_out("dump_interfaces.vat", node)
- interface_dump_json = vat_executor.get_script_stdout()
- self._update_node_interface_data_from_json(node,
- interface_dump_json)
+ return Topology._get_interface_by_key_value(node, "vpp_sw_index",
+ sw_index)
@staticmethod
- def update_tg_interface_data_on_node(node):
- """Update interface name for TG/linux node in DICT__nodes
+ def get_interface_sw_index(node, iface_key):
+ """Get VPP sw_if_index for the interface using interface key.
- :param node: Node selected from DICT__nodes.
+ :param node: Node to get interface sw_if_index on.
+ :param iface_key: Interface key from topology file, or sw_index.
:type node: dict
+ :type iface_key: str/int
+ :returns: Return sw_if_index or None if not found.
+ """
+ try:
+ if isinstance(iface_key, basestring):
+ return node['interfaces'][iface_key].get('vpp_sw_index')
+ # TODO: use only iface_key, do not use integer
+ else:
+ return int(iface_key)
+ except (KeyError, ValueError):
+ return None
- .. note::
- # for dev in `ls /sys/class/net/`;
- > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
- "52:54:00:9f:82:63": "eth0"
- "52:54:00:77:ae:a9": "eth1"
- "52:54:00:e1:8a:0f": "eth2"
- "00:00:00:00:00:00": "lo"
+ @staticmethod
+ def get_interface_sw_index_by_name(node, iface_name):
+ """Get VPP sw_if_index for the interface using interface name.
- .. todo:: parse lshw -json instead
+ :param node: Node to get interface sw_if_index on.
+ :param iface_name: Interface name.
+ :type node: dict
+ :type iface_name: str
+ :returns: Return sw_if_index or None if not found.
+ :raises TypeError: If provided interface name is not a string.
"""
- # First setup interface driver specified in yaml file
- InterfaceSetup.tg_set_interfaces_default_driver(node)
+ try:
+ if isinstance(iface_name, basestring):
+ iface_key = Topology.get_interface_by_name(node, iface_name)
+ return node['interfaces'][iface_key].get('vpp_sw_index')
+ else:
+ raise TypeError("Interface name must be a string.")
+ except (KeyError, ValueError):
+ return None
- # Get interface names
- ssh = SSH()
- ssh.connect(node)
+ @staticmethod
+ def get_interface_mtu(node, iface_key):
+ """Get interface MTU.
- cmd = 'for dev in `ls /sys/class/net/`; do echo "\\"`cat ' \
- '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;'
+ Returns physical layer MTU (max. size of Ethernet frame).
+ :param node: Node to get interface MTU on.
+ :param iface_key: Interface key from topology file.
+ :type node: dict
+ :type iface_key: str
+ :returns: MTU or None if not found.
+ :rtype: int
+ """
+ try:
+ return node['interfaces'][iface_key].get('mtu')
+ except KeyError:
+ return None
- (ret_code, stdout, _) = ssh.exec_command(cmd)
- if int(ret_code) != 0:
- raise Exception('Get interface name and MAC failed')
- tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
- interfaces = JsonParser().parse_data(tmp)
- for if_k, if_v in node['interfaces'].items():
- if if_k == 'mgmt':
- continue
- name = interfaces.get(if_v['mac_address'])
- if name is None:
- continue
- if_v['name'] = name
+ @staticmethod
+ def get_interface_name(node, iface_key):
+ """Get interface name (retrieved from DUT/TG).
- # Set udev rules for interfaces
- InterfaceSetup.tg_set_interfaces_udev_rules(node)
+ Returns name in string format, retrieved from the node.
+ :param node: Node to get interface name on.
+ :param iface_key: Interface key from topology file.
+ :type node: dict
+ :type iface_key: str
+ :returns: Interface name or None if not found.
+ :rtype: str
+ """
+ try:
+ return node['interfaces'][iface_key].get('name')
+ except KeyError:
+ return None
- def update_all_interface_data_on_all_nodes(self, nodes):
- """Update interface names on all nodes in DICT__nodes
+ @staticmethod
+ def convert_interface_reference_to_key(node, interface):
+ """Takes interface reference in any format
+ (name, link name, interface key or sw_if_index)
+ and converts to interface key using Topology methods.
+
+ :param node: Node in topology.
+ :param interface: Name, sw_if_index, link name or key of an interface
+ on the node.
+ :type node: dict
+ :type interface: str or int
- :param nodes: Nodes in the topology.
- :type nodes: dict
+ :returns: Interface key.
+ :rtype: str
- This method updates the topology dictionary by querying interface lists
- of all nodes mentioned in the topology dictionary.
- It does this by dumping interface list to json output from all devices
- using vpp_api_test, and pairing known information from topology
- (mac address/pci address of interface) to state from VPP.
- For TG/linux nodes add interface name only.
+ :raises TypeError: If provided with invalid interface argument.
+ :raises RuntimeError: If the interface does not exist in topology.
"""
- for node_data in nodes.values():
- if node_data['type'] == NodeType.DUT:
- self.update_vpp_interface_data_on_node(node_data)
- elif node_data['type'] == NodeType.TG:
- self.update_tg_interface_data_on_node(node_data)
+ if isinstance(interface, int):
+ key = Topology.get_interface_by_sw_index(node, interface)
+ if key is None:
+ raise RuntimeError("Interface with sw_if_index={0} does not "
+ "exist in topology.".format(interface))
+ elif interface in Topology.get_node_interfaces(node):
+ key = interface
+ elif interface in Topology.get_links({"dut": node}):
+ key = Topology.get_interface_by_link_name(node, interface)
+ elif isinstance(interface, basestring):
+ key = Topology.get_interface_by_name(node, interface)
+ if key is None:
+ raise RuntimeError("Interface with key, name or link name "
+ "\"{0}\" does not exist in topology."
+ .format(interface))
+ else:
+ raise TypeError("Type of interface argument must be integer"
+ " or string.")
+ return key
@staticmethod
- def get_interface_sw_index(node, interface):
- """Get VPP sw_index for the interface.
-
- :param node: Node to get interface sw_index on.
- :param interface: Interface name.
+ def convert_interface_reference(node, interface, wanted_format):
+ """Takes interface reference in any format
+ (name, link name, topology key or sw_if_index) and returns
+ its equivalent in the desired format.
+
+ :param node: Node in topology.
+ :param interface: Name, sw_if_index, link name or key of an interface
+ on the node.
+ :param wanted_format: Format of return value wanted.
+ Valid options are: sw_if_index, key, name.
:type node: dict
- :type interface: str
- :return: Return sw_index or None if not found.
+ :type interface: str or int
+ :type wanted_format: str
+ :returns: Interface name, interface key or sw_if_index.
+ :rtype: str or int
+ :raises TypeError, ValueError: If provided with invalid arguments.
+ :raises RuntimeError: If the interface does not exist in topology.
"""
- for port in node['interfaces'].values():
- port_name = port.get('name')
- if port_name == interface:
- return port.get('vpp_sw_index')
- return None
+ key = Topology.convert_interface_reference_to_key(node, interface)
+
+ conversions = {
+ "key": lambda x, y: y,
+ "name": Topology.get_interface_name,
+ "sw_if_index": Topology.get_interface_sw_index
+ }
+
+ try:
+ return conversions[wanted_format](node, key)
+ except KeyError:
+ raise ValueError("Unrecognized return value wanted: {0}."
+ "Valid options are key, name, sw_if_index"
+ .format(wanted_format))
@staticmethod
- def get_interface_mtu(node, interface):
- """Get interface MTU.
+ def get_interface_numa_node(node, iface_key):
+ """Get interface numa node.
- Returns physical layer MTU (max. size of Ethernet frame).
- :param node: Node to get interface MTU on.
- :param interface: Interface name.
+ Returns physical relation to numa node, numa_id.
+
+ :param node: Node to get numa id on.
+ :param iface_key: Interface key from topology file.
:type node: dict
- :type interface: str
- :return: MTU or None if not found.
+ :type iface_key: str
+ :returns: numa node id, None if not available.
:rtype: int
"""
- for port in node['interfaces'].values():
- port_name = port.get('name')
- if port_name == interface:
- return port.get('mtu')
-
- return None
+ try:
+ return node['interfaces'][iface_key].get('numa_node')
+ except KeyError:
+ return None
@staticmethod
- def get_interface_mac_by_port_key(node, port_key):
- """Get MAC address for the interface based on port key.
-
- :param node: Node to get interface mac on.
- :param port_key: Dictionary key name of interface.
+ def get_interfaces_numa_node(node, *iface_keys):
+ """Get numa node on which are located most of the interfaces.
+
+ Return numa node with highest count of interfaces provided as arguments.
+ Return 0 if the interface does not have numa_node information available.
+ If all interfaces have unknown location (-1), then return 0.
+ If most of interfaces have unknown location (-1), but there are
+ some interfaces with known location, then return the second most
+ location of the provided interfaces.
+
+ :param node: Node from DICT__nodes.
+ :param iface_keys: Interface keys for lookup.
:type node: dict
- :type port_key: str
- :return: Return MAC or None if not found.
+ :type iface_keys: strings
"""
- for port_name, port_data in node['interfaces'].iteritems():
- if port_name == port_key:
- return port_data['mac_address']
-
- return None
+ numa_list = []
+ for if_key in iface_keys:
+ try:
+ numa_list.append(node['interfaces'][if_key].get('numa_node'))
+ except KeyError:
+ pass
+
+ numa_cnt_mc = Counter(numa_list).most_common()
+
+ if len(numa_cnt_mc) > 0 and numa_cnt_mc[0][0] != -1:
+ return numa_cnt_mc[0][0]
+ elif len(numa_cnt_mc) > 1 and numa_cnt_mc[0][0] == -1:
+ return numa_cnt_mc[1][0]
+ else:
+ return 0
@staticmethod
- def get_interface_mac(node, interface):
+ def get_interface_mac(node, iface_key):
"""Get MAC address for the interface.
- :param node: Node to get interface sw_index on.
- :param interface: Interface name.
+ :param node: Node to get interface mac on.
+ :param iface_key: Interface key from topology file.
:type node: dict
- :type interface: str
- :return: Return MAC or None if not found.
+ :type iface_key: str
+ :returns: Return MAC or None if not found.
"""
- for port in node['interfaces'].values():
- port_name = port.get('name')
- if port_name == interface:
- return port.get('mac_address')
-
- return None
+ try:
+ return node['interfaces'][iface_key].get('mac_address')
+ except KeyError:
+ return None
@staticmethod
- def get_adjacent_node_and_interface_by_key(nodes_info, node, port_key):
- """Get node and interface adjacent to specified interface
- on local network.
+ def get_interface_ip4(node, iface_key):
+ """Get IP4 address for the interface.
- :param nodes_info: Dictionary containing information on all nodes
- in topology.
- :param node: Node that contains specified interface.
- :param port_key: Interface port key.
- :type nodes_info: dict
+ :param node: Node to get interface mac on.
+ :param iface_key: Interface key from topology file.
:type node: dict
- :type port_key: str
- :return: Return (node, interface info) tuple or None if not found.
- :rtype: (dict, dict)
+ :type iface_key: str
+ :returns: Return IP4 or None if not found.
"""
- link_name = None
- # get link name where the interface belongs to
- for port_name, port_data in node['interfaces'].iteritems():
- if port_name == 'mgmt':
- continue
- if port_name == port_key:
- link_name = port_data['link']
- break
-
- if link_name is None:
+ try:
+ return node['interfaces'][iface_key].get('ip4_address', None)
+ except KeyError:
return None
- # find link
- for node_data in nodes_info.values():
- # skip self
- if node_data['host'] == node['host']:
- continue
- for interface, interface_data \
- in node_data['interfaces'].iteritems():
- if 'link' not in interface_data:
- continue
- if interface_data['link'] == link_name:
- return node_data, node_data['interfaces'][interface]
-
@staticmethod
- def get_adjacent_node_and_interface(nodes_info, node, interface_name):
+ def get_adjacent_node_and_interface(nodes_info, node, iface_key):
"""Get node and interface adjacent to specified interface
on local network.
:param nodes_info: Dictionary containing information on all nodes
- in topology.
+ in topology.
:param node: Node that contains specified interface.
- :param interface_name: Interface name.
+ :param iface_key: Interface key from topology file.
:type nodes_info: dict
:type node: dict
- :type interface_name: str
- :return: Return (node, interface info) tuple or None if not found.
- :rtype: (dict, dict)
+ :type iface_key: str
+ :returns: Return (node, interface_key) tuple or None if not found.
+ :rtype: (dict, str)
"""
link_name = None
# get link name where the interface belongs to
- for port_name, port_data in node['interfaces'].iteritems():
- if port_name == 'mgmt':
+ for if_key, if_val in node['interfaces'].iteritems():
+ if if_key == 'mgmt':
continue
- if port_data['name'] == interface_name:
- link_name = port_data['link']
+ if if_key == iface_key:
+ link_name = if_val['link']
break
if link_name is None:
# skip self
if node_data['host'] == node['host']:
continue
- for interface, interface_data \
+ for if_key, if_val \
in node_data['interfaces'].iteritems():
- if 'link' not in interface_data:
+ if 'link' not in if_val:
continue
- if interface_data['link'] == link_name:
- return node_data, node_data['interfaces'][interface]
+ if if_val['link'] == link_name:
+ return node_data, if_key
@staticmethod
- def get_interface_pci_addr(node, interface):
+ def get_interface_pci_addr(node, iface_key):
"""Get interface PCI address.
:param node: Node to get interface PCI address on.
- :param interface: Interface name.
+ :param iface_key: Interface key from topology file.
:type node: dict
- :type interface: str
- :return: Return PCI address or None if not found.
+ :type iface_key: str
+ :returns: Return PCI address or None if not found.
"""
- for port in node['interfaces'].values():
- if interface == port.get('name'):
- return port.get('pci_address')
- return None
+ try:
+ return node['interfaces'][iface_key].get('pci_address')
+ except KeyError:
+ return None
@staticmethod
- def get_interface_driver(node, interface):
+ def get_interface_driver(node, iface_key):
"""Get interface driver.
:param node: Node to get interface driver on.
- :param interface: Interface name.
+ :param iface_key: Interface key from topology file.
:type node: dict
- :type interface: str
- :return: Return interface driver or None if not found.
+ :type iface_key: str
+ :returns: Return interface driver or None if not found.
"""
- for port in node['interfaces'].values():
- if interface == port.get('name'):
- return port.get('driver')
- return None
+ try:
+ return node['interfaces'][iface_key].get('driver')
+ except KeyError:
+ return None
+
+ @staticmethod
+ def get_node_interfaces(node):
+ """Get all node interfaces.
+
+ :param node: Node to get list of interfaces from.
+ :type node: dict
+ :returns: Return list of keys of all interfaces.
+ :rtype: list
+ """
+ return node['interfaces'].keys()
@staticmethod
def get_node_link_mac(node, link_name):
- """Return interface mac address by link name
+ """Return interface mac address by link name.
- :param node: Node to get interface sw_index on
- :param link_name: link name
+ :param node: Node to get interface sw_index on.
+ :param link_name: Link name.
:type node: dict
- :type link_name: string
- :return: mac address string
+ :type link_name: str
+ :returns: MAC address string.
+ :rtype: str
"""
for port in node['interfaces'].values():
if port.get('link') == link_name:
return None
@staticmethod
- def _get_node_active_link_names(node):
- """Return list of link names that are other than mgmt links
+ def _get_node_active_link_names(node, filter_list=None):
+ """Return list of link names that are other than mgmt links.
- :param node: node topology dictionary
- :return: list of strings that represent link names occupied by the node
+ :param node: Node topology dictionary.
+ :param filter_list: Link filter criteria.
+ :type node: dict
+ :type filter_list: list of strings
+ :returns: List of strings representing link names occupied by the node.
+ :rtype: list
"""
interfaces = node['interfaces']
link_names = []
for interface in interfaces.values():
if 'link' in interface:
- link_names.append(interface['link'])
+ if (filter_list is not None) and ('model' in interface):
+ for filt in filter_list:
+ if filt == interface['model']:
+ link_names.append(interface['link'])
+ elif (filter_list is not None) and ('model' not in interface):
+ logger.trace('Cannot apply filter on interface: {}'
+ .format(str(interface)))
+ else:
+ link_names.append(interface['link'])
if len(link_names) == 0:
link_names = None
return link_names
@keyword('Get active links connecting "${node1}" and "${node2}"')
- def get_active_connecting_links(self, node1, node2):
- """Return list of link names that connect together node1 and node2
-
- :param node1: node topology dictionary
- :param node2: node topology dictionary
- :return: list of strings that represent connecting link names
+ def get_active_connecting_links(self, node1, node2,
+ filter_list_node1=None,
+ filter_list_node2=None):
+ """Return list of link names that connect together node1 and node2.
+
+ :param node1: Node topology dictionary.
+ :param node2: Node topology dictionary.
+ :param filter_list_node1: Link filter criteria for node1.
+ :param filter_list_node2: Link filter criteria for node2.
+ :type node1: dict
+ :type node2: dict
+ :type filter_list_node1: list of strings
+ :type filter_list_node2: list of strings
+ :returns: List of strings that represent connecting link names.
+ :rtype: list
"""
logger.trace("node1: {}".format(str(node1)))
logger.trace("node2: {}".format(str(node2)))
- node1_links = self._get_node_active_link_names(node1)
- node2_links = self._get_node_active_link_names(node2)
- connecting_links = list(set(node1_links).intersection(node2_links))
+ node1_links = self._get_node_active_link_names(
+ node1,
+ filter_list=filter_list_node1)
+ node2_links = self._get_node_active_link_names(
+ node2,
+ filter_list=filter_list_node2)
+
+ connecting_links = None
+ if node1_links is None:
+ logger.error("Unable to find active links for node1")
+ elif node2_links is None:
+ logger.error("Unable to find active links for node2")
+ else:
+ connecting_links = list(set(node1_links).intersection(node2_links))
return connecting_links
def get_first_active_connecting_link(self, node1, node2):
"""
- :param node1: Connected node
+ :param node1: Connected node.
+ :param node2: Connected node.
:type node1: dict
- :param node2: Connected node
:type node2: dict
- :return: name of link connecting the two nodes together
- :raises: RuntimeError
+ :returns: Name of link connecting the two nodes together.
+ :rtype: str
+ :raises RuntimeError: If no links are found.
"""
-
connecting_links = self.get_active_connecting_links(node1, node2)
if len(connecting_links) == 0:
raise RuntimeError("No links connecting the nodes were found")
else:
return connecting_links[0]
- @keyword('Get egress interfaces on "${node1}" for link with "${node2}"')
- def get_egress_interfaces_for_nodes(self, node1, node2):
+ @keyword('Get egress interfaces name on "${node1}" for link with '
+ '"${node2}"')
+ def get_egress_interfaces_name_for_nodes(self, node1, node2):
"""Get egress interfaces on node1 for link with node2.
:param node1: First node, node to get egress interface on.
:param node2: Second node.
:type node1: dict
:type node2: dict
- :return: Egress interfaces.
+ :returns: Egress interfaces.
:rtype: list
"""
interfaces = []
interfaces.append(name)
return interfaces
- @keyword('Get first egress interface on "${node1}" for link with '
+ @keyword('Get first egress interface name on "${node1}" for link with '
'"${node2}"')
def get_first_egress_interface_for_nodes(self, node1, node2):
"""Get first egress interface on node1 for link with node2.
- :param node1: First node, node to get egress interface on.
+ :param node1: First node, node to get egress interface name on.
:param node2: Second node.
:type node1: dict
:type node2: dict
- :return: Egress interface.
+ :returns: Egress interface name.
:rtype: str
"""
- interfaces = self.get_egress_interfaces_for_nodes(node1, node2)
+ interfaces = self.get_egress_interfaces_name_for_nodes(node1, node2)
if not interfaces:
raise RuntimeError('No egress interface for nodes')
return interfaces[0]
For the time being it returns links from the Node path:
TG->DUT1->DUT2->TG
- :param tgen: traffic generator node data
- :param dut1: DUT1 node data
- :param dut2: DUT2 node data
- :type tgen: dict
- :type dut1: dict
- :type dut2: dict
- :return: dictionary of possible link combinations
- the naming convention until changed to something more general is
+ The naming convention until changed to something more general is
implemented is this:
DUT1_DUT2_LINK: link name between DUT! and DUT2
DUT1_TG_LINK: link name between DUT1 and TG
domain on DUT1
DUT2_BD_LINKS: list of link names that will be connected by the bridge
domain on DUT2
+
+ :param tgen: Traffic generator node data.
+ :param dut1: DUT1 node data.
+ :param dut2: DUT2 node data.
+ :type tgen: dict
+ :type dut1: dict
+ :type dut2: dict
+ :returns: Dictionary of possible link combinations.
+ :rtype: dict
"""
# TODO: replace with generic function.
dut1_dut2_link = self.get_first_active_connecting_link(dut1, dut2)
@staticmethod
def is_tg_node(node):
- """Find out whether the node is TG
+ """Find out whether the node is TG.
- :param node: node to examine
- :return: True if node is type of TG; False otherwise
+ :param node: Node to examine.
+ :type node: dict
+ :returns: True if node is type of TG, otherwise False.
+ :rtype: bool
"""
return node['type'] == NodeType.TG
:param node: Node created from topology.
:type node: dict
- :return: host as 'str' type
+ :returns: Hostname or IP address.
+ :rtype: str
"""
return node['host']
+
+ @staticmethod
+ def get_node_arch(node):
+ """Return arch of the node.
+ Default to x86_64 if no arch present
+
+ :param node: Node created from topology.
+ :type node: dict
+ :returns: Node architecture
+ :rtype: str
+ """
+ try:
+ return node['arch']
+ except KeyError:
+ node['arch'] = 'x86_64'
+ return 'x86_64'
+
+ @staticmethod
+ def get_cryptodev(node):
+ """Return Crytodev configuration of the node.
+
+ :param node: Node created from topology.
+ :type node: dict
+ :returns: Cryptodev configuration string.
+ :rtype: str
+ """
+ try:
+ return node['cryptodev']
+ except KeyError:
+ return None
+
+ @staticmethod
+ def get_uio_driver(node):
+ """Return uio-driver configuration of the node.
+
+ :param node: Node created from topology.
+ :type node: dict
+ :returns: uio-driver configuration string.
+ :rtype: str
+ """
+ try:
+ return node['uio_driver']
+ except KeyError:
+ return None
+
+ @staticmethod
+ def set_interface_numa_node(node, iface_key, numa_node_id):
+ """Set interface numa_node location.
+
+ :param node: Node to set numa_node on.
+ :param iface_key: Interface key from topology file.
+ :type node: dict
+ :type iface_key: str
+ :returns: Return iface_key or None if not found.
+ """
+ try:
+ node['interfaces'][iface_key]['numa_node'] = numa_node_id
+ return iface_key
+ except KeyError:
+ return None