diff --git a/rally/plugins/openstack/context/vm/custom_image.py b/rally/plugins/openstack/context/vm/custom_image.py index 13f87b7d..c7c103eb 100644 --- a/rally/plugins/openstack/context/vm/custom_image.py +++ b/rally/plugins/openstack/context/vm/custom_image.py @@ -26,6 +26,7 @@ from rally import osclients from rally.plugins.openstack.scenarios.nova import utils as nova_utils from rally.plugins.openstack.scenarios.vm import vmtasks from rally.plugins.openstack import types +from rally.plugins.openstack.wrappers import glance as glance_wrapper from rally.task import context LOG = logging.getLogger(__name__) @@ -125,7 +126,6 @@ class BaseCustomImageGenerator(context.Context): nics = [{"net-id": tenant["networks"][0]["id"]}] custom_image = self.create_one_image(user, nics=nics) - self.make_image_public(custom_image) for tenant in self.context["tenants"].values(): tenant["custom_image"] = custom_image @@ -146,6 +146,7 @@ class BaseCustomImageGenerator(context.Context): """Create one image for the user.""" clients = osclients.Clients(user["credential"]) + admin_clients = osclients.Clients(self.context["admin"]["credential"]) image_id = types.GlanceImage.transform( clients=clients, resource_config=self.config["image"]) @@ -154,6 +155,8 @@ class BaseCustomImageGenerator(context.Context): vm_scenario = vmtasks.VMTasks(self.context, clients=clients) + glance_wrap = glance_wrapper.wrap(admin_clients.glance, self) + server, fip = vm_scenario._boot_server_with_fip( image=image_id, flavor=flavor_id, floating_network=self.config.get("floating_network"), @@ -170,21 +173,19 @@ class BaseCustomImageGenerator(context.Context): vm_scenario._stop_server(server) LOG.debug("Creating snapshot for %r", server) - custom_image = vm_scenario._create_image(server).to_dict() + custom_image = vm_scenario._create_image(server) + glance_wrap.set_visibility(custom_image) finally: vm_scenario._delete_server_with_fip(server, fip) + if hasattr(custom_image, "to_dict"): + # NOTE(stpierre): Glance v1 images are objects that can be + # converted to dicts; Glance v2 images are already + # dict-like + custom_image = custom_image.to_dict() + return custom_image - def make_image_public(self, custom_image): - """Make the image available publicly.""" - - admin_clients = osclients.Clients(self.context["admin"]["credential"]) - - LOG.debug("Making image %r public", custom_image["id"]) - admin_clients.glance().images.get( - custom_image["id"]).update(is_public=True) - @logging.log_task_wrapper(LOG.info, _("Exit context: `custom_image`")) def cleanup(self): """Delete created custom image(s).""" diff --git a/rally/plugins/openstack/wrappers/glance.py b/rally/plugins/openstack/wrappers/glance.py index 26eb0d61..f926ba30 100644 --- a/rally/plugins/openstack/wrappers/glance.py +++ b/rally/plugins/openstack/wrappers/glance.py @@ -78,6 +78,10 @@ class GlanceWrapper(object): Accepts all Glance v2 parameters. """ + @abc.abstractmethod + def set_visibility(self, image, visibility="public"): + """Set an existing image to public or private.""" + @abc.abstractmethod def list_images(self, **filters): """List images. @@ -123,6 +127,9 @@ class GlanceV1Wrapper(GlanceWrapper): return image + def set_visibility(self, image, visibility="public"): + self.client.images.update(image.id, is_public=(visibility == "public")) + def list_images(self, **filters): kwargs = {"filters": filters} if "owner" in filters: @@ -187,6 +194,9 @@ class GlanceV2Wrapper(GlanceWrapper): check_interval=CONF.benchmark. glance_image_create_poll_interval) + def set_visibility(self, image, visibility="public"): + self.client.images.update(image.id, visibility=visibility) + def list_images(self, **filters): return self.client.images.list(filters=filters) diff --git a/tests/unit/plugins/openstack/context/vm/test_custom_image.py b/tests/unit/plugins/openstack/context/vm/test_custom_image.py index ad66575c..28a580da 100644 --- a/tests/unit/plugins/openstack/context/vm/test_custom_image.py +++ b/tests/unit/plugins/openstack/context/vm/test_custom_image.py @@ -66,9 +66,10 @@ class BaseCustomImageContextVMTestCase(test.TestCase): @mock.patch("%s.osclients.Clients" % BASE) @mock.patch("%s.types.GlanceImage.transform" % BASE, return_value="image") @mock.patch("%s.types.Flavor.transform" % BASE, return_value="flavor") + @mock.patch("rally.plugins.openstack.wrappers.glance.wrap") def test_create_one_image( - self, mock_flavor_transform, mock_glance_image_transform, - mock_clients, mock_vm_tasks): + self, mock_glance_wrap, mock_flavor_transform, + mock_glance_image_transform, mock_clients, mock_vm_tasks): ip = {"ip": "foo_ip", "id": "foo_id", "is_floating": True} fake_server = mock.Mock() @@ -93,6 +94,9 @@ class BaseCustomImageContextVMTestCase(test.TestCase): custom_image = generator_ctx.create_one_image(user, foo_arg="foo_value") + mock_glance_wrap.assert_called_once_with( + mock_clients.return_value.glance, generator_ctx) + mock_flavor_transform.assert_called_once_with( clients=mock_clients.return_value, resource_config={"name": "flavor"}) @@ -114,6 +118,8 @@ class BaseCustomImageContextVMTestCase(test.TestCase): fake_server, ip, user) mock_vm_scenario._create_image.assert_called_once_with(fake_server) + mock_glance_wrap.return_value.set_visibility.assert_called_once_with( + fake_image) mock_vm_scenario._delete_server_with_fip.assert_called_once_with( fake_server, ip) @@ -126,8 +132,9 @@ class BaseCustomImageContextVMTestCase(test.TestCase): return_value="image") @mock.patch("%s.types.Flavor.transform" % BASE, return_value="flavor") + @mock.patch("rally.plugins.openstack.wrappers.glance.wrap") def test_create_one_image_cleanup( - self, mock_flavor_transform, + self, mock_glance_wrap, mock_flavor_transform, mock_glance_image_transform, mock_clients, mock_vm_tasks): ip = {"ip": "foo_ip", "id": "foo_id", "is_floating": True} @@ -163,24 +170,6 @@ class BaseCustomImageContextVMTestCase(test.TestCase): mock_vm_scenario._delete_server_with_fip.assert_called_once_with( fake_server, ip) - @mock.patch("%s.osclients.Clients" % BASE) - def test_make_image_public(self, mock_clients): - fc = mock.MagicMock() - mock_clients.return_value = fc - - generator_ctx = TestImageGenerator(self.context) - custom_image = {"id": "image"} - - generator_ctx.make_image_public(custom_image=custom_image) - - mock_clients.assert_called_once_with( - self.context["admin"]["credential"]) - - fc.glance.assert_called_once_with() - fc.glance.return_value.images.get.assert_called_once_with("image") - (fc.glance.return_value.images.get. - return_value.update.assert_called_once_with(is_public=True)) - @mock.patch("%s.nova_utils.NovaScenario" % BASE) @mock.patch("%s.osclients.Clients" % BASE) def test_delete_one_image(self, mock_clients, mock_nova_scenario): @@ -217,8 +206,6 @@ class BaseCustomImageContextVMTestCase(test.TestCase): generator_ctx.create_one_image.assert_called_once_with( self.context["users"][0], nics=[{"net-id": "network_id"}]) - generator_ctx.make_image_public.assert_called_once_with( - "custom_image") def test_cleanup_admin(self): tenant = self.context["tenants"]["tenant_id0"] diff --git a/tests/unit/plugins/openstack/wrappers/test_glance.py b/tests/unit/plugins/openstack/wrappers/test_glance.py index 39221863..b2f5613f 100644 --- a/tests/unit/plugins/openstack/wrappers/test_glance.py +++ b/tests/unit/plugins/openstack/wrappers/test_glance.py @@ -115,6 +115,19 @@ class GlanceV1WrapperTestCase(test.ScenarioTestCase): self.assertEqual(self.mock_wait_for_status.mock.return_value, return_image) + @ddt.data({"expected": True}, + {"visibility": "public", "expected": True}, + {"visibility": "private", "expected": False}) + @ddt.unpack + def test_set_visibility(self, visibility=None, expected=None): + image = mock.Mock() + if visibility is None: + self.wrapped_client.set_visibility(image) + else: + self.wrapped_client.set_visibility(image, visibility=visibility) + self.client().images.update.assert_called_once_with( + image.id, is_public=expected) + @ddt.data({}, {"fakearg": "fake"}) def test_list_images_basic(self, filters): self.assertEqual(self.wrapped_client.list_images(**filters), @@ -233,6 +246,20 @@ class GlanceV2WrapperTestCase(test.ScenarioTestCase): timeout=mock.ANY)]) self.assertEqual(uploaded_image, return_image) + @ddt.data({}, + {"visibility": "public"}, + {"visibility": "private"}) + @ddt.unpack + def test_set_visibility(self, visibility=None): + image = mock.Mock() + if visibility is None: + self.wrapped_client.set_visibility(image) + visibility = "public" + else: + self.wrapped_client.set_visibility(image, visibility=visibility) + self.client().images.update.assert_called_once_with( + image.id, visibility=visibility) + @ddt.data({}, {"fakearg": "fake"}) def test_list_images(self, filters): self.assertEqual(self.wrapped_client.list_images(**filters),