From 68f7a467223ef0b4217757b9c84842442d30656c Mon Sep 17 00:00:00 2001 From: Sanjay Chari Date: Fri, 29 Jul 2022 14:37:53 +0530 Subject: [PATCH] Add nova_boot_from_context_provider_networks_ping workload This patch adds a workload which creates provider networks as part of Rally context, and boots VMs on the provider networks in round robin order. Co-authored-by: Jaison Raju Change-Id: I00999c81eaf78bbd72933df524bf55cf452d40fc --- browbeat-config.yaml | 13 ++ ...oot_from_context_provider_networks_ping.py | 86 ++++++++++ ...ot_from_context_provider_networks_ping.yml | 46 +++++ .../netcreate-boot/rally_context.py | 161 ++++++++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.py create mode 100644 rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.yml create mode 100644 rally/rally-plugins/netcreate-boot/rally_context.py diff --git a/browbeat-config.yaml b/browbeat-config.yaml index bed6ec9dc..ff024dde7 100644 --- a/browbeat-config.yaml +++ b/browbeat-config.yaml @@ -530,6 +530,19 @@ workloads: iface_name: "ens7f0" iface_mac: "3c:fd:fe:c1:8c:70" file: rally/rally-plugins/netcreate-boot/provider_netcreate_nova_boot_ping.yml + - name: nova-boot-from-context-provider-networks-ping + # Creates provider networks as part of rally context. Number of VMs booted is equal to times. + # VMs are booted on the provider networks in round robin order. + enabled: true + num_provider_networks: 8 + image_name: cirro5 + flavor_name: m1.tiny-cirros + provider_phys_net: "provider" + iface_name: "ens7f0" + iface_mac: "3c:fd:fe:c1:8c:70" + cidr_prefix: "172.31" + ping_timeout: 30 + file: rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.yml - name: plugin-workloads enabled: false diff --git a/rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.py b/rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.py new file mode 100644 index 000000000..045a929fd --- /dev/null +++ b/rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.py @@ -0,0 +1,86 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import os +import subprocess + +from rally_openstack.common import consts +from rally_openstack.task.scenarios.vm import utils as vm_utils +from rally_openstack.task.scenarios.neutron import utils as neutron_utils +from rally.task import atomic +from rally.task import scenario +from rally.task import types +from rally.task import validation + + +LOG = logging.getLogger(__name__) + + +@types.convert(image={"type": "glance_image"}, flavor={"type": "nova_flavor"}) +@validation.add("image_valid_on_flavor", flavor_param="flavor", image_param="image") +@validation.add("required_services", services=[consts.Service.NEUTRON, consts.Service.NOVA]) +@validation.add("required_platform", platform="openstack", admin=True) +@scenario.configure(context={"cleanup@openstack": ["neutron", "nova"], "keypair@openstack": {}, + "allow_ssh@openstack": None}, + name="BrowbeatPlugin.nova_boot_from_context_provider_networks_ping", + platform="openstack") +class NovaBootFromContextProviderNetworksPing(vm_utils.VMScenario, + neutron_utils.NeutronScenario): + + def run(self, image, flavor, num_provider_networks, ping_timeout, **kwargs): + network_id = self.context["provider_networks"][((self.context["iteration"]-1) + % num_provider_networks)]["id"] + network = self._show_provider_network(network_id) + subnet = self.context["provider_subnets"][network_id] + kwargs["nics"] = [{'net-id': network_id}] + server = self._boot_server(image, flavor, **kwargs) + + # ping server + internal_network = list(server.networks)[0] + server_ip = server.addresses[internal_network][0]["addr"] + server_mac = server.addresses[internal_network][0]["OS-EXT-IPS-MAC:mac_addr"] + gateway = subnet['gateway_ip'] + vlan = network['network']['provider:segmentation_id'] + + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path, "scapy_icmp.py") + + with atomic.ActionTimer(self, "nova.wait_for_custom_ping"): + cmd = ["sudo", file_path, server_ip, server_mac, gateway, self.context["iface_mac"], + self.context["iface_name"], str(vlan)] + proc = subprocess.Popen(cmd, start_new_session=True) + try: + proc.wait(timeout=ping_timeout) + except subprocess.TimeoutExpired: + pgid = os.getpgid(proc.pid) + procout = subprocess.check_output(['sudo', 'kill', str(pgid)]).decode("utf-8") + if not procout: + LOG.info("{} process group terminated successfully".format(pgid)) + else: + LOG.info("{} process group did not terminate successfully. Stdout : {}".format( + pgid, procout)) + raise Exception("Rally tired waiting {} seconds for ping to {}".format( + ping_timeout, server_ip)) + + if proc.returncode == 0: + LOG.info("Ping to {} is successful".format(server_ip)) + else: + raise Exception("Ping to {} has failed".format(server_ip)) + + @atomic.action_timer("neutron.show_network") + def _show_provider_network(self, provider_network_id): + """Fetches information of a certain provider network. + :param provider_network: provider network object + """ + + return self.admin_clients("neutron").show_network(provider_network_id) diff --git a/rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.yml b/rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.yml new file mode 100644 index 000000000..a7aae1a66 --- /dev/null +++ b/rally/rally-plugins/netcreate-boot/nova_boot_from_context_provider_networks_ping.yml @@ -0,0 +1,46 @@ +{% set flavor_name = flavor_name or "m1.xtiny" %} +{% set image_name = image_name or "cirros" %} +{% set num_provider_networks = num_provider_networks or 1 %} +{% set ping_timeout = ping_timeout or 30 %} +{% set sla_max_avg_duration = sla_max_avg_duration or 60 %} +{% set sla_max_failure = sla_max_failure or 0 %} +{% set sla_max_seconds = sla_max_seconds or 60 %} +--- + BrowbeatPlugin.nova_boot_from_context_provider_networks_ping: + - + args: + flavor: + name: "{{flavor_name}}" + image: + name: "{{image_name}}" + num_provider_networks: {{num_provider_networks}} + ping_timeout: {{ping_timeout}} + runner: + type: "constant" + times: {{times}} + concurrency: {{concurrency}} + context: + users: + tenants: 1 + users_per_tenant: 1 + quotas: + neutron: + network: -1 + port: -1 + subnet: -1 + floatingip: -1 + nova: + instances: -1 + cores: -1 + ram: -1 + create_provider_networks: + num_provider_networks: {{ num_provider_networks }} + iface_name: '{{ iface_name }}' + iface_mac: '{{ iface_mac }}' + provider_phys_net: '{{ provider_phys_net }}' + cidr_prefix: '{{ cidr_prefix }}' + sla: + max_avg_duration: {{sla_max_avg_duration}} + max_seconds_per_iteration: {{sla_max_seconds}} + failure_rate: + max: {{sla_max_failure}} diff --git a/rally/rally-plugins/netcreate-boot/rally_context.py b/rally/rally-plugins/netcreate-boot/rally_context.py new file mode 100644 index 000000000..a0581a6ff --- /dev/null +++ b/rally/rally-plugins/netcreate-boot/rally_context.py @@ -0,0 +1,161 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from rally.task import context +from rally.common import logging +from rally.common import utils +from rally import consts +from rally_openstack import osclients +from rally_openstack.wrappers import network as network_wrapper + +LOG = logging.getLogger(__name__) + + +@context.configure(name="create_provider_networks", order=1000) +class CreateProviderNetworksContext(context.Context): + """This plugin creates provider networks with specified option.""" + + CONFIG_SCHEMA = { + "type": "object", + "$schema": consts.JSON_SCHEMA, + "additionalProperties": False, + "properties": { + "num_provider_networks": { + "type": "integer", + "minimum": 0 + }, + "iface_name": { + "type": "string" + }, + "iface_mac": { + "type": "string" + }, + "provider_phys_net": { + "type": "string" + }, + "cidr_prefix": { + "type": "string" + } + } + } + + def _create_subnet(self, tenant_id, network_id, network_number): + """Create subnet for provider network + + :param tenant_id: ID of tenant + :param network_id: ID of provider network + :param network_number: int, number for CIDR of subnet + :returns: subnet object + """ + subnet_args = { + "subnet": { + "tenant_id": tenant_id, + "network_id": network_id, + "name": self.net_wrapper.owner.generate_random_name(), + "ip_version": 4, + "cidr": "{}.{}.0/23".format(self.cidr_prefix, network_number), + "gateway_ip": "{}.{}.1".format(self.cidr_prefix, network_number), + "allocation_pools": [{"start": "{}.{}.2".format(self.cidr_prefix, network_number), + "end": "{}.{}.254".format( + self.cidr_prefix, network_number+1)}] + } + } + return self.net_wrapper.client.create_subnet(subnet_args)["subnet"] + + def setup(self): + """This method is called before the task starts.""" + self.net_wrapper = network_wrapper.wrap( + osclients.Clients(self.context["admin"]["credential"]), + self, + config=self.config, + ) + self.context["provider_networks"] = [] + self.context["provider_subnets"] = {} + self.num_provider_networks = self.config.get("num_provider_networks", 16) + self.context["iface_name"] = self.config.get("iface_name", "ens7f0") + self.context["iface_mac"] = self.config.get("iface_mac", " ") + self.provider_phys_net = self.config.get("provider_phys_net", "datacentre") + self.cidr_prefix = self.config.get("cidr_prefix", "172.31") + num_provider_networks_created = 0 + + while num_provider_networks_created < self.num_provider_networks: + has_error_occured = False + for user, tenant_id in utils.iterate_per_tenants( + self.context.get("users", []) + ): + try: + kwargs = { + "network_create_args": { + "provider:network_type": "vlan", + "provider:physical_network": self.provider_phys_net + } + } + self.context["provider_networks"].append( + self.net_wrapper.create_network(tenant_id, **kwargs) + ) + LOG.debug( + "Provider network with id '%s' created as part of context" + % self.context["provider_networks"][-1]["id"] + ) + num_provider_networks_created += 1 + except Exception as e: + msg = "Can't create provider network {} as part of context: {}".format( + num_provider_networks_created, e + ) + LOG.exception(msg) + has_error_occured = True + break + + try: + subnet = self._create_subnet(tenant_id, + self.context["provider_networks"][-1]["id"], + (num_provider_networks_created - 1) * 2) + self.context["provider_subnets"][ + self.context["provider_networks"][-1]["id"]] = subnet + LOG.debug( + "Provider subnet with id '%s' created as part of context" + % subnet["id"] + ) + except Exception as e: + msg = "Can't create provider subnet {} as part of context: {}".format( + num_provider_networks_created, e + ) + LOG.exception(msg) + has_error_occured = True + break + + if has_error_occured: + break + + def cleanup(self): + """This method is called after the task finishes.""" + for i in range(self.num_provider_networks): + try: + provider_net = self.context["provider_networks"][i] + provider_net_id = provider_net["id"] + provider_subnet = self.context["provider_subnets"][provider_net_id] + provider_subnet_id = provider_subnet["id"] + self.net_wrapper._delete_subnet(provider_subnet_id) + LOG.debug( + "Provider subnet with id '%s' deleted from context" + % provider_subnet_id + ) + self.net_wrapper.delete_network(provider_net) + LOG.debug( + "Provider network with id '%s' deleted from context" + % provider_net_id + ) + except Exception as e: + msg = "Can't delete provider network {} from context: {}".format( + provider_net_id, e + ) + LOG.warning(msg)