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:
Amala Basha 2013-07-02 06:50:48 -05:00 committed by amalaba
parent 99f6ddab24
commit 5670f142d4
6 changed files with 122 additions and 12 deletions

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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})