diff --git a/rally/benchmark/scenarios/nova/servers.py b/rally/benchmark/scenarios/nova/servers.py index 4b51264ea8..a872e4e62b 100644 --- a/rally/benchmark/scenarios/nova/servers.py +++ b/rally/benchmark/scenarios/nova/servers.py @@ -341,6 +341,94 @@ class NovaServers(utils.NovaScenario, self._delete_server(server) + @types.set(image=types.ImageResourceType, + flavor=types.FlavorResourceType) + @validation.image_valid_on_flavor("flavor", "image") + @validation.required_services(consts.Service.NOVA, consts.Service.CINDER) + @validation.required_openstack(admin=True, users=True) + @base.scenario(context={"cleanup": ["nova", "cinder"]}) + def boot_server_from_volume_and_live_migrate(self, image, flavor, + volume_size, + block_migration=False, + disk_over_commit=False, + force_delete=False, **kwargs): + """Boot a server from volume and then migrate it. + + The scenario first creates a volume and a server booted from + the volume on a compute node available in the availability zone and + then migrates the VM to another compute node on the same availability + zone. + + :param image: image to be used to boot an instance + :param flavor: flavor to be used to boot an instance + :param volume_size: volume size (in GB) + :param block_migration: Specifies the migration type + :param disk_over_commit: Specifies whether to allow overcommit + on migrated instance or not + :param force_delete: True if force_delete should be used + :param kwargs: Optional additional arguments for server creation + """ + volume = self._create_volume(volume_size, imageRef=image) + block_device_mapping = {"vda": "%s:::1" % volume.id} + server = self._boot_server(image, flavor, + block_device_mapping=block_device_mapping, + **kwargs) + + new_host = self._find_host_to_migrate(server) + self._live_migrate(server, new_host, + block_migration, disk_over_commit) + + self._delete_server(server, force=force_delete) + + @types.set(image=types.ImageResourceType, + flavor=types.FlavorResourceType) + @validation.image_valid_on_flavor("flavor", "image") + @validation.required_services(consts.Service.NOVA, consts.Service.CINDER) + @validation.required_openstack(admin=True, users=True) + @base.scenario(context={"cleanup": ["cinder", "nova"]}) + def boot_server_attach_created_volume_and_live_migrate( + self, + image, + flavor, + size, + block_migration=False, + disk_over_commit=False, + boot_server_kwargs=None, + create_volume_kwargs=None): + """Create a VM, attach a volume to it amd live migrate. + + Simple test to create a VM and attach a volume, then migrate the VM, + detach the volume and delete volume/VM. + + :param image: Glance image name to use for the VM + :param flavor: VM flavor name + :param size: volume size (in GB) + :param block_migration: Specifies the migration type + :param disk_over_commit: Specifies whether to allow overcommit + on migrated instance or not + :param boot_server_kwargs: optional arguments for VM creation + :param create_volume_kwargs: optional arguments for volume creation + """ + + if boot_server_kwargs is None: + boot_server_kwargs = {} + if create_volume_kwargs is None: + create_volume_kwargs = {} + + server = self._boot_server(image, flavor, boot_server_kwargs) + volume = self._create_volume(size, create_volume_kwargs) + + self._attach_volume(server, volume) + + new_host = self._find_host_to_migrate(server) + self._live_migrate(server, new_host, + block_migration, disk_over_commit) + + self._detach_volume(server, volume) + + self._delete_volume(volume) + self._delete_server(server) + @types.set(image=types.ImageResourceType, flavor=types.FlavorResourceType) @validation.image_valid_on_flavor("flavor", "image") diff --git a/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-live-migrate.json b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-live-migrate.json new file mode 100644 index 0000000000..e426c9e714 --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-live-migrate.json @@ -0,0 +1,27 @@ +{ + "NovaServers.boot_server_attach_created_volume_and_live_migrate": [ + { + "args": { + "size": 10, + "block_migration": false, + "image": { + "name": "^cirros.*uec$" + }, + "flavor": { + "name": "m1.nano" + } + }, + "runner": { + "type": "constant", + "times": 5, + "concurrency": 1 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-live-migrate.yaml b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-live-migrate.yaml new file mode 100644 index 0000000000..dcaf0bc0c6 --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-live-migrate.yaml @@ -0,0 +1,17 @@ +--- +NovaServers.boot_server_attach_created_volume_and_live_migrate: +- args: + flavor: + name: m1.nano + image: + name: "^cirros.*uec$" + size: 10 + block_migration: false + runner: + type: constant + times: 5 + concurrency: 1 + context: + users: + tenants: 2 + users_per_tenant: 2 diff --git a/samples/tasks/scenarios/nova/boot-server-from-volume-and-live-migrate.json b/samples/tasks/scenarios/nova/boot-server-from-volume-and-live-migrate.json new file mode 100644 index 0000000000..b7fd3b8bdb --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-from-volume-and-live-migrate.json @@ -0,0 +1,28 @@ +{ + "NovaServers.boot_server_from_volume_and_live_migrate": [ + { + "args": { + "flavor": { + "name": "m1.nano" + }, + "image": { + "name": "^cirros.*uec$" + }, + "block_migration": false, + "volume_size": 10, + "force_delete": false + }, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 1, + "users_per_tenant": 1 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/nova/boot-server-from-volume-and-live-migrate.yaml b/samples/tasks/scenarios/nova/boot-server-from-volume-and-live-migrate.yaml new file mode 100644 index 0000000000..ad31a367b6 --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-from-volume-and-live-migrate.yaml @@ -0,0 +1,18 @@ +--- +NovaServers.boot_server_from_volume_and_live_migrate: +- args: + flavor: + name: m1.nano + image: + name: "^cirros.*uec$" + block_migration: false + volume_size: 10 + force_delete: false + runner: + type: constant + times: 10 + concurrency: 2 + context: + users: + tenants: 1 + users_per_tenant: 1 diff --git a/tests/unit/benchmark/scenarios/nova/test_servers.py b/tests/unit/benchmark/scenarios/nova/test_servers.py index f3d7dca639..c7c2daf938 100644 --- a/tests/unit/benchmark/scenarios/nova/test_servers.py +++ b/tests/unit/benchmark/scenarios/nova/test_servers.py @@ -351,6 +351,72 @@ class NovaServersTestCase(test.TestCase): False, False) scenario._delete_server.assert_called_once_with(fake_server) + def test_boot_server_from_volume_and_live_migrate(self): + fake_server = mock.MagicMock() + + scenario = servers.NovaServers() + scenario._generate_random_name = mock.MagicMock(return_value="name") + scenario._boot_server = mock.MagicMock(return_value=fake_server) + scenario._find_host_to_migrate = mock.MagicMock( + return_value="host_name") + scenario._live_migrate = mock.MagicMock() + scenario._delete_server = mock.MagicMock() + + fake_volume = fakes.FakeVolumeManager().create() + fake_volume.id = "volume_id" + scenario._create_volume = mock.MagicMock(return_value=fake_volume) + + scenario.boot_server_from_volume_and_live_migrate("img", 0, 5, + fakearg="f") + + scenario._create_volume.assert_called_once_with(5, imageRef="img") + + scenario._boot_server.assert_called_once_with( + "img", 0, + block_device_mapping={"vda": "volume_id:::1"}, + fakearg="f") + + scenario._find_host_to_migrate.assert_called_once_with(fake_server) + + scenario._live_migrate.assert_called_once_with(fake_server, + "host_name", + False, False) + scenario._delete_server.assert_called_once_with(fake_server, + force=False) + + def test_boot_server_attach_created_volume_and_live_migrate(self): + fake_volume = mock.MagicMock() + fake_server = mock.MagicMock() + + scenario = servers.NovaServers() + + scenario._attach_volume = mock.MagicMock() + scenario._detach_volume = mock.MagicMock() + + scenario._find_host_to_migrate = mock.MagicMock( + return_value="host_name") + scenario._live_migrate = mock.MagicMock() + + scenario._boot_server = mock.MagicMock(return_value=fake_server) + scenario._delete_server = mock.MagicMock() + scenario._create_volume = mock.MagicMock(return_value=fake_volume) + scenario._delete_volume = mock.MagicMock() + + scenario.boot_server_attach_created_volume_and_live_migrate( + "img", + 0, + 5) + scenario._attach_volume.assert_called_once_with(fake_server, + fake_volume) + scenario._detach_volume.assert_called_once_with(fake_server, + fake_volume) + scenario._live_migrate.assert_called_once_with(fake_server, + "host_name", + False, False) + + scenario._delete_volume.assert_called_once_with(fake_volume) + scenario._delete_server.assert_called_once_with(fake_server) + def _test_boot_and_migrate_server(self, confirm=False): fake_server = mock.MagicMock()