diff options
Diffstat (limited to 'nfvbench')
-rwxr-xr-x | nfvbench/cfg.default.yaml | 32 | ||||
-rw-r--r-- | nfvbench/chaining.py | 86 | ||||
-rw-r--r-- | nfvbench/cleanup.py | 4 |
3 files changed, 120 insertions, 2 deletions
diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml index 3152bb9..eb5fa11 100755 --- a/nfvbench/cfg.default.yaml +++ b/nfvbench/cfg.default.yaml @@ -385,6 +385,38 @@ internal_networks: segmentation_id: physical_network: +# IDLE INTERFACES: PVP, PVVP and non shared net only. +# By default each test VM will have 2 virtual interfaces for looping traffic. +# If service_chain_shared_net is false, additional virtual interfaces can be +# added at VM creation time, these interfaces will not carry any traffic and +# can be used to test the impact of idle interfaces in the overall performance. +# All these idle interfaces will use normal ports (not direct). +# Number of idle interfaces per VM (none by default) +idle_interfaces_per_vm: 0 + +# A new network is created for each idle interface. +# If service_chain_shared_net is true, the options below will be ignored +# and no idle interfaces will be added. +idle_networks: + # Prefix for all idle networks + name: 'nfvbench-idle-net' + # Prefix for all idle subnetworks + subnet: 'nfvbench-idle-subnet' + # CIDR to use for all idle networks (value should not matter) + cidr: '192.169.1.0/24' + # Type of network associated to the idle virtual interfaces (vlan or vxlan) + network_type: 'vlan' + # segmentation ID to use for the network attached to the idle virtual interfaces + # vlan: leave empty to let neutron pick the segmentation ID + # vxlan: must specify the VNI value to be used (cannot be empty) + # Note that NFVbench will use as many consecutive segmentation IDs as needed. + # For example, for 4 PVP chains and 8 idle + # interfaces per VM, NFVbench will use 32 consecutive values of segmentation ID + # starting from the value provided. + segmentation_id: + # physnet name to use for all idle interfaces + physical_network: + # In the scenario of PVVP + SRIOV, there is choice of how the traffic will be # handled in the middle network. The default (false) will use vswitch, while # SRIOV can be used by toggling below setting. diff --git a/nfvbench/chaining.py b/nfvbench/chaining.py index a719c8a..60f3832 100644 --- a/nfvbench/chaining.py +++ b/nfvbench/chaining.py @@ -193,13 +193,15 @@ class ChainVnfPort(object): class ChainNetwork(object): """Could be a shared network across all chains or a chain private network.""" - def __init__(self, manager, network_config, chain_id=None, lookup_only=False): + def __init__(self, manager, network_config, chain_id=None, lookup_only=False, + suffix=None): """Create a network for given chain. network_config: a dict containing the network properties (name, segmentation_id and physical_network) chain_id: to which chain the networks belong. a None value will mean that these networks are shared by all chains + suffix: a suffix to add to the network name (if not None) """ self.manager = manager if chain_id is None: @@ -211,6 +213,8 @@ class ChainNetwork(object): else: # network_config.name is a prefix string self.name = network_config.name + str(chain_id) + if suffix: + self.name = self.name + suffix self.segmentation_id = self._get_item(network_config.segmentation_id, chain_id, auto_index=True) self.physical_network = self._get_item(network_config.physical_network, chain_id) @@ -299,6 +303,8 @@ class ChainNetwork(object): if self.physical_network: body['network']['provider:physical_network'] = self.physical_network self.network = self.manager.neutron_client.create_network(body)['network'] + # create associated subnet, all subnets have the same name (which is ok since + # we do not need to address them directly by name) body = { 'subnet': {'name': network_config.subnet, 'cidr': network_config.cidr, @@ -376,11 +382,18 @@ class ChainVnf(object): if len(networks) > 2: # we will have more than 1 VM in each chain self.name += '-' + str(vnf_id) + # A list of ports for this chain + # There are normally 2 ports carrying traffic (index 0, and index 1) and + # potentially multiple idle ports not carrying traffic (index 2 and up) + # For example if 7 idle interfaces are requested, the corresp. ports will be + # at index 2 to 8 self.ports = [] self.status = None self.instance = None self.reuse = False self.host_ip = None + self.idle_networks = [] + self.idle_ports = [] try: # the vnf_id is conveniently also the starting index in networks # for the left and right networks associated to this VNF @@ -415,7 +428,7 @@ class ChainVnf(object): def _get_vnic_type(self, port_index): """Get the right vnic type for given port indexself. - If SR-IOV is speficied, middle ports in multi-VNF chains + If SR-IOV is specified, middle ports in multi-VNF chains can use vswitch or SR-IOV based on config.use_sriov_middle_net """ if self.manager.config.sriov: @@ -430,6 +443,62 @@ class ChainVnf(object): return 'direct' return 'normal' + def _get_idle_networks_ports(self): + """Get the idle networks for PVP or PVVP chain (non shared net only) + + For EXT packet path or shared net, returns empty list. + For PVP, PVVP these networks will be created if they do not exist. + chain_id: to which chain the networks belong. + a None value will mean that these networks are shared by all chains + """ + networks = [] + ports = [] + config = self.manager.config + chain_id = self.chain.chain_id + idle_interfaces_per_vm = config.idle_interfaces_per_vm + if config.service_chain == ChainType.EXT or chain_id is None or \ + idle_interfaces_per_vm == 0: + return + + # Make a copy of the idle networks dict as we may have to modify the + # segmentation ID + idle_network_cfg = AttrDict(config.idle_networks) + if idle_network_cfg.segmentation_id: + segmentation_id = idle_network_cfg.segmentation_id + \ + chain_id * idle_interfaces_per_vm + else: + segmentation_id = None + try: + # create as many idle networks and ports as requested + for idle_index in range(idle_interfaces_per_vm): + if config.service_chain == ChainType.PVP: + suffix = '.%d' % (idle_index) + else: + suffix = '.%d.%d' % (self.vnf_id, idle_index) + port_name = self.name + '-idle' + str(idle_index) + # update the segmentation id based on chain id and idle index + if segmentation_id: + idle_network_cfg.segmentation_id = segmentation_id + idle_index + port_name = port_name + "." + str(segmentation_id) + + networks.append(ChainNetwork(self.manager, + idle_network_cfg, + chain_id, + suffix=suffix)) + ports.append(ChainVnfPort(port_name, + self, + networks[idle_index], + 'normal')) + except Exception: + # need to cleanup all successful networks + for net in networks: + net.delete() + for port in ports: + port.delete() + raise + self.idle_networks = networks + self.idle_ports = ports + def _setup(self, networks): flavor_id = self.manager.flavor.flavor.id # Check if we can reuse an instance with same name @@ -461,6 +530,12 @@ class ChainVnf(object): self, networks[index], self._get_vnic_type(index)) for index in [0, 1]] + + # create idle networks and ports only if instance is not reused + # if reused, we do not care about idle networks/ports + if not self.reuse: + self._get_idle_networks_ports() + # if no reuse, actual vm creation is deferred after all ports in the chain are created # since we need to know the next mac in a multi-vnf chain @@ -469,6 +544,9 @@ class ChainVnf(object): if self.instance is None: port_ids = [{'port-id': vnf_port.port['id']} for vnf_port in self.ports] + # add idle ports + for idle_port in self.idle_ports: + port_ids.append({'port-id': idle_port.port['id']}) vm_config = self._get_vm_config(remote_mac_pair) az = self.manager.placer.get_required_az() server = self.manager.comp.create_server(self.name, @@ -575,6 +653,10 @@ class ChainVnf(object): LOG.info("Deleted instance %s", self.name) for port in self.ports: port.delete() + for port in self.idle_ports: + port.delete() + for network in self.idle_networks: + network.delete() class Chain(object): """A class to manage a single chain. diff --git a/nfvbench/cleanup.py b/nfvbench/cleanup.py index 819514a..6b13f69 100644 --- a/nfvbench/cleanup.py +++ b/nfvbench/cleanup.py @@ -107,6 +107,7 @@ class NetworkCleaner(object): except Exception: LOG.exception("Port deletion failed") + # associated subnets are automatically deleted by neutron for net in self.networks: LOG.info("Deleting network %s...", net['name']) try: @@ -147,6 +148,9 @@ class Cleaner(object): self.neutron_client = nclient.Client('2.0', session=session) self.nova_client = Client(2, session=session) network_names = [inet['name'] for inet in config.internal_networks.values()] + # add idle networks as well + if config.idle_networks.name: + network_names.append(config.idle_networks.name) self.cleaners = [ComputeCleaner(self.nova_client, config.loop_vm_name), FlavorCleaner(self.nova_client, config.flavor_type), NetworkCleaner(self.neutron_client, network_names)] |