diff --git a/browbeat-config.yaml b/browbeat-config.yaml index fe996e351..7cbd373c9 100644 --- a/browbeat-config.yaml +++ b/browbeat-config.yaml @@ -585,8 +585,8 @@ workloads: # 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 - # create_loadbalancers, delete_loadbalancers, delete_members_random_lb, - # pod_fip_simulation, add_subports_to_random_trunks, + # swap_floating_ips_between_servers, create_loadbalancers, delete_loadbalancers, + # delete_members_random_lb, pod_fip_simulation, add_subports_to_random_trunks, # delete_subports_from_random_trunks, swap_floating_ips_between_random_subports, # provider_netcreate_nova_boot_ping, provider_net_nova_boot_ping, provider_net_nova_delete # Note: Octavia and Provider scenarios are not included in 'all' by default, diff --git a/rally/rally-plugins/dynamic-workloads/README.rst b/rally/rally-plugins/dynamic-workloads/README.rst index dab6273f7..e716f5f91 100644 --- a/rally/rally-plugins/dynamic-workloads/README.rst +++ b/rally/rally-plugins/dynamic-workloads/README.rst @@ -9,6 +9,7 @@ Functions: - _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 +- swap_floating_ips_between_servers: Swap floating IPs between 2 servers - create_loadbalancers: Create 'N' loadbalancers - delete_loadbalancers: Deletes 'M' loadbalancers randomly from 'N' loadbalancers - create_clients: Create 'N' clients @@ -20,10 +21,11 @@ Functions: - build_jump_host: Builds Jump host - _run_command_with_attempts: Run command over ssh connection with multiple attempts - _run_command_until_failure: Run command over ssh connection until failure +- _wait_for_ping_failure: Wait for ping failure to floating IP of server - add_route_from_vm_to_jumphost: Add route from trunk vm to jumphost via trunk subport - delete_route_from_vm_to_jumphost: Delete route from trunk vm to jumphost via trunk subport - get_jumphost_by_trunk: Get jumphost details for a given trunk -- ping_subport_fip_from_jumphost: Ping subport floating ip from jumphost +- assign_ping_fip_from_jumphost: Assign floating IP to port(optional), and ping floating ip from jumphost - create_subnets_and_subports: Create N subnets and subports - add_subports_to_trunk_and_vm: Add subports to trunk and create vlan interfaces for subport inside trunk VM - simulate_subport_connection: Simulate connection from jumphost to random subport of trunk VM diff --git a/rally/rally-plugins/dynamic-workloads/dynamic_utils.py b/rally/rally-plugins/dynamic-workloads/dynamic_utils.py index 23c1d176d..ea894b047 100644 --- a/rally/rally-plugins/dynamic-workloads/dynamic_utils.py +++ b/rally/rally-plugins/dynamic-workloads/dynamic_utils.py @@ -14,6 +14,8 @@ import logging import time from rally.common import cfg +from rally.common import sshutils + from rally_openstack.scenarios.vm import utils as vm_utils from rally_openstack.scenarios.neutron import utils as neutron_utils from rally.task import atomic @@ -30,10 +32,16 @@ logging.getLogger("paramiko").setLevel(logging.WARNING) class NovaUtils(vm_utils.VMScenario): def log_info(self, msg): + """Log information with iteration number + :param msg: str, message to log + """ log_msg = " DYNAMIC_WORKLOADS ITER: {} {} ".format(self.context["iteration"], msg) LOG.info(log_msg) def log_error(self, msg): + """Log error with iteration number + :param msg: str, message to log + """ log_msg = " DYNAMIC_WORKLOADS ITER: {} {} ".format(self.context["iteration"], msg) LOG.error(log_msg) @@ -76,26 +84,44 @@ class NovaUtils(vm_utils.VMScenario): else: break - def _create_sec_group_rule(self, security_group, protocol): - security_group_rule_args = {} - security_group_rule_args["security_group_id"] = security_group["security_group"]["id"] - security_group_rule_args["direction"] = "ingress" - security_group_rule_args["remote_ip_prefix"] = "0.0.0.0/0" - security_group_rule_args["protocol"] = protocol - if protocol == "tcp": - security_group_rule_args["port_range_min"] = 22 - security_group_rule_args["port_range_max"] = 22 - self.clients("neutron").create_security_group_rule( - {"security_group_rule": security_group_rule_args}) + def assign_ping_fip_from_jumphost(self, jumphost_fip, jumphost_user, + fip, port_id, success_on_ping_failure=False): + """Ping floating ip from jumphost + :param jumphost_fip: floating ip of jumphost + :param jumphost_user: str, ssh user for jumphost + :param fip: floating ip of port + :param port_id: id of port to ping from jumphost + :param success_on_ping_failure: bool, flag to ping till failure/success + """ + keypair = self.context["user"]["keypair"] + if not(success_on_ping_failure): + fip_update_dict = {"port_id": port_id} + self.clients("neutron").update_floatingip( + fip["id"], {"floatingip": fip_update_dict} + ) - def create_sec_group_with_icmp_ssh(self): - security_group_args = {} - security_group_args["name"] = self.generate_random_name() - security_group = self.clients("neutron").create_security_group( - {"security_group": security_group_args}) - self._create_sec_group_rule(security_group, "icmp") - self._create_sec_group_rule(security_group, "tcp") - return security_group["security_group"] + address = fip["floating_ip_address"] + jumphost_ssh = sshutils.SSH(jumphost_user, jumphost_fip, pkey=keypair["private"]) + self._wait_for_ssh(jumphost_ssh) + cmd = f"ping -c1 -w1 {address}" + if success_on_ping_failure: + self._run_command_until_failure(jumphost_ssh, cmd) + else: + self._run_command_with_attempts(jumphost_ssh, cmd) + + @atomic.action_timer("vm.wait_for_ping_failure") + def _wait_for_ping_failure(self, server_ip): + """Wait for ping failure to floating IP of server + :param server_ip: floating IP of server + """ + server = vm_utils.Host(server_ip) + utils.wait_for_status( + server, + ready_statuses=[vm_utils.Host.ICMP_DOWN_STATUS], + update_resource=vm_utils.Host.update_status, + timeout=CONF.openstack.vm_ping_timeout, + check_interval=CONF.openstack.vm_ping_poll_interval + ) def _boot_server_with_tag(self, image, flavor, tag, auto_assign_nic=False, **kwargs): @@ -201,10 +227,16 @@ class NovaUtils(vm_utils.VMScenario): class NeutronUtils(neutron_utils.NeutronScenario): def log_info(self, msg): + """Log information with iteration number + :param msg: str, message to log + """ log_msg = " DYNAMIC_WORKLOADS ITER: {} {} ".format(self.context["iteration"], msg) LOG.info(log_msg) def log_error(self, msg): + """Log error with iteration number + :param msg: str, message to log + """ log_msg = " DYNAMIC_WORKLOADS ITER: {} {} ".format(self.context["iteration"], msg) LOG.error(log_msg) @@ -241,6 +273,34 @@ class NeutronUtils(neutron_utils.NeutronScenario): ) return port_fip + def _create_sec_group_rule(self, security_group, protocol): + """Create rule for security group + :param security_group: security group object to create rule + :param protocol: str, protocol of rule to create + """ + security_group_rule_args = {} + security_group_rule_args["security_group_id"] = security_group["security_group"]["id"] + security_group_rule_args["direction"] = "ingress" + security_group_rule_args["remote_ip_prefix"] = "0.0.0.0/0" + security_group_rule_args["protocol"] = protocol + if protocol == "tcp": + security_group_rule_args["port_range_min"] = 22 + security_group_rule_args["port_range_max"] = 22 + self.clients("neutron").create_security_group_rule( + {"security_group_rule": security_group_rule_args}) + + def create_sec_group_with_icmp_ssh(self): + """Create security group with icmp and ssh rules + :returns: security group dict + """ + security_group_args = {} + security_group_args["name"] = self.generate_random_name() + security_group = self.clients("neutron").create_security_group( + {"security_group": security_group_args}) + self._create_sec_group_rule(security_group, "icmp") + self._create_sec_group_rule(security_group, "tcp") + return security_group["security_group"] + class LockUtils: def acquire_lock(self, object_id): diff --git a/rally/rally-plugins/dynamic-workloads/dynamic_workload.py b/rally/rally-plugins/dynamic-workloads/dynamic_workload.py index 4fefe4833..853cee181 100644 --- a/rally/rally-plugins/dynamic-workloads/dynamic_workload.py +++ b/rally/rally-plugins/dynamic-workloads/dynamic_workload.py @@ -74,6 +74,9 @@ class DynamicWorkload(vm.VMDynamicScenario, trunk.TrunkDynamicScenario, network_create_args, subnet_create_args, **kwargs) self.migrate_servers_with_fip(num_vms_to_migrate) + if workloads == "all" or "swap_floating_ips_between_servers" in workloads_list: + self.swap_floating_ips_between_servers() + if workloads == "all" or "pod_fip_simulation" in workloads_list: self.pod_fip_simulation(ext_net_id, trunk_image, trunk_flavor, smallest_image, smallest_flavor, num_initial_subports, num_trunk_vms) diff --git a/rally/rally-plugins/dynamic-workloads/trunk.py b/rally/rally-plugins/dynamic-workloads/trunk.py index cbd113bb5..f34ed3898 100644 --- a/rally/rally-plugins/dynamic-workloads/trunk.py +++ b/rally/rally-plugins/dynamic-workloads/trunk.py @@ -75,30 +75,6 @@ class TrunkDynamicScenario( self._wait_for_ssh(source_ssh) self._run_command_with_attempts(source_ssh, script) - def ping_subport_fip_from_jumphost(self, dest_vm, dest_vm_user, - fip, port, success_on_ping_failure=False): - """Ping subport floating ip from jumphost - :param dest_vm: floating ip of destination VM - :param dest_vm_user: str, ssh user for destination VM - :param fip: floating ip of subport - :param port: subport to ping from jumphost - :param success_on_ping_failure: bool, flag to ping till failure/success - """ - if not(success_on_ping_failure): - fip_update_dict = {"port_id": port["id"]} - self.clients("neutron").update_floatingip( - fip["id"], {"floatingip": fip_update_dict} - ) - - address = fip["floating_ip_address"] - dest_ssh = sshutils.SSH(dest_vm_user, dest_vm, pkey=self.keypair["private"]) - self._wait_for_ssh(dest_ssh) - cmd = f"ping -c1 -w1 {address}" - if success_on_ping_failure: - self._run_command_until_failure(dest_ssh, cmd) - else: - self._run_command_with_attempts(dest_ssh, cmd) - def simulate_subport_connection(self, trunk_id, vm_fip, jump_fip): """Simulate connection from jumphost to random subport of trunk VM :param trunk_id: id of trunk on which subport is present @@ -120,8 +96,8 @@ class TrunkDynamicScenario( " with fip: {}".format(subport_for_route["port"], subport_fip, trunk["trunk"], vm_fip, jump_fip) self.log_info(msg) - self.ping_subport_fip_from_jumphost(jump_fip, self.jumphost_user, subport_fip, - subport_for_route["port"]) + self.assign_ping_fip_from_jumphost(jump_fip, self.jumphost_user, subport_fip, + subport_for_route["port"]["id"]) # We delete the route from vm to jumphost through the randomly # chosen subport after simulate subport connection is executed, # as additional subports can be tested for connection in the @@ -467,16 +443,16 @@ class TrunkDynamicScenario( msg = "Ping until failure after dissociating subports' floating IPs, before swapping" self.log_info(msg) - self.ping_subport_fip_from_jumphost(jumphost1_fip, self.jumphost_user, subport1_fip, - subport1["port"], True) - self.ping_subport_fip_from_jumphost(jumphost2_fip, self.jumphost_user, subport2_fip, - subport2["port"], True) + self.assign_ping_fip_from_jumphost(jumphost1_fip, self.jumphost_user, subport1_fip, + subport1["port"]["id"], True) + self.assign_ping_fip_from_jumphost(jumphost2_fip, self.jumphost_user, subport2_fip, + subport2["port"]["id"], True) self.log_info("Ping until success by swapping subports' floating IPs") - self.ping_subport_fip_from_jumphost(jumphost1_fip, self.jumphost_user, subport2_fip, - subport1["port"]) - self.ping_subport_fip_from_jumphost(jumphost2_fip, self.jumphost_user, subport1_fip, - subport2["port"]) + self.assign_ping_fip_from_jumphost(jumphost1_fip, self.jumphost_user, subport2_fip, + subport1["port"]["id"]) + self.assign_ping_fip_from_jumphost(jumphost2_fip, self.jumphost_user, subport1_fip, + subport2["port"]["id"]) self.delete_route_from_vm_to_jumphost(trunk_vm1_fip, jumphost1_fip, self.trunk_vm_user, subport1_number_for_route, diff --git a/rally/rally-plugins/dynamic-workloads/vm.py b/rally/rally-plugins/dynamic-workloads/vm.py index 24018a6d3..1d0d23a05 100644 --- a/rally/rally-plugins/dynamic-workloads/vm.py +++ b/rally/rally-plugins/dynamic-workloads/vm.py @@ -81,11 +81,13 @@ class VMDynamicScenario(dynamic_utils.NovaUtils, 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): + keypair = self.context["user"]["keypair"] + + for _ in range(num_vms): kwargs["nics"] = [{"net-id": network["network"]["id"]}] guest = self._boot_server_with_fip_and_tag( image, flavor, "migrate_or_swap", - True, ext_net_name, **kwargs + True, ext_net_name, key_name=keypair["name"], **kwargs ) self._wait_for_ping(guest[1]["ip"]) @@ -133,3 +135,65 @@ class VMDynamicScenario(dynamic_utils.NovaUtils, if num_migrated == 0: self.log_info("""No servers which are not under lock, so cannot migrate any servers.""") + + def swap_floating_ips_between_servers(self): + """Swap floating IPs between servers + """ + eligible_servers = self._get_servers_by_tag("migrate_or_swap") + + servers_for_swapping = [] + for server in eligible_servers: + if not self.acquire_lock(server.id): + continue + servers_for_swapping.append(server) + if len(servers_for_swapping) == 2: + break + + if len(servers_for_swapping) < 2: + self.log_info("""Number of unlocked servers not sufficient + for swapping floating IPs between servers""") + return + + kwargs = {"floating_ip_address": list(servers_for_swapping[0].addresses.values()) + [0][1]['addr']} + server1_fip = self._list_floating_ips(**kwargs)["floatingips"][0] + + kwargs = {"floating_ip_address": list(servers_for_swapping[1].addresses.values()) + [0][1]['addr']} + server2_fip = self._list_floating_ips(**kwargs)["floatingips"][0] + + server1_port = server1_fip["port_id"] + server2_port = server2_fip["port_id"] + + fip_update_dict = {"port_id": None} + self.clients("neutron").update_floatingip( + server1_fip["id"], {"floatingip": fip_update_dict}) + self.clients("neutron").update_floatingip( + server2_fip["id"], {"floatingip": fip_update_dict}) + + self.log_info("""Ping until failure after dissociating servers' floating IPs, + before swapping""") + self.log_info("Ping server 1 {} until failure".format(server1_fip["floating_ip_address"])) + self._wait_for_ping_failure(server1_fip["floating_ip_address"]) + self.log_info("Ping server 2 {} until failure".format(server2_fip["floating_ip_address"])) + self._wait_for_ping_failure(server2_fip["floating_ip_address"]) + + # Swap floating IPs between server1 and server2 + fip_update_dict = {"port_id": server2_port} + self.clients("neutron").update_floatingip( + server1_fip["id"], {"floatingip": fip_update_dict} + ) + fip_update_dict = {"port_id": server1_port} + self.clients("neutron").update_floatingip( + server2_fip["id"], {"floatingip": fip_update_dict} + ) + + self.log_info("Ping until success by swapping servers' floating IPs") + self.log_info("Ping server 1 {} until success".format(server1_fip["floating_ip_address"])) + self._wait_for_ping(server1_fip["floating_ip_address"]) + self.log_info("Ping server 2 {} until success".format(server2_fip["floating_ip_address"])) + self._wait_for_ping(server2_fip["floating_ip_address"]) + + # Release locks from servers + self.release_lock(servers_for_swapping[0].id) + self.release_lock(servers_for_swapping[1].id)