index checksum image property
checksum image property will be indexed so that users can search for an image by specifying the checksum Change-Id: I31543afed31512f8f5f613640012bc7c1d7ea993 Implements: blueprint index-using-checksum-image-property
This commit is contained in:
parent
99f6ddab24
commit
5670f142d4
@ -607,6 +607,11 @@ def image_get_all(context, filters=None, marker=None, limit=None,
|
|||||||
query = query.filter(spec)
|
query = query.filter(spec)
|
||||||
|
|
||||||
showing_deleted = False
|
showing_deleted = False
|
||||||
|
|
||||||
|
if 'checksum' in filters:
|
||||||
|
checksum = filters.get('checksum')
|
||||||
|
query = query.filter_by(checksum=checksum)
|
||||||
|
|
||||||
if 'changes-since' in filters:
|
if 'changes-since' in filters:
|
||||||
# normalize timestamp to UTC, as sqlalchemy doesn't appear to
|
# normalize timestamp to UTC, as sqlalchemy doesn't appear to
|
||||||
# respect timezone offsets
|
# respect timezone offsets
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 Rackspace Hosting
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from sqlalchemy import MetaData, Table, Index
|
||||||
|
|
||||||
|
INDEX_NAME = 'checksum_image_idx'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(migrate_engine):
|
||||||
|
meta = MetaData()
|
||||||
|
meta.bind = migrate_engine
|
||||||
|
|
||||||
|
images = Table('images', meta, autoload=True)
|
||||||
|
|
||||||
|
index = Index(INDEX_NAME, images.c.checksum)
|
||||||
|
index.create(migrate_engine)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade(migrate_engine):
|
||||||
|
meta = MetaData()
|
||||||
|
meta.bind = migrate_engine
|
||||||
|
|
||||||
|
images = Table('images', meta, autoload=True)
|
||||||
|
|
||||||
|
index = Index(INDEX_NAME, images.c.checksum)
|
||||||
|
index.drop(migrate_engine)
|
@ -107,6 +107,7 @@ class ModelBase(object):
|
|||||||
class Image(BASE, ModelBase):
|
class Image(BASE, ModelBase):
|
||||||
"""Represents an image in the datastore"""
|
"""Represents an image in the datastore"""
|
||||||
__tablename__ = 'images'
|
__tablename__ = 'images'
|
||||||
|
__table_args__ = (Index('checksum_image_idx', 'checksum'),)
|
||||||
|
|
||||||
id = Column(String(36), primary_key=True, default=uuidutils.generate_uuid)
|
id = Column(String(36), primary_key=True, default=uuidutils.generate_uuid)
|
||||||
name = Column(String(255))
|
name = Column(String(255))
|
||||||
@ -115,7 +116,7 @@ class Image(BASE, ModelBase):
|
|||||||
size = Column(BigInteger)
|
size = Column(BigInteger)
|
||||||
status = Column(String(30), nullable=False)
|
status = Column(String(30), nullable=False)
|
||||||
is_public = Column(Boolean, nullable=False, default=False)
|
is_public = Column(Boolean, nullable=False, default=False)
|
||||||
checksum = Column(String(32))
|
checksum = Column(String(32), index=True)
|
||||||
min_disk = Column(Integer(), nullable=False, default=0)
|
min_disk = Column(Integer(), nullable=False, default=0)
|
||||||
min_ram = Column(Integer(), nullable=False, default=0)
|
min_ram = Column(Integer(), nullable=False, default=0)
|
||||||
owner = Column(String(255))
|
owner = Column(String(255))
|
||||||
|
@ -43,6 +43,9 @@ UUID1_LOCATION = 'file:///path/to/image'
|
|||||||
UUID1_LOCATION_METADATA = {'key': 'value'}
|
UUID1_LOCATION_METADATA = {'key': 'value'}
|
||||||
UUID3_LOCATION = 'http://somehost.com/place'
|
UUID3_LOCATION = 'http://somehost.com/place'
|
||||||
|
|
||||||
|
CHECKSUM = '93264c3edf5972c9f1cb309543d38a5c'
|
||||||
|
CHCKSUM1 = '43264c3edf4972c9f1cb309543d38a55'
|
||||||
|
|
||||||
|
|
||||||
def _db_fixture(id, **kwargs):
|
def _db_fixture(id, **kwargs):
|
||||||
obj = {
|
obj = {
|
||||||
@ -92,16 +95,17 @@ class TestImageRepo(test_utils.BaseTestCase):
|
|||||||
def _create_images(self):
|
def _create_images(self):
|
||||||
self.db.reset()
|
self.db.reset()
|
||||||
self.images = [
|
self.images = [
|
||||||
_db_fixture(UUID1, owner=TENANT1, name='1', size=256,
|
_db_fixture(UUID1, owner=TENANT1, checksum=CHECKSUM,
|
||||||
|
name='1', size=256,
|
||||||
is_public=True, status='active',
|
is_public=True, status='active',
|
||||||
locations=[{'url': UUID1_LOCATION,
|
locations=[{'url': UUID1_LOCATION,
|
||||||
'metadata': UUID1_LOCATION_METADATA}]),
|
'metadata': UUID1_LOCATION_METADATA}]),
|
||||||
_db_fixture(UUID2, owner=TENANT1, name='2',
|
_db_fixture(UUID2, owner=TENANT1, checksum=CHCKSUM1,
|
||||||
size=512, is_public=False),
|
name='2', size=512, is_public=False),
|
||||||
_db_fixture(UUID3, owner=TENANT3, name='3',
|
_db_fixture(UUID3, owner=TENANT3, checksum=CHCKSUM1,
|
||||||
size=1024, is_public=True,
|
name='3', size=1024, is_public=True,
|
||||||
locations=[{'url': UUID3_LOCATION,
|
locations=[{'url': UUID3_LOCATION,
|
||||||
'metadata': {}}]),
|
'metadata': {}}]),
|
||||||
_db_fixture(UUID4, owner=TENANT4, name='4', size=2048),
|
_db_fixture(UUID4, owner=TENANT4, name='4', size=2048),
|
||||||
]
|
]
|
||||||
[self.db.image_create(None, image) for image in self.images]
|
[self.db.image_create(None, image) for image in self.images]
|
||||||
@ -200,6 +204,26 @@ class TestImageRepo(test_utils.BaseTestCase):
|
|||||||
image_ids = set([i.image_id for i in images])
|
image_ids = set([i.image_id for i in images])
|
||||||
self.assertEqual(set([UUID2]), image_ids)
|
self.assertEqual(set([UUID2]), image_ids)
|
||||||
|
|
||||||
|
def test_list_with_checksum_filter_single_image(self):
|
||||||
|
filters = {'checksum': CHECKSUM}
|
||||||
|
images = self.image_repo.list(filters=filters)
|
||||||
|
image_ids = list([i.image_id for i in images])
|
||||||
|
self.assertEquals(1, len(image_ids))
|
||||||
|
self.assertEqual([UUID1], image_ids)
|
||||||
|
|
||||||
|
def test_list_with_checksum_filter_multiple_images(self):
|
||||||
|
filters = {'checksum': CHCKSUM1}
|
||||||
|
images = self.image_repo.list(filters=filters)
|
||||||
|
image_ids = list([i.image_id for i in images])
|
||||||
|
self.assertEquals(2, len(image_ids))
|
||||||
|
self.assertEqual([UUID3, UUID2], image_ids)
|
||||||
|
|
||||||
|
def test_list_with_wrong_checksum(self):
|
||||||
|
WRONG_CHKSUM = 'd2fd42f979e1ed1aafadc7eb9354bff839c858cd'
|
||||||
|
filters = {'checksum': WRONG_CHKSUM}
|
||||||
|
images = self.image_repo.list(filters=filters)
|
||||||
|
self.assertEquals(0, len(images))
|
||||||
|
|
||||||
def test_list_public_images(self):
|
def test_list_public_images(self):
|
||||||
filters = {'visibility': 'public'}
|
filters = {'visibility': 'public'}
|
||||||
images = self.image_repo.list(filters=filters)
|
images = self.image_repo.list(filters=filters)
|
||||||
|
@ -712,3 +712,18 @@ class TestMigrations(utils.BaseTestCase):
|
|||||||
self.assertTrue('meta_data' in r[0])
|
self.assertTrue('meta_data' in r[0])
|
||||||
x = pickle.loads(r[0]['meta_data'])
|
x = pickle.loads(r[0]['meta_data'])
|
||||||
self.assertEqual(x, {})
|
self.assertEqual(x, {})
|
||||||
|
|
||||||
|
def _check_027(self, engine, data):
|
||||||
|
table = "images"
|
||||||
|
index = "checksum_image_idx"
|
||||||
|
columns = ["checksum"]
|
||||||
|
|
||||||
|
meta = sqlalchemy.MetaData()
|
||||||
|
meta.bind = engine
|
||||||
|
|
||||||
|
new_table = sqlalchemy.Table(table, meta, autoload=True)
|
||||||
|
|
||||||
|
index_data = [(idx.name, idx.columns.keys())
|
||||||
|
for idx in new_table.indexes]
|
||||||
|
|
||||||
|
self.assertIn((index, columns), index_data)
|
||||||
|
@ -47,6 +47,9 @@ TENANT2 = '2c014f32-55eb-467d-8fcb-4bd706012f81'
|
|||||||
TENANT3 = '5a3e60e8-cfa9-4a9e-a90a-62b42cea92b8'
|
TENANT3 = '5a3e60e8-cfa9-4a9e-a90a-62b42cea92b8'
|
||||||
TENANT4 = 'c6c87f25-8a94-47ed-8c83-053c25f42df4'
|
TENANT4 = 'c6c87f25-8a94-47ed-8c83-053c25f42df4'
|
||||||
|
|
||||||
|
CHKSUM = '93264c3edf5972c9f1cb309543d38a5c'
|
||||||
|
CHKSUM1 = '43254c3edf6972c9f1cb309543d38a8c'
|
||||||
|
|
||||||
|
|
||||||
def _db_fixture(id, **kwargs):
|
def _db_fixture(id, **kwargs):
|
||||||
obj = {
|
obj = {
|
||||||
@ -120,21 +123,22 @@ class TestImagesController(test_utils.BaseTestCase):
|
|||||||
def _create_images(self):
|
def _create_images(self):
|
||||||
self.db.reset()
|
self.db.reset()
|
||||||
self.images = [
|
self.images = [
|
||||||
_db_fixture(UUID1, owner=TENANT1, name='1', size=256,
|
_db_fixture(UUID1, owner=TENANT1, checksum=CHKSUM,
|
||||||
|
name='1', size=256,
|
||||||
is_public=True,
|
is_public=True,
|
||||||
locations=[{'url': '%s/%s' % (BASE_URI, UUID1),
|
locations=[{'url': '%s/%s' % (BASE_URI, UUID1),
|
||||||
'metadata': {}}],
|
'metadata': {}}],
|
||||||
disk_format='raw',
|
disk_format='raw',
|
||||||
container_format='bare',
|
container_format='bare',
|
||||||
status='active'),
|
status='active'),
|
||||||
_db_fixture(UUID2, owner=TENANT1, name='2',
|
_db_fixture(UUID2, owner=TENANT1, checksum=CHKSUM1,
|
||||||
size=512,
|
name='2', size=512,
|
||||||
is_public=True,
|
is_public=True,
|
||||||
disk_format='raw',
|
disk_format='raw',
|
||||||
container_format='bare',
|
container_format='bare',
|
||||||
status='active'),
|
status='active'),
|
||||||
_db_fixture(UUID3, owner=TENANT3, name='3',
|
_db_fixture(UUID3, owner=TENANT3, checksum=CHKSUM1,
|
||||||
size=512, is_public=True),
|
name='3', size=512, is_public=True),
|
||||||
_db_fixture(UUID4, owner=TENANT4, name='4', size=1024),
|
_db_fixture(UUID4, owner=TENANT4, name='4', size=1024),
|
||||||
]
|
]
|
||||||
[self.db.image_create(None, image) for image in self.images]
|
[self.db.image_create(None, image) for image in self.images]
|
||||||
@ -229,6 +233,27 @@ class TestImagesController(test_utils.BaseTestCase):
|
|||||||
expected = set([UUID1])
|
expected = set([UUID1])
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_index_with_checksum_filter_single_image(self):
|
||||||
|
req = unit_test_utils.get_fake_request('/images?checksum=%s' % CHKSUM)
|
||||||
|
output = self.controller.index(req, filters={'checksum': CHKSUM})
|
||||||
|
self.assertEqual(1, len(output['images']))
|
||||||
|
actual = list([image.image_id for image in output['images']])
|
||||||
|
expected = [UUID1]
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_index_with_checksum_filter_multiple_images(self):
|
||||||
|
req = unit_test_utils.get_fake_request('/images?checksum=%s' % CHKSUM1)
|
||||||
|
output = self.controller.index(req, filters={'checksum': CHKSUM1})
|
||||||
|
self.assertEqual(2, len(output['images']))
|
||||||
|
actual = list([image.image_id for image in output['images']])
|
||||||
|
expected = [UUID3, UUID2]
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_index_with_non_existent_checksum(self):
|
||||||
|
req = unit_test_utils.get_fake_request('/images?checksum=236231827')
|
||||||
|
output = self.controller.index(req, filters={'checksum': '236231827'})
|
||||||
|
self.assertEqual(0, len(output['images']))
|
||||||
|
|
||||||
def test_index_size_max_filter(self):
|
def test_index_size_max_filter(self):
|
||||||
request = unit_test_utils.get_fake_request('/images?size_max=512')
|
request = unit_test_utils.get_fake_request('/images?size_max=512')
|
||||||
output = self.controller.index(request, filters={'size_max': 512})
|
output = self.controller.index(request, filters={'size_max': 512})
|
||||||
|
Loading…
Reference in New Issue
Block a user