Sync with trunk.

This commit is contained in:
Donal Lafferty 2011-05-26 13:53:48 +01:00
commit c54e7bf264
6 changed files with 217 additions and 21 deletions

View File

@ -63,6 +63,57 @@ Using Glance's Client, we can do this using the following code
print c.get_images_detailed()
Filtering Images Returned via ``get_images()`` and ``get_images_detailed()``
----------------------------------------------------------------------------
Both the ``get_images()`` and ``get_images_detailed()`` methods take query
parameters that serve to filter the returned list of images.
When calling, simply pass an optional dictionary to the method containing
the filters by which you wish to limit results, with the filter keys being one
or more of the below:
* ``name: NAME``
Filters images having a ``name`` attribute matching ``NAME``.
* ``container_format: FORMAT``
Filters images having a ``container_format`` attribute matching ``FORMAT``
For more information, see :doc:`About Disk and Container Formats <formats>`
* ``disk_format: FORMAT``
Filters images having a ``disk_format`` attribute matching ``FORMAT``
For more information, see :doc:`About Disk and Container Formats <formats>`
* ``status: STATUS``
Filters images having a ``status`` attribute matching ``STATUS``
For more information, see :doc:`About Image Statuses <statuses>`
* ``size_min: BYTES``
Filters images having a ``size`` attribute greater than or equal to ``BYTES``
* ``size_max: BYTES``
Filters images having a ``size`` attribute less than or equal to ``BYTES``
Here's a quick example that will return all images less than or equal to 5G
in size and in the `saving` status.
.. code-block:: python
from glance.client import Client
c = Client("glance.example.com", 9292)
filters = {'status': 'saving', 'size_max': (5 * 1024 * 1024 * 1024)}
print c.get_images_detailed(filters)
Requesting Detailed Metadata on a Specific Image
------------------------------------------------
@ -86,7 +137,6 @@ first public image returned, we can use the following code
print c.get_image_meta("http://glance.example.com/images/1")
Retrieving a Virtual Machine Image
----------------------------------

View File

@ -92,6 +92,42 @@ JSON-encoded mapping in the following format::
The `checksum` field is an MD5 checksum of the image file data
Filtering Images Returned via ``GET /images`` and ``GET /images/detail``
------------------------------------------------------------------------
Both the ``GET /images`` and ``GET /images/detail`` requests take query
parameters that serve to filter the returned list of images. The following
list details these query parameters.
* ``name=NAME``
Filters images having a ``name`` attribute matching ``NAME``.
* ``container_format=FORMAT``
Filters images having a ``container_format`` attribute matching ``FORMAT``
For more information, see :doc:`About Disk and Container Formats <formats>`
* ``disk_format=FORMAT``
Filters images having a ``disk_format`` attribute matching ``FORMAT``
For more information, see :doc:`About Disk and Container Formats <formats>`
* ``status=STATUS``
Filters images having a ``status`` attribute matching ``STATUS``
For more information, see :doc:`About Image Statuses <statuses>`
* ``size_min=BYTES``
Filters images having a ``size`` attribute greater than or equal to ``BYTES``
* ``size_max=BYTES``
Filters images having a ``size`` attribute less than or equal to ``BYTES``
Requesting Detailed Metadata on a Specific Image
------------------------------------------------

View File

@ -46,6 +46,43 @@ The following is a brief description of the Glance API::
PUT /images/<ID> Update metadata about an existing image
DELETE /images/<ID> Remove an image's metadata from the registry
Filtering Images Returned via ``GET /images`` and ``GET /images/detail``
------------------------------------------------------------------------
Both the ``GET /images`` and ``GET /images/detail`` requests take query
parameters that serve to filter the returned list of images. The following
list details these query parameters.
* ``name=NAME``
Filters images having a ``name`` attribute matching ``NAME``.
* ``container_format=FORMAT``
Filters images having a ``container_format`` attribute matching ``FORMAT``
For more information, see :doc:`About Disk and Container Formats <formats>`
* ``disk_format=FORMAT``
Filters images having a ``disk_format`` attribute matching ``FORMAT``
For more information, see :doc:`About Disk and Container Formats <formats>`
* ``status=STATUS``
Filters images having a ``status`` attribute matching ``STATUS``
For more information, see :doc:`About Image Statuses <statuses>`
* ``size_min=BYTES``
Filters images having a ``size`` attribute greater than or equal to ``BYTES``
* ``size_max=BYTES``
Filters images having a ``size`` attribute less than or equal to ``BYTES``
``POST /images``
----------------

View File

@ -25,6 +25,7 @@ import logging
import urlparse
import socket
import sys
import urllib
from glance import utils
from glance.common import exception
@ -94,7 +95,8 @@ class BaseClient(object):
else:
return httplib.HTTPConnection
def do_request(self, method, action, body=None, headers=None):
def do_request(self, method, action, body=None, headers=None,
params=None):
"""
Connects to the server and issues a request. Handles converting
any returned HTTP error status codes to OpenStack/Glance exceptions
@ -105,6 +107,8 @@ class BaseClient(object):
:param action: part of URL after root netloc
:param body: string of data to send, or None (default)
:param headers: mapping of key/value pairs to add as headers
:param params: dictionary of key/value pairs to add to append
to action
:note
@ -115,6 +119,9 @@ class BaseClient(object):
objects to be transferred efficiently without buffering the entire
body in memory.
"""
if type(params) is dict:
action += '?' + urllib.urlencode(params)
try:
connection_type = self.get_connection_type()
headers = headers or {}
@ -197,24 +204,24 @@ class V1Client(BaseClient):
self.doc_root = doc_root
super(Client, self).__init__(host, port, use_ssl)
def do_request(self, method, action, body=None, headers=None):
def do_request(self, method, action, body=None, headers=None, params=None):
action = "%s/%s" % (self.doc_root, action.lstrip("/"))
return super(V1Client, self).do_request(method, action,
body, headers)
return super(V1Client, self).do_request(method, action, body,
headers, params)
def get_images(self):
def get_images(self, filters=None):
"""
Returns a list of image id/name mappings from Registry
"""
res = self.do_request("GET", "/images")
res = self.do_request("GET", "/images", params=filters)
data = json.loads(res.read())['images']
return data
def get_images_detailed(self):
def get_images_detailed(self, filters=None):
"""
Returns a list of detailed image data mappings from Registry
"""
res = self.do_request("GET", "/images/detail")
res = self.do_request("GET", "/images/detail", params=filters)
data = json.loads(res.read())['images']
return data

View File

@ -48,12 +48,7 @@ class RegistryClient(BaseClient):
"""
Returns a list of image id/name mappings from Registry
"""
if filters != None:
action = "/images?%s" % urllib.urlencode(filters)
else:
action = "/images"
res = self.do_request("GET", action)
res = self.do_request("GET", "/images", params=filters)
data = json.loads(res.read())['images']
return data
@ -61,12 +56,7 @@ class RegistryClient(BaseClient):
"""
Returns a list of detailed image data mappings from Registry
"""
if filters != None:
action = "/images/detail?%s" % urllib.urlencode(filters)
else:
action = "/images/detail"
res = self.do_request("GET", action)
res = self.do_request("GET", "/images/detail", params=filters)
data = json.loads(res.read())['images']
return data

View File

@ -457,6 +457,43 @@ class TestClient(unittest.TestCase):
for k, v in fixture.items():
self.assertEquals(v, images[0][k])
def test_get_image_index_by_base_attribute(self):
"""Tests that an index call can be filtered by a base attribute"""
extra_fixture = {'id': 3,
'status': 'active',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'new name! #123',
'size': 19,
'checksum': None}
glance.registry.db.api.image_create(None, extra_fixture)
images = self.client.get_images({'name': 'new name! #123'})
self.assertEquals(len(images), 1)
self.assertEquals('new name! #123', images[0]['name'])
def test_get_image_index_by_property(self):
"""Tests that an index call can be filtered by a property"""
extra_fixture = {'id': 3,
'status': 'saving',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'new name! #123',
'size': 19,
'checksum': None,
'properties': {'p a': 'v a'}}
glance.registry.db.api.image_create(None, extra_fixture)
images = self.client.get_images({'property-p a': 'v a'})
self.assertEquals(len(images), 1)
self.assertEquals(3, images[0]['id'])
def test_get_image_details(self):
"""Tests that the detailed info about public images returned"""
fixture = {'id': 2,
@ -485,6 +522,45 @@ class TestClient(unittest.TestCase):
for k, v in expected.items():
self.assertEquals(v, images[0][k])
def test_get_image_details_by_base_attribute(self):
"""Tests that a detailed call can be filtered by a base attribute"""
extra_fixture = {'id': 3,
'status': 'active',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'new name! #123',
'size': 19,
'checksum': None}
glance.registry.db.api.image_create(None, extra_fixture)
images = self.client.get_images_detailed({'name': 'new name! #123'})
self.assertEquals(len(images), 1)
for image in images:
self.assertEquals('new name! #123', image['name'])
def test_get_image_details_by_property(self):
"""Tests that a detailed call can be filtered by a property"""
extra_fixture = {'id': 3,
'status': 'saving',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'new name! #123',
'size': 19,
'checksum': None,
'properties': {'p a': 'v a'}}
glance.registry.db.api.image_create(None, extra_fixture)
images = self.client.get_images_detailed({'property-p a': 'v a'})
self.assertEquals(len(images), 1)
for image in images:
self.assertEquals('v a', image['properties']['p a'])
def test_get_image_meta(self):
"""Tests that the detailed info about an image returned"""
fixture = {'id': 2,