From 013792c458d9c85eebdad02b51963a855615de5c Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 23 Dec 2015 15:50:40 -0600 Subject: [PATCH] Images context: accept arbitrary image create args This deprecates the min_ram and min_disk arguments in favor of image_args, which lets the user specify any image creation keyword arguments they want. This provides a workaround for the bug that prevents images created in the images context from actually being used by letting the user specify that the image should be public, and thus can be used by scenarios running other tenants than the admin user's tenant. Also adds a gate job to ensure that images created by the images context can be used in a scenario. Partial-Bug: 1503744 Change-Id: I200821f4850073990878c38f04959ecf1ff9c522 --- rally-jobs/cinder.yaml | 27 +++++++ .../openstack/context/glance/images.py | 18 ++++- .../openstack/context/glance/test_images.py | 72 ++++++++++++------- 3 files changed, 91 insertions(+), 26 deletions(-) diff --git a/rally-jobs/cinder.yaml b/rally-jobs/cinder.yaml index 0a207d4348..2d31c5fb65 100644 --- a/rally-jobs/cinder.yaml +++ b/rally-jobs/cinder.yaml @@ -100,6 +100,33 @@ failure_rate: max: 0 + - + args: + size: 1 + image: + name: "image-context-test" + runner: + type: "constant" + times: 2 + concurrency: 2 + context: + users: + tenants: 1 + users_per_tenant: 2 + roles: + - admin + images: + image_url: "~/.rally/extra/fake-image.img" + image_type: "qcow2" + image_container: "bare" + images_per_tenant: 1 + image_name: "image-context-test" + image_args: + is_public: True + sla: + failure_rate: + max: 0 + CinderVolumes.create_and_update_volume: - args: diff --git a/rally/plugins/openstack/context/glance/images.py b/rally/plugins/openstack/context/glance/images.py index b42ee9bf0e..d46c15ba66 100644 --- a/rally/plugins/openstack/context/glance/images.py +++ b/rally/plugins/openstack/context/glance/images.py @@ -57,6 +57,10 @@ class ImageGenerator(context.Context): "type": "integer", "minimum": 1 }, + "image_args": { + "type": "object", + "additionalProperties": True + } }, "required": ["image_url", "image_type", "image_container", "images_per_tenant"], @@ -76,6 +80,17 @@ class ImageGenerator(context.Context): current_images = [] glance_scenario = glance_utils.GlanceScenario( {"user": user, "task": self.context["task"]}) + + kwargs = self.config.get("image_args", {}) + if self.config.get("min_ram") is not None: + LOG.warning("The 'min_ram' argument is deprecated; specify " + "arbitrary arguments with 'image_args' instead") + kwargs["min_ram"] = self.config["min_ram"] + if self.config.get("min_disk") is not None: + LOG.warning("The 'min_disk' argument is deprecated; specify " + "arbitrary arguments with 'image_args' instead") + kwargs["min_disk"] = self.config["min_disk"] + for i in range(images_per_tenant): if image_name and i > 0: cur_name = image_name + str(i) @@ -86,8 +101,7 @@ class ImageGenerator(context.Context): image = glance_scenario._create_image( image_container, image_url, image_type, - name=cur_name, min_ram=self.config.get("min_ram", 0), - min_disk=self.config.get("min_disk", 0)) + name=cur_name, **kwargs) current_images.append(image.id) self.context["tenants"][tenant_id]["images"] = current_images diff --git a/tests/unit/plugins/openstack/context/glance/test_images.py b/tests/unit/plugins/openstack/context/glance/test_images.py index 6cc28e2da3..5df9523756 100644 --- a/tests/unit/plugins/openstack/context/glance/test_images.py +++ b/tests/unit/plugins/openstack/context/glance/test_images.py @@ -15,17 +15,18 @@ import copy +import ddt import jsonschema import mock from rally.plugins.openstack.context.glance import images -from tests.unit import fakes from tests.unit import test CTX = "rally.plugins.openstack.context.glance" SCN = "rally.plugins.openstack.scenarios.glance" +@ddt.ddt class ImageGeneratorTestCase(test.ScenarioTestCase): def _gen_tenants(self, count): @@ -44,54 +45,77 @@ class ImageGeneratorTestCase(test.ScenarioTestCase): self.assertRaises(jsonschema.ValidationError, images.ImageGenerator.validate, self.context) - @mock.patch("%s.utils.GlanceScenario._create_image" % SCN, - return_value=fakes.FakeImage(id="uuid")) - def test_setup(self, mock_glance_scenario__create_image): - - tenants_count = 2 - users_per_tenant = 5 - images_per_tenant = 5 - - tenants = self._gen_tenants(tenants_count) + @ddt.data( + {}, + {"min_disk": 1, "min_ram": 2}, + {"image_name": "foo"}, + {"tenants": 3, "users_per_tenant": 2, "images_per_tenant": 5}, + {"image_args": {"min_disk": 1, "min_ram": 2, "visibility": "public"}}) + @ddt.unpack + @mock.patch("%s.utils.GlanceScenario._create_image" % SCN) + def test_setup(self, mock_glance_scenario__create_image, + image_container="bare", image_type="qcow2", + image_url="http://example.com/fake/url", + tenants=1, users_per_tenant=1, images_per_tenant=1, + image_name=None, min_ram=None, min_disk=None, + image_args=None): + tenant_data = self._gen_tenants(tenants) users = [] - for id_ in tenants: + for tenant_id in tenant_data: for i in range(users_per_tenant): - users.append({"id": i, "tenant_id": id_, + users.append({"id": i, "tenant_id": tenant_id, "credential": mock.MagicMock()}) self.context.update({ "config": { "users": { - "tenants": tenants_count, + "tenants": tenants, "users_per_tenant": users_per_tenant, "concurrent": 10, }, "images": { - "image_url": "mock_url", - "image_type": "qcow2", - "image_container": "bare", + "image_url": image_url, + "image_type": image_type, + "image_container": image_container, "images_per_tenant": images_per_tenant, - "image_name": "some_name", - "min_ram": 128, - "min_disk": 1, } }, "admin": { "credential": mock.MagicMock() }, "users": users, - "tenants": tenants + "tenants": tenant_data }) + expected_image_args = {} + if image_args is not None: + self.context["config"]["images"]["image_args"] = image_args + expected_image_args.update(image_args) + if image_name is not None: + self.context["config"]["images"]["image_name"] = image_name + if min_ram is not None: + self.context["config"]["images"]["min_ram"] = min_ram + expected_image_args["min_ram"] = min_ram + if min_disk is not None: + self.context["config"]["images"]["min_disk"] = min_disk + expected_image_args["min_disk"] = min_disk + new_context = copy.deepcopy(self.context) - for id_ in new_context["tenants"].keys(): - new_context["tenants"][id_].setdefault("images", []) - for j in range(images_per_tenant): - new_context["tenants"][id_]["images"].append("uuid") + for tenant_id in new_context["tenants"].keys(): + new_context["tenants"][tenant_id]["images"] = [ + mock_glance_scenario__create_image.return_value.id + ] * images_per_tenant images_ctx = images.ImageGenerator(self.context) images_ctx.setup() self.assertEqual(new_context, self.context) + mock_glance_scenario__create_image.assert_has_calls( + [mock.call(image_container, image_url, image_type, + name=mock.ANY, + **expected_image_args)] * tenants * images_per_tenant) + if image_name: + for args in mock_glance_scenario__create_image.call_args_list: + self.assertTrue(args[1]["name"].startswith(image_name)) @mock.patch("%s.images.resource_manager.cleanup" % CTX) def test_cleanup(self, mock_cleanup):