diff --git a/rally-jobs/nova.yaml b/rally-jobs/nova.yaml index 1e4bf35b42..cb7ce151fa 100755 --- a/rally-jobs/nova.yaml +++ b/rally-jobs/nova.yaml @@ -287,6 +287,40 @@ users: tenants: 2 users_per_tenant: 1 + - + title: NovaServers.boot_and_associate_floating_ip tests + scenario: + NovaServers.boot_and_associate_floating_ip: + flavor: + name: {{flavor_name}} + image: + name: {{image_name}} + runner: + constant: + times: 2 + concurrency: 2 + contexts: + users: + tenants: 2 + users_per_tenant: 1 + network: {} + - + title: NovaServers.boot_server_associate_and_dissociate_floating_ip tests + scenario: + NovaServers.boot_server_associate_and_dissociate_floating_ip: + flavor: + name: {{flavor_name}} + image: + name: {{image_name}} + runner: + constant: + times: 2 + concurrency: 2 + contexts: + users: + tenants: 2 + users_per_tenant: 2 + network: {} - title: NovaServers.list_servers tests scenario: diff --git a/rally-jobs/rally-neutron.yaml b/rally-jobs/rally-neutron.yaml index 6df9769890..ee22f40e6c 100644 --- a/rally-jobs/rally-neutron.yaml +++ b/rally-jobs/rally-neutron.yaml @@ -682,3 +682,136 @@ sla: failure_rate: max: 0 + + VMTasks.boot_runcommand_delete: + - + args: + flavor: + name: "m1.tiny" + image: + name: {{image_name}} + command: + script_file: "~/.rally/extra/instance_test.sh" + interpreter: "/bin/sh" + username: "cirros" + runner: + type: "constant" + times: {{smoke or 2}} + concurrency: {{smoke or 2}} + context: + users: + tenants: {{smoke or 2}} + users_per_tenant: {{smoke or 2}} + network: {} + sla: + failure_rate: + max: 0 + - + args: + flavor: + name: "m1.tiny" + image: + name: {{image_name}} + command: + script_file: "~/.rally/extra/instance_test.sh" + interpreter: "/bin/sh" + username: "cirros" + volume_args: + size: 2 + runner: + type: "constant" + times: {{smoke or 2}} + concurrency: {{smoke or 2}} + context: + users: + tenants: {{smoke or 2}} + users_per_tenant: {{smoke or 1}} + network: {} + sla: + failure_rate: + max: 0 + - + args: + flavor: + name: {{flavor_name}} + image: + name: {{image_name}} + floating_network: "public" + command: + script_inline: | + time_seconds(){ (time -p $1 ) 2>&1 |awk '/real/{print $2}'; } + file=/tmp/test.img + c=100 #100M + write_seq=$(time_seconds "dd if=/dev/zero of=$file bs=1M count=$c") + read_seq=$(time_seconds "dd if=$file of=/dev/null bs=1M count=$c") + [ -f $file ] && rm $file + + echo "{ + \"write_seq\": $write_seq, + \"read_seq\": $read_seq + }" + interpreter: "/bin/sh" + username: "cirros" + runner: + type: "constant" + times: 2 + concurrency: 2 + context: + users: + tenants: 1 + users_per_tenant: 1 + network: {} + sla: + failure_rate: + max: 0 + - + args: + command: + # The `image_command_customizer` context prepares an image and + # executes `rally-jobs/extra/install_benchmark.sh` script. The script + # itself creates a new file inside the image "dd_test.sh" which is + # called in the scenario. It doesn't have anything related to + # VMTask.dd_load_test scenario + remote_path: "./dd_test.sh" + flavor: + name: "m1.tiny" + username: "cirros" + runner: + type: "constant" + times: 1 + concurrency: 1 + context: + image_command_customizer: + command: + local_path: "~/.rally/extra/install_benchmark.sh" + remote_path: "./install_benchmark.sh" + flavor: + name: "m1.tiny" + image: + name: {{image_name}} + username: "cirros" + users: + tenants: 1 + users_per_tenant: 1 + network: + dns_nameservers: [] + + VMTasks.dd_load_test: + - + args: + flavor: + name: "m1.tiny" + image: + name: {{image_name}} + floating_network: "public" + force_delete: false + username: "cirros" + runner: + type: "constant" + times: 2 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 1 + network: {} diff --git a/rally/plugins/openstack/scenarios/nova/utils.py b/rally/plugins/openstack/scenarios/nova/utils.py index f9aaa3f2eb..c7a0bb0a20 100644 --- a/rally/plugins/openstack/scenarios/nova/utils.py +++ b/rally/plugins/openstack/scenarios/nova/utils.py @@ -606,13 +606,42 @@ class NovaScenario(scenario.OpenStackScenario): """Add floating IP to an instance :param server: The :class:`Server` to add an IP to. - :param address: The ip address or FloatingIP to add to the instance + :param address: The dict-like representation of FloatingIP to add + to the instance :param fixed_address: The fixedIP address the FloatingIP is to be associated with (optional) """ - server.add_floating_ip(address, fixed_address=fixed_address) + with atomic.ActionTimer(self, "neutron.list_ports"): + ports = self.clients("neutron").list_ports(device_id=server.id) + port = ports["ports"][0] + + fip = address + if not isinstance(address, dict): + LOG.warning( + "The argument 'address' of " + "NovaScenario._associate_floating_ip method accepts a " + "dict-like representation of floating ip. Transmitting a " + "string with just an IP is deprecated.") + with atomic.ActionTimer(self, "neutron.list_floating_ips"): + all_fips = self.clients("neutron").list_floatingips( + tenant_id=self.context["tenant"]["id"]) + filtered_fip = [f for f in all_fips["floatingips"] + if f["floating_ip_address"] == address] + if not filtered_fip: + raise exceptions.NotFoundException( + "There is no floating ip with '%s' address." % address) + fip = filtered_fip[0] + # the first case: fip object is returned from network wrapper + # the second case: from neutronclient directly + fip_ip = fip.get("ip", fip.get("floating_ip_address", None)) + fip_update_dict = {"port_id": port["id"]} + if fixed_address: + fip_update_dict["fixed_ip_address"] = fixed_address + self.clients("neutron").update_floatingip( + fip["id"], {"floatingip": fip_update_dict} + ) utils.wait_for(server, - is_ready=self.check_ip_address(address), + is_ready=self.check_ip_address(fip_ip), update_resource=utils.get_from_manager()) # Update server data server.addresses = server.manager.get(server.id).addresses @@ -622,12 +651,34 @@ class NovaScenario(scenario.OpenStackScenario): """Remove floating IP from an instance :param server: The :class:`Server` to add an IP to. - :param address: The ip address or FloatingIP to remove + :param address: The dict-like representation of FloatingIP to remove """ - server.remove_floating_ip(address) + fip = address + if not isinstance(fip, dict): + LOG.warning( + "The argument 'address' of " + "NovaScenario._dissociate_floating_ip method accepts a " + "dict-like representation of floating ip. Transmitting a " + "string with just an IP is deprecated.") + with atomic.ActionTimer(self, "neutron.list_floating_ips"): + all_fips = self.clients("neutron").list_floatingips( + tenant_id=self.context["tenant"]["id"] + ) + filtered_fip = [f for f in all_fips["floatingips"] + if f["floating_ip_address"] == address] + if not filtered_fip: + raise exceptions.NotFoundException( + "There is no floating ip with '%s' address." % address) + fip = filtered_fip[0] + self.clients("neutron").update_floatingip( + fip["id"], {"floatingip": {"port_id": None}} + ) + # the first case: fip object is returned from network wrapper + # the second case: from neutronclient directly + fip_ip = fip.get("ip", fip.get("floating_ip_address", None)) utils.wait_for( server, - is_ready=self.check_ip_address(address, must_exist=False), + is_ready=self.check_ip_address(fip_ip, must_exist=False), update_resource=utils.get_from_manager() ) # Update server data @@ -640,8 +691,8 @@ class NovaScenario(scenario.OpenStackScenario): def _check_addr(resource): for network, addr_list in resource.addresses.items(): for addr in addr_list: - if ip_to_check == addr["addr"]: - return must_exist + if ip_to_check == addr["addr"]: + return must_exist return not must_exist return _check_addr diff --git a/rally/plugins/openstack/scenarios/vm/utils.py b/rally/plugins/openstack/scenarios/vm/utils.py index 536bc0ee19..894f590ad7 100644 --- a/rally/plugins/openstack/scenarios/vm/utils.py +++ b/rally/plugins/openstack/scenarios/vm/utils.py @@ -170,7 +170,7 @@ class VMScenario(nova_utils.NovaScenario): ext_network=floating_network, tenant_id=server.tenant_id, fixed_ip=fixed_ip) - self._associate_floating_ip(server, fip["ip"], fixed_address=fixed_ip) + self._associate_floating_ip(server, fip, fixed_address=fixed_ip) return fip @@ -179,7 +179,7 @@ class VMScenario(nova_utils.NovaScenario): with logging.ExceptionLogger( LOG, "Unable to delete IP: %s" % fip["ip"]): if self.check_ip_address(fip["ip"])(server): - self._dissociate_floating_ip(server, fip["ip"]) + self._dissociate_floating_ip(server, fip) with atomic.ActionTimer(self, "neutron.delete_floating_ip"): network_wrapper.wrap(self.clients, self).delete_floating_ip( diff --git a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py index 6492d42cdb..7bbef8f592 100644 --- a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py @@ -38,12 +38,8 @@ class NovaScenarioTestCase(test.ScenarioTestCase): self.floating_ip = mock.Mock() self.image = mock.Mock() self.context.update( - {"user": {"id": "fake_user_id", "credential": mock.MagicMock()}}) - - def _context_with_networks(self, networks): - retval = {"tenant": {"networks": networks}} - retval.update(self.context) - return retval + {"user": {"id": "fake_user_id", "credential": mock.MagicMock()}, + "tenant": {"id": "fake_tenant"}}) def _context_with_secgroup(self, secgroup): retval = {"user": {"secgroup": secgroup, @@ -140,9 +136,12 @@ class NovaScenarioTestCase(test.ScenarioTestCase): "nova.boot_server") def test__boot_server_with_network_exception(self): + self.context.update({"tenant": {"networks": None}}) + self.clients("nova").servers.create.return_value = self.server + nova_scenario = utils.NovaScenario( - context=self._context_with_networks(None)) + context=self.context) self.assertRaises(TypeError, nova_scenario._boot_server, "image_id", "flavor_id", auto_assign_nic=True) @@ -506,32 +505,121 @@ class NovaScenarioTestCase(test.ScenarioTestCase): def test__associate_floating_ip(self): nova_scenario = utils.NovaScenario(context=self.context) - nova_scenario._associate_floating_ip(self.server, self.floating_ip) - self.server.add_floating_ip.assert_called_once_with(self.floating_ip, - fixed_address=None) + neutronclient = nova_scenario.clients("neutron") + neutronclient.list_ports.return_value = {"ports": [{"id": "p1"}, + {"id": "p2"}]} + + fip_ip = "172.168.0.1" + fip_id = "some" + # case #1- an object from neutronclient + floating_ip = {"floating_ip_address": fip_ip, "id": fip_id} + + nova_scenario._associate_floating_ip(self.server, floating_ip) + + neutronclient.update_floatingip.assert_called_once_with( + fip_id, {"floatingip": {"port_id": "p1"}} + ) + # case #2 - an object from network wrapper + neutronclient.update_floatingip.reset_mock() + floating_ip = {"ip": fip_ip, "id": fip_id} + + nova_scenario._associate_floating_ip(self.server, floating_ip) + + neutronclient.update_floatingip.assert_called_once_with( + fip_id, {"floatingip": {"port_id": "p1"}} + ) + + # these should not be called in both cases + self.assertFalse(neutronclient.list_floatingips.called) + # it is an old behavior. let's check that it was not called + self.assertFalse(self.server.add_floating_ip.called) + + self._test_atomic_action_timer(nova_scenario.atomic_actions(), + "nova.associate_floating_ip", count=2) + + def test__associate_floating_ip_deprecated_behavior(self): + nova_scenario = utils.NovaScenario(context=self.context) + neutronclient = nova_scenario.clients("neutron") + neutronclient.list_ports.return_value = {"ports": [{"id": "p1"}, + {"id": "p2"}]} + + fip_id = "fip1" + fip_ip = "172.168.0.1" + neutronclient.list_floatingips.return_value = { + "floatingips": [ + {"id": fip_id, "floating_ip_address": fip_ip}, + {"id": "fip2", "floating_ip_address": "127.0.0.1"}]} + + nova_scenario._associate_floating_ip(self.server, fip_ip) + + neutronclient.update_floatingip.assert_called_once_with( + fip_id, {"floatingip": {"port_id": "p1"}} + ) + + neutronclient.list_floatingips.assert_called_once_with( + tenant_id="fake_tenant") + + # it is an old behavior. let's check that it was not called + self.assertFalse(self.server.add_floating_ip.called) + self._test_atomic_action_timer(nova_scenario.atomic_actions(), "nova.associate_floating_ip") - def test__associate_floating_ip_with_no_atomic_action(self): - nova_scenario = utils.NovaScenario(context=self.context) - nova_scenario._associate_floating_ip(self.server, self.floating_ip) - self.server.add_floating_ip.assert_called_once_with(self.floating_ip, - fixed_address=None) - def test__dissociate_floating_ip(self): nova_scenario = utils.NovaScenario(context=self.context) - nova_scenario._dissociate_floating_ip(self.server, self.floating_ip) - self.server.remove_floating_ip.assert_called_once_with( - self.floating_ip) + neutronclient = nova_scenario.clients("neutron") + + fip_ip = "172.168.0.1" + fip_id = "some" + # case #1- an object from neutronclient + floating_ip = {"floating_ip_address": fip_ip, "id": fip_id} + + nova_scenario._dissociate_floating_ip(self.server, floating_ip) + + neutronclient.update_floatingip.assert_called_once_with( + fip_id, {"floatingip": {"port_id": None}} + ) + # case #2 - an object from network wrapper + neutronclient.update_floatingip.reset_mock() + floating_ip = {"ip": fip_ip, "id": fip_id} + + nova_scenario._dissociate_floating_ip(self.server, floating_ip) + + neutronclient.update_floatingip.assert_called_once_with( + fip_id, {"floatingip": {"port_id": None}} + ) + + # these should not be called in both cases + self.assertFalse(neutronclient.list_floatingips.called) + # it is an old behavior. let's check that it was not called + self.assertFalse(self.server.add_floating_ip.called) + + self._test_atomic_action_timer(nova_scenario.atomic_actions(), + "nova.dissociate_floating_ip", count=2) + + def test__disassociate_floating_ip_deprecated_behavior(self): + nova_scenario = utils.NovaScenario(context=self.context) + neutronclient = nova_scenario.clients("neutron") + + fip_id = "fip1" + fip_ip = "172.168.0.1" + neutronclient.list_floatingips.return_value = { + "floatingips": [ + {"id": fip_id, "floating_ip_address": fip_ip}, + {"id": "fip2", "floating_ip_address": "127.0.0.1"}]} + + nova_scenario._dissociate_floating_ip(self.server, fip_ip) + + neutronclient.update_floatingip.assert_called_once_with( + fip_id, {"floatingip": {"port_id": None}} + ) + + neutronclient.list_floatingips.assert_called_once_with( + tenant_id="fake_tenant") + self._test_atomic_action_timer(nova_scenario.atomic_actions(), "nova.dissociate_floating_ip") - def test__dissociate_floating_ip_with_no_atomic_action(self): - nova_scenario = utils.NovaScenario(context=self.context) - nova_scenario._dissociate_floating_ip(self.server, self.floating_ip) - self.server.remove_floating_ip.assert_called_once_with( - self.floating_ip) - def test__check_ip_address(self): nova_scenario = utils.NovaScenario(context=self.context) fake_server = fakes.FakeServerManager().create("test_server", diff --git a/tests/unit/plugins/openstack/scenarios/vm/test_utils.py b/tests/unit/plugins/openstack/scenarios/vm/test_utils.py index ee8bd74394..75a8b257da 100644 --- a/tests/unit/plugins/openstack/scenarios/vm/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/vm/test_utils.py @@ -209,8 +209,8 @@ class VMScenarioTestCase(test.ScenarioTestCase): scenario, server = self.get_scenario() netwrap = mock_wrap.return_value - netwrap.create_floating_ip.return_value = { - "id": "foo_id", "ip": "foo_ip"} + fip = {"id": "foo_id", "ip": "foo_ip"} + netwrap.create_floating_ip.return_value = fip scenario._attach_floating_ip( server, floating_network="bar_network") @@ -221,7 +221,7 @@ class VMScenarioTestCase(test.ScenarioTestCase): tenant_id="foo_tenant", fixed_ip="foo_ip") scenario._associate_floating_ip.assert_called_once_with( - server, "foo_ip", fixed_address="foo_ip") + server, fip, fixed_address="foo_ip") @mock.patch(VMTASKS_UTILS + ".network_wrapper.wrap") def test__delete_floating_ip(self, mock_wrap): @@ -231,14 +231,14 @@ class VMScenarioTestCase(test.ScenarioTestCase): scenario.check_ip_address = mock.Mock(return_value=_check_addr) scenario._dissociate_floating_ip = mock.Mock() - scenario._delete_floating_ip( - server, fip={"id": "foo_id", "ip": "foo_ip"}) + fip = {"id": "foo_id", "ip": "foo_ip"} + scenario._delete_floating_ip(server, fip=fip) scenario.check_ip_address.assert_called_once_with( "foo_ip") _check_addr.assert_called_once_with(server) scenario._dissociate_floating_ip.assert_called_once_with( - server, "foo_ip") + server, fip) mock_wrap.assert_called_once_with(scenario.clients, scenario) mock_wrap.return_value.delete_floating_ip.assert_called_once_with( "foo_id", wait=True)