aboutsummaryrefslogtreecommitdiffstats
path: root/yardstick/benchmark
diff options
context:
space:
mode:
authorMartin Banszel <martinx.banszel@intel.com>2017-07-19 19:35:02 +0000
committerRoss Brattain <ross.b.brattain@intel.com>2017-09-18 01:28:28 -0700
commit86083c8023f40d368f6339d2ab68d49c9ac8b8ee (patch)
tree0ca209f5bf0f00e36e856ffecd21184921265077 /yardstick/benchmark
parent38eb33a092e903b9854267d3e36496c919517103 (diff)
NSB: fix port topology
Add a new PortPair class to resolve the topology into list of public and private ports. Before we were calculating public/private in multiple locations and using different conventions. In addition for all the DPDK test we need to use the DPDK port number and no rely on interface ordering or interface naming conventions. We used to use xe0 -> 0, xe1 -> 1, etc. This is not the DPDK port number. Use the new dpdknicbind_helper class to parse the output of dpdk-devbind.py to find the actual DPDK port number at runtime. We then use this DPDK port number to correctly calculate the port_mask_hex. The port mask maps the DPDK port num (PMD ID) to the LINK ID used in the pipeline config We also need to make sure we only use the interfaces matched to the topology and not use all the interfaces, because in some cases we will have unused interfaces. In particular TRex always requires an even number of interfaces, so for single port TRex tests we have to create the second port and not use it. Thus we had to modify the traffic generator stats code to only dump stats for used ports and no unused ports. Ixia was using interface ordering to map to Ixia ports, instead we use the dpdk_port_num which must be hardcoded for Ixia. Renamed traffic_profile.execute to traffic_profile.execute_traffic so we can trace the code easier. We pass the port used by the traffic profile to generate_samples so we don't get stats for unused ports. Fixed up vPE config creation and bring up issues. Fixed up CGNAPT and UDP_Replay to work correctly. Tested with 4-port scale-out Change-Id: I2e4f328bff2904108081e92a4bf712333fa73869 Signed-off-by: Ross Brattain <ross.b.brattain@intel.com> Signed-off-by: Edward MacGillivray <edward.s.macgillivray@intel.com>
Diffstat (limited to 'yardstick/benchmark')
-rw-r--r--yardstick/benchmark/contexts/heat.py32
-rw-r--r--yardstick/benchmark/contexts/model.py79
-rw-r--r--yardstick/benchmark/scenarios/networking/vnf_generic.py53
3 files changed, 101 insertions, 63 deletions
diff --git a/yardstick/benchmark/contexts/heat.py b/yardstick/benchmark/contexts/heat.py
index d2309cc10..c7586abf4 100644
--- a/yardstick/benchmark/contexts/heat.py
+++ b/yardstick/benchmark/contexts/heat.py
@@ -83,9 +83,14 @@ class HeatContext(Context):
external_network = os.environ.get("EXTERNAL_NETWORK", "net04_ext")
have_external_network = any(net.get("external_network") for net in networks.values())
- if sorted_networks and not have_external_network:
- # no external net defined, assign it to first network using os.environ
- sorted_networks[0][1]["external_network"] = external_network
+ if not have_external_network:
+ # try looking for mgmt network first
+ try:
+ networks['mgmt']["external_network"] = external_network
+ except KeyError:
+ if sorted_networks:
+ # otherwise assign it to first network using os.environ
+ sorted_networks[0][1]["external_network"] = external_network
return sorted_networks
@@ -328,16 +333,21 @@ class HeatContext(Context):
LOG.info("Deploying context '%s' DONE", self.name)
def add_server_port(self, server):
- # TODO(hafe) can only handle one internal network for now
- # use private ip from first port
- private_port = next(iter(server.ports.values()))
+ # use private ip from first port in first network
+ try:
+ private_port = next(iter(server.ports.values()))[0]
+ except IndexError:
+ LOG.exception("Unable to find first private port in %s", server.ports)
+ raise
server.private_ip = self.stack.outputs[private_port["stack_name"]]
server.interfaces = {}
- for network_name, port in server.ports.items():
- # port['port'] is either port name from mapping or default network_name
- server.interfaces[port['port']] = self.make_interface_dict(network_name, port['port'],
- port['stack_name'],
- self.stack.outputs)
+ for network_name, ports in server.ports.items():
+ for port in ports:
+ # port['port'] is either port name from mapping or default network_name
+ server.interfaces[port['port']] = self.make_interface_dict(network_name,
+ port['port'],
+ port['stack_name'],
+ self.stack.outputs)
def make_interface_dict(self, network_name, port, stack_name, outputs):
private_ip = outputs[stack_name]
diff --git a/yardstick/benchmark/contexts/model.py b/yardstick/benchmark/contexts/model.py
index da2b74e1c..facfab892 100644
--- a/yardstick/benchmark/contexts/model.py
+++ b/yardstick/benchmark/contexts/model.py
@@ -11,9 +11,15 @@
"""
from __future__ import absolute_import
+
+import six
+import logging
from six.moves import range
+LOG = logging.getLogger(__name__)
+
+
class Object(object):
"""Base class for classes in the logical model
Contains common attributes and methods
@@ -257,44 +263,51 @@ class Server(Object): # pragma: no cover
# if explicit mapping skip unused networks
if self.network_ports:
try:
- port = self.network_ports[network.name]
+ ports = self.network_ports[network.name]
except KeyError:
# no port for this network
continue
+ else:
+ if isinstance(ports, six.string_types):
+ if ports.startswith('-'):
+ LOG.warning("possible YAML error, port name starts with - '%s", ports)
+ ports = [ports]
# otherwise add a port for every network with port name as network name
else:
- port = network.name
- port_name = "{0}-{1}-port".format(server_name, port)
- self.ports[network.name] = {"stack_name": port_name, "port": port}
- # we can't use secgroups if port_security_enabled is False
- if network.port_security_enabled is False:
- sec_group_id = None
- else:
- # if port_security_enabled is None we still need to add to secgroup
- sec_group_id = self.secgroup_name
- # don't refactor to pass in network object, that causes JSON
- # circular ref encode errors
- template.add_port(port_name, network.stack_name, network.subnet_stack_name,
- network.vnic_type, sec_group_id=sec_group_id,
- provider=network.provider,
- allowed_address_pairs=network.allowed_address_pairs)
- port_name_list.append(port_name)
-
- if self.floating_ip:
- external_network = self.floating_ip["external_network"]
- if network.has_route_to(external_network):
- self.floating_ip["stack_name"] = server_name + "-fip"
- template.add_floating_ip(self.floating_ip["stack_name"],
- external_network,
- port_name,
- network.router.stack_if_name,
- sec_group_id)
- self.floating_ip_assoc["stack_name"] = \
- server_name + "-fip-assoc"
- template.add_floating_ip_association(
- self.floating_ip_assoc["stack_name"],
- self.floating_ip["stack_name"],
- port_name)
+ ports = [network.name]
+ for port in ports:
+ port_name = "{0}-{1}-port".format(server_name, port)
+ self.ports.setdefault(network.name, []).append(
+ {"stack_name": port_name, "port": port})
+ # we can't use secgroups if port_security_enabled is False
+ if network.port_security_enabled is False:
+ sec_group_id = None
+ else:
+ # if port_security_enabled is None we still need to add to secgroup
+ sec_group_id = self.secgroup_name
+ # don't refactor to pass in network object, that causes JSON
+ # circular ref encode errors
+ template.add_port(port_name, network.stack_name, network.subnet_stack_name,
+ network.vnic_type, sec_group_id=sec_group_id,
+ provider=network.provider,
+ allowed_address_pairs=network.allowed_address_pairs)
+ port_name_list.append(port_name)
+
+ if self.floating_ip:
+ external_network = self.floating_ip["external_network"]
+ if network.has_route_to(external_network):
+ self.floating_ip["stack_name"] = server_name + "-fip"
+ template.add_floating_ip(self.floating_ip["stack_name"],
+ external_network,
+ port_name,
+ network.router.stack_if_name,
+ sec_group_id)
+ self.floating_ip_assoc["stack_name"] = \
+ server_name + "-fip-assoc"
+ template.add_floating_ip_association(
+ self.floating_ip_assoc["stack_name"],
+ self.floating_ip["stack_name"],
+ port_name)
if self.flavor:
if isinstance(self.flavor, dict):
self.flavor["name"] = \
diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py
index 0e6ceab6e..ada92121b 100644
--- a/yardstick/benchmark/scenarios/networking/vnf_generic.py
+++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py
@@ -25,7 +25,6 @@ import re
from itertools import chain
import six
-from operator import itemgetter
from collections import defaultdict
from yardstick.benchmark.scenarios import base
@@ -134,6 +133,7 @@ class NetworkServiceTestCase(base.Scenario):
self.vnfs = []
self.collector = None
self.traffic_profile = None
+ self.node_netdevs = {}
def _get_ip_flow_range(self, ip_start_range):
@@ -168,15 +168,17 @@ class NetworkServiceTestCase(base.Scenario):
def _get_traffic_flow(self):
flow = {}
try:
+ # TODO: should be .0 or .1 so we can use list
+ # but this also roughly matches private_0, public_0
fflow = self.scenario_cfg["options"]["flow"]
for index, src in enumerate(fflow.get("src_ip", [])):
- flow["src_ip{}".format(index)] = self._get_ip_flow_range(src)
+ flow["src_ip_{}".format(index)] = self._get_ip_flow_range(src)
for index, dst in enumerate(fflow.get("dst_ip", [])):
- flow["dst_ip{}".format(index)] = self._get_ip_flow_range(dst)
+ flow["dst_ip_{}".format(index)] = self._get_ip_flow_range(dst)
- for index, publicip in enumerate(fflow.get("publicip", [])):
- flow["public_ip{}".format(index)] = publicip
+ for index, publicip in enumerate(fflow.get("public_ip", [])):
+ flow["public_ip_{}".format(index)] = publicip
flow["count"] = fflow["count"]
except KeyError:
@@ -263,7 +265,6 @@ class NetworkServiceTestCase(base.Scenario):
node0_if["node_name"] = node0_name
node1_if["node_name"] = node1_name
- vld_networks = self.get_vld_networks(self.context_cfg["networks"])
node0_if["vld_id"] = vld["id"]
node1_if["vld_id"] = vld["id"]
@@ -276,6 +277,7 @@ class NetworkServiceTestCase(base.Scenario):
node1_if["peer_ifname"] = node0_if_name
# just load the network
+ vld_networks = self.get_vld_networks(self.context_cfg["networks"])
node0_if["network"] = vld_networks.get(vld["id"], {})
node1_if["network"] = vld_networks.get(vld["id"], {})
@@ -325,16 +327,15 @@ class NetworkServiceTestCase(base.Scenario):
vnfd = self._find_vnfd_from_vnf_idx(vnf_idx)
self.context_cfg["nodes"][vnf_name].update(vnfd)
- @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):
- netdev['dpdk_port_num'] = dpdk_port_num
+ def _probe_netdevs(self, node, node_dict, timeout=120):
+ try:
+ return self.node_netdevs[node]
+ except KeyError:
+ pass
- def _probe_netdevs(self, node, node_dict):
- cmd = "PATH=$PATH:/sbin:/usr/sbin ip addr show"
netdevs = {}
+ cmd = "PATH=$PATH:/sbin:/usr/sbin ip addr show"
+
with SshManager(node_dict) as conn:
if conn:
exit_status = conn.execute(cmd)[0]
@@ -346,6 +347,8 @@ class NetworkServiceTestCase(base.Scenario):
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
@@ -458,10 +461,22 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \
(expected_name, classes_found))
@staticmethod
- def update_interfaces_from_node(vnfd, node):
- for intf in vnfd["vdu"][0]["external-interface"]:
- node_intf = node['interfaces'][intf['name']]
- intf['virtual-interface'].update(node_intf)
+ def create_interfaces_from_node(vnfd, node):
+ ext_intfs = vnfd["vdu"][0]["external-interface"] = []
+ # have to sort so xe0 goes first
+ for intf_name, intf in sorted(node['interfaces'].items()):
+ if intf.get('vld_id'):
+ # force dpkd_port_num to int so we can do reverse lookup
+ try:
+ intf['dpdk_port_num'] = int(intf['dpdk_port_num'])
+ except KeyError:
+ pass
+ ext_intf = {
+ "name": intf_name,
+ "virtual-interface": intf,
+ "vnfd-connection-point-ref": intf_name,
+ }
+ ext_intfs.append(ext_intf)
def load_vnf_models(self, scenario_cfg=None, context_cfg=None):
""" Create VNF objects based on YAML descriptors
@@ -491,7 +506,7 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \
vnfd = vnfdgen.generate_vnfd(vnf_model, node)
# TODO: here add extra context_cfg["nodes"] regardless of template
vnfd = vnfd["vnfd:vnfd-catalog"]["vnfd"][0]
- self.update_interfaces_from_node(vnfd, node)
+ self.create_interfaces_from_node(vnfd, node)
vnf_impl = self.get_vnf_impl(vnfd['id'])
vnf_instance = vnf_impl(node_name, vnfd)
vnfs.append(vnf_instance)