diff --git a/rally-jobs/nova.yaml b/rally-jobs/nova.yaml index 974a802f..2e56dbcf 100755 --- a/rally-jobs/nova.yaml +++ b/rally-jobs/nova.yaml @@ -480,6 +480,28 @@ users: tenants: 2 users_per_tenant: 1 + - + title: NovaServers.boot_server_attach_created_volume_and_extend tests + scenario: + NovaServers.boot_server_attach_created_volume_and_extend: + flavor: + name: {{flavor_name}} + image: + name: {{image_name}} + volume_size: 1 + new_volume_size: 2 + force_delete: false + do_delete: true + boot_server_kwargs: {} + create_volume_kwargs: {} + runner: + constant: + times: 2 + concurrency: 2 + contexts: + users: + tenants: 2 + users_per_tenant: 1 - title: NovaServers.boot_server_from_volume_and_resize tests scenario: diff --git a/rally_openstack/task/scenarios/nova/servers.py b/rally_openstack/task/scenarios/nova/servers.py index 11dc2f15..8bc70b19 100644 --- a/rally_openstack/task/scenarios/nova/servers.py +++ b/rally_openstack/task/scenarios/nova/servers.py @@ -491,6 +491,61 @@ class BootServerAttachCreatedVolumeAndResize(utils.NovaScenario, self._delete_server(server, force=force_delete) +@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.NOVA, + consts.Service.CINDER]) +@validation.add("required_platform", platform="openstack", users=True) +@scenario.configure( + context={"cleanup@openstack": ["cinder", "nova"]}, + name="NovaServers.boot_server_attach_created_volume_and_extend", + platform="openstack") +class BootServerAttachCreatedVolumeAndExtend(utils.NovaScenario, + cinder_utils.CinderBasic): + + def run(self, image, flavor, volume_size, new_volume_size, min_sleep=0, + max_sleep=0, force_delete=False, do_delete=True, + boot_server_kwargs=None, create_volume_kwargs=None): + """Create a VM from image, attach a volume then extend volume + + Simple test to create a VM and attach a volume, then extend the + volume while its running, detach the volume then delete volume + and VM. + + Optional 'min_sleep' and 'max_sleep' parameters allow the scenario + to simulate a pause between attaching a volume and running resize + (of random duration from range [min_sleep, max_sleep]). + + :param image: Glance image name to use for the VM + :param flavor: VM flavor name + :param volume_size: volume size (in GB) + :param new_volume_size: new volume size (in GB) + :param min_sleep: Minimum sleep time in seconds (non-negative) + :param max_sleep: Maximum sleep time in seconds (non-negative) + :param force_delete: True if force_delete should be used + :param do_delete: True if resources needs to be deleted explicitly + else use rally cleanup to remove resources + :param boot_server_kwargs: optional arguments for VM creation + :param create_volume_kwargs: optional arguments for volume creation + """ + boot_server_kwargs = boot_server_kwargs or {} + create_volume_kwargs = create_volume_kwargs or {} + + server = self._boot_server(image, flavor, **boot_server_kwargs) + volume = self.cinder.create_volume(volume_size, **create_volume_kwargs) + + self._attach_volume(server, volume) + self.sleep_between(min_sleep, max_sleep) + self.cinder.extend_volume(volume, new_size=new_volume_size) + + if do_delete: + self._detach_volume(server, volume) + self.cinder.delete_volume(volume) + self._delete_server(server, force=force_delete) + + @validation.add("number", param_name="volume_num", minval=1, integer_only=True) @validation.add("number", param_name="volume_size", minval=1, diff --git a/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-extend.json b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-extend.json new file mode 100644 index 00000000..70843d85 --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-extend.json @@ -0,0 +1,37 @@ +{% set flavor_name = flavor_name or "m1.tiny" %} +{ + "NovaServers.boot_server_attach_created_volume_and_extend": [ + { + "args": { + "flavor": { + "name": "{{flavor_name}}" + }, + "image": { + "name": "^cirros.*-disk$" + }, + "volume_size": 1, + "new_volume_size": 2, + "force_delete": false, + "do_delete": true, + "boot_server_kwargs": {}, + "create_volume_kwargs": {} + }, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 3, + "users_per_tenant": 2 + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-extend.yaml b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-extend.yaml new file mode 100644 index 00000000..fbaad3aa --- /dev/null +++ b/samples/tasks/scenarios/nova/boot-server-attach-created-volume-and-extend.yaml @@ -0,0 +1,26 @@ +{% set flavor_name = flavor_name or "m1.tiny" %} +--- + NovaServers.boot_server_attach_created_volume_and_extend: + - + args: + flavor: + name: "{{flavor_name}}" + image: + name: "^cirros.*-disk$" + volume_size: 1 + new_volume_size: 2 + force_delete: false + do_delete: true + boot_server_kwargs: {} + create_volume_kwargs: {} + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 3 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 diff --git a/tests/unit/task/scenarios/nova/test_servers.py b/tests/unit/task/scenarios/nova/test_servers.py index cabee216..f98b2ee7 100644 --- a/tests/unit/task/scenarios/nova/test_servers.py +++ b/tests/unit/task/scenarios/nova/test_servers.py @@ -567,6 +567,46 @@ class NovaServersTestCase(test.ScenarioTestCase): scenario._delete_server.assert_called_once_with(fake_server, force=False) + @mock.patch("rally_openstack.common.services.storage.block.BlockStorage") + def test_boot_server_attach_created_volume_and_extend( + self, mock_block_storage, do_delete=False): + fake_volume = mock.MagicMock() + fake_server = mock.MagicMock() + flavor = mock.MagicMock() + fake_attachment = mock.MagicMock() + + cinder = mock_block_storage.return_value + cinder.create_volume.return_value = fake_volume + + scenario = servers.BootServerAttachCreatedVolumeAndExtend( + self.context, clients=mock.Mock()) + scenario.generate_random_name = mock.MagicMock(return_value="name") + scenario._boot_server = mock.MagicMock(return_value=fake_server) + scenario._attach_volume = mock.MagicMock(return_value=fake_attachment) + scenario._detach_volume = mock.MagicMock() + scenario._delete_server = mock.MagicMock() + scenario.sleep_between = mock.MagicMock() + + volume_size = 10 + new_volume_size = 20 + scenario.run("img", flavor, volume_size, new_volume_size, + min_sleep=10, max_sleep=20, do_delete=do_delete) + + scenario._boot_server.assert_called_once_with("img", flavor) + cinder.create_volume.assert_called_once_with(volume_size) + scenario._attach_volume.assert_called_once_with(fake_server, + fake_volume) + scenario.sleep_between.assert_called_once_with(10, 20) + cinder.extend_volume.assert_called_once_with( + fake_volume, new_size=new_volume_size) + + if do_delete: + scenario._detach_volume.assert_called_once_with(fake_server, + fake_volume) + cinder.delete_volume.assert_called_once_with(fake_volume) + scenario._delete_server.assert_called_once_with(fake_server, + force=False) + @mock.patch("rally_openstack.common.services.storage.block.BlockStorage") def test_list_attachments(self, mock_block_storage): mock_volume_service = mock_block_storage.return_value