Merge changes.
Update tests. New image indexes relative to existing indexes.
This commit is contained in:
commit
593fa8e523
@ -42,6 +42,9 @@ from glance import utils
|
||||
|
||||
logger = logging.getLogger('glance.api.v1.images')
|
||||
|
||||
SUPPORTED_FILTERS = ['name', 'status', 'container_format', 'disk_format',
|
||||
'size_min', 'size_max']
|
||||
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
|
||||
@ -89,7 +92,8 @@ class Controller(wsgi.Controller):
|
||||
'size': <SIZE>}, ...
|
||||
]}
|
||||
"""
|
||||
images = registry.get_images_list(self.options)
|
||||
filters = self._get_filters(req)
|
||||
images = registry.get_images_list(self.options, filters)
|
||||
return dict(images=images)
|
||||
|
||||
def detail(self, req):
|
||||
@ -114,9 +118,24 @@ class Controller(wsgi.Controller):
|
||||
'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ...
|
||||
]}
|
||||
"""
|
||||
images = registry.get_images_detail(self.options)
|
||||
filters = self._get_filters(req)
|
||||
images = registry.get_images_detail(self.options, filters)
|
||||
return dict(images=images)
|
||||
|
||||
def _get_filters(self, req):
|
||||
"""
|
||||
Return a dictionary of query param filters from the request
|
||||
|
||||
:param req: the Request object coming from the wsgi layer
|
||||
:retval a dict of key/value filters
|
||||
"""
|
||||
filters = {}
|
||||
for param in req.str_params:
|
||||
if param in SUPPORTED_FILTERS or param.startswith('property-'):
|
||||
filters[param] = req.str_params.get(param)
|
||||
|
||||
return filters
|
||||
|
||||
def meta(self, req, id):
|
||||
"""
|
||||
Returns metadata about an image in the HTTP headers of the
|
||||
|
@ -32,14 +32,14 @@ def get_registry_client(options):
|
||||
return client.RegistryClient(host, port)
|
||||
|
||||
|
||||
def get_images_list(options):
|
||||
def get_images_list(options, filters):
|
||||
c = get_registry_client(options)
|
||||
return c.get_images()
|
||||
return c.get_images(filters)
|
||||
|
||||
|
||||
def get_images_detail(options):
|
||||
def get_images_detail(options, filters):
|
||||
c = get_registry_client(options)
|
||||
return c.get_images_detailed()
|
||||
return c.get_images_detailed(filters)
|
||||
|
||||
|
||||
def get_image_metadata(options, image_id):
|
||||
|
@ -20,14 +20,9 @@ Simple client class to speak with any RESTful service that implements
|
||||
the Glance Registry API
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import json
|
||||
import logging
|
||||
import urlparse
|
||||
import socket
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
from glance.common import exception
|
||||
from glance.client import BaseClient
|
||||
|
||||
|
||||
@ -49,28 +44,34 @@ class RegistryClient(BaseClient):
|
||||
port = port or self.DEFAULT_PORT
|
||||
super(RegistryClient, self).__init__(host, port, use_ssl)
|
||||
|
||||
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")
|
||||
if filters != None:
|
||||
action = "/images?%s" % urllib.urlencode(filters)
|
||||
else:
|
||||
action = "/images"
|
||||
|
||||
res = self.do_request("GET", action)
|
||||
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")
|
||||
if filters != None:
|
||||
action = "/images/detail?%s" % urllib.urlencode(filters)
|
||||
else:
|
||||
action = "/images/detail"
|
||||
|
||||
res = self.do_request("GET", action)
|
||||
data = json.loads(res.read())['images']
|
||||
return data
|
||||
|
||||
def get_image(self, image_id):
|
||||
"""
|
||||
Returns a mapping of image metadata from Registry
|
||||
|
||||
:raises exception.NotFound if image is not in registry
|
||||
"""
|
||||
"""Returns a mapping of image metadata from Registry"""
|
||||
res = self.do_request("GET", "/images/%s" % image_id)
|
||||
data = json.loads(res.read())['image']
|
||||
return data
|
||||
|
@ -140,15 +140,39 @@ def image_get(context, image_id, session=None):
|
||||
raise exception.NotFound("No image found with ID %s" % image_id)
|
||||
|
||||
|
||||
def image_get_all_public(context):
|
||||
"""Get all public images."""
|
||||
def image_get_all_public(context, filters=None):
|
||||
"""Get all public images that match zero or more filters.
|
||||
|
||||
:param filters: dict of filter keys and values. If a 'properties'
|
||||
key is present, it is treated as a dict of key/value
|
||||
filters on the image properties attribute
|
||||
|
||||
"""
|
||||
if filters == None:
|
||||
filters = {}
|
||||
|
||||
session = get_session()
|
||||
return session.query(models.Image).\
|
||||
query = session.query(models.Image).\
|
||||
options(joinedload(models.Image.properties)).\
|
||||
filter_by(deleted=_deleted(context)).\
|
||||
filter_by(is_public=True).\
|
||||
filter(models.Image.status != 'killed').\
|
||||
all()
|
||||
filter(models.Image.status != 'killed')
|
||||
|
||||
if 'size_min' in filters:
|
||||
query = query.filter(models.Image.size >= filters['size_min'])
|
||||
del filters['size_min']
|
||||
|
||||
if 'size_max' in filters:
|
||||
query = query.filter(models.Image.size <= filters['size_max'])
|
||||
del filters['size_max']
|
||||
|
||||
for (k, v) in filters.pop('properties', {}).items():
|
||||
query = query.filter(models.Image.properties.any(name=k, value=v))
|
||||
|
||||
for (k, v) in filters.items():
|
||||
query = query.filter(getattr(models.Image, k) == v)
|
||||
|
||||
return query.all()
|
||||
|
||||
|
||||
def _drop_protected_attrs(model_class, values):
|
||||
|
@ -36,6 +36,9 @@ DISPLAY_FIELDS_IN_INDEX = ['id', 'name', 'size',
|
||||
'disk_format', 'container_format',
|
||||
'checksum']
|
||||
|
||||
SUPPORTED_FILTERS = ['name', 'status', 'container_format', 'disk_format',
|
||||
'size_min', 'size_max']
|
||||
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
"""Controller for the reference implementation registry server"""
|
||||
@ -45,7 +48,7 @@ class Controller(wsgi.Controller):
|
||||
db_api.configure_db(options)
|
||||
|
||||
def index(self, req):
|
||||
"""Return basic information for all public, non-deleted images
|
||||
"""Return a basic filtered list of public, non-deleted images
|
||||
|
||||
:param req: the Request object coming from the wsgi layer
|
||||
:retval a mapping of the following form::
|
||||
@ -64,7 +67,8 @@ class Controller(wsgi.Controller):
|
||||
}
|
||||
|
||||
"""
|
||||
images = db_api.image_get_all_public(None)
|
||||
images = db_api.image_get_all_public(None, self._get_filters(req))
|
||||
|
||||
results = []
|
||||
for image in images:
|
||||
result = {}
|
||||
@ -74,7 +78,7 @@ class Controller(wsgi.Controller):
|
||||
return dict(images=results)
|
||||
|
||||
def detail(self, req):
|
||||
"""Return detailed information for all public, non-deleted images
|
||||
"""Return a filtered list of public, non-deleted images in detail
|
||||
|
||||
:param req: the Request object coming from the wsgi layer
|
||||
:retval a mapping of the following form::
|
||||
@ -85,10 +89,33 @@ class Controller(wsgi.Controller):
|
||||
all image model fields.
|
||||
|
||||
"""
|
||||
images = db_api.image_get_all_public(None)
|
||||
images = db_api.image_get_all_public(None, self._get_filters(req))
|
||||
|
||||
image_dicts = [make_image_dict(i) for i in images]
|
||||
return dict(images=image_dicts)
|
||||
|
||||
def _get_filters(self, req):
|
||||
"""Return a dictionary of query param filters from the request
|
||||
|
||||
:param req: the Request object coming from the wsgi layer
|
||||
:retval a dict of key/value filters
|
||||
|
||||
"""
|
||||
filters = {}
|
||||
properties = {}
|
||||
|
||||
for param in req.str_params:
|
||||
if param in SUPPORTED_FILTERS:
|
||||
filters[param] = req.str_params.get(param)
|
||||
if param.startswith('property-'):
|
||||
_param = param[9:]
|
||||
properties[_param] = req.str_params.get(param)
|
||||
|
||||
if len(properties) > 0:
|
||||
filters['properties'] = properties
|
||||
|
||||
return filters
|
||||
|
||||
def show(self, req, id):
|
||||
"""Return data about the given image id."""
|
||||
try:
|
||||
|
@ -787,3 +787,213 @@ class TestCurlApi(functional.FunctionalTest):
|
||||
"Could not find '%s' in '%s'" % (expected, out))
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
def test_filtered_images(self):
|
||||
"""
|
||||
Set up three test images and ensure each query param filter works
|
||||
"""
|
||||
self.cleanup()
|
||||
self.start_servers()
|
||||
|
||||
api_port = self.api_port
|
||||
registry_port = self.registry_port
|
||||
|
||||
# 0. GET /images
|
||||
# Verify no public images
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
self.assertEqual('{"images": []}', out.strip())
|
||||
|
||||
# 1. POST /images with three public images with various attributes
|
||||
cmd = ("curl -i -X POST "
|
||||
"-H 'Expect: ' " # Necessary otherwise sends 100 Continue
|
||||
"-H 'X-Image-Meta-Name: Image1' "
|
||||
"-H 'X-Image-Meta-Status: active' "
|
||||
"-H 'X-Image-Meta-Container-Format: ovf' "
|
||||
"-H 'X-Image-Meta-Disk-Format: vdi' "
|
||||
"-H 'X-Image-Meta-Size: 19' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"-H 'X-Image-Meta-Property-pants: are on' "
|
||||
"http://0.0.0.0:%d/v1/images") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 201 Created", status_line)
|
||||
|
||||
cmd = ("curl -i -X POST "
|
||||
"-H 'Expect: ' " # Necessary otherwise sends 100 Continue
|
||||
"-H 'X-Image-Meta-Name: My Image!' "
|
||||
"-H 'X-Image-Meta-Status: active' "
|
||||
"-H 'X-Image-Meta-Container-Format: ovf' "
|
||||
"-H 'X-Image-Meta-Disk-Format: vhd' "
|
||||
"-H 'X-Image-Meta-Size: 20' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"-H 'X-Image-Meta-Property-pants: are on' "
|
||||
"http://0.0.0.0:%d/v1/images") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 201 Created", status_line)
|
||||
cmd = ("curl -i -X POST "
|
||||
"-H 'Expect: ' " # Necessary otherwise sends 100 Continue
|
||||
"-H 'X-Image-Meta-Name: My Image!' "
|
||||
"-H 'X-Image-Meta-Status: saving' "
|
||||
"-H 'X-Image-Meta-Container-Format: ami' "
|
||||
"-H 'X-Image-Meta-Disk-Format: ami' "
|
||||
"-H 'X-Image-Meta-Size: 21' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"-H 'X-Image-Meta-Property-pants: are off' "
|
||||
"http://0.0.0.0:%d/v1/images") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 201 Created", status_line)
|
||||
|
||||
# 2. GET /images
|
||||
# Verify three public images
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 3)
|
||||
|
||||
# 3. GET /images with name filter
|
||||
# Verify correct images returned with name
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images?name=My%%20Image!" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["name"], "My Image!")
|
||||
|
||||
# 4. GET /images with status filter
|
||||
# Verify correct images returned with status
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images/detail?status=queued"
|
||||
% api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 3)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["status"], "queued")
|
||||
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images/detail?status=active"
|
||||
% api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 0)
|
||||
|
||||
# 5. GET /images with container_format filter
|
||||
# Verify correct images returned with container_format
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images?container_format=ovf"
|
||||
% api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["container_format"], "ovf")
|
||||
|
||||
# 6. GET /images with disk_format filter
|
||||
# Verify correct images returned with disk_format
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images?disk_format=vdi"
|
||||
% api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 1)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["disk_format"], "vdi")
|
||||
|
||||
# 7. GET /images with size_max filter
|
||||
# Verify correct images returned with size <= expected
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images?size_max=20"
|
||||
% api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertTrue(image["size"] <= 20)
|
||||
|
||||
# 8. GET /images with size_min filter
|
||||
# Verify correct images returned with size >= expected
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images?size_min=20"
|
||||
% api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertTrue(image["size"] >= 20)
|
||||
|
||||
# 9. GET /images with property filter
|
||||
# Verify correct images returned with property
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images/detail?"
|
||||
"property-pants=are%%20on" % api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["properties"]["pants"], "are on")
|
||||
|
||||
# 10. GET /images with property filter and name filter
|
||||
# Verify correct images returned with property and name
|
||||
# Make sure you quote the url when using more than one param!
|
||||
cmd = ("curl 'http://0.0.0.0:%d/v1/images/detail?"
|
||||
"name=My%%20Image!&property-pants=are%%20on'" % api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 1)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["properties"]["pants"], "are on")
|
||||
self.assertEqual(image["name"], "My Image!")
|
||||
|
@ -400,9 +400,32 @@ def stub_out_registry_db_image_api(stubs):
|
||||
else:
|
||||
return images[0]
|
||||
|
||||
def image_get_all_public(self, _context, public=True):
|
||||
return [f for f in self.images
|
||||
if f['is_public'] == public]
|
||||
def image_get_all_public(self, _context, filters):
|
||||
images = [f for f in self.images if f['is_public'] == True]
|
||||
|
||||
if 'size_min' in filters:
|
||||
size_min = int(filters.pop('size_min'))
|
||||
images = [f for f in images if int(f['size']) >= size_min]
|
||||
|
||||
if 'size_max' in filters:
|
||||
size_max = int(filters.pop('size_max'))
|
||||
images = [f for f in images if int(f['size']) <= size_max]
|
||||
|
||||
def _prop_filter(key, value):
|
||||
def _func(image):
|
||||
for prop in image['properties']:
|
||||
if prop['name'] == key:
|
||||
return prop['value'] == value
|
||||
return False
|
||||
return _func
|
||||
|
||||
for k, v in filters.pop('properties', {}).items():
|
||||
images = filter(_prop_filter(k, v), images)
|
||||
|
||||
for k, v in filters.items():
|
||||
images = [f for f in images if f[k] == v]
|
||||
|
||||
return images
|
||||
|
||||
fake_datastore = FakeDatastore()
|
||||
stubs.Set(glance.registry.db.api, 'image_create',
|
||||
|
@ -26,6 +26,7 @@ import webob
|
||||
|
||||
from glance.api import v1 as server
|
||||
from glance.registry import server as rserver
|
||||
import glance.registry.db.api
|
||||
from tests import stubs
|
||||
|
||||
VERBOSE = False
|
||||
@ -87,6 +88,50 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEquals(v, images[0][k])
|
||||
|
||||
def test_get_index_filter_name(self):
|
||||
"""Tests that the /images registry API returns list of
|
||||
public images that have a specific name. This is really a sanity
|
||||
check, filtering is tested more in-depth using /images/detail
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'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)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'new name! #123',
|
||||
'size': 20,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images?name=new name! #123')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('new name! #123', image['name'])
|
||||
|
||||
def test_get_details(self):
|
||||
"""Tests that the /images/detail registry API returns
|
||||
a mapping containing a list of detailed image information
|
||||
@ -112,6 +157,324 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
for k, v in fixture.iteritems():
|
||||
self.assertEquals(v, images[0][k])
|
||||
|
||||
def test_get_details_filter_name(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a specific name
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'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)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'new name! #123',
|
||||
'size': 20,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?name=new name! #123')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('new name! #123', image['name'])
|
||||
|
||||
def test_get_details_filter_status(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a specific status
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'saving',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #4',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?status=saving')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('saving', image['status'])
|
||||
|
||||
def test_get_details_filter_container_format(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a specific container_format
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vdi',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?container_format=ovf')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('ovf', image['container_format'])
|
||||
|
||||
def test_get_details_filter_disk_format(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a specific disk_format
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?disk_format=vhd')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('vhd', image['disk_format'])
|
||||
|
||||
def test_get_details_filter_size_min(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a size greater than or equal to size_min
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 18,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 20,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?size_min=19')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] >= 19)
|
||||
|
||||
def test_get_details_filter_size_max(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a size less than or equal to size_max
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 18,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 20,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?size_max=19')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] <= 19)
|
||||
|
||||
def test_get_details_filter_size_min_max(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a size less than or equal to size_max
|
||||
and greater than or equal to size_min
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 18,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 20,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 2,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #5',
|
||||
'size': 6,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?size_min=18&size_max=19')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] <= 19 and image['size'] >= 18)
|
||||
|
||||
def test_get_details_filter_property(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a specific custom property
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 19,
|
||||
'checksum': None,
|
||||
'properties': {'prop_123': 'v a'}}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': self.next_image_id + 1,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 19,
|
||||
'checksum': None,
|
||||
'properties': {'prop_123': 'v b'}}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?property-prop_123=v%20a')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('v a', image['properties']['prop_123'])
|
||||
|
||||
def test_create_image(self):
|
||||
"""Tests that the /images POST registry API creates the image"""
|
||||
fixture = {'name': 'fake public image',
|
||||
|
@ -24,8 +24,9 @@ import unittest
|
||||
import webob
|
||||
|
||||
from glance import client
|
||||
from glance.registry import client as rclient
|
||||
from glance.common import exception
|
||||
import glance.registry.db.api
|
||||
from glance.registry import client as rclient
|
||||
from tests import stubs
|
||||
|
||||
|
||||
@ -69,6 +70,26 @@ class TestRegistryClient(unittest.TestCase):
|
||||
for k, v in fixture.items():
|
||||
self.assertEquals(v, images[0][k])
|
||||
|
||||
def test_get_image_index_by_name(self):
|
||||
"""Test correct set of public, name-filtered image returned. This
|
||||
is just a sanity check, we test the details call more in-depth."""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'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)
|
||||
|
||||
for image in images:
|
||||
self.assertEquals('new name! #123', image['name'])
|
||||
|
||||
def test_get_image_details(self):
|
||||
"""Tests that the detailed info about public images returned"""
|
||||
fixture = {'id': 2,
|
||||
@ -87,6 +108,140 @@ class TestRegistryClient(unittest.TestCase):
|
||||
for k, v in fixture.items():
|
||||
self.assertEquals(v, images[0][k])
|
||||
|
||||
def test_get_image_details_by_name(self):
|
||||
"""Tests that a detailed call can be filtered by name"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'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_status(self):
|
||||
"""Tests that a detailed call can be filtered by status"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'saving',
|
||||
'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({'status': 'saving'})
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertEquals('saving', image['status'])
|
||||
|
||||
def test_get_image_details_by_container_format(self):
|
||||
"""Tests that a detailed call can be filtered by container_format"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'saving',
|
||||
'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({'container_format': 'ovf'})
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertEquals('ovf', image['container_format'])
|
||||
|
||||
def test_get_image_details_by_disk_format(self):
|
||||
"""Tests that a detailed call can be filtered by disk_format"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'saving',
|
||||
'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({'disk_format': 'vhd'})
|
||||
self.assertEquals(len(images), 2)
|
||||
|
||||
for image in images:
|
||||
self.assertEquals('vhd', image['disk_format'])
|
||||
|
||||
def test_get_image_details_with_maximum_size(self):
|
||||
"""Tests that a detailed call can be filtered by size_max"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'saving',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'new name! #123',
|
||||
'size': 21,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
images = self.client.get_images_detailed({'size_max': 20})
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] <= 20)
|
||||
|
||||
def test_get_image_details_with_minimum_size(self):
|
||||
"""Tests that a detailed call can be filtered by size_min"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'status': 'saving',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'new name! #123',
|
||||
'size': 20,
|
||||
'checksum': None}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
images = self.client.get_images_detailed({'size_min': 20})
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] >= 20)
|
||||
|
||||
def test_get_image_details_by_property(self):
|
||||
"""Tests that a detailed call can be filtered by a property"""
|
||||
extra_fixture = {'id': self.next_image_id,
|
||||
'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(self):
|
||||
"""Tests that the detailed info about an image returned"""
|
||||
fixture = {'id': 1,
|
||||
|
Loading…
Reference in New Issue
Block a user