summaryrefslogtreecommitdiffstats
path: root/yardstick/benchmark/scenarios/networking/vnf_generic.py
diff options
context:
space:
mode:
Diffstat (limited to 'yardstick/benchmark/scenarios/networking/vnf_generic.py')
-rw-r--r--yardstick/benchmark/scenarios/networking/vnf_generic.py117
1 files changed, 91 insertions, 26 deletions
diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py
index 0e6ceab6e..450f83f6a 100644
--- a/yardstick/benchmark/scenarios/networking/vnf_generic.py
+++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py
@@ -25,10 +25,11 @@ import re
from itertools import chain
import six
-from operator import itemgetter
+import yaml
from collections import defaultdict
from yardstick.benchmark.scenarios import base
+from yardstick.common.constants import LOG_DIR
from yardstick.common.utils import import_modules_from_package, itersubclasses
from yardstick.common.yaml_loader import yaml_load
from yardstick.network_services.collector.subscriber import Collector
@@ -63,10 +64,11 @@ class IncorrectSetup(Exception):
class SshManager(object):
- def __init__(self, node):
+ def __init__(self, node, timeout=120):
super(SshManager, self).__init__()
self.node = node
self.conn = None
+ self.timeout = timeout
def __enter__(self):
"""
@@ -75,7 +77,7 @@ class SshManager(object):
"""
try:
self.conn = ssh.SSH.from_node(self.node)
- self.conn.wait()
+ 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
@@ -134,6 +136,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 +171,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 uplink_0, downlink_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:
@@ -201,8 +206,8 @@ class NetworkServiceTestCase(base.Scenario):
traffic_map_data = {
'flow': self._get_traffic_flow(),
'imix': self._get_traffic_imix(),
- 'private': {},
- 'public': {},
+ TrafficProfile.UPLINK: {},
+ TrafficProfile.DOWNLINK: {},
}
traffic_vnfd = vnfdgen.generate_vnfd(traffic_mapping, traffic_map_data)
@@ -231,7 +236,7 @@ class NetworkServiceTestCase(base.Scenario):
# check for xe0, xe1
intf = nodes[name]["interfaces"][if_name]
except KeyError:
- # if not xe0, then maybe vld_id, private_0, public_0
+ # if not xe0, then maybe vld_id, uplink_0, downlink_0
# pop it and re-insert with the correct name from topology
intf = nodes[name]["interfaces"].pop(vld_id)
nodes[name]["interfaces"][if_name] = intf
@@ -263,7 +268,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 +280,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,17 +330,16 @@ 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 = {}
- with SshManager(node_dict) as conn:
+ 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:
@@ -346,6 +350,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
@@ -361,6 +367,36 @@ class NetworkServiceTestCase(base.Scenario):
'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
+ # pod.yaml nodes is a list
+ nodes = []
+ for node in self.context_cfg["nodes"].values():
+ # name field is required
+ # remove context suffix
+ node['name'] = node['name'].split('.')[0]
+ nodes.append(node)
+ nodes = self._convert_pkeys_to_string(nodes)
+ pod_dict = {
+ "nodes": nodes,
+ "networks": self.context_cfg["networks"]
+ }
+ with open(context_yaml, "w") as context_out:
+ yaml.safe_dump(pod_dict, context_out, default_flow_style=False,
+ explicit_start=True)
+
+ @staticmethod
+ def _convert_pkeys_to_string(nodes):
+ # make copy because we are mutating
+ nodes = nodes[:]
+ for i, node in enumerate(nodes):
+ try:
+ nodes[i] = dict(node, pkey=ssh.convert_key_to_str(node["pkey"]))
+ except KeyError:
+ pass
+ return nodes
+
TOPOLOGY_REQUIRED_KEYS = frozenset({
"vpci", "local_ip", "netmask", "local_mac", "driver"})
@@ -370,6 +406,10 @@ class NetworkServiceTestCase(base.Scenario):
: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():
@@ -380,7 +420,7 @@ class NetworkServiceTestCase(base.Scenario):
# 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)
+ netdevs = self._probe_netdevs(node, node_dict, timeout=timeout)
except (SSHError, SSHTimeout):
raise IncorrectConfig(
"Unable to probe missing interface fields '%s', on node %s "
@@ -397,6 +437,8 @@ class NetworkServiceTestCase(base.Scenario):
"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()
@@ -458,10 +500,26 @@ 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()):
+ # only interfaces with vld_id are added.
+ # Thus there are two layers of filters, only intefaces with vld_id
+ # show up in interfaces, and only interfaces with traffic profiles
+ # are used by the generators
+ 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 +549,14 @@ 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)
+ # force inject pkey if it exists
+ # we want to standardize Heat using pkey as a string so we don't rely
+ # on the filesystem
+ try:
+ vnfd['mgmt-interface']['pkey'] = node['pkey']
+ except KeyError:
+ pass
+ 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)