diff options
author | Ross Brattain <ross.b.brattain@intel.com> | 2017-10-04 13:38:43 -0700 |
---|---|---|
committer | Ross Brattain <ross.b.brattain@intel.com> | 2018-03-01 08:21:53 -0800 |
commit | 6f7dd8ff0e6358ef958426a6baeae5deee1a57d8 (patch) | |
tree | bb3a46048a7ccd7d45b45352824d18612a25f7d3 /yardstick/benchmark/scenarios/networking | |
parent | 9316c6c49957f2d8c680ed8acfaccac9070ed2f4 (diff) |
NSB: move interface probe to VNF, and attempt driver-only probe first
If no devices are present we can't detect MAC address so
we can't match Heat ports to interfaces.
If only the driver is missing we can try to probe the driver using
lspci. We can use lspci to ask the kernel what driver it should use
for the PCI device.
If we can't probe at all because the device is already bound, we can
use dpkd-devind to find all the PCI address we care about and create a
map with PCI device and real kernel driver.
Then we can dpdk force rebind to the kernel driver.
Once we have rebound to the kernel driver we can detect
MAC address and all the other attributes that are required.
Fix VnfSshHelper to allow override of wait timeout
And a bunch of other refactors that got swept up in this
JIRA: YARDSTICK-835
Change-Id: I14cb657ed289a77941d048345d06ced5b5d5da52
Signed-off-by: Ross Brattain <ross.b.brattain@intel.com>
Diffstat (limited to 'yardstick/benchmark/scenarios/networking')
-rw-r--r-- | yardstick/benchmark/scenarios/networking/vnf_generic.py | 183 |
1 files changed, 14 insertions, 169 deletions
diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py index 45c151b59..0e4785294 100644 --- a/yardstick/benchmark/scenarios/networking/vnf_generic.py +++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py @@ -12,19 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections import defaultdict import copy import logging + import ipaddress from itertools import chain import os -import re import sys import six import yaml from yardstick.benchmark.scenarios import base as scenario_base +from yardstick.error import IncorrectConfig from yardstick.common.constants import LOG_DIR from yardstick.common.process import terminate_children from yardstick.common import utils @@ -36,58 +36,12 @@ from yardstick.network_services.traffic_profile import base as tprofile_base from yardstick.network_services.utils import get_nsb_option from yardstick import ssh - traffic_profile.register_modules() LOG = logging.getLogger(__name__) -class SSHError(Exception): - """Class handles ssh connection error exception""" - pass - - -class SSHTimeout(SSHError): - """Class handles ssh connection timeout exception""" - pass - - -class IncorrectConfig(Exception): - """Class handles incorrect configuration during setup""" - pass - - -class IncorrectSetup(Exception): - """Class handles incorrect setup during setup""" - pass - - -class SshManager(object): - def __init__(self, node, timeout=120): - super(SshManager, self).__init__() - self.node = node - self.conn = None - self.timeout = timeout - - def __enter__(self): - """ - args -> network device mappings - returns -> ssh connection ready to be used - """ - try: - self.conn = ssh.SSH.from_node(self.node) - self.conn.wait(timeout=self.timeout) - except SSHError as error: - LOG.info("connect failed to %s, due to %s", self.node["ip"], error) - # self.conn defaults to None - return self.conn - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.conn: - self.conn.close() - - class NetworkServiceTestCase(scenario_base.Scenario): """Class handles Generic framework to do pre-deployment VNF & Network service testing """ @@ -104,6 +58,7 @@ class NetworkServiceTestCase(scenario_base.Scenario): self.collector = None self.traffic_profile = None self.node_netdevs = {} + self.bin_path = get_nsb_option('bin_path', '') def _get_ip_flow_range(self, ip_start_range): @@ -212,16 +167,10 @@ class NetworkServiceTestCase(scenario_base.Scenario): for vnfd in self.topology["constituent-vnfd"] if vnf_id == vnfd["member-vnf-index"]), None) - @staticmethod - def get_vld_networks(networks): - # network name is vld_id - vld_map = {} - for name, n in networks.items(): - try: - vld_map[n['vld_id']] = n - except KeyError: - vld_map[name] = n - return vld_map + def _find_vnfd_from_vnf_idx(self, vnf_id): + return next((vnfd + for vnfd in self.topology["constituent-vnfd"] + if vnf_id == vnfd["member-vnf-index"]), None) @staticmethod def find_node_if(nodes, name, if_name, vld_id): @@ -273,7 +222,9 @@ class NetworkServiceTestCase(scenario_base.Scenario): node1_if["peer_ifname"] = node0_if_name # just load the network - vld_networks = self.get_vld_networks(self.context_cfg["networks"]) + vld_networks = {n.get('vld_id', name): n for name, n in + self.context_cfg["networks"].items()} + node0_if["network"] = vld_networks.get(vld["id"], {}) node1_if["network"] = vld_networks.get(vld["id"], {}) @@ -312,10 +263,6 @@ class NetworkServiceTestCase(scenario_base.Scenario): node0_if["peer_intf"] = node1_copy node1_if["peer_intf"] = node0_copy - def _find_vnfd_from_vnf_idx(self, vnf_idx): - return next((vnfd for vnfd in self.topology["constituent-vnfd"] - if vnf_idx == vnfd["member-vnf-index"]), None) - def _update_context_with_topology(self): for vnfd in self.topology["constituent-vnfd"]: vnf_idx = vnfd["member-vnf-index"] @@ -323,43 +270,6 @@ class NetworkServiceTestCase(scenario_base.Scenario): vnfd = self._find_vnfd_from_vnf_idx(vnf_idx) self.context_cfg["nodes"][vnf_name].update(vnfd) - def _probe_netdevs(self, node, node_dict, timeout=120): - try: - return self.node_netdevs[node] - except KeyError: - pass - - netdevs = {} - cmd = "PATH=$PATH:/sbin:/usr/sbin ip addr show" - - with SshManager(node_dict, timeout=timeout) as conn: - if conn: - exit_status = conn.execute(cmd)[0] - if exit_status != 0: - raise IncorrectSetup("Node's %s lacks ip tool." % node) - exit_status, stdout, _ = conn.execute( - self.FIND_NETDEVICE_STRING) - if exit_status != 0: - raise IncorrectSetup( - "Cannot find netdev info in sysfs" % node) - netdevs = node_dict['netdevs'] = self.parse_netdev_info(stdout) - - self.node_netdevs[node] = netdevs - return netdevs - - @classmethod - def _probe_missing_values(cls, netdevs, network): - - mac_lower = network['local_mac'].lower() - for netdev in netdevs.values(): - if netdev['address'].lower() != mac_lower: - continue - network.update({ - 'driver': netdev['driver'], - 'vpci': netdev['pci_bus_id'], - 'ifindex': netdev['ifindex'], - }) - def _generate_pod_yaml(self): context_yaml = os.path.join(LOG_DIR, "pod-{}.yaml".format(self.scenario_cfg['task_id'])) # convert OrderedDict to a list @@ -385,84 +295,16 @@ class NetworkServiceTestCase(scenario_base.Scenario): pass return new_node - TOPOLOGY_REQUIRED_KEYS = frozenset({ - "vpci", "local_ip", "netmask", "local_mac", "driver"}) - def map_topology_to_infrastructure(self): """ This method should verify if the available resources defined in pod.yaml match the topology.yaml file. :return: None. Side effect: context_cfg is updated """ - num_nodes = len(self.context_cfg["nodes"]) - # OpenStack instance creation time is probably proportional to the number - # of instances - timeout = 120 * num_nodes - for node, node_dict in self.context_cfg["nodes"].items(): - - for network in node_dict["interfaces"].values(): - missing = self.TOPOLOGY_REQUIRED_KEYS.difference(network) - if not missing: - continue - - # only ssh probe if there are missing values - # ssh probe won't work on Ixia, so we had better define all our values - try: - netdevs = self._probe_netdevs(node, node_dict, timeout=timeout) - except (SSHError, SSHTimeout): - raise IncorrectConfig( - "Unable to probe missing interface fields '%s', on node %s " - "SSH Error" % (', '.join(missing), node)) - try: - self._probe_missing_values(netdevs, network) - except KeyError: - pass - else: - missing = self.TOPOLOGY_REQUIRED_KEYS.difference( - network) - if missing: - raise IncorrectConfig( - "Require interface fields '%s' not found, topology file " - "corrupted" % ', '.join(missing)) - - # we have to generate pod.yaml here so we have vpci and driver - self._generate_pod_yaml() # 3. Use topology file to find connections & resolve dest address self._resolve_topology() self._update_context_with_topology() - FIND_NETDEVICE_STRING = ( - r"""find /sys/devices/pci* -type d -name net -exec sh -c '{ grep -sH ^ \ - $1/ifindex $1/address $1/operstate $1/device/vendor $1/device/device \ - $1/device/subsystem_vendor $1/device/subsystem_device ; \ - printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ - ' sh \{\}/* \; - """) - - BASE_ADAPTER_RE = re.compile( - '^/sys/devices/(.*)/net/([^/]*)/([^:]*):(.*)$', re.M) - - @classmethod - def parse_netdev_info(cls, stdout): - network_devices = defaultdict(dict) - matches = cls.BASE_ADAPTER_RE.findall(stdout) - for bus_path, interface_name, name, value in matches: - dirname, bus_id = os.path.split(bus_path) - if 'virtio' in bus_id: - # for some stupid reason VMs include virtio1/ - # in PCI device path - bus_id = os.path.basename(dirname) - # remove extra 'device/' from 'device/vendor, - # device/subsystem_vendor', etc. - if 'device/' in name: - name = name.split('/')[1] - network_devices[interface_name][name] = value - network_devices[interface_name][ - 'interface_name'] = interface_name - network_devices[interface_name]['pci_bus_id'] = bus_id - # convert back to regular dict - return dict(network_devices) - @classmethod def get_vnf_impl(cls, vnf_model_id): """ Find the implementing class from vnf_model["vnf"]["name"] field @@ -530,7 +372,7 @@ class NetworkServiceTestCase(scenario_base.Scenario): context_cfg = self.context_cfg vnfs = [] - # we assume OrderedDict for consistenct in instantiation + # we assume OrderedDict for consistency in instantiation for node_name, node in context_cfg["nodes"].items(): LOG.debug(node) try: @@ -589,6 +431,9 @@ class NetworkServiceTestCase(scenario_base.Scenario): vnf.terminate() raise + # we have to generate pod.yaml here after VNF has probed so we know vpci and driver + self._generate_pod_yaml() + # 3. Run experiment # Start listeners first to avoid losing packets for traffic_gen in traffic_runners: |