diff options
author | Ross Brattain <ross.b.brattain@intel.com> | 2017-06-22 15:10:21 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@opnfv.org> | 2017-06-22 15:10:21 +0000 |
commit | e80c35164e7dfee4fe4a3652b71b8775c1c0857a (patch) | |
tree | b62143208b719c39de73c001a547258ab19bfa98 /yardstick/benchmark/scenarios | |
parent | 6b3ee75dc0b5fc0e66c914d0b72b4396411526fd (diff) | |
parent | 653902770572c780777d1dc7a371794b670585b1 (diff) |
Merge "Acquire NSB specific data from Heat."
Diffstat (limited to 'yardstick/benchmark/scenarios')
-rw-r--r-- | yardstick/benchmark/scenarios/networking/vnf_generic.py | 130 |
1 files changed, 108 insertions, 22 deletions
diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py index be179631e..594edeaa8 100644 --- a/yardstick/benchmark/scenarios/networking/vnf_generic.py +++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py @@ -15,6 +15,14 @@ from __future__ import absolute_import import logging + +import errno +import os + +import re +from operator import itemgetter +from collections import defaultdict + import yaml from yardstick.benchmark.scenarios import base @@ -72,6 +80,15 @@ class SshManager(object): self.conn.close() +def open_relative_file(path, task_path): + try: + return open(path) + except IOError as e: + if e.errno == errno.ENOENT: + return open(os.path.join(task_path, path)) + raise + + class NetworkServiceTestCase(base.Scenario): """Class handles Generic framework to do pre-deployment VNF & Network service testing """ @@ -84,8 +101,11 @@ class NetworkServiceTestCase(base.Scenario): self.context_cfg = context_cfg # fixme: create schema to validate all fields have been provided - with open(scenario_cfg["topology"]) as stream: - self.topology = yaml.load(stream)["nsd:nsd-catalog"]["nsd"][0] + with open_relative_file(scenario_cfg["topology"], + scenario_cfg['task_path']) as stream: + topology_yaml = yaml.load(stream) + + self.topology = topology_yaml["nsd:nsd-catalog"]["nsd"][0] self.vnfs = [] self.collector = None self.traffic_profile = None @@ -114,7 +134,8 @@ class NetworkServiceTestCase(base.Scenario): private = {} public = {} try: - with open(scenario_cfg["traffic_profile"]) as infile: + with open_relative_file(scenario_cfg["traffic_profile"], + scenario_cfg["task_path"]) as infile: traffic_profile_tpl = infile.read() except (KeyError, IOError, OSError): @@ -123,8 +144,6 @@ class NetworkServiceTestCase(base.Scenario): return [traffic_profile_tpl, private, public] def _fill_traffic_profile(self, scenario_cfg, context_cfg): - traffic_profile = {} - flow = self._get_traffic_flow(scenario_cfg) imix = self._get_traffic_imix(scenario_cfg) @@ -193,6 +212,26 @@ class NetworkServiceTestCase(base.Scenario): list_idx = self._find_list_index_from_vnf_idx(topology, vnf_idx) nodes[node].update(topology["constituent-vnfd"][list_idx]) + @staticmethod + def _sort_dpdk_port_num(netdevs): + # dpdk_port_num is PCI BUS ID ordering, lowest first + s = sorted(netdevs.values(), key=itemgetter('pci_bus_id')) + for dpdk_port_num, netdev in enumerate(s, 1): + netdev['dpdk_port_num'] = dpdk_port_num + + @classmethod + def _probe_missing_values(cls, netdevs, network, missing): + mac = network['local_mac'] + for netdev in netdevs.values(): + if netdev['address'].lower() == mac.lower(): + network['driver'] = netdev['driver'] + network['vpci'] = netdev['pci_bus_id'] + network['dpdk_port_num'] = netdev['dpdk_port_num'] + network['ifindex'] = netdev['ifindex'] + + TOPOLOGY_REQUIRED_KEYS = frozenset({ + "vpci", "local_ip", "netmask", "local_mac", "driver", "dpdk_port_num"}) + def map_topology_to_infrastructure(self, context_cfg, topology): """ This method should verify if the available resources defined in pod.yaml match the topology.yaml file. @@ -208,21 +247,66 @@ class NetworkServiceTestCase(base.Scenario): exit_status = conn.execute(cmd)[0] if exit_status != 0: raise IncorrectSetup("Node's %s lacks ip tool." % node) - - for interface in node_dict["interfaces"]: - network = node_dict["interfaces"][interface] - keys = ["vpci", "local_ip", "netmask", - "local_mac", "driver", "dpdk_port_num"] - missing = set(keys).difference(network) + 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._sort_dpdk_port_num(netdevs) + + for network in node_dict["interfaces"].values(): + missing = self.TOPOLOGY_REQUIRED_KEYS.difference(network) if missing: - raise IncorrectConfig("Require interface fields '%s' " - "not found, topology file " - "corrupted" % ', '.join(missing)) + try: + self._probe_missing_values(netdevs, network, + missing) + 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)) # 3. Use topology file to find connections & resolve dest address self._resolve_topology(context_cfg, topology) self._update_context_with_topology(context_cfg, 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): """ Find the implementing class from vnf_model["vnf"]["name"] field @@ -240,21 +324,24 @@ class NetworkServiceTestCase(base.Scenario): except StopIteration: raise IncorrectConfig("No implementation for %s", expected_name) - def load_vnf_models(self, context_cfg): + def load_vnf_models(self, scenario_cfg, context_cfg): """ Create VNF objects based on YAML descriptors + :param scenario_cfg: + :type scenario_cfg: :param context_cfg: :return: """ vnfs = [] - for node in context_cfg["nodes"]: - LOG.debug(context_cfg["nodes"][node]) - with open(context_cfg["nodes"][node]["VNF model"]) as stream: + for node_name, node in context_cfg["nodes"].items(): + LOG.debug(node) + with open_relative_file(node["VNF model"], + scenario_cfg['task_path']) as stream: vnf_model = stream.read() - vnfd = vnfdgen.generate_vnfd(vnf_model, context_cfg["nodes"][node]) + vnfd = vnfdgen.generate_vnfd(vnf_model, node) vnf_impl = self.get_vnf_impl(vnfd["vnfd:vnfd-catalog"]["vnfd"][0]) vnf_instance = vnf_impl(vnfd["vnfd:vnfd-catalog"]["vnfd"][0]) - vnf_instance.name = node + vnf_instance.name = node_name vnfs.append(vnf_instance) return vnfs @@ -264,11 +351,10 @@ class NetworkServiceTestCase(base.Scenario): :return: """ - # 1. Verify if infrastructure mapping can meet topology self.map_topology_to_infrastructure(self.context_cfg, self.topology) # 1a. Load VNF models - self.vnfs = self.load_vnf_models(self.context_cfg) + self.vnfs = self.load_vnf_models(self.scenario_cfg, self.context_cfg) # 1b. Fill traffic profile with information from topology self.traffic_profile = self._fill_traffic_profile(self.scenario_cfg, self.context_cfg) |