Merge changes.
Update tests. New image indexes relative to existing indexes.
This commit is contained in:
@@ -42,6 +42,9 @@ from glance import utils
|
|||||||
|
|
||||||
logger = logging.getLogger('glance.api.v1.images')
|
logger = logging.getLogger('glance.api.v1.images')
|
||||||
|
|
||||||
|
SUPPORTED_FILTERS = ['name', 'status', 'container_format', 'disk_format',
|
||||||
|
'size_min', 'size_max']
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
|
|
||||||
@@ -89,7 +92,8 @@ class Controller(wsgi.Controller):
|
|||||||
'size': <SIZE>}, ...
|
'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)
|
return dict(images=images)
|
||||||
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
@@ -114,9 +118,24 @@ class Controller(wsgi.Controller):
|
|||||||
'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ...
|
'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)
|
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):
|
def meta(self, req, id):
|
||||||
"""
|
"""
|
||||||
Returns metadata about an image in the HTTP headers of the
|
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)
|
return client.RegistryClient(host, port)
|
||||||
|
|
||||||
|
|
||||||
def get_images_list(options):
|
def get_images_list(options, filters):
|
||||||
c = get_registry_client(options)
|
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)
|
c = get_registry_client(options)
|
||||||
return c.get_images_detailed()
|
return c.get_images_detailed(filters)
|
||||||
|
|
||||||
|
|
||||||
def get_image_metadata(options, image_id):
|
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
|
the Glance Registry API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import httplib
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import urllib
|
||||||
import urlparse
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from glance.common import exception
|
|
||||||
from glance.client import BaseClient
|
from glance.client import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@@ -49,28 +44,34 @@ class RegistryClient(BaseClient):
|
|||||||
port = port or self.DEFAULT_PORT
|
port = port or self.DEFAULT_PORT
|
||||||
super(RegistryClient, self).__init__(host, port, use_ssl)
|
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
|
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']
|
data = json.loads(res.read())['images']
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_images_detailed(self):
|
def get_images_detailed(self, filters=None):
|
||||||
"""
|
"""
|
||||||
Returns a list of detailed image data mappings from Registry
|
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']
|
data = json.loads(res.read())['images']
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_image(self, image_id):
|
def get_image(self, image_id):
|
||||||
"""
|
"""Returns a mapping of image metadata from Registry"""
|
||||||
Returns a mapping of image metadata from Registry
|
|
||||||
|
|
||||||
:raises exception.NotFound if image is not in registry
|
|
||||||
"""
|
|
||||||
res = self.do_request("GET", "/images/%s" % image_id)
|
res = self.do_request("GET", "/images/%s" % image_id)
|
||||||
data = json.loads(res.read())['image']
|
data = json.loads(res.read())['image']
|
||||||
return data
|
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)
|
raise exception.NotFound("No image found with ID %s" % image_id)
|
||||||
|
|
||||||
|
|
||||||
def image_get_all_public(context):
|
def image_get_all_public(context, filters=None):
|
||||||
"""Get all public images."""
|
"""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()
|
session = get_session()
|
||||||
return session.query(models.Image).\
|
query = session.query(models.Image).\
|
||||||
options(joinedload(models.Image.properties)).\
|
options(joinedload(models.Image.properties)).\
|
||||||
filter_by(deleted=_deleted(context)).\
|
filter_by(deleted=_deleted(context)).\
|
||||||
filter_by(is_public=True).\
|
filter_by(is_public=True).\
|
||||||
filter(models.Image.status != 'killed').\
|
filter(models.Image.status != 'killed')
|
||||||
all()
|
|
||||||
|
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):
|
def _drop_protected_attrs(model_class, values):
|
||||||
|
@@ -36,6 +36,9 @@ DISPLAY_FIELDS_IN_INDEX = ['id', 'name', 'size',
|
|||||||
'disk_format', 'container_format',
|
'disk_format', 'container_format',
|
||||||
'checksum']
|
'checksum']
|
||||||
|
|
||||||
|
SUPPORTED_FILTERS = ['name', 'status', 'container_format', 'disk_format',
|
||||||
|
'size_min', 'size_max']
|
||||||
|
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
"""Controller for the reference implementation registry server"""
|
"""Controller for the reference implementation registry server"""
|
||||||
@@ -45,7 +48,7 @@ class Controller(wsgi.Controller):
|
|||||||
db_api.configure_db(options)
|
db_api.configure_db(options)
|
||||||
|
|
||||||
def index(self, req):
|
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
|
:param req: the Request object coming from the wsgi layer
|
||||||
:retval a mapping of the following form::
|
: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 = []
|
results = []
|
||||||
for image in images:
|
for image in images:
|
||||||
result = {}
|
result = {}
|
||||||
@@ -74,7 +78,7 @@ class Controller(wsgi.Controller):
|
|||||||
return dict(images=results)
|
return dict(images=results)
|
||||||
|
|
||||||
def detail(self, req):
|
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
|
:param req: the Request object coming from the wsgi layer
|
||||||
:retval a mapping of the following form::
|
:retval a mapping of the following form::
|
||||||
@@ -85,10 +89,33 @@ class Controller(wsgi.Controller):
|
|||||||
all image model fields.
|
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]
|
image_dicts = [make_image_dict(i) for i in images]
|
||||||
return dict(images=image_dicts)
|
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):
|
def show(self, req, id):
|
||||||
"""Return data about the given image id."""
|
"""Return data about the given image id."""
|
||||||
try:
|
try:
|
||||||
|
@@ -787,3 +787,213 @@ class TestCurlApi(functional.FunctionalTest):
|
|||||||
"Could not find '%s' in '%s'" % (expected, out))
|
"Could not find '%s' in '%s'" % (expected, out))
|
||||||
|
|
||||||
self.stop_servers()
|
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:
|
else:
|
||||||
return images[0]
|
return images[0]
|
||||||
|
|
||||||
def image_get_all_public(self, _context, public=True):
|
def image_get_all_public(self, _context, filters):
|
||||||
return [f for f in self.images
|
images = [f for f in self.images if f['is_public'] == True]
|
||||||
if f['is_public'] == public]
|
|
||||||
|
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()
|
fake_datastore = FakeDatastore()
|
||||||
stubs.Set(glance.registry.db.api, 'image_create',
|
stubs.Set(glance.registry.db.api, 'image_create',
|
||||||
|
@@ -26,6 +26,7 @@ import webob
|
|||||||
|
|
||||||
from glance.api import v1 as server
|
from glance.api import v1 as server
|
||||||
from glance.registry import server as rserver
|
from glance.registry import server as rserver
|
||||||
|
import glance.registry.db.api
|
||||||
from tests import stubs
|
from tests import stubs
|
||||||
|
|
||||||
VERBOSE = False
|
VERBOSE = False
|
||||||
@@ -87,6 +88,50 @@ class TestRegistryAPI(unittest.TestCase):
|
|||||||
for k, v in fixture.iteritems():
|
for k, v in fixture.iteritems():
|
||||||
self.assertEquals(v, images[0][k])
|
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):
|
def test_get_details(self):
|
||||||
"""Tests that the /images/detail registry API returns
|
"""Tests that the /images/detail registry API returns
|
||||||
a mapping containing a list of detailed image information
|
a mapping containing a list of detailed image information
|
||||||
@@ -112,6 +157,324 @@ class TestRegistryAPI(unittest.TestCase):
|
|||||||
for k, v in fixture.iteritems():
|
for k, v in fixture.iteritems():
|
||||||
self.assertEquals(v, images[0][k])
|
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):
|
def test_create_image(self):
|
||||||
"""Tests that the /images POST registry API creates the image"""
|
"""Tests that the /images POST registry API creates the image"""
|
||||||
fixture = {'name': 'fake public image',
|
fixture = {'name': 'fake public image',
|
||||||
|
@@ -24,8 +24,9 @@ import unittest
|
|||||||
import webob
|
import webob
|
||||||
|
|
||||||
from glance import client
|
from glance import client
|
||||||
from glance.registry import client as rclient
|
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
|
import glance.registry.db.api
|
||||||
|
from glance.registry import client as rclient
|
||||||
from tests import stubs
|
from tests import stubs
|
||||||
|
|
||||||
|
|
||||||
@@ -69,6 +70,26 @@ class TestRegistryClient(unittest.TestCase):
|
|||||||
for k, v in fixture.items():
|
for k, v in fixture.items():
|
||||||
self.assertEquals(v, images[0][k])
|
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):
|
def test_get_image_details(self):
|
||||||
"""Tests that the detailed info about public images returned"""
|
"""Tests that the detailed info about public images returned"""
|
||||||
fixture = {'id': 2,
|
fixture = {'id': 2,
|
||||||
@@ -87,6 +108,140 @@ class TestRegistryClient(unittest.TestCase):
|
|||||||
for k, v in fixture.items():
|
for k, v in fixture.items():
|
||||||
self.assertEquals(v, images[0][k])
|
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):
|
def test_get_image(self):
|
||||||
"""Tests that the detailed info about an image returned"""
|
"""Tests that the detailed info about an image returned"""
|
||||||
fixture = {'id': 1,
|
fixture = {'id': 1,
|
||||||
|
Reference in New Issue
Block a user