Move to python-glanceclient.
This depends on https://review.openstack.org/#/c/6506/ for image filtering support. Do not merge this until that has been merged. Change-Id: I12e420f153b7b8323956e741bf9a202e31daa3b5
This commit is contained in:
parent
5864097f5a
commit
0b94c431ab
@ -20,123 +20,45 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import urlparse
|
||||
|
||||
from django.utils.decorators import available_attrs
|
||||
from glanceclient.v1 import client as glance_client
|
||||
|
||||
from glance import client as glance_client
|
||||
from glance.common import exception as glance_exception
|
||||
|
||||
from horizon.api.base import APIDictWrapper, url_for
|
||||
from horizon.api.base import url_for
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def catch_glance_exception(func):
|
||||
"""
|
||||
The glance client sometimes throws ``Exception`` classed exceptions for
|
||||
HTTP communication issues. Catch those, and rethrow them as
|
||||
``glance_client.ClientConnectionErrors`` so that we can do something
|
||||
useful with them.
|
||||
"""
|
||||
# TODO(johnp): Remove this once Bug 952618 is fixed in the glance client.
|
||||
@functools.wraps(func, assigned=available_attrs(func))
|
||||
def inner_func(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as exc:
|
||||
exc_message = str(exc)
|
||||
if('Unknown error occurred' in exc_message or
|
||||
'Internal Server error' in exc_message):
|
||||
raise glance_exception.ClientConnectionError(exc_message)
|
||||
raise
|
||||
return inner_func
|
||||
|
||||
|
||||
class Image(APIDictWrapper):
|
||||
"""
|
||||
Wrapper around glance image dictionary to make it object-like and provide
|
||||
access to image properties.
|
||||
"""
|
||||
_attrs = ['checksum', 'container_format', 'created_at', 'deleted',
|
||||
'deleted_at', 'disk_format', 'id', 'is_public', 'location',
|
||||
'name', 'properties', 'size', 'status', 'updated_at', 'owner']
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
if attrname == "properties":
|
||||
if not hasattr(self, "_properties"):
|
||||
properties_dict = super(Image, self).__getattr__(attrname)
|
||||
self._properties = ImageProperties(properties_dict)
|
||||
return self._properties
|
||||
else:
|
||||
return super(Image, self).__getattr__(attrname)
|
||||
|
||||
|
||||
class ImageProperties(APIDictWrapper):
|
||||
"""
|
||||
Wrapper around glance image properties dictionary to make it object-like.
|
||||
"""
|
||||
_attrs = ['architecture', 'image_location', 'image_state', 'kernel_id',
|
||||
'project_id', 'ramdisk_id', 'image_type']
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def glanceclient(request):
|
||||
o = urlparse.urlparse(url_for(request, 'image'))
|
||||
LOG.debug('glanceclient connection created for host "%s:%d"' %
|
||||
(o.hostname, o.port))
|
||||
return glance_client.Client(o.hostname,
|
||||
o.port,
|
||||
auth_tok=request.user.token)
|
||||
url = "://".join((o.scheme, o.netloc))
|
||||
LOG.debug('glanceclient connection created using token "%s" and url "%s"' %
|
||||
(request.user.token, url))
|
||||
return glance_client.Client(endpoint=url, token=request.user.token)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_create(request, image_meta, image_file):
|
||||
return Image(glanceclient(request).add_image(image_meta, image_file))
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_delete(request, image_id):
|
||||
return glanceclient(request).delete_image(image_id)
|
||||
return glanceclient(request).images.delete(image_id)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_get(request, image_id):
|
||||
"""
|
||||
Returns the actual image file from Glance for image with
|
||||
supplied identifier
|
||||
"""
|
||||
return glanceclient(request).get_image(image_id)[1]
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_get_meta(request, image_id):
|
||||
"""
|
||||
Returns an Image object populated with metadata for image
|
||||
with supplied identifier.
|
||||
"""
|
||||
return Image(glanceclient(request).get_image_meta(image_id))
|
||||
return glanceclient(request).images.get(image_id)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_list_detailed(request):
|
||||
return [Image(i) for i in glanceclient(request).get_images_detailed()]
|
||||
return glanceclient(request).images.list()
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_update(request, image_id, image_meta=None):
|
||||
image_meta = image_meta and image_meta or {}
|
||||
return Image(glanceclient(request).update_image(image_id,
|
||||
image_meta=image_meta))
|
||||
def image_update(request, image_id, **kwargs):
|
||||
return glanceclient(request).images.update(image_id, **kwargs)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def snapshot_list_detailed(request):
|
||||
filters = {}
|
||||
filters['property-image_type'] = 'snapshot'
|
||||
filters['is_public'] = 'none'
|
||||
return [Image(i) for i in glanceclient(request)
|
||||
.get_images_detailed(filters=filters)]
|
||||
filters = {'property-image_type': 'snapshot'}
|
||||
return glanceclient(request).images.list(filters=filters)
|
||||
|
@ -95,12 +95,12 @@ class Server(APIResourceWrapper):
|
||||
|
||||
@property
|
||||
def image_name(self):
|
||||
from glance.common import exception as glance_exceptions
|
||||
from glanceclient.common import exceptions as glance_exceptions
|
||||
from horizon.api import glance
|
||||
try:
|
||||
image = glance.image_get_meta(self.request, self.image['id'])
|
||||
image = glance.image_get(self.request, self.image['id'])
|
||||
return image.name
|
||||
except glance_exceptions.NotFound:
|
||||
except glance_exceptions.ClientException:
|
||||
return "(not found)"
|
||||
|
||||
@property
|
||||
|
@ -66,13 +66,15 @@ class UpdateImageForm(forms.SelfHandlingForm):
|
||||
widget=forms.TextInput(
|
||||
attrs={'readonly': 'readonly'}
|
||||
))
|
||||
public = forms.BooleanField(label=_("Public"),
|
||||
required=False)
|
||||
|
||||
def handle(self, request, data):
|
||||
# TODO add public flag to image meta properties
|
||||
image_id = data['image_id']
|
||||
error_updating = _('Unable to update image "%s".')
|
||||
|
||||
meta = {'is_public': True,
|
||||
meta = {'is_public': data['public'],
|
||||
'disk_format': data['disk_format'],
|
||||
'container_format': data['container_format'],
|
||||
'name': data['name'],
|
||||
@ -85,7 +87,7 @@ class UpdateImageForm(forms.SelfHandlingForm):
|
||||
meta['properties']['architecture'] = data['architecture']
|
||||
|
||||
try:
|
||||
api.image_update(request, image_id, meta)
|
||||
api.image_update(request, image_id, **meta)
|
||||
messages.success(request, _('Image was successfully updated.'))
|
||||
except:
|
||||
exceptions.handle(request, error_updating % image_id)
|
||||
|
@ -30,7 +30,7 @@ class OverviewTab(tabs.Tab):
|
||||
def get_context_data(self, request):
|
||||
image_id = self.tab_group.kwargs['image_id']
|
||||
try:
|
||||
image = api.glance.image_get_meta(self.request, image_id)
|
||||
image = api.glance.image_get(self.request, image_id)
|
||||
except:
|
||||
redirect = reverse('horizon:nova:images_and_snapshots:index')
|
||||
exceptions.handle(request,
|
||||
|
@ -21,7 +21,7 @@
|
||||
from django import http
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from glance.common import exception as glance_exception
|
||||
from glanceclient.common import exceptions as glance_exception
|
||||
|
||||
from horizon import api
|
||||
from horizon import test
|
||||
@ -39,14 +39,14 @@ class ImageViewTests(test.TestCase):
|
||||
image = self.images.first()
|
||||
quota_usages = self.quota_usages.first()
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
self.mox.StubOutWithMock(api, 'tenant_quota_usages')
|
||||
# Two flavor_list calls, however, flavor_list is now memoized.
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.image_get(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_usages)
|
||||
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
|
||||
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
|
||||
@ -79,7 +79,7 @@ class ImageViewTests(test.TestCase):
|
||||
volume_choice = "%s:vol" % volume.id
|
||||
block_device_mapping = {device_name: u"%s::0" % volume_choice}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
@ -90,7 +90,7 @@ class ImageViewTests(test.TestCase):
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
|
||||
api.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.image_get(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
|
||||
api.server_create(IsA(http.HttpRequest),
|
||||
server.name,
|
||||
@ -124,13 +124,13 @@ class ImageViewTests(test.TestCase):
|
||||
def test_launch_flavorlist_error(self):
|
||||
image = self.images.first()
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
self.mox.StubOutWithMock(api, 'tenant_quota_usages')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.image_get_meta(IsA(http.HttpRequest),
|
||||
api.image_get(IsA(http.HttpRequest),
|
||||
image.id).AndReturn(image)
|
||||
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(
|
||||
self.quota_usages.first())
|
||||
@ -151,13 +151,13 @@ class ImageViewTests(test.TestCase):
|
||||
def test_launch_keypairlist_error(self):
|
||||
image = self.images.first()
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
self.mox.StubOutWithMock(api, 'tenant_quota_usages')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.image_get(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(
|
||||
self.quota_usages.first())
|
||||
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
|
||||
@ -183,7 +183,7 @@ class ImageViewTests(test.TestCase):
|
||||
sec_group = self.security_groups.first()
|
||||
USER_DATA = 'userData'
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
@ -194,7 +194,7 @@ class ImageViewTests(test.TestCase):
|
||||
api.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list())
|
||||
api.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.image_get_meta(IgnoreArg(), image.id).AndReturn(image)
|
||||
api.image_get(IgnoreArg(), image.id).AndReturn(image)
|
||||
api.volume_list(IgnoreArg()).AndReturn(self.volumes.list())
|
||||
exc = keystone_exceptions.ClientException('Failed')
|
||||
api.server_create(IsA(http.HttpRequest),
|
||||
@ -233,7 +233,7 @@ class ImageViewTests(test.TestCase):
|
||||
device_name = u'vda'
|
||||
volume_choice = "%s:vol" % volume.id
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
@ -247,7 +247,7 @@ class ImageViewTests(test.TestCase):
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
|
||||
api.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.image_get(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
|
||||
api.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
api.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
@ -272,8 +272,8 @@ class ImageViewTests(test.TestCase):
|
||||
|
||||
def test_image_detail_get(self):
|
||||
image = self.images.first()
|
||||
self.mox.StubOutWithMock(api.glance, 'image_get_meta')
|
||||
api.glance.image_get_meta(IsA(http.HttpRequest), str(image.id)) \
|
||||
self.mox.StubOutWithMock(api.glance, 'image_get')
|
||||
api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \
|
||||
.AndReturn(self.images.first())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -286,12 +286,12 @@ class ImageViewTests(test.TestCase):
|
||||
|
||||
def test_image_detail_get_with_exception(self):
|
||||
image = self.images.first()
|
||||
self.mox.StubOutWithMock(api.glance, 'image_get_meta')
|
||||
api.glance.image_get_meta(IsA(http.HttpRequest), str(image.id)) \
|
||||
.AndRaise(glance_exception.NotFound)
|
||||
self.mox.StubOutWithMock(api.glance, 'image_get')
|
||||
api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \
|
||||
.AndRaise(glance_exception.ClientException('Error'))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(
|
||||
reverse('horizon:nova:images_and_snapshots:images:detail',
|
||||
args=[image.id]))
|
||||
url = reverse('horizon:nova:images_and_snapshots:images:detail',
|
||||
args=[image.id])
|
||||
res = self.client.get(url)
|
||||
self.assertRedirectsNoFollow(res, IMAGES_INDEX_URL)
|
||||
|
@ -55,7 +55,7 @@ class LaunchView(forms.ModalFormView):
|
||||
def get_object(self, *args, **kwargs):
|
||||
image_id = self.kwargs["image_id"]
|
||||
try:
|
||||
self.object = api.image_get_meta(self.request, image_id)
|
||||
self.object = api.image_get(self.request, image_id)
|
||||
except:
|
||||
msg = _('Unable to retrieve image "%s".') % image_id
|
||||
redirect = reverse('horizon:nova:images_and_snapshots:index')
|
||||
@ -155,22 +155,26 @@ class UpdateView(forms.ModalFormView):
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
try:
|
||||
self.object = api.image_get_meta(self.request, kwargs['image_id'])
|
||||
self.object = api.image_get(self.request, kwargs['image_id'])
|
||||
except:
|
||||
msg = _('Unable to retrieve image "%s".') % kwargs['image_id']
|
||||
msg = _('Unable to retrieve image.')
|
||||
redirect = reverse('horizon:nova:images_and_snapshots:index')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
return self.object
|
||||
|
||||
def get_initial(self):
|
||||
properties = self.object['properties']
|
||||
properties = self.object.properties
|
||||
# NOTE(gabriel): glanceclient currently treats "is_public" as a string
|
||||
# rather than a boolean. This should be fixed in the client.
|
||||
public = self.object.is_public == "True"
|
||||
return {'image_id': self.kwargs['image_id'],
|
||||
'name': self.object.get('name', ''),
|
||||
'name': self.object.name,
|
||||
'kernel': properties.get('kernel_id', ''),
|
||||
'ramdisk': properties.get('ramdisk_id', ''),
|
||||
'architecture': properties.get('architecture', ''),
|
||||
'container_format': self.object.get('container_format', ''),
|
||||
'disk_format': self.object.get('disk_format', ''), }
|
||||
'container_format': self.object.container_format,
|
||||
'disk_format': self.object.disk_format,
|
||||
'public': public}
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
|
@ -22,7 +22,7 @@
|
||||
from copy import deepcopy
|
||||
from django import http
|
||||
from django.core.urlresolvers import reverse
|
||||
from glance.common import exception as glance_exception
|
||||
from glanceclient.common import exceptions as glance_exception
|
||||
from mox import IsA
|
||||
|
||||
from horizon import api
|
||||
@ -61,10 +61,10 @@ class ImagesAndSnapshotsTests(test.TestCase):
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
|
||||
|
||||
def test_index_client_conn_error(self):
|
||||
def test_index_error(self):
|
||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||
exc = glance_exception.ClientConnectionError('clientConnError')
|
||||
exc = glance_exception.ClientException('error')
|
||||
api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exc)
|
||||
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.snapshots.list())
|
||||
|
@ -46,8 +46,8 @@ class IndexView(tables.MultiTableView):
|
||||
try:
|
||||
all_images = api.image_list_detailed(self.request)
|
||||
images = [im for im in all_images
|
||||
if im['container_format'] not in ['aki', 'ari'] and
|
||||
getattr(im.properties, "image_type", '') != "snapshot"]
|
||||
if im.container_format not in ['aki', 'ari'] and
|
||||
im.properties.get("image_type", '') != "snapshot"]
|
||||
except:
|
||||
images = []
|
||||
exceptions.handle(self.request, _("Unable to retrieve images."))
|
||||
|
@ -25,7 +25,7 @@ from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import ugettext as _
|
||||
from cloudfiles import errors as swiftclient
|
||||
from glance.common import exception as glanceclient
|
||||
from glanceclient.common import exceptions as glanceclient
|
||||
from keystoneclient import exceptions as keystoneclient
|
||||
from novaclient import exceptions as novaclient
|
||||
|
||||
@ -128,7 +128,7 @@ UNAUTHORIZED = (keystoneclient.Unauthorized,
|
||||
novaclient.Unauthorized,
|
||||
novaclient.Forbidden,
|
||||
glanceclient.AuthorizationFailure,
|
||||
glanceclient.NotAuthorized,
|
||||
glanceclient.Unauthorized,
|
||||
swiftclient.AuthenticationFailed,
|
||||
swiftclient.AuthenticationError)
|
||||
UNAUTHORIZED += tuple(EXCEPTION_CONFIG.get('unauthorized', []))
|
||||
@ -146,7 +146,7 @@ RECOVERABLE = (keystoneclient.ClientException,
|
||||
# AuthorizationFailure is raised when Keystone is "unavailable".
|
||||
keystoneclient.AuthorizationFailure,
|
||||
novaclient.ClientException,
|
||||
glanceclient.GlanceException,
|
||||
glanceclient.ClientException,
|
||||
swiftclient.Error,
|
||||
AlreadyExists)
|
||||
RECOVERABLE += tuple(EXCEPTION_CONFIG.get('recoverable', []))
|
||||
|
@ -27,7 +27,7 @@ from django.conf import settings
|
||||
from django.contrib.messages.storage import default_storage
|
||||
from django.core.handlers import wsgi
|
||||
from django.test.client import RequestFactory
|
||||
from glance import client as glance_client
|
||||
from glanceclient.v1 import client as glance_client
|
||||
from keystoneclient.v2_0 import client as keystone_client
|
||||
from novaclient.v1_1 import client as nova_client
|
||||
import httplib2
|
||||
@ -299,7 +299,6 @@ class APITestCase(TestCase):
|
||||
if not hasattr(self, "glanceclient"):
|
||||
self.mox.StubOutWithMock(glance_client, 'Client')
|
||||
self.glanceclient = self.mox.CreateMock(glance_client.Client)
|
||||
self.glanceclient.token = self.tokens.first().id
|
||||
return self.glanceclient
|
||||
|
||||
def stub_swiftclient(self, expected_calls=1):
|
||||
|
@ -18,100 +18,19 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from glance.common import exception as glance_exception
|
||||
|
||||
from horizon import api
|
||||
from horizon import test
|
||||
|
||||
|
||||
class GlanceApiTests(test.APITestCase):
|
||||
def test_get_glanceclient(self):
|
||||
""" Verify the client connection method does what we expect. """
|
||||
# Replace the original client which is stubbed out in setUp()
|
||||
api.glance.glanceclient = self._original_glanceclient
|
||||
|
||||
client = api.glance.glanceclient(self.request)
|
||||
self.assertEqual(client.auth_tok, self.tokens.first().id)
|
||||
|
||||
def test_image_get_meta(self):
|
||||
""" Verify "get" returns our custom Image class. """
|
||||
image = self.images.get(id='1')
|
||||
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_image_meta(image.id).AndReturn(image)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.image_get_meta(self.request, image.id)
|
||||
self.assertIsInstance(ret_val, api.glance.Image)
|
||||
self.assertEqual(ret_val._apidict, image)
|
||||
|
||||
def test_image_list_detailed(self):
|
||||
""" Verify "list" returns our custom Image class. """
|
||||
def test_snapshot_list_detailed(self):
|
||||
images = self.images.list()
|
||||
filters = {'property-image_type': 'snapshot'}
|
||||
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndReturn(images)
|
||||
glanceclient.images = self.mox.CreateMockAnything()
|
||||
glanceclient.images.list(filters=filters).AndReturn(images)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.image_list_detailed(self.request)
|
||||
for image in ret_val:
|
||||
self.assertIsInstance(image, api.glance.Image)
|
||||
self.assertIn(image._apidict, images)
|
||||
|
||||
def test_glance_exception_wrapping_for_internal_server_errors(self):
|
||||
"""
|
||||
Verify that generic "Exception" classed exceptions from the glance
|
||||
client's HTTP Internal Service Errors get converted to
|
||||
ClientConnectionError's.
|
||||
"""
|
||||
# TODO(johnp): Remove once Bug 952618 is fixed in the glance client.
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndRaise(
|
||||
Exception("Internal Server error: "))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
with self.assertRaises(glance_exception.ClientConnectionError):
|
||||
api.image_list_detailed(self.request)
|
||||
|
||||
def test_glance_exception_wrapping_for_generic_http_errors(self):
|
||||
"""
|
||||
Verify that generic "Exception" classed exceptions from the glance
|
||||
client's HTTP errors get converted to ClientConnectionError's.
|
||||
"""
|
||||
# TODO(johnp): Remove once Bug 952618 is fixed in the glance client.
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndRaise(
|
||||
Exception("Unknown error occurred! 503 Service Unavailable"))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
with self.assertRaises(glance_exception.ClientConnectionError):
|
||||
api.image_list_detailed(self.request)
|
||||
|
||||
|
||||
class ImageWrapperTests(test.TestCase):
|
||||
""" Tests for wrapper classes since they have extra logic attached. """
|
||||
WITHOUT_PROPERTIES = {'size': 100}
|
||||
WITH_PROPERTIES = {'properties': {'image_state': 'running'},
|
||||
'size': 100}
|
||||
|
||||
def test_get_properties(self):
|
||||
image = api.Image(self.WITH_PROPERTIES)
|
||||
image_props = image.properties
|
||||
self.assertIsInstance(image_props, api.ImageProperties)
|
||||
self.assertEqual(image_props.image_state, 'running')
|
||||
|
||||
def test_get_other(self):
|
||||
image = api.Image(self.WITH_PROPERTIES)
|
||||
self.assertEqual(image.size, 100)
|
||||
|
||||
def test_get_properties_missing(self):
|
||||
image = api.Image(self.WITHOUT_PROPERTIES)
|
||||
with self.assertRaises(AttributeError):
|
||||
image.properties
|
||||
|
||||
def test_get_other_missing(self):
|
||||
image = api.Image(self.WITHOUT_PROPERTIES)
|
||||
with self.assertRaises(AttributeError):
|
||||
self.assertNotIn('missing', image._attrs,
|
||||
msg="Test assumption broken. Find new missing attribute")
|
||||
image.missing
|
||||
# No assertions are necessary. Verification is handled by mox.
|
||||
api.glance.snapshot_list_detailed(self.request)
|
||||
|
@ -35,9 +35,9 @@ class ServerWrapperTests(test.TestCase):
|
||||
self.assertEqual(server.id, self.servers.first().id)
|
||||
|
||||
def test_image_name(self):
|
||||
image = api.Image(self.images.first())
|
||||
self.mox.StubOutWithMock(api.glance, 'image_get_meta')
|
||||
api.glance.image_get_meta(IsA(http.HttpRequest),
|
||||
image = self.images.first()
|
||||
self.mox.StubOutWithMock(api.glance, 'image_get')
|
||||
api.glance.image_get(IsA(http.HttpRequest),
|
||||
image.id).AndReturn(image)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
|
@ -12,7 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from horizon.api import glance
|
||||
from glanceclient.v1.images import Image, ImageManager
|
||||
|
||||
from .utils import TestDataContainer
|
||||
|
||||
|
||||
@ -23,24 +24,21 @@ def data(TEST):
|
||||
# Snapshots
|
||||
snapshot_dict = {'name': u'snapshot',
|
||||
'container_format': u'ami',
|
||||
'id': 3}
|
||||
snapshot = glance.Image(snapshot_dict)
|
||||
snapshot_properties_dict = {'image_type': u'snapshot'}
|
||||
snapshot.properties = glance.ImageProperties(snapshot_properties_dict)
|
||||
'id': 3,
|
||||
'properties': {'image_type': u'snapshot'}}
|
||||
snapshot = Image(ImageManager(None), snapshot_dict)
|
||||
TEST.snapshots.add(snapshot)
|
||||
|
||||
# Images
|
||||
image_properties_dict = {'image_type': u'image'}
|
||||
image_dict = {'id': '1',
|
||||
'name': 'public_image',
|
||||
'container_format': 'novaImage'}
|
||||
public_image = glance.Image(image_dict)
|
||||
public_image.properties = glance.ImageProperties(image_properties_dict)
|
||||
'container_format': 'novaImage',
|
||||
'properties': {'image_type': u'image'}}
|
||||
public_image = Image(ImageManager(None), image_dict)
|
||||
|
||||
image_dict = {'id': '2',
|
||||
'name': 'private_image',
|
||||
'container_format': 'aki'}
|
||||
private_image = glance.Image(image_dict)
|
||||
private_image.properties = glance.ImageProperties(image_properties_dict)
|
||||
private_image = Image(ImageManager(None), image_dict)
|
||||
|
||||
TEST.images.add(public_image, private_image)
|
||||
|
@ -101,6 +101,10 @@ LOGGING = {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'openstack_dashboard': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'novaclient': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
@ -109,6 +113,10 @@ LOGGING = {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'glanceclient': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'nose.plugins.manager': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
|
@ -6,7 +6,7 @@ set -o errexit
|
||||
# Increment me any time the environment should be rebuilt.
|
||||
# This includes dependncy changes, directory renames, etc.
|
||||
# Simple integer secuence: 1, 2, 3...
|
||||
environment_version=15
|
||||
environment_version=16
|
||||
#--------------------------------------------------------#
|
||||
|
||||
function usage {
|
||||
|
@ -6,4 +6,4 @@ python-dateutil
|
||||
# Horizon Non-pip Requirements
|
||||
-e git+https://github.com/openstack/python-novaclient.git#egg=python-novaclient
|
||||
-e git+https://github.com/openstack/python-keystoneclient.git#egg=python-keystoneclient
|
||||
-e git+https://github.com/openstack/glance.git#egg=glance
|
||||
-e git+https://github.com/openstack/python-glanceclient.git#egg=python-glanceclient
|
||||
|
Loading…
Reference in New Issue
Block a user