Merge "Add an image repo to encapsulate db api access"

This commit is contained in:
Jenkins 2012-11-20 00:48:26 +00:00 committed by Gerrit Code Review
commit 8f7903d7a3
4 changed files with 310 additions and 6 deletions

View File

@ -17,6 +17,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from glance.common import exception
import glance.domain
from glance.openstack.common import cfg
from glance.openstack.common import importutils
@ -56,3 +58,123 @@ IMAGE_ATTRS = BASE_MODEL_ATTRS | set(['name', 'status', 'size',
'min_disk', 'min_ram', 'is_public',
'location', 'checksum', 'owner',
'protected'])
class ImageRepo(object):
def __init__(self, context, db_api):
self.context = context
self.db_api = db_api
def get(self, image_id):
try:
db_api_image = dict(self.db_api.image_get(self.context, image_id))
assert not db_api_image['deleted']
except (exception.NotFound, exception.Forbidden, AssertionError):
raise exception.NotFound(image_id=image_id)
tags = self.db_api.image_tag_get_all(self.context, image_id)
image = self._format_image_from_db(db_api_image, tags)
return image
def list(self, marker=None, limit=None, sort_key='created_at',
sort_dir='desc', filters=None):
db_filters = self._translate_filters(filters)
db_api_images = self.db_api.image_get_all(
self.context, filters=db_filters, marker=marker, limit=limit,
sort_key=sort_key, sort_dir=sort_dir)
images = []
for db_api_image in db_api_images:
tags = self.db_api.image_tag_get_all(self.context,
db_api_image['id'])
image = self._format_image_from_db(dict(db_api_image), tags)
images.append(image)
return images
def _translate_filters(self, filters):
db_filters = {}
if filters is None:
return None
for key, value in filters.iteritems():
if key == 'visibility':
db_filters['is_public'] = value == 'public'
else:
db_filters[key] = value
return db_filters
def _format_image_from_db(self, db_image, db_tags):
visibility = 'public' if db_image['is_public'] else 'private'
properties = {}
for prop in db_image.pop('properties'):
# NOTE(markwash) db api requires us to filter deleted
if not prop['deleted']:
properties[prop['name']] = prop['value']
return glance.domain.Image(
image_id=db_image['id'],
name=db_image['name'],
status=db_image['status'],
created_at=db_image['created_at'],
updated_at=db_image['updated_at'],
visibility=visibility,
min_disk=db_image['min_disk'],
min_ram=db_image['min_ram'],
protected=db_image['protected'],
location=db_image['location'],
checksum=db_image['checksum'],
owner=db_image['owner'],
disk_format=db_image['disk_format'],
container_format=db_image['container_format'],
size=db_image['size'],
extra_properties=properties,
tags=db_tags
)
def _format_image_to_db(self, image):
return {
'id': image.image_id,
'name': image.name,
'status': image.status,
'created_at': image.created_at,
'min_disk': image.min_disk,
'min_ram': image.min_ram,
'protected': image.protected,
'location': image.location,
'checksum': image.checksum,
'owner': image.owner,
'disk_format': image.disk_format,
'container_format': image.container_format,
'size': image.size,
'is_public': image.visibility == 'public',
'properties': dict(image.extra_properties),
}
def add(self, image):
image_values = self._format_image_to_db(image)
new_values = self.db_api.image_create(self.context, image_values)
self.db_api.image_tag_set_all(self.context,
image.image_id, image.tags)
image.created_at = new_values['created_at']
image.updated_at = new_values['updated_at']
def save(self, image):
image_values = self._format_image_to_db(image)
try:
new_values = self.db_api.image_update(self.context,
image.image_id,
image_values,
purge_props=True)
except (exception.NotFound, exception.Forbidden):
raise exception.NotFound(image_id=image.image_id)
self.db_api.image_tag_set_all(self.context, image.image_id,
image.tags)
image.updated_at = new_values['updated_at']
def remove(self, image):
image_values = self._format_image_to_db(image)
try:
self.db_api.image_update(self.context, image.image_id,
image_values, purge_props=True)
except (exception.NotFound, exception.Forbidden):
raise exception.NotFound(image_id=image.image_id)
# NOTE(markwash): don't update tags?
new_values = self.db_api.image_destroy(self.context, image.image_id)
image.updated_at = new_values['updated_at']

View File

@ -167,7 +167,7 @@ def _do_pagination(context, images, marker, limit, show_deleted):
start = 0
else:
# Check that the image is accessible
image_get(context, marker, force_show_deleted=show_deleted)
_image_get(context, marker, force_show_deleted=show_deleted)
for i, image in enumerate(images):
if image['id'] == marker:
@ -191,8 +191,7 @@ def _sort_images(images, sort_key, sort_dir):
return images
@log_call
def image_get(context, image_id, session=None, force_show_deleted=False):
def _image_get(context, image_id, force_show_deleted=False):
try:
image = DATA['images'][image_id]
except KeyError:
@ -210,6 +209,12 @@ def image_get(context, image_id, session=None, force_show_deleted=False):
return image
@log_call
def image_get(context, image_id, session=None, force_show_deleted=False):
image = _image_get(context, image_id, force_show_deleted)
return copy.deepcopy(image)
@log_call
def image_get_all(context, filters=None, marker=None, limit=None,
sort_key='created_at', sort_dir='desc'):
@ -224,7 +229,7 @@ def image_get_all(context, filters=None, marker=None, limit=None,
@log_call
def image_property_create(context, values):
image = image_get(context, values['image_id'])
image = _image_get(context, values['image_id'])
prop = _image_property_format(values['image_id'],
values['name'],
values['value'])
@ -351,13 +356,14 @@ def image_destroy(context, image_id):
try:
DATA['images'][image_id]['deleted'] = True
DATA['images'][image_id]['deleted_at'] = timeutils.utcnow()
return copy.deepcopy(DATA['images'][image_id])
except KeyError:
raise exception.NotFound()
@log_call
def image_tag_get_all(context, image_id):
image_get(context, image_id)
_image_get(context, image_id)
return DATA['tags'].get(image_id, [])

View File

@ -579,7 +579,7 @@ def _image_update(context, values, image_id, purge_props=False):
# Perform authorization check
check_mutate_authorization(context, image_ref)
else:
if 'size' in values:
if values.get('size') is not None:
values['size'] = int(values['size'])
if 'min_ram' in values:

View File

@ -0,0 +1,176 @@
# Copyright 2012 OpenStack LLC.
# 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.
import glance.context
from glance.common import exception
import glance.db
from glance.openstack.common import uuidutils
import glance.tests.unit.utils as unit_test_utils
import glance.tests.utils as test_utils
UUID1 = 'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d'
UUID2 = 'a85abd86-55b3-4d5b-b0b4-5d0a6e6042fc'
UUID3 = '971ec09a-8067-4bc8-a91f-ae3557f1c4c7'
UUID4 = '6bbe7cc2-eae7-4c0f-b50d-a7160b0c6a86'
TENANT1 = '6838eb7b-6ded-434a-882c-b344c77fe8df'
TENANT2 = '2c014f32-55eb-467d-8fcb-4bd706012f81'
TENANT3 = '5a3e60e8-cfa9-4a9e-a90a-62b42cea92b8'
TENANT4 = 'c6c87f25-8a94-47ed-8c83-053c25f42df4'
USER1 = '54492ba0-f4df-4e4e-be62-27f4d76b29cf'
def _db_fixture(id, **kwargs):
obj = {
'id': id,
'name': None,
'is_public': False,
'properties': {},
'checksum': None,
'owner': None,
'status': 'queued',
'tags': [],
'size': None,
'location': None,
'protected': False,
'disk_format': None,
'container_format': None,
'deleted': False,
'min_ram': None,
'min_disk': None,
}
obj.update(kwargs)
return obj
class TestImageRepo(test_utils.BaseTestCase):
def setUp(self):
self.db = unit_test_utils.FakeDB()
self.db.reset()
self.context = glance.context.RequestContext(
user=USER1, tenant=TENANT1)
self.image_repo = glance.db.ImageRepo(self.context, self.db)
self.image_factory = glance.domain.ImageFactory()
self._create_images()
super(TestImageRepo, self).setUp()
def _create_images(self):
self.db.reset()
self.images = [
_db_fixture(UUID1, owner=TENANT1, name='1', size=256,
is_public=True, status='active'),
_db_fixture(UUID2, owner=TENANT1, name='2',
size=512, is_public=False),
_db_fixture(UUID3, owner=TENANT3, name='3',
size=1024, is_public=True),
_db_fixture(UUID4, owner=TENANT4, name='4', size=2048),
]
[self.db.image_create(None, image) for image in self.images]
self.db.image_tag_set_all(None, UUID1, ['ping', 'pong'])
def test_get(self):
image = self.image_repo.get(UUID1)
self.assertEquals(image.image_id, UUID1)
self.assertEquals(image.name, '1')
self.assertEquals(image.tags, set(['ping', 'pong']))
self.assertEquals(image.visibility, 'public')
self.assertEquals(image.status, 'active')
self.assertEquals(image.size, 256)
self.assertEquals(image.owner, TENANT1)
def test_get_not_found(self):
self.assertRaises(exception.NotFound, self.image_repo.get,
uuidutils.generate_uuid())
def test_get_forbidden(self):
self.assertRaises(exception.NotFound, self.image_repo.get, UUID4)
def test_list(self):
images = self.image_repo.list()
image_ids = set([i.image_id for i in images])
self.assertEqual(set([UUID1, UUID2, UUID3]), image_ids)
def test_list_with_marker(self):
full_images = self.image_repo.list()
full_ids = [i.image_id for i in full_images]
marked_images = self.image_repo.list(marker=full_ids[0])
actual_ids = [i.image_id for i in marked_images]
self.assertEqual(actual_ids, full_ids[1:])
def test_list_with_last_marker(self):
images = self.image_repo.list()
marked_images = self.image_repo.list(marker=images[-1].image_id)
self.assertEqual(len(marked_images), 0)
def test_limited_list(self):
limited_images = self.image_repo.list(limit=2)
self.assertEqual(len(limited_images), 2)
def test_list_with_marker_and_limit(self):
full_images = self.image_repo.list()
full_ids = [i.image_id for i in full_images]
marked_images = self.image_repo.list(marker=full_ids[0], limit=1)
actual_ids = [i.image_id for i in marked_images]
self.assertEqual(actual_ids, full_ids[1:2])
def test_list_private_images(self):
filters = {'visibility': 'private'}
images = self.image_repo.list(filters=filters)
image_ids = set([i.image_id for i in images])
self.assertEqual(set([UUID2]), image_ids)
def test_list_public_images(self):
filters = {'visibility': 'public'}
images = self.image_repo.list(filters=filters)
image_ids = set([i.image_id for i in images])
self.assertEqual(set([UUID1, UUID3]), image_ids)
def test_sorted_list(self):
images = self.image_repo.list(sort_key='size', sort_dir='asc')
image_ids = [i.image_id for i in images]
self.assertEqual([UUID1, UUID2, UUID3], image_ids)
def test_add_image(self):
image = self.image_factory.new_image(name='added image')
self.assertEqual(image.updated_at, image.created_at)
self.image_repo.add(image)
self.assertTrue(image.updated_at > image.created_at)
retreived_image = self.image_repo.get(image.image_id)
self.assertEqual(retreived_image.name, 'added image')
self.assertEqual(retreived_image.updated_at, image.updated_at)
def test_save_image(self):
image = self.image_repo.get(UUID1)
original_update_time = image.updated_at
image.name = 'foo'
image.tags = ['king', 'kong']
self.image_repo.save(image)
current_update_time = image.updated_at
self.assertTrue(current_update_time > original_update_time)
image = self.image_repo.get(UUID1)
self.assertEqual(image.name, 'foo')
self.assertEqual(image.tags, set(['king', 'kong']))
self.assertEqual(image.updated_at, current_update_time)
def test_remove_image(self):
image = self.image_repo.get(UUID1)
previous_update_time = image.updated_at
self.image_repo.remove(image)
self.assertTrue(image.updated_at > previous_update_time)
self.assertRaises(exception.NotFound, self.image_repo.get, UUID1)