From f55cd6c8eb7c6ac01cfab36ac2b6aaa0a1c7596e Mon Sep 17 00:00:00 2001 From: Sanjay Chari Date: Tue, 27 Jul 2021 17:32:15 +0530 Subject: [PATCH] Server Migration Dynamic workload This patch introduces a dynamic workload which creates num_vms servers with floating IPs, and migrates num_migrate_vms servers between computes. It also adds a parameter in browbeat-config.yaml to specify the dynamic workloads which the user wants to run in a given iteration. Change-Id: Ib7eff2e12c6d256ecd1f36ad30f1b19841cb1549 --- browbeat-config.yaml | 10 +- .../dynamic-workloads/README.rst | 6 +- rally/rally-plugins/dynamic-workloads/base.py | 103 +++++++++++++++++- .../dynamic-workloads/dynamic_workload.py | 22 +++- .../dynamic-workloads/dynamic_workload.yml | 15 ++- 5 files changed, 143 insertions(+), 13 deletions(-) diff --git a/browbeat-config.yaml b/browbeat-config.yaml index 6c1ae09ba..a3b4fe174 100644 --- a/browbeat-config.yaml +++ b/browbeat-config.yaml @@ -535,11 +535,15 @@ workloads: - 1 times: 1 scenarios: - - name: dynamic-workload-1 + - name: dynamic-workload enabled: true - enable_dhcp: true image_name: cirros flavor_name: m1.small + ext_net_id: num_vms: 10 + num_migrate_vms: 5 + # workloads can be 'all', a single workload(Eg. : create_delete_servers), + # or a comma separated string(Eg. : create_delete_servers,migrate_servers). + # Currently supported workloads : create_delete_servers, migrate_servers + workloads: all file: rally/rally-plugins/dynamic-workloads/dynamic_workload.yml - diff --git a/rally/rally-plugins/dynamic-workloads/README.rst b/rally/rally-plugins/dynamic-workloads/README.rst index e175d0db2..0cc6ab255 100644 --- a/rally/rally-plugins/dynamic-workloads/README.rst +++ b/rally/rally-plugins/dynamic-workloads/README.rst @@ -3,4 +3,8 @@ Browbeat Rally Plugin: dynamic-workloads Functions: ---------- -- create_delete_servers: Create 'N' VMs and delete 'N/2' VMs during test. +- create_delete_servers: Create 'N' VMs and delete 'N/2' VMs. +- server_boot_floatingip: Create 'N' VMs with floating IPs. +- _create_router: Create neutron router. +- get_servers_migration_list: Generate list of servers to migrate between computes. +- migrate_servers_with_fip: Migrate servers between computes diff --git a/rally/rally-plugins/dynamic-workloads/base.py b/rally/rally-plugins/dynamic-workloads/base.py index 97959c428..13c53defb 100644 --- a/rally/rally-plugins/dynamic-workloads/base.py +++ b/rally/rally-plugins/dynamic-workloads/base.py @@ -11,8 +11,10 @@ # limitations under the License. import logging +import random from rally_openstack.scenarios.neutron import utils as neutron_utils from rally_openstack.scenarios.vm import utils as vm_utils +from rally.task import atomic LOG = logging.getLogger(__name__) @@ -20,7 +22,7 @@ LOG = logging.getLogger(__name__) class DynamicBase(vm_utils.VMScenario, neutron_utils.NeutronScenario): def create_delete_servers(self, image, flavor, num_vms=1, min_sleep=0, max_sleep=10, network_create_args=None, subnet_create_args=None): - """Creates servers and deletes servers. + """Create servers and delete servers. :param image: image ID or instance for server creation :param flavor: int, flavor ID or instance for server creation @@ -35,7 +37,6 @@ class DynamicBase(vm_utils.VMScenario, neutron_utils.NeutronScenario): kwargs = {} kwargs["nics"] = [{"net-id": network["network"]["id"]}] servers = [] - for i in range(num_vms): server = self._boot_server(image, flavor, **kwargs) LOG.info("Created server {} when i = {}".format(server,i)) @@ -47,3 +48,101 @@ class DynamicBase(vm_utils.VMScenario, neutron_utils.NeutronScenario): self._delete_server(server_to_delete, force=True) LOG.info("Deleted server {} when i = {}".format(server_to_delete,i)) servers.pop(0) + + def server_boot_floatingip(self, image, flavor, ext_net_id, num_vms=1, router_create_args=None, + network_create_args=None, subnet_create_args=None, **kwargs): + """Create servers with floating IPs + + :param image: image ID or instance for server creation + :param flavor: int, flavor ID or instance for server creation + :param ext_net_id: external network ID for floating IP creation + :param num_vms: int, number of servers to create + :param router_create_args: dict, arguments for router creation + :param network_create_args: dict, arguments for network creation + :param subnet_create_args: dict, arguments for subnet creation + :param kwargs: dict, Keyword arguments to function + """ + ext_net_name = None + if ext_net_id: + ext_net_name = self.clients("neutron").show_network(ext_net_id)["network"][ + "name" + ] + router_create_args["name"] = self.generate_random_name() + router_create_args["tenant_id"] = self.context["tenant"]["id"] + router_create_args.setdefault( + "external_gateway_info", {"network_id": ext_net_id, "enable_snat": True} + ) + router = self._create_router(router_create_args) + + network = self._create_network(network_create_args or {}) + subnet = self._create_subnet(network, subnet_create_args or {}) + self._add_interface_router(subnet["subnet"], router["router"]) + for i in range(num_vms): + kwargs["nics"] = [{"net-id": network["network"]["id"]}] + guest = self._boot_server_with_fip( + image, flavor, True, ext_net_name, **kwargs + ) + self._wait_for_ping(guest[1]["ip"]) + + @atomic.action_timer("neutron.create_router") + def _create_router(self, router_create_args): + """Create neutron router. + + :param router_create_args: POST /v2.0/routers request options + :returns: neutron router dict + """ + return self.admin_clients("neutron").create_router( + {"router": router_create_args} + ) + + def get_servers_migration_list(self, num_migrate_vms): + """Generate list of servers to migrate between computes + + :param num_migrate_vms: int, number of servers to migrate between computes + :returns: list of server objects to migrate between computes + """ + servers = self._list_servers(True) + trunks = self._list_trunks() + eligible_servers = [] + + for server in servers: + has_floating_ip = False + is_not_trunk_network_server = True + + for network, addr_list in server.addresses.items(): + if len(addr_list) > 1: + has_floating_ip = True + + for interface in self._list_interfaces(server): + for trunk in trunks: + if interface._info['port_id'] == trunk['port_id']: + is_not_trunk_network_server = False + break + if not(is_not_trunk_network_server): + break + + if has_floating_ip and is_not_trunk_network_server: + eligible_servers.append(server) + + random.shuffle(eligible_servers) + num_servers_to_migrate = min(num_migrate_vms, len(eligible_servers)) + list_of_servers_to_migrate = [] + + for i in range(num_servers_to_migrate): + list_of_servers_to_migrate.append(eligible_servers[i]) + + return list_of_servers_to_migrate + + def migrate_servers_with_fip(self, num_migrate_vms): + """Migrate servers between computes + + :param num_migrate_vms: int, number of servers to migrate between computes + """ + for server in self.get_servers_migration_list(num_migrate_vms): + fip = list(server.addresses.values())[0][1]['addr'] + LOG.info("ping {} before server migration".format(fip)) + self._wait_for_ping(fip) + self._migrate(server) + self._resize_confirm(server, status="ACTIVE") + LOG.info("ping {} after server migration".format(fip)) + self._wait_for_ping(fip) diff --git a/rally/rally-plugins/dynamic-workloads/dynamic_workload.py b/rally/rally-plugins/dynamic-workloads/dynamic_workload.py index c0441b40f..a71d1910b 100644 --- a/rally/rally-plugins/dynamic-workloads/dynamic_workload.py +++ b/rally/rally-plugins/dynamic-workloads/dynamic_workload.py @@ -24,10 +24,26 @@ import base ) @validation.add("required_platform", platform="openstack", users=True) @scenario.configure( - context={"cleanup@openstack": ["neutron", "nova"]}, + context={ + "cleanup@openstack": ["neutron", "nova"], + "keypair@openstack": {}, + "allow_ssh@openstack": None, + }, name="BrowbeatPlugin.dynamic_workload", platform="openstack", ) class DynamicWorkload(base.DynamicBase): - def run(self, image, flavor, num_vms, subnet_create_args, **kwargs): - self.create_delete_servers(image, flavor, num_vms, subnet_create_args=subnet_create_args) + def run(self, image, flavor, ext_net_id, num_migrate_vms, num_vms=1, workloads="all", + router_create_args=None, network_create_args=None, subnet_create_args=None, **kwargs): + + if workloads != "all": + workloads_list = workloads.split(",") + + if workloads == "all" or "create_delete_servers" in workloads_list: + self.create_delete_servers(image, flavor, num_vms, + subnet_create_args=subnet_create_args) + + if workloads == "all" or "migrate_servers" in workloads_list: + self.server_boot_floatingip(image, flavor, ext_net_id, num_vms, router_create_args, + network_create_args, subnet_create_args, **kwargs) + self.migrate_servers_with_fip(num_migrate_vms) diff --git a/rally/rally-plugins/dynamic-workloads/dynamic_workload.yml b/rally/rally-plugins/dynamic-workloads/dynamic_workload.yml index b46524875..d4b2f2d6b 100644 --- a/rally/rally-plugins/dynamic-workloads/dynamic_workload.yml +++ b/rally/rally-plugins/dynamic-workloads/dynamic_workload.yml @@ -1,25 +1,31 @@ {% set image_name = image_name or 'cirros' %} {% set flavor_name = flavor_name or 'm1.xtiny' %} -{% set enable_dhcp = enable_dhcp or true %} {% set num_vms = num_vms or 1 %} +{% set num_migrate_vms = num_migrate_vms or 1 %} +{% set workloads = workloads or 'all' %} {% 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.dynamic_workload: - - +- args: + floating: True flavor: name: '{{flavor_name}}' image: name: '{{image_name}}' network_create_args: {} - subnet_create_args: {'enable_dhcp': '{{enable_dhcp}}'} + router_create_args: {} + subnet_create_args: {} num_vms: {{num_vms}} + num_migrate_vms: {{num_migrate_vms}} + ext_net_id: '{{ext_net_id}}' + workloads: '{{workloads}}' runner: concurrency: {{concurrency}} times: {{times}} - type: 'constant' + type: "constant" context: users: tenants: 1 @@ -30,6 +36,7 @@ BrowbeatPlugin.dynamic_workload: port: -1 router: -1 subnet: -1 + floatingip: -1 nova: instances: -1 cores: -1