summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Smith <dansmith@redhat.com>2019-10-18 07:37:19 -0700
committerDan Smith <dansmith@redhat.com>2019-10-18 10:49:53 -0700
commite1bb4378db1e5be85a59861c2d473388fa336da8 (patch)
treef7f83828a170ebda30bfc2af3b774b26318c4e4d
parent8744bea0e3ebe5bc4d0d899189bfa0bcdcb0a08f (diff)
downloadpython-novaclient-e1bb4378db1e5be85a59861c2d473388fa336da8.tar.gz
Add images.GlanceManager.find_images() bulk query
This adds a bulk query method to GlanceManager that takes multiple image names or ids and attempts to query glance for all of them in fewer requests than one-per-image. Change-Id: I1c6ce27b65920356df6b7001c581a2922765ce0c
-rw-r--r--novaclient/tests/unit/v2/fakes.py13
-rw-r--r--novaclient/tests/unit/v2/test_shell.py24
-rw-r--r--novaclient/v2/images.py37
-rw-r--r--novaclient/v2/shell.py8
4 files changed, 80 insertions, 2 deletions
diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py
index 6302c116..4cb4025e 100644
--- a/novaclient/tests/unit/v2/fakes.py
+++ b/novaclient/tests/unit/v2/fakes.py
@@ -1141,7 +1141,7 @@ class FakeSessionClient(base_client.SessionClient):
# Images
#
def get_images(self, **kw):
- return (200, {}, {'images': [
+ images = [
{
"id": FAKE_IMAGE_UUID_SNAPSHOT,
"name": "My Server Backup",
@@ -1191,7 +1191,16 @@ class FakeSessionClient(base_client.SessionClient):
"progress": 80,
"links": {},
},
- ]})
+ ]
+
+ if 'id' in kw:
+ requested = kw['id'].replace('in:', '').split(',')
+ images = [i for i in images if i['id'] in requested]
+ if 'names' in kw:
+ requested = kw['names'].replace('in:', '').split(',')
+ images = [i for i in images if i['name'] in requested]
+
+ return (200, {}, {'images': images})
def get_images_555cae93_fb41_4145_9c52_f5b923538a26(self, **kw):
return (200, {}, {'image': self.get_images()[2]['images'][0]})
diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py
index 79118afc..56fa3544 100644
--- a/novaclient/tests/unit/v2/test_shell.py
+++ b/novaclient/tests/unit/v2/test_shell.py
@@ -4676,3 +4676,27 @@ class PollForStatusTestCase(utils.TestCase):
action=action,
show_progress=True,
silent=False)
+
+
+class TestUtilMethods(utils.TestCase):
+ def setUp(self):
+ super(TestUtilMethods, self).setUp()
+ self.shell = self.useFixture(ShellFixture()).shell
+ # NOTE(danms): Get a client that we can use to call things outside of
+ # the shell main
+ self.shell.cs = fakes.FakeClient('2.1')
+
+ def test_find_images(self):
+ """Test find_images() with a name and id."""
+ images = novaclient.v2.shell._find_images(self.shell.cs,
+ [FAKE_UUID_1,
+ 'back1'])
+ self.assertEqual(2, len(images))
+ self.assertEqual(FAKE_UUID_1, images[0].id)
+ self.assertEqual(fakes.FAKE_IMAGE_UUID_BACKUP, images[1].id)
+
+ def test_find_images_missing(self):
+ """Test find_images() where one of the images is not found."""
+ self.assertRaises(exceptions.CommandError,
+ novaclient.v2.shell._find_images,
+ self.shell.cs, [FAKE_UUID_1, 'foo'])
diff --git a/novaclient/v2/images.py b/novaclient/v2/images.py
index e83d9b2f..0aced5c7 100644
--- a/novaclient/v2/images.py
+++ b/novaclient/v2/images.py
@@ -66,6 +66,43 @@ class GlanceManager(base.Manager):
matches[0].append_request_ids(matches.request_ids)
return matches[0]
+ def find_images(self, names_or_ids):
+ """Find multiple images by name or id (user provided input).
+
+ :param names_or_ids: A list of strings to use to find images.
+ :returns: novaclient.v2.images.Image objects for each images found
+ :raises exceptions.NotFound: If one or more images is not found
+ :raises exceptions.ClientException: If the image service returns any
+ unexpected images.
+
+ NOTE: This method always makes two calls to the image service, even if
+ only one image is provided by ID and is returned in the first query.
+ """
+ with self.alternate_service_type(
+ 'image', allowed_types=('image',)):
+ matches = self._list('/v2/images?id=in:%s' % ','.join(
+ names_or_ids), 'images')
+ matches.extend(self._list('/v2/images?names=in:%s' % ','.join(
+ names_or_ids), 'images'))
+ missed = (set(names_or_ids) -
+ set(m.name for m in matches) -
+ set(m.id for m in matches))
+ if missed:
+ msg = _("Unable to find image(s): %(images)s") % {
+ "images": ",".join(missed)}
+ raise exceptions.NotFound(404, msg)
+ for match in matches:
+ match.append_request_ids(matches.request_ids)
+
+ additional = []
+ for i in matches:
+ if i.name not in names_or_ids and i.id not in names_or_ids:
+ additional.append(i)
+ if additional:
+ msg = _('Additional images found in response')
+ raise exceptions.ClientException(500, msg)
+ return matches
+
def list(self):
"""
Get a detailed list of all images.
diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py
index 2125ea99..818006b9 100644
--- a/novaclient/v2/shell.py
+++ b/novaclient/v2/shell.py
@@ -2570,6 +2570,14 @@ def _find_image(cs, image):
raise exceptions.CommandError(six.text_type(e))
+def _find_images(cs, images):
+ """Get images by name or ID."""
+ try:
+ return cs.glance.find_images(images)
+ except (exceptions.NotFound, exceptions.NoUniqueMatch) as e:
+ raise exceptions.CommandError(six.text_type(e))
+
+
def _find_flavor(cs, flavor):
"""Get a flavor by name, ID, or RAM size."""
try: