From 0b94c431ab8e510b5fd4e7d1e8719c7d2055fa0f Mon Sep 17 00:00:00 2001 From: Gabriel Hurley Date: Thu, 12 Apr 2012 17:13:56 -0700 Subject: [PATCH] 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 --- horizon/api/glance.py | 104 +++--------------- horizon/api/nova.py | 6 +- .../nova/images_and_snapshots/images/forms.py | 6 +- .../nova/images_and_snapshots/images/tabs.py | 2 +- .../nova/images_and_snapshots/images/tests.py | 42 +++---- .../nova/images_and_snapshots/images/views.py | 18 +-- .../nova/images_and_snapshots/tests.py | 6 +- .../nova/images_and_snapshots/views.py | 4 +- horizon/exceptions.py | 6 +- horizon/test.py | 3 +- horizon/tests/api_tests/glance_tests.py | 93 +--------------- horizon/tests/api_tests/nova_tests.py | 6 +- horizon/tests/test_data/glance_data.py | 20 ++-- .../local/local_settings.py.example | 8 ++ run_tests.sh | 2 +- tools/pip-requires | 2 +- 16 files changed, 90 insertions(+), 238 deletions(-) diff --git a/horizon/api/glance.py b/horizon/api/glance.py index 1f8f8cd31..5fd84a8aa 100644 --- a/horizon/api/glance.py +++ b/horizon/api/glance.py @@ -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) diff --git a/horizon/api/nova.py b/horizon/api/nova.py index 91820d9fc..e87dac11f 100644 --- a/horizon/api/nova.py +++ b/horizon/api/nova.py @@ -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 diff --git a/horizon/dashboards/nova/images_and_snapshots/images/forms.py b/horizon/dashboards/nova/images_and_snapshots/images/forms.py index 3ae2b5f74..365119981 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/forms.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/forms.py @@ -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) diff --git a/horizon/dashboards/nova/images_and_snapshots/images/tabs.py b/horizon/dashboards/nova/images_and_snapshots/images/tabs.py index 78d71350e..ef4d30e02 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/tabs.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/tabs.py @@ -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, diff --git a/horizon/dashboards/nova/images_and_snapshots/images/tests.py b/horizon/dashboards/nova/images_and_snapshots/images/tests.py index cb1df2980..4cb04ac00 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/tests.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/tests.py @@ -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) diff --git a/horizon/dashboards/nova/images_and_snapshots/images/views.py b/horizon/dashboards/nova/images_and_snapshots/images/views.py index 36dcf335f..145f7e0cc 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/views.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/views.py @@ -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): diff --git a/horizon/dashboards/nova/images_and_snapshots/tests.py b/horizon/dashboards/nova/images_and_snapshots/tests.py index 124fad570..0d9f73e96 100644 --- a/horizon/dashboards/nova/images_and_snapshots/tests.py +++ b/horizon/dashboards/nova/images_and_snapshots/tests.py @@ -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()) diff --git a/horizon/dashboards/nova/images_and_snapshots/views.py b/horizon/dashboards/nova/images_and_snapshots/views.py index 82d0d0b9c..aef5e3663 100644 --- a/horizon/dashboards/nova/images_and_snapshots/views.py +++ b/horizon/dashboards/nova/images_and_snapshots/views.py @@ -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.")) diff --git a/horizon/exceptions.py b/horizon/exceptions.py index c5f245029..7af8a0a69 100644 --- a/horizon/exceptions.py +++ b/horizon/exceptions.py @@ -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', [])) diff --git a/horizon/test.py b/horizon/test.py index 1ca163336..d56c0f7e7 100644 --- a/horizon/test.py +++ b/horizon/test.py @@ -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): diff --git a/horizon/tests/api_tests/glance_tests.py b/horizon/tests/api_tests/glance_tests.py index f6960971f..65e4584d0 100644 --- a/horizon/tests/api_tests/glance_tests.py +++ b/horizon/tests/api_tests/glance_tests.py @@ -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) diff --git a/horizon/tests/api_tests/nova_tests.py b/horizon/tests/api_tests/nova_tests.py index dbdc3a9e0..0963e828e 100644 --- a/horizon/tests/api_tests/nova_tests.py +++ b/horizon/tests/api_tests/nova_tests.py @@ -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() diff --git a/horizon/tests/test_data/glance_data.py b/horizon/tests/test_data/glance_data.py index 82554ec7e..d307f91ad 100644 --- a/horizon/tests/test_data/glance_data.py +++ b/horizon/tests/test_data/glance_data.py @@ -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) diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index 0be278ac2..5e293150c 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -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, diff --git a/run_tests.sh b/run_tests.sh index 6b853c744..aca7a8024 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -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 { diff --git a/tools/pip-requires b/tools/pip-requires index db2d82376..9584143ff 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -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