diff --git a/rally-jobs/nova.yaml b/rally-jobs/nova.yaml index dc907633e4..177c17e99d 100755 --- a/rally-jobs/nova.yaml +++ b/rally-jobs/nova.yaml @@ -1393,3 +1393,27 @@ sla: failure_rate: max: 0 + + NovaAggregates.create_aggregate_add_host_and_boot_server: + - + args: + image: + name: {{image_name}} + metadata: + test_metadata: "true" + availability_zone: "nova" + ram: 512 + vcpus: 1 + disk: 1 + boot_server_kwargs: {} + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 3 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 diff --git a/rally/plugins/openstack/scenarios/nova/aggregates.py b/rally/plugins/openstack/scenarios/nova/aggregates.py index 1ece718380..8e27841687 100644 --- a/rally/plugins/openstack/scenarios/nova/aggregates.py +++ b/rally/plugins/openstack/scenarios/nova/aggregates.py @@ -14,8 +14,10 @@ # under the License. from rally import consts +from rally import exceptions from rally.plugins.openstack import scenario from rally.plugins.openstack.scenarios.nova import utils +from rally.task import types from rally.task import validation @@ -98,6 +100,7 @@ class CreateAggregateAddAndRemoveHost(utils.NovaScenario): Measure "nova aggregate-add-host" and "nova aggregate-remove-host" command performance. + :param availability_zone: The availability zone of the aggregate """ aggregate = self._create_aggregate(availability_zone) hosts = self._list_hosts(zone=None) @@ -120,3 +123,53 @@ class CreateAndGetAggregateDetails(utils.NovaScenario): """ aggregate = self._create_aggregate(availability_zone) self._get_aggregate_details(aggregate) + + +@types.convert(image={"type": "glance_image"}) +@validation.required_services(consts.Service.NOVA) +@validation.required_openstack(admin=True, users=True) +@scenario.configure(context={"admin_cleanup": ["nova"], "cleanup": ["nova"]}, + name="NovaAggregates." + "create_aggregate_add_host_and_boot_server") +class CreateAggregateAddHostAndBootServer(utils.NovaScenario): + """Scenario to verify an aggregate.""" + + def run(self, image, metadata, availability_zone=None, ram=512, vcpus=1, + disk=1, boot_server_kwargs=None): + """Scenario to create and verify an aggregate + + This scenario creates an aggregate, adds a compute host and metadata + to the aggregate, adds the same metadata to the flavor and creates an + instance. Verifies that instance host is one of the hosts in the + aggregate. + + :param image: The image ID to boot from + :param metadata: The metadata to be set as flavor extra specs + :param availability_zone: The availability zone of the aggregate + :param ram: Memory in MB for the flavor + :param vcpus: Number of VCPUs for the flavor + :param disk: Size of local disk in GB + :param boot_server_kwargs: Optional additional arguments to verify host + aggregates + :raises RallyException: if instance and aggregate hosts do not match + """ + + boot_server_kwargs = boot_server_kwargs or {} + + aggregate = self._create_aggregate(availability_zone) + hosts = self._list_hypervisors() + host_name = hosts[0].hypervisor_hostname + self._aggregate_set_metadata(aggregate, metadata) + self._aggregate_add_host(aggregate, host_name) + flavor = self._create_flavor(ram, vcpus, disk) + flavor.set_keys(metadata) + + server = self._boot_server(image, flavor.id, **boot_server_kwargs) + # NOTE: we need to get server object by admin user to obtain + # "hypervisor_hostname" attribute + server = self.admin_clients("nova").servers.get(server.id) + instance_hostname = getattr(server, + "OS-EXT-SRV-ATTR:hypervisor_hostname") + if instance_hostname != host_name: + raise exceptions.RallyException("Instance host and aggregate " + "host are different") diff --git a/rally/plugins/openstack/scenarios/nova/utils.py b/rally/plugins/openstack/scenarios/nova/utils.py index 8701603ce0..621c19be92 100755 --- a/rally/plugins/openstack/scenarios/nova/utils.py +++ b/rally/plugins/openstack/scenarios/nova/utils.py @@ -1266,3 +1266,14 @@ class NovaScenario(scenario.OpenStackScenario): """ return self.admin_clients("nova").aggregates.remove_host(aggregate, host) + + @atomic.action_timer("nova.aggregate_set_metadata") + def _aggregate_set_metadata(self, aggregate, metadata): + """Set metadata to an aggregate + + :param aggregate: The aggregate to set metadata to + :param metadata: The metadata to be set + :return: The aggregate that has the set metadata + """ + return self.admin_clients("nova").aggregates.set_metadata(aggregate, + metadata) diff --git a/samples/tasks/scenarios/nova/create-aggregate-add-host-and-boot-server.json b/samples/tasks/scenarios/nova/create-aggregate-add-host-and-boot-server.json new file mode 100644 index 0000000000..0767838594 --- /dev/null +++ b/samples/tasks/scenarios/nova/create-aggregate-add-host-and-boot-server.json @@ -0,0 +1,35 @@ +{ + "NovaAggregates.create_aggregate_add_host_and_boot_server": [ + { + "args": { + "image": { + "name": "^cirros.*uec$" + }, + "metadata": { + "test_metadata": "true" + }, + "availability_zone": "nova", + "ram": 512, + "vcpus": 1, + "disk": 1, + "boot_server_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/create-aggregate-add-host-and-boot-server.yaml b/samples/tasks/scenarios/nova/create-aggregate-add-host-and-boot-server.yaml new file mode 100644 index 0000000000..fbe721d000 --- /dev/null +++ b/samples/tasks/scenarios/nova/create-aggregate-add-host-and-boot-server.yaml @@ -0,0 +1,24 @@ +--- + NovaAggregates.create_aggregate_add_host_and_boot_server: + - + args: + image: + name: "^cirros.*uec$" + metadata: + test_metadata: "true" + availability_zone: "nova" + ram: 512 + vcpus: 1 + disk: 1 + boot_server_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/plugins/openstack/scenarios/nova/test_aggregates.py b/tests/unit/plugins/openstack/scenarios/nova/test_aggregates.py index b173a3d76e..c36f593695 100644 --- a/tests/unit/plugins/openstack/scenarios/nova/test_aggregates.py +++ b/tests/unit/plugins/openstack/scenarios/nova/test_aggregates.py @@ -15,11 +15,12 @@ import mock +from rally import exceptions from rally.plugins.openstack.scenarios.nova import aggregates from tests.unit import test -class NovaAggregatesTestCase(test.TestCase): +class NovaAggregatesTestCase(test.ScenarioTestCase): def test_list_aggregates(self): scenario = aggregates.ListAggregates() @@ -80,3 +81,66 @@ class NovaAggregatesTestCase(test.TestCase): scenario._create_aggregate.assert_called_once_with("nova") aggregate = scenario._create_aggregate.return_value scenario._get_aggregate_details.assert_called_once_with(aggregate) + + def test_create_aggregate_add_host_and_boot_server(self): + fake_aggregate = mock.Mock() + fake_hosts = [mock.Mock(hypervisor_hostname="fake_host_name")] + fake_flavor = mock.MagicMock(id="flavor-id-0", ram=512, disk=1, + vcpus=1) + fake_metadata = {"test_metadata": "true"} + fake_server = mock.MagicMock(id="server-id-0") + setattr(fake_server, "OS-EXT-SRV-ATTR:hypervisor_hostname", + "fake_host_name") + fake_aggregate_kwargs = {"fake_arg1": "f"} + + scenario = aggregates.CreateAggregateAddHostAndBootServer() + scenario._create_aggregate = mock.MagicMock( + return_value=fake_aggregate) + scenario._list_hypervisors = mock.MagicMock(return_value=fake_hosts) + scenario._aggregate_add_host = mock.MagicMock() + scenario._aggregate_set_metadata = mock.MagicMock() + scenario._create_flavor = mock.MagicMock(return_value=fake_flavor) + scenario._boot_server = mock.MagicMock(return_value=fake_server) + self.admin_clients("nova").servers.get.return_value = fake_server + + scenario.run("img", fake_metadata, availability_zone="nova", + boot_server_kwargs=fake_aggregate_kwargs) + scenario._create_aggregate.assert_called_once_with("nova") + scenario._list_hypervisors.assert_called_once_with() + scenario._aggregate_set_metadata.assert_called_once_with( + fake_aggregate, fake_metadata) + scenario._aggregate_add_host(fake_aggregate, "fake_host_name") + scenario._create_flavor.assert_called_once_with(512, 1, 1) + fake_flavor.set_keys.assert_called_once_with(fake_metadata) + scenario._boot_server.assert_called_once_with("img", "flavor-id-0", + **fake_aggregate_kwargs) + self.admin_clients("nova").servers.get.assert_called_once_with( + "server-id-0") + + self.assertEqual(getattr( + fake_server, "OS-EXT-SRV-ATTR:hypervisor_hostname"), + "fake_host_name") + + def test_create_aggregate_add_host_and_boot_server_failure(self): + fake_aggregate = mock.Mock() + fake_hosts = [mock.Mock(hypervisor_hostname="fake_host_name")] + fake_flavor = mock.MagicMock(id="flavor-id-0", ram=512, disk=1, + vcpus=1) + fake_metadata = {"test_metadata": "true"} + fake_server = mock.MagicMock(id="server-id-0") + setattr(fake_server, "OS-EXT-SRV-ATTR:hypervisor_hostname", + "wrong_host_name") + fake_boot_server_kwargs = {"fake_arg1": "f"} + + scenario = aggregates.CreateAggregateAddHostAndBootServer() + scenario._create_aggregate = mock.MagicMock( + return_value=fake_aggregate) + scenario._list_hypervisors = mock.MagicMock(return_value=fake_hosts) + scenario._aggregate_add_host = mock.MagicMock() + scenario._aggregate_set_metadata = mock.MagicMock() + scenario._create_flavor = mock.MagicMock(return_value=fake_flavor) + scenario._boot_server = mock.MagicMock(return_value=fake_server) + self.admin_clients("nova").servers.get.return_value = fake_server + + self.assertRaises(exceptions.RallyException, scenario.run, "img", + fake_metadata, "nova", fake_boot_server_kwargs) diff --git a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py index 67d2ac8a14..ce7eec7273 100755 --- a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py @@ -1200,3 +1200,17 @@ class NovaScenarioTestCase(test.ScenarioTestCase): "fake_hostname") self._test_atomic_action_timer(nova_scenario.atomic_actions(), "nova.uptime_hypervisor") + + def test_aggregate_set_metadata(self): + nova_scenario = utils.NovaScenario(context=self.context) + fake_metadata = {"test_metadata": "true"} + result = nova_scenario._aggregate_set_metadata("fake_aggregate", + fake_metadata) + self.assertEqual( + self.admin_clients("nova").aggregates.set_metadata.return_value, + result) + self.admin_clients( + "nova").aggregates.set_metadata.assert_called_once_with( + "fake_aggregate", fake_metadata) + self._test_atomic_action_timer(nova_scenario.atomic_actions(), + "nova.aggregate_set_metadata") \ No newline at end of file