From 856983fbcdecdeff4d370f47871c7e791e8b22da Mon Sep 17 00:00:00 2001 From: Gabriel Hurley Date: Wed, 9 May 2012 17:37:18 -0700 Subject: [PATCH] Allow proper log output during test runs. This involved a huge amount of cleanup to existing code in order to make sure that existing failures were properly fixed or silenced when intentional. Additionally, this introduces a set of pre-built exception instances for test purposes which are marked with the "silence_logging" attribute to denote expected failures which should not be logged in test output. Change-Id: I6b420382dcdc5792c7be4d727853f438154c641e --- horizon/base.py | 4 +- .../access_and_security/floating_ips/forms.py | 7 +- .../access_and_security/floating_ips/tests.py | 45 ++++++--- .../access_and_security/floating_ips/views.py | 2 +- .../access_and_security/keypairs/tests.py | 28 +++++- .../security_groups/forms.py | 7 +- .../security_groups/tests.py | 34 ++++--- .../nova/access_and_security/tests.py | 7 +- horizon/dashboards/nova/containers/tables.py | 26 ++---- horizon/dashboards/nova/containers/tests.py | 1 + .../nova/images_and_snapshots/images/tests.py | 42 ++++++--- .../images_and_snapshots/snapshots/tests.py | 8 +- .../nova/images_and_snapshots/tests.py | 22 ++++- .../instances_and_volumes/instances/tables.py | 4 +- .../instances_and_volumes/instances/tests.py | 84 ++++++++++++----- .../nova/instances_and_volumes/tests.py | 8 +- horizon/dashboards/nova/overview/tests.py | 3 +- .../dashboards/syspanel/instances/tests.py | 3 +- horizon/dashboards/syspanel/users/tables.py | 92 +++++++------------ horizon/dashboards/syspanel/users/tests.py | 18 ++-- horizon/exceptions.py | 25 ++++- horizon/test.py | 3 + horizon/tests/auth_tests.py | 5 +- horizon/tests/tabs_tests.py | 4 +- horizon/tests/test_data/exceptions.py | 36 ++++++++ horizon/tests/test_data/glance_data.py | 6 ++ horizon/tests/test_data/keystone_data.py | 3 +- horizon/tests/test_data/utils.py | 4 +- horizon/tests/testsettings.py | 16 +++- 29 files changed, 349 insertions(+), 198 deletions(-) create mode 100644 horizon/tests/test_data/exceptions.py diff --git a/horizon/base.py b/horizon/base.py index ed4d779af2..ddf055dcda 100644 --- a/horizon/base.py +++ b/horizon/base.py @@ -237,10 +237,10 @@ class Panel(HorizonComponent): return reverse('horizon:%s:%s:%s' % (self._registered_with.slug, self.slug, self.index_url_name)) - except: + except Exception as exc: # Logging here since this will often be called in a template # where the exception would be hidden. - LOG.exception("Error reversing absolute URL for %s." % self) + LOG.info("Error reversing absolute URL for %s: %s" % (self, exc)) raise @property diff --git a/horizon/dashboards/nova/access_and_security/floating_ips/forms.py b/horizon/dashboards/nova/access_and_security/floating_ips/forms.py index 8169c02ea1..4810b6e53b 100644 --- a/horizon/dashboards/nova/access_and_security/floating_ips/forms.py +++ b/horizon/dashboards/nova/access_and_security/floating_ips/forms.py @@ -24,7 +24,6 @@ import logging from django.contrib import messages from django import shortcuts from django.utils.translation import ugettext_lazy as _ -from novaclient import exceptions as novaclient_exceptions from horizon import api from horizon import exceptions @@ -65,9 +64,9 @@ class FloatingIpAssociate(forms.SelfHandlingForm): 'with Instance: %(inst)s') % {"ip": data['floating_ip'], "inst": data['instance_id']}) - except novaclient_exceptions.ClientException, e: - LOG.exception("ClientException in FloatingIpAssociate") - messages.error(request, _('Error associating Floating IP: %s') % e) + except: + exceptions.handle(request, + _('Unable to associate floating IP.')) return shortcuts.redirect('horizon:nova:access_and_security:index') diff --git a/horizon/dashboards/nova/access_and_security/floating_ips/tests.py b/horizon/dashboards/nova/access_and_security/floating_ips/tests.py index 74586f4bdd..cee9bf90e6 100644 --- a/horizon/dashboards/nova/access_and_security/floating_ips/tests.py +++ b/horizon/dashboards/nova/access_and_security/floating_ips/tests.py @@ -22,7 +22,6 @@ from django import http from django.core.urlresolvers import reverse from mox import IsA -from novaclient import exceptions as novaclient_exceptions from horizon import api from horizon import test @@ -35,9 +34,10 @@ NAMESPACE = "horizon:nova:access_and_security:floating_ips" class FloatingIpViewTests(test.TestCase): def test_associate(self): floating_ip = self.floating_ips.first() - self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api.nova, 'server_list') self.mox.StubOutWithMock(api, 'tenant_floating_ip_get') - api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) + api.nova.server_list(IsA(http.HttpRequest)) \ + .AndReturn(self.servers.list()) api.tenant_floating_ip_get(IsA(http.HttpRequest), floating_ip.id).AndReturn(floating_ip) self.mox.ReplayAll() @@ -50,11 +50,19 @@ class FloatingIpViewTests(test.TestCase): def test_associate_post(self): floating_ip = self.floating_ips.first() server = self.servers.first() - self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'security_group_list') self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') self.mox.StubOutWithMock(api, 'server_add_floating_ip') self.mox.StubOutWithMock(api, 'tenant_floating_ip_get') - api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) + self.mox.StubOutWithMock(api.nova, 'server_list') + self.mox.StubOutWithMock(api.nova, 'keypair_list') + + api.nova.keypair_list(IsA(http.HttpRequest)) \ + .AndReturn(self.keypairs.list()) + api.security_group_list(IsA(http.HttpRequest)) \ + .AndReturn(self.security_groups.list()) + api.nova.server_list(IsA(http.HttpRequest)) \ + .AndReturn(self.servers.list()) api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.server_add_floating_ip(IsA(http.HttpRequest), @@ -62,6 +70,8 @@ class FloatingIpViewTests(test.TestCase): floating_ip.id) api.tenant_floating_ip_get(IsA(http.HttpRequest), floating_ip.id).AndReturn(floating_ip) + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) self.mox.ReplayAll() form_data = {'instance_id': server.id, @@ -75,25 +85,29 @@ class FloatingIpViewTests(test.TestCase): def test_associate_post_with_exception(self): floating_ip = self.floating_ips.first() server = self.servers.first() - self.mox.StubOutWithMock(api, 'server_list') self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') self.mox.StubOutWithMock(api, 'security_group_list') self.mox.StubOutWithMock(api.nova, 'keypair_list') self.mox.StubOutWithMock(api, 'server_add_floating_ip') self.mox.StubOutWithMock(api, 'tenant_floating_ip_get') - api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) + self.mox.StubOutWithMock(api.nova, 'server_list') + + api.nova.server_list(IsA(http.HttpRequest)) \ + .AndReturn(self.servers.list()) api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.security_group_list(IsA(http.HttpRequest)) \ .AndReturn(self.security_groups.list()) api.nova.keypair_list(IsA(http.HttpRequest)) \ .AndReturn(self.keypairs.list()) - exc = novaclient_exceptions.ClientException('ClientException') api.server_add_floating_ip(IsA(http.HttpRequest), server.id, - floating_ip.id).AndRaise(exc) + floating_ip.id) \ + .AndRaise(self.exceptions.nova) api.tenant_floating_ip_get(IsA(http.HttpRequest), floating_ip.id).AndReturn(floating_ip) + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) self.mox.ReplayAll() url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id]) @@ -102,7 +116,6 @@ class FloatingIpViewTests(test.TestCase): 'floating_ip_id': floating_ip.id, 'floating_ip': floating_ip.ip, 'method': 'FloatingIpAssociate'}) - self.assertRaises(novaclient_exceptions.ClientException) self.assertRedirects(res, INDEX_URL) def test_disassociate_post(self): @@ -113,7 +126,10 @@ class FloatingIpViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') self.mox.StubOutWithMock(api, 'tenant_floating_ip_get') self.mox.StubOutWithMock(api, 'server_remove_floating_ip') + self.mox.StubOutWithMock(api.nova, 'server_list') + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) api.nova.keypair_list(IsA(http.HttpRequest)) \ .AndReturn(self.keypairs.list()) api.security_group_list(IsA(http.HttpRequest)) \ @@ -138,6 +154,10 @@ class FloatingIpViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') self.mox.StubOutWithMock(api, 'tenant_floating_ip_get') self.mox.StubOutWithMock(api, 'server_remove_floating_ip') + self.mox.StubOutWithMock(api.nova, 'server_list') + + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) api.nova.keypair_list(IsA(http.HttpRequest)) \ .AndReturn(self.keypairs.list()) api.security_group_list(IsA(http.HttpRequest)) \ @@ -145,13 +165,12 @@ class FloatingIpViewTests(test.TestCase): api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) - exc = novaclient_exceptions.ClientException('ClientException') api.server_remove_floating_ip(IsA(http.HttpRequest), server.id, - floating_ip.id).AndRaise(exc) + floating_ip.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() action = "floating_ips__disassociate__%s" % floating_ip.id res = self.client.post(INDEX_URL, {"action": action}) - self.assertRaises(novaclient_exceptions.ClientException) self.assertRedirectsNoFollow(res, INDEX_URL) diff --git a/horizon/dashboards/nova/access_and_security/floating_ips/views.py b/horizon/dashboards/nova/access_and_security/floating_ips/views.py index 07bd4c8948..864230fa2b 100644 --- a/horizon/dashboards/nova/access_and_security/floating_ips/views.py +++ b/horizon/dashboards/nova/access_and_security/floating_ips/views.py @@ -53,7 +53,7 @@ class AssociateView(forms.ModalFormView): def get_initial(self): try: - servers = api.server_list(self.request) + servers = api.nova.server_list(self.request) except: redirect = reverse('horizon:nova:access_and_security:index') exceptions.handle(self.request, diff --git a/horizon/dashboards/nova/access_and_security/keypairs/tests.py b/horizon/dashboards/nova/access_and_security/keypairs/tests.py index 226aac6d4f..b64f2315d2 100644 --- a/horizon/dashboards/nova/access_and_security/keypairs/tests.py +++ b/horizon/dashboards/nova/access_and_security/keypairs/tests.py @@ -21,7 +21,6 @@ from django import http from django.core.urlresolvers import reverse from mox import IsA -from novaclient import exceptions as novaclient_exceptions from horizon import api from horizon import test @@ -36,6 +35,16 @@ class KeyPairViewTests(test.TestCase): self.mox.StubOutWithMock(api.nova, 'keypair_list') self.mox.StubOutWithMock(api.nova, 'keypair_delete') + self.mox.StubOutWithMock(api, 'security_group_list') + self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') + self.mox.StubOutWithMock(api.nova, 'server_list') + + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) + api.security_group_list(IsA(http.HttpRequest)) \ + .AndReturn(self.security_groups.list()) + api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ + .AndReturn(self.floating_ips.list()) api.nova.keypair_list(IsA(http.HttpRequest)) \ .AndReturn(self.keypairs.list()) api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) @@ -49,11 +58,20 @@ class KeyPairViewTests(test.TestCase): keypair = self.keypairs.first() self.mox.StubOutWithMock(api.nova, 'keypair_list') self.mox.StubOutWithMock(api.nova, 'keypair_delete') + self.mox.StubOutWithMock(api, 'security_group_list') + self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') + self.mox.StubOutWithMock(api.nova, 'server_list') + + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) + api.security_group_list(IsA(http.HttpRequest)) \ + .AndReturn(self.security_groups.list()) + api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ + .AndReturn(self.floating_ips.list()) api.nova.keypair_list(IsA(http.HttpRequest)) \ .AndReturn(self.keypairs.list()) - exc = novaclient_exceptions.ClientException('clientException') api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \ - .AndRaise(exc) + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'keypairs__delete__%s' % keypair.name} @@ -93,10 +111,10 @@ class KeyPairViewTests(test.TestCase): def test_generate_keypair_exception(self): keypair = self.keypairs.first() - exc = novaclient_exceptions.ClientException('clientException') self.mox.StubOutWithMock(api, 'keypair_create') - api.keypair_create(IsA(http.HttpRequest), keypair.name).AndRaise(exc) + api.keypair_create(IsA(http.HttpRequest), keypair.name) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() context = {'keypair_name': keypair.name} diff --git a/horizon/dashboards/nova/access_and_security/security_groups/forms.py b/horizon/dashboards/nova/access_and_security/security_groups/forms.py index 8f0fe5ff12..f516cf3154 100644 --- a/horizon/dashboards/nova/access_and_security/security_groups/forms.py +++ b/horizon/dashboards/nova/access_and_security/security_groups/forms.py @@ -146,8 +146,7 @@ class AddRule(forms.SelfHandlingForm): data['source_group']) messages.success(request, _('Successfully added rule: %s') \ % unicode(rule)) - except novaclient_exceptions.ClientException, e: - LOG.exception("ClientException in AddRule") - messages.error(request, _('Error adding rule security group: %s') - % e.message) + except: + exceptions.handle(request, + _('Unable to add rule to security group.')) return shortcuts.redirect("horizon:nova:access_and_security:index") diff --git a/horizon/dashboards/nova/access_and_security/security_groups/tests.py b/horizon/dashboards/nova/access_and_security/security_groups/tests.py index a9da39542a..f19e8e3513 100644 --- a/horizon/dashboards/nova/access_and_security/security_groups/tests.py +++ b/horizon/dashboards/nova/access_and_security/security_groups/tests.py @@ -21,7 +21,7 @@ from django import http from django.conf import settings from django.core.urlresolvers import reverse -from novaclient import exceptions as novaclient_exceptions + from mox import IsA from horizon import api @@ -67,10 +67,10 @@ class SecurityGroupsViewTests(test.TestCase): def test_create_security_groups_post_exception(self): sec_group = self.security_groups.first() self.mox.StubOutWithMock(api, 'security_group_create') - exc = novaclient_exceptions.ClientException('ClientException') api.security_group_create(IsA(http.HttpRequest), sec_group.name, - sec_group.description).AndRaise(exc) + sec_group.description) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'method': 'CreateGroup', @@ -103,10 +103,19 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() self.mox.StubOutWithMock(api, 'security_group_get') - exc = novaclient_exceptions.ClientException('ClientException') - api.security_group_get(IsA(http.HttpRequest), - sec_group.id).AndRaise(exc) self.mox.StubOutWithMock(api, 'security_group_list') + self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') + self.mox.StubOutWithMock(api.nova, 'keypair_list') + self.mox.StubOutWithMock(api.nova, 'server_list') + + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) + api.nova.keypair_list(IsA(http.HttpRequest)) \ + .AndReturn(self.keypairs.list()) + api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ + .AndReturn(self.floating_ips.list()) + api.security_group_get(IsA(http.HttpRequest), + sec_group.id).AndRaise(self.exceptions.nova) api.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) api.security_group_list( @@ -122,6 +131,7 @@ class SecurityGroupsViewTests(test.TestCase): rule = self.security_group_rules.first() self.mox.StubOutWithMock(api, 'security_group_rule_create') + self.mox.StubOutWithMock(api, 'security_group_list') api.security_group_rule_create(IsA(http.HttpRequest), sec_group.id, rule.ip_protocol, @@ -129,7 +139,6 @@ class SecurityGroupsViewTests(test.TestCase): int(rule.to_port), rule.ip_range['cidr'], None).AndReturn(rule) - self.mox.StubOutWithMock(api, 'security_group_list') api.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) self.mox.ReplayAll() @@ -198,17 +207,16 @@ class SecurityGroupsViewTests(test.TestCase): sec_group = self.security_groups.first() sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - exc = novaclient_exceptions.ClientException('ClientException') self.mox.StubOutWithMock(api, 'security_group_rule_create') + self.mox.StubOutWithMock(api, 'security_group_list') api.security_group_rule_create(IsA(http.HttpRequest), sec_group.id, rule.ip_protocol, int(rule.from_port), int(rule.to_port), rule.ip_range['cidr'], - None).AndRaise(exc) - self.mox.StubOutWithMock(api, 'security_group_list') + None).AndRaise(self.exceptions.nova) api.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) self.mox.ReplayAll() @@ -241,9 +249,8 @@ class SecurityGroupsViewTests(test.TestCase): rule = self.security_group_rules.first() self.mox.StubOutWithMock(api, 'security_group_rule_delete') - exc = novaclient_exceptions.ClientException('ClientException') api.security_group_rule_delete(IsA(http.HttpRequest), - rule.id).AndRaise(exc) + rule.id).AndRaise(self.exceptions.nova) self.mox.ReplayAll() form_data = {"action": "rules__delete__%s" % rule.id} @@ -271,9 +278,8 @@ class SecurityGroupsViewTests(test.TestCase): sec_group = self.security_groups.get(name="other_group") self.mox.StubOutWithMock(api, 'security_group_delete') - exc = novaclient_exceptions.ClientException('ClientException') api.security_group_delete(IsA(http.HttpRequest), - sec_group.id).AndRaise(exc) + sec_group.id).AndRaise(self.exceptions.nova) self.mox.ReplayAll() diff --git a/horizon/dashboards/nova/access_and_security/tests.py b/horizon/dashboards/nova/access_and_security/tests.py index 1dcd408cb6..eb2978ca06 100644 --- a/horizon/dashboards/nova/access_and_security/tests.py +++ b/horizon/dashboards/nova/access_and_security/tests.py @@ -35,7 +35,10 @@ class AccessAndSecurityTests(test.TestCase): self.mox.StubOutWithMock(api, 'tenant_floating_ip_list') self.mox.StubOutWithMock(api, 'security_group_list') self.mox.StubOutWithMock(api.nova, 'keypair_list') + self.mox.StubOutWithMock(api.nova, 'server_list') + api.nova.server_list(IsA(http.HttpRequest), + all_tenants=True).AndReturn(self.servers.list()) api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(keypairs) api.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(floating_ips) @@ -66,10 +69,10 @@ class AccessAndSecurityTests(test.TestCase): self.servers.add(server3) self.mox.StubOutWithMock(api, 'tenant_floating_ip_get') - self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api.nova, 'server_list') api.tenant_floating_ip_get(IsA(http.HttpRequest), floating_ip.id).AndReturn(floating_ip) - api.server_list(IsA(http.HttpRequest)).AndReturn(servers) + api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers) self.mox.ReplayAll() res = self.client.get( diff --git a/horizon/dashboards/nova/containers/tables.py b/horizon/dashboards/nova/containers/tables.py index 27b915e269..1432f006fa 100644 --- a/horizon/dashboards/nova/containers/tables.py +++ b/horizon/dashboards/nova/containers/tables.py @@ -36,26 +36,12 @@ class DeleteContainer(tables.DeleteAction): data_type_plural = _("Containers") def delete(self, request, obj_id): - api.swift_delete_container(request, obj_id) - - def handle(self, table, request, object_ids): - # Overriden to show clearer error messages instead of generic message - deleted = [] - for obj_id in object_ids: - obj = table.get_object_by_id(obj_id) - try: - self.delete(request, obj_id) - deleted.append(obj) - except ContainerNotEmpty: - LOG.exception('Unable to delete container "%s".' % obj.name) - messages.error(request, - _('Unable to delete non-empty container: %s') % - obj.name) - if deleted: - messages.success(request, - _('Successfully deleted containers: %s') - % ", ".join([obj.name for obj in deleted])) - return shortcuts.redirect('horizon:nova:containers:index') + try: + api.swift_delete_container(request, obj_id) + except ContainerNotEmpty: + messages.error(request, + _('Containers must be empty before deletion.')) + raise class CreateContainer(tables.LinkAction): diff --git a/horizon/dashboards/nova/containers/tests.py b/horizon/dashboards/nova/containers/tests.py index 71b9be6fca..5256fe62c5 100644 --- a/horizon/dashboards/nova/containers/tests.py +++ b/horizon/dashboards/nova/containers/tests.py @@ -67,6 +67,7 @@ class ContainerViewTests(test.TestCase): container = self.containers.first() self.mox.StubOutWithMock(api, 'swift_delete_container') exc = ContainerNotEmpty('containerNotEmpty') + exc.silence_logging = True api.swift_delete_container(IsA(http.HttpRequest), container.name).AndRaise(exc) self.mox.ReplayAll() diff --git a/horizon/dashboards/nova/images_and_snapshots/images/tests.py b/horizon/dashboards/nova/images_and_snapshots/images/tests.py index dde2449cfe..53bd688792 100644 --- a/horizon/dashboards/nova/images_and_snapshots/images/tests.py +++ b/horizon/dashboards/nova/images_and_snapshots/images/tests.py @@ -21,13 +21,9 @@ from django import http from django.core.urlresolvers import reverse -from glanceclient.common import exceptions as glance_exception - from horizon import api from horizon import test -from keystoneclient import exceptions as keystone_exceptions - from mox import IgnoreArg, IsA @@ -45,6 +41,12 @@ class ImageViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'flavor_list') self.mox.StubOutWithMock(api, 'keypair_list') self.mox.StubOutWithMock(api, 'security_group_list') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + self.mox.StubOutWithMock(api, 'volume_list') + + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) 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()) @@ -84,7 +86,10 @@ class ImageViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'security_group_list') self.mox.StubOutWithMock(api, 'server_create') self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list()) api.security_group_list(IsA(http.HttpRequest)) \ @@ -128,13 +133,18 @@ class ImageViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'flavor_list') self.mox.StubOutWithMock(api, 'keypair_list') self.mox.StubOutWithMock(api, 'security_group_list') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + self.mox.StubOutWithMock(api, 'volume_list') + + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.image_get(IsA(http.HttpRequest), image.id).AndReturn(image) api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn( self.quota_usages.first()) - exc = keystone_exceptions.ClientException('Failed.') - api.flavor_list(IsA(http.HttpRequest)).AndRaise(exc) - api.flavor_list(IsA(http.HttpRequest)).AndRaise(exc) + api.flavor_list(IsA(http.HttpRequest)).AndRaise(self.exceptions.nova) + api.flavor_list(IsA(http.HttpRequest)).AndRaise(self.exceptions.nova) api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list()) api.security_group_list(IsA(http.HttpRequest)) \ .AndReturn(self.security_groups.list()) @@ -154,13 +164,18 @@ class ImageViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'flavor_list') self.mox.StubOutWithMock(api, 'keypair_list') self.mox.StubOutWithMock(api, 'security_group_list') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + self.mox.StubOutWithMock(api, 'volume_list') + + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) 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()) api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) - exception = keystone_exceptions.ClientException('Failed.') - api.keypair_list(IsA(http.HttpRequest)).AndRaise(exception) + api.keypair_list(IsA(http.HttpRequest)).AndRaise(self.exceptions.nova) api.security_group_list(IsA(http.HttpRequest)) \ .AndReturn(self.security_groups.list()) self.mox.ReplayAll() @@ -186,14 +201,16 @@ class ImageViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'security_group_list') self.mox.StubOutWithMock(api, 'server_create') self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list()) api.security_group_list(IsA(http.HttpRequest)) \ .AndReturn(self.security_groups.list()) 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), server.name, image.id, @@ -202,7 +219,8 @@ class ImageViewTests(test.TestCase): USER_DATA, [sec_group.name], None, - instance_count=IsA(int)).AndRaise(exc) + instance_count=IsA(int)) \ + .AndRaise(self.exceptions.keystone) self.mox.ReplayAll() form_data = {'method': 'LaunchForm', @@ -284,7 +302,7 @@ class ImageViewTests(test.TestCase): image = self.images.first() self.mox.StubOutWithMock(api.glance, 'image_get') api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \ - .AndRaise(glance_exception.ClientException('Error')) + .AndRaise(self.exceptions.glance) self.mox.ReplayAll() url = reverse('horizon:nova:images_and_snapshots:images:detail', diff --git a/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py b/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py index 62c8c1ac0d..d406f21002 100644 --- a/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py +++ b/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py @@ -20,7 +20,6 @@ from django import http from django.core.urlresolvers import reverse -from novaclient import exceptions as novaclient_exceptions from mox import IsA from horizon import api @@ -58,8 +57,8 @@ class SnapshotsViewTests(test.TestCase): def test_create_get_server_exception(self): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_get') - exc = novaclient_exceptions.ClientException('apiException') - api.server_get(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_get(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() url = reverse('horizon:nova:images_and_snapshots:snapshots:create', @@ -97,9 +96,8 @@ class SnapshotsViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'server_get') self.mox.StubOutWithMock(api, 'snapshot_create') api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) - exc = novaclient_exceptions.ClientException('apiException') api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \ - .AndRaise(exc) + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'method': 'CreateSnapshot', diff --git a/horizon/dashboards/nova/images_and_snapshots/tests.py b/horizon/dashboards/nova/images_and_snapshots/tests.py index 0d9f73e969..591ab34731 100644 --- a/horizon/dashboards/nova/images_and_snapshots/tests.py +++ b/horizon/dashboards/nova/images_and_snapshots/tests.py @@ -22,7 +22,6 @@ from copy import deepcopy from django import http from django.core.urlresolvers import reverse -from glanceclient.common import exceptions as glance_exception from mox import IsA from horizon import api @@ -38,6 +37,9 @@ class ImagesAndSnapshotsTests(test.TestCase): snapshots = self.snapshots.list() self.mox.StubOutWithMock(api, 'image_list_detailed') self.mox.StubOutWithMock(api, 'snapshot_list_detailed') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(images) api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn(snapshots) self.mox.ReplayAll() @@ -53,6 +55,9 @@ class ImagesAndSnapshotsTests(test.TestCase): def test_index_no_images(self): self.mox.StubOutWithMock(api, 'snapshot_list_detailed') self.mox.StubOutWithMock(api, 'image_list_detailed') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([]) api.snapshot_list_detailed(IsA(http.HttpRequest)) \ .AndReturn(self.snapshots.list()) @@ -64,8 +69,11 @@ class ImagesAndSnapshotsTests(test.TestCase): def test_index_error(self): self.mox.StubOutWithMock(api, 'image_list_detailed') self.mox.StubOutWithMock(api, 'snapshot_list_detailed') - exc = glance_exception.ClientException('error') - api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exc) + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) + api.image_list_detailed(IsA(http.HttpRequest)) \ + .AndRaise(self.exceptions.glance) api.snapshot_list_detailed(IsA(http.HttpRequest)) \ .AndReturn(self.snapshots.list()) self.mox.ReplayAll() @@ -75,17 +83,25 @@ class ImagesAndSnapshotsTests(test.TestCase): def test_queued_snapshot_actions(self): images = self.images.list() + snapshots = self.snapshots.list() snapshot1 = deepcopy(snapshots[0]) snapshot1.status = 'active' + snapshot1.owner = None + snapshot2 = deepcopy(snapshots[0]) snapshot2.id = 4 snapshot2.name = "snap2" snapshot2.status = "queued" snapshot2.owner = '1' + new_snapshots = [snapshot1, snapshot2] + self.mox.StubOutWithMock(api, 'image_list_detailed') self.mox.StubOutWithMock(api, 'snapshot_list_detailed') + self.mox.StubOutWithMock(api, 'volume_snapshot_list') + api.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(images) api.snapshot_list_detailed(IsA(http.HttpRequest)).\ AndReturn(new_snapshots) diff --git a/horizon/dashboards/nova/instances_and_volumes/instances/tables.py b/horizon/dashboards/nova/instances_and_volumes/instances/tables.py index 1901adbf7e..b35366dd9f 100644 --- a/horizon/dashboards/nova/instances_and_volumes/instances/tables.py +++ b/horizon/dashboards/nova/instances_and_volumes/instances/tables.py @@ -83,7 +83,7 @@ class TogglePause(tables.BatchAction): action_past = (_("Paused"), _("Unpaused")) data_type_singular = _("Instance") data_type_plural = _("Instances") - classes = ("btn-pause") + classes = ("btn-pause",) def allowed(self, request, instance=None): self.paused = False @@ -111,7 +111,7 @@ class ToggleSuspend(tables.BatchAction): action_past = (_("Suspended"), _("Resumed")) data_type_singular = _("Instance") data_type_plural = _("Instances") - classes = ("btn-suspend") + classes = ("btn-suspend",) def allowed(self, request, instance=None): self.suspended = False diff --git a/horizon/dashboards/nova/instances_and_volumes/instances/tests.py b/horizon/dashboards/nova/instances_and_volumes/instances/tests.py index ed0077d20e..fc98863a07 100644 --- a/horizon/dashboards/nova/instances_and_volumes/instances/tests.py +++ b/horizon/dashboards/nova/instances_and_volumes/instances/tests.py @@ -21,7 +21,6 @@ from django import http from django.core.urlresolvers import reverse from mox import IsA, IgnoreArg -from novaclient import exceptions as nova_exceptions from horizon import api from horizon import test @@ -45,6 +44,8 @@ class InstanceViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'server_list') self.mox.StubOutWithMock(api, 'flavor_list') self.mox.StubOutWithMock(api, 'server_delete') + self.mox.StubOutWithMock(api, 'volume_list') + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.server_delete(IsA(http.HttpRequest), server.id) @@ -59,10 +60,12 @@ class InstanceViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'server_list') self.mox.StubOutWithMock(api, 'flavor_list') self.mox.StubOutWithMock(api, 'server_delete') + self.mox.StubOutWithMock(api, 'volume_list') + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) - exc = nova_exceptions.ClientException(500) - api.server_delete(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_delete(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'instances__terminate__%s' % server.id} @@ -73,6 +76,10 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_pause') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.server_pause(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -83,11 +90,15 @@ class InstanceViewTests(test.TestCase): def test_pause_instance_exception(self): server = self.servers.first() + self.mox.StubOutWithMock(api, 'volume_list') self.mox.StubOutWithMock(api, 'server_pause') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) - exc = nova_exceptions.ClientException(500) - api.server_pause(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_pause(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'instances__pause__%s' % server.id} @@ -97,8 +108,12 @@ class InstanceViewTests(test.TestCase): def test_unpause_instance(self): server = self.servers.first() server.status = "PAUSED" + self.mox.StubOutWithMock(api, 'volume_list') self.mox.StubOutWithMock(api, 'server_unpause') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.server_unpause(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -110,11 +125,15 @@ class InstanceViewTests(test.TestCase): def test_unpause_instance_exception(self): server = self.servers.first() server.status = "PAUSED" - self.mox.StubOutWithMock(api, 'server_unpause') + self.mox.StubOutWithMock(api, 'volume_list') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'server_unpause') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) - exc = nova_exceptions.ClientException(500) - api.server_unpause(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_unpause(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'instances__pause__%s' % server.id} @@ -125,6 +144,10 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_reboot') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.server_reboot(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -137,9 +160,13 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_reboot') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) - exc = nova_exceptions.ClientException(500) - api.server_reboot(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_reboot(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'instances__reboot__%s' % server.id} @@ -150,6 +177,10 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_suspend') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.server_suspend(IsA(http.HttpRequest), unicode(server.id)) self.mox.ReplayAll() @@ -162,10 +193,13 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_suspend') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) - exception = nova_exceptions.ClientException(500) api.server_suspend(IsA(http.HttpRequest), - unicode(server.id)).AndRaise(exception) + unicode(server.id)).AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'instances__suspend__%s' % server.id} @@ -177,6 +211,10 @@ class InstanceViewTests(test.TestCase): server.status = "SUSPENDED" self.mox.StubOutWithMock(api, 'server_resume') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.server_resume(IsA(http.HttpRequest), unicode(server.id)) self.mox.ReplayAll() @@ -190,10 +228,13 @@ class InstanceViewTests(test.TestCase): server.status = "SUSPENDED" self.mox.StubOutWithMock(api, 'server_resume') self.mox.StubOutWithMock(api, 'server_list') + self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) + api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) - exception = nova_exceptions.ClientException(500) api.server_resume(IsA(http.HttpRequest), - unicode(server.id)).AndRaise(exception) + unicode(server.id)).AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'action': 'instances__suspend__%s' % server.id} @@ -223,9 +264,9 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_console_output') - exc = nova_exceptions.ClientException(500) api.server_console_output(IsA(http.HttpRequest), - server.id, tail_length=None).AndRaise(exc) + server.id, tail_length=None) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() url = reverse('horizon:nova:instances_and_volumes:instances:console', @@ -258,8 +299,8 @@ class InstanceViewTests(test.TestCase): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_vnc_console') - exc = nova_exceptions.ClientException(500) - api.server_vnc_console(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_vnc_console(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() url = reverse('horizon:nova:instances_and_volumes:instances:vnc', @@ -283,8 +324,8 @@ class InstanceViewTests(test.TestCase): def test_instance_update_get_server_get_exception(self): server = self.servers.first() self.mox.StubOutWithMock(api, 'server_get') - exc = nova_exceptions.ClientException(500) - api.server_get(IsA(http.HttpRequest), server.id).AndRaise(exc) + api.server_get(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() url = reverse('horizon:nova:instances_and_volumes:instances:update', @@ -316,9 +357,8 @@ class InstanceViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'server_get') self.mox.StubOutWithMock(api, 'server_update') api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) - exc = nova_exceptions.ClientException(500) api.server_update(IsA(http.HttpRequest), server.id, server.name) \ - .AndRaise(exc) + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'method': 'UpdateInstance', diff --git a/horizon/dashboards/nova/instances_and_volumes/tests.py b/horizon/dashboards/nova/instances_and_volumes/tests.py index 9cee9a0927..a6c143b8eb 100644 --- a/horizon/dashboards/nova/instances_and_volumes/tests.py +++ b/horizon/dashboards/nova/instances_and_volumes/tests.py @@ -22,7 +22,6 @@ from copy import deepcopy from django import http from django.core.urlresolvers import reverse from mox import IsA -from novaclient import exceptions as novaclient_exceptions from horizon import api from horizon import test @@ -30,8 +29,10 @@ from horizon import test class InstancesAndVolumesViewTest(test.TestCase): def test_index(self): + self.mox.StubOutWithMock(api, 'flavor_list') self.mox.StubOutWithMock(api, 'server_list') self.mox.StubOutWithMock(api, 'volume_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) @@ -61,6 +62,8 @@ class InstancesAndVolumesViewTest(test.TestCase): self.mox.StubOutWithMock(api, 'server_list') self.mox.StubOutWithMock(api, 'volume_list') + self.mox.StubOutWithMock(api, 'flavor_list') + api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) api.volume_list(IsA(http.HttpRequest)).AndReturn(volumes) @@ -91,8 +94,7 @@ class InstancesAndVolumesViewTest(test.TestCase): def test_index_server_list_exception(self): self.mox.StubOutWithMock(api, 'server_list') self.mox.StubOutWithMock(api, 'volume_list') - exception = novaclient_exceptions.ClientException('apiException') - api.server_list(IsA(http.HttpRequest)).AndRaise(exception) + api.server_list(IsA(http.HttpRequest)).AndRaise(self.exceptions.nova) api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list()) api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list()) diff --git a/horizon/dashboards/nova/overview/tests.py b/horizon/dashboards/nova/overview/tests.py index 908332b6c9..c6ff81160b 100644 --- a/horizon/dashboards/nova/overview/tests.py +++ b/horizon/dashboards/nova/overview/tests.py @@ -80,13 +80,12 @@ class UsageViewTests(test.TestCase): self.mox.StubOutWithMock(api, 'usage_get') timestamp = datetime.datetime(now.year, now.month, 1, now.hour, now.minute, now.second) - exception = nova_exceptions.ClientException(500) api.usage_get(IsA(http.HttpRequest), self.tenant.id, timestamp, datetime.datetime(now.year, now.month, now.day, now.hour, now.minute, now.second)) \ - .AndRaise(exception) + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() res = self.client.get(reverse('horizon:nova:overview:index')) diff --git a/horizon/dashboards/syspanel/instances/tests.py b/horizon/dashboards/syspanel/instances/tests.py index 506eb82988..72a96718b4 100644 --- a/horizon/dashboards/syspanel/instances/tests.py +++ b/horizon/dashboards/syspanel/instances/tests.py @@ -46,9 +46,8 @@ class InstanceViewTest(test.BaseAdminViewTests): def test_index_server_list_exception(self): self.mox.StubOutWithMock(api.nova, 'server_list') self.mox.StubOutWithMock(api.nova, 'flavor_list') - exception = novaclient_exceptions.ClientException('apiException') api.nova.server_list(IsA(http.HttpRequest), - all_tenants=True).AndRaise(exception) + all_tenants=True).AndRaise(self.exceptions.nova) self.mox.ReplayAll() diff --git a/horizon/dashboards/syspanel/users/tables.py b/horizon/dashboards/syspanel/users/tables.py index b6a63fa7b8..0dbd70333d 100644 --- a/horizon/dashboards/syspanel/users/tables.py +++ b/horizon/dashboards/syspanel/users/tables.py @@ -1,6 +1,5 @@ import logging -from django import shortcuts from django.contrib import messages from django.utils.translation import ugettext_lazy as _ @@ -10,6 +9,9 @@ from horizon import tables LOG = logging.getLogger(__name__) +ENABLE = 0 +DISABLE = 1 + class CreateUserLink(tables.LinkAction): name = "create" @@ -30,67 +32,41 @@ class EditUserLink(tables.LinkAction): classes = ("ajax-modal", "btn-edit") -class EnableUsersAction(tables.Action): +class ToggleEnabled(tables.BatchAction): name = "enable" - verbose_name = _("Enable") - verbose_name_plural = _("Enable Users") + action_present = (_("Enable"), _("Disable")) + action_past = (_("Enabled"), _("Disabled")) + data_type_singular = _("User") + data_type_plural = _("Users") classes = ("btn-enable",) - def allowed(self, request, user): - return not user.enabled - - def handle(self, data_table, request, object_ids): - failures = 0 - enabled = [] - for obj_id in object_ids: - try: - api.keystone.user_update_enabled(request, obj_id, True) - enabled.append(obj_id) - except Exception, e: - failures += 1 - messages.error(request, _("Error enabling user: %s") % e) - LOG.exception("Error enabling user.") - if failures: - messages.info(request, _("Enabled the following users: %s") - % ", ".join(enabled)) + def allowed(self, request, user=None): + self.enabled = True + if not user: + return self.enabled + self.enabled = user.enabled + if self.enabled: + self.current_present_action = DISABLE else: - messages.success(request, _("Successfully enabled users: %s") - % ", ".join(enabled)) - return shortcuts.redirect('horizon:syspanel:users:index') + self.current_present_action = ENABLE + return True + def update(self, request, user=None): + super(ToggleEnabled, self).update(request, user) + if user and user.id == request.user.id: + self.attrs["disabled"] = "disabled" -class DisableUsersAction(tables.Action): - name = "disable" - verbose_name = _("Disable") - verbose_name_plural = _("Disable Users") - classes = ("btn-disable",) - - def allowed(self, request, user): - return user.enabled - - def handle(self, data_table, request, object_ids): - failures = 0 - disabled = [] - for obj_id in object_ids: - if obj_id == request.user.id: - messages.info(request, _('You cannot disable the user you are ' - 'currently logged in as.')) - continue - try: - api.keystone.user_update_enabled(request, obj_id, False) - disabled.append(obj_id) - except Exception, e: - failures += 1 - messages.error(request, _("Error disabling user: %s") % e) - LOG.exception("Error disabling user.") - if failures: - messages.info(request, _("Disabled the following users: %s") - % ", ".join(disabled)) + def action(self, request, obj_id): + if obj_id == request.user.id: + messages.info(request, _('You cannot disable the user you are ' + 'currently logged in as.')) + return + if self.enabled: + api.keystone.user_update_enabled(request, obj_id, False) + self.current_past_action = DISABLE else: - if disabled: - messages.success(request, _("Successfully disabled users: %s") - % ", ".join(disabled)) - return shortcuts.redirect('horizon:syspanel:users:index') + api.keystone.user_update_enabled(request, obj_id, True) + self.current_past_action = ENABLE class DeleteUsersAction(tables.DeleteAction): @@ -135,11 +111,11 @@ class UsersTable(tables.DataTable): # verbose_name=_('Default Project')) enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, - status_choices=STATUS_CHOICES) + status_choices=STATUS_CHOICES, + empty_value="False") class Meta: name = "users" verbose_name = _("Users") - row_actions = (EditUserLink, EnableUsersAction, DisableUsersAction, - DeleteUsersAction) + row_actions = (EditUserLink, ToggleEnabled, DeleteUsersAction) table_actions = (UserFilterAction, CreateUserLink, DeleteUsersAction) diff --git a/horizon/dashboards/syspanel/users/tests.py b/horizon/dashboards/syspanel/users/tests.py index 8e7614a6c3..3875b4dfc7 100644 --- a/horizon/dashboards/syspanel/users/tests.py +++ b/horizon/dashboards/syspanel/users/tests.py @@ -20,7 +20,6 @@ from django import http from django.core.urlresolvers import reverse -from keystoneclient import exceptions as keystone_exceptions from mox import IgnoreArg, IsA from horizon import api @@ -255,7 +254,7 @@ class UsersViewTests(test.BaseAdminViewTests): @test.create_stubs({api.keystone: ('user_update_enabled', 'user_list')}) def test_enable_user(self): user = self.users.get(id="2") - + user.enabled = False api.keystone.user_list(IgnoreArg()).AndReturn(self.users.list()) api.keystone.user_update_enabled(IgnoreArg(), user.id, @@ -271,6 +270,7 @@ class UsersViewTests(test.BaseAdminViewTests): @test.create_stubs({api.keystone: ('user_update_enabled', 'user_list')}) def test_disable_user(self): user = self.users.get(id="2") + self.assertTrue(user.enabled) api.keystone.user_list(IgnoreArg()).AndReturn(self.users.list()) api.keystone.user_update_enabled(IgnoreArg(), @@ -279,7 +279,7 @@ class UsersViewTests(test.BaseAdminViewTests): self.mox.ReplayAll() - formData = {'action': 'users__disable__%s' % user.id} + formData = {'action': 'users__enable__%s' % user.id} res = self.client.post(USERS_INDEX_URL, formData) self.assertRedirectsNoFollow(res, USERS_INDEX_URL) @@ -287,14 +287,10 @@ class UsersViewTests(test.BaseAdminViewTests): @test.create_stubs({api.keystone: ('user_update_enabled', 'user_list')}) def test_enable_disable_user_exception(self): user = self.users.get(id="2") - + user.enabled = False api.keystone.user_list(IgnoreArg()).AndReturn(self.users.list()) - api_exception = keystone_exceptions.ClientException('apiException', - message='apiException') - api.keystone.user_update_enabled(IgnoreArg(), - user.id, - True).AndRaise(api_exception) - + api.keystone.user_update_enabled(IgnoreArg(), user.id, True) \ + .AndRaise(self.exceptions.keystone) self.mox.ReplayAll() formData = {'action': 'users__enable__%s' % user.id} @@ -309,7 +305,7 @@ class UsersViewTests(test.BaseAdminViewTests): self.mox.ReplayAll() - formData = {'action': 'users__disable__%s' % self.request.user.id} + formData = {'action': 'users__enable__%s' % self.request.user.id} res = self.client.post(USERS_INDEX_URL, formData, follow=True) self.assertEqual(list(res.context['messages'])[0].message, diff --git a/horizon/exceptions.py b/horizon/exceptions.py index 778719e3dc..083ba92c60 100644 --- a/horizon/exceptions.py +++ b/horizon/exceptions.py @@ -19,10 +19,12 @@ Exceptions raised by the Horizon code and the machinery for handling them. """ import logging +import os import sys from django.conf import settings from django.contrib import messages +from django.utils import termcolors from django.utils.translation import ugettext as _ from cloudfiles import errors as swiftclient from glanceclient.common import exceptions as glanceclient @@ -31,6 +33,7 @@ from novaclient import exceptions as novaclient LOG = logging.getLogger(__name__) +PALETTE = termcolors.PALETTES[termcolors.DEFAULT_PALETTE] class HorizonException(Exception): @@ -106,6 +109,9 @@ class AlreadyExists(HorizonException): def __repr__(self): return self.msg % self.attrs + def __str__(self): + return self.msg % self.attrs + def __unicode__(self): return _(self.msg) % self.attrs @@ -152,7 +158,12 @@ RECOVERABLE = (keystoneclient.ClientException, RECOVERABLE += tuple(EXCEPTION_CONFIG.get('recoverable', [])) -def handle(request, message=None, redirect=None, ignore=False, escalate=False): +def _error_color(msg): + return termcolors.colorize(msg, **PALETTE['ERROR']) + + +def handle(request, message=None, redirect=None, ignore=False, + escalate=False, log_level=None, force_log=None): """ Centralized error handling for Horizon. Because Horizon consumes so many different APIs with completely @@ -181,6 +192,9 @@ def handle(request, message=None, redirect=None, ignore=False, escalate=False): returned. """ exc_type, exc_value, exc_traceback = sys.exc_info() + log_method = getattr(LOG, log_level or "exception") + force_log = force_log or os.environ.get("HORIZON_TEST_RUN", False) + force_silence = getattr(exc_value, "silence_logging", False) # Because the same exception may travel through this method more than # once (if it's re-raised) we may want to treat it differently @@ -204,8 +218,9 @@ def handle(request, message=None, redirect=None, ignore=False, escalate=False): if ignore: return NotAuthorized request.user_logout() + if not force_silence and not handled: + log_method(_error_color("Unauthorized: %s" % exc_value)) if not handled: - LOG.debug("Unauthorized: %s" % exc_value) # We get some pretty useless error messages back from # some clients, so let's define our own fallback. fallback = _("Unauthorized. Please try logging in again.") @@ -214,8 +229,9 @@ def handle(request, message=None, redirect=None, ignore=False, escalate=False): if issubclass(exc_type, NOT_FOUND): wrap = True + if not force_silence and not handled and (not ignore or force_log): + log_method(_error_color("Not Found: %s" % exc_value)) if not ignore and not handled: - LOG.debug("Not Found: %s" % exc_value) messages.error(request, message or exc_value) if redirect: raise Http302(redirect) @@ -224,8 +240,9 @@ def handle(request, message=None, redirect=None, ignore=False, escalate=False): if issubclass(exc_type, RECOVERABLE): wrap = True + if not force_silence and not handled and (not ignore or force_log): + log_method(_error_color("Recoverable error: %s" % exc_value)) if not ignore and not handled: - LOG.debug("Recoverable error: %s" % exc_value) messages.error(request, message or exc_value) if redirect: raise Http302(redirect) diff --git a/horizon/test.py b/horizon/test.py index c12988d857..82681f0b23 100644 --- a/horizon/test.py +++ b/horizon/test.py @@ -19,6 +19,7 @@ # under the License. import datetime +import os import cloudfiles as swift_client from django import http @@ -131,6 +132,7 @@ class TestCase(django_test.TestCase): self.request.session = self.client._session() self.request.session['token'] = self.token.id middleware.HorizonMiddleware().process_request(self.request) + os.environ["HORIZON_TEST_RUN"] = "True" def tearDown(self): self.mox.UnsetStubs() @@ -138,6 +140,7 @@ class TestCase(django_test.TestCase): context_processors.horizon = self._real_horizon_context_processor users.get_user_from_request = self._real_get_user_from_request self.mox.VerifyAll() + del os.environ["HORIZON_TEST_RUN"] def setActiveUser(self, id=None, token=None, username=None, tenant_id=None, service_catalog=None, tenant_name=None, roles=None, diff --git a/horizon/tests/auth_tests.py b/horizon/tests/auth_tests.py index 9f295d0c03..4c1ed3903e 100644 --- a/horizon/tests/auth_tests.py +++ b/horizon/tests/auth_tests.py @@ -118,6 +118,7 @@ class AuthViewTests(test.TestCase): api.tenant_list_for_token(IsA(http.HttpRequest), aToken.id).AndReturn(tenants) exc = keystone_exceptions.Unauthorized("Not authorized.") + exc.silence_logging = True api.token_create_scoped(IsA(http.HttpRequest), disabled_tenant.id, aToken.id).AndRaise(exc) @@ -135,6 +136,7 @@ class AuthViewTests(test.TestCase): def test_login_invalid_credentials(self): self.mox.StubOutWithMock(api, 'token_create') unauthorized = keystone_exceptions.Unauthorized("Invalid") + unauthorized.silence_logging = True api.token_create(IsA(http.HttpRequest), "", self.user.name, self.user.password).AndRaise(unauthorized) @@ -152,11 +154,10 @@ class AuthViewTests(test.TestCase): def test_login_exception(self): self.mox.StubOutWithMock(api, 'token_create') - ex = keystone_exceptions.BadRequest('Cannot talk to keystone') api.token_create(IsA(http.HttpRequest), "", self.user.name, - self.user.password).AndRaise(ex) + self.user.password).AndRaise(self.exceptions.keystone) self.mox.ReplayAll() diff --git a/horizon/tests/tabs_tests.py b/horizon/tests/tabs_tests.py index 625ef9c9d7..976c58d96c 100644 --- a/horizon/tests/tabs_tests.py +++ b/horizon/tests/tabs_tests.py @@ -88,7 +88,9 @@ class RecoverableErrorTab(horizon_tabs.Tab): def get_context_data(self, request): # Raise a known recoverable error. - raise exceptions.AlreadyExists("Recoverable!", None) + exc = exceptions.AlreadyExists("Recoverable!", horizon_tabs.Tab) + exc.silence_logging = True + raise exc class TableTabGroup(horizon_tabs.TabGroup): diff --git a/horizon/tests/test_data/exceptions.py b/horizon/tests/test_data/exceptions.py new file mode 100644 index 0000000000..248bb024d9 --- /dev/null +++ b/horizon/tests/test_data/exceptions.py @@ -0,0 +1,36 @@ +# Copyright 2012 Nebula, Inc. +# +# 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 glanceclient.common import exceptions as glance_exceptions +from keystoneclient import exceptions as keystone_exceptions +from novaclient import exceptions as nova_exceptions + +from .utils import TestDataContainer + + +def data(TEST): + TEST.exceptions = TestDataContainer() + msg = "Expected failure." + + keystone_exception = keystone_exceptions.ClientException(500, message=msg) + keystone_exception.silence_logging = True + TEST.exceptions.keystone = keystone_exception + + nova_exception = nova_exceptions.ClientException(500, message=msg) + nova_exception.silence_logging = True + TEST.exceptions.nova = nova_exception + + glance_exception = glance_exceptions.ClientException(500, message=msg) + glance_exception.silence_logging = True + TEST.exceptions.glance = glance_exception diff --git a/horizon/tests/test_data/glance_data.py b/horizon/tests/test_data/glance_data.py index d307f91ad7..ae2621ccf5 100644 --- a/horizon/tests/test_data/glance_data.py +++ b/horizon/tests/test_data/glance_data.py @@ -25,6 +25,8 @@ def data(TEST): snapshot_dict = {'name': u'snapshot', 'container_format': u'ami', 'id': 3, + 'status': "active", + 'owner': TEST.tenant.id, 'properties': {'image_type': u'snapshot'}} snapshot = Image(ImageManager(None), snapshot_dict) TEST.snapshots.add(snapshot) @@ -32,12 +34,16 @@ def data(TEST): # Images image_dict = {'id': '1', 'name': 'public_image', + 'status': "active", + 'owner': TEST.tenant.id, 'container_format': 'novaImage', 'properties': {'image_type': u'image'}} public_image = Image(ImageManager(None), image_dict) image_dict = {'id': '2', 'name': 'private_image', + 'status': "active", + 'owner': TEST.tenant.id, 'container_format': 'aki'} private_image = Image(ImageManager(None), image_dict) diff --git a/horizon/tests/test_data/keystone_data.py b/horizon/tests/test_data/keystone_data.py index c2f6507d36..588febfac7 100644 --- a/horizon/tests/test_data/keystone_data.py +++ b/horizon/tests/test_data/keystone_data.py @@ -103,7 +103,8 @@ def data(TEST): 'name': 'test_user', 'email': 'test@example.com', 'password': 'password', - 'token': 'test_token'} + 'token': 'test_token', + 'enabled': True} user = users.User(users.UserManager(None), user_dict) user_dict.update({'id': "2", 'name': 'user_two', diff --git a/horizon/tests/test_data/utils.py b/horizon/tests/test_data/utils.py index f7720a7bdc..b8a3f78602 100644 --- a/horizon/tests/test_data/utils.py +++ b/horizon/tests/test_data/utils.py @@ -14,13 +14,15 @@ def load_test_data(load_onto=None): + from . import exceptions from . import glance_data from . import keystone_data from . import nova_data from . import swift_data # The order of these loaders matters, some depend on others. - loaders = (keystone_data.data, + loaders = (exceptions.data, + keystone_data.data, glance_data.data, nova_data.data, swift_data.data) diff --git a/horizon/tests/testsettings.py b/horizon/tests/testsettings.py index b2b6d53c77..7976380b98 100644 --- a/horizon/tests/testsettings.py +++ b/horizon/tests/testsettings.py @@ -115,6 +115,10 @@ LOGGING = { 'level': 'DEBUG', 'class': 'django.utils.log.NullHandler', }, + 'test': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + } }, 'loggers': { 'django.db.backends': { @@ -122,19 +126,23 @@ LOGGING = { 'propagate': False, }, 'horizon': { - 'handlers': ['null'], + 'handlers': ['test'], 'propagate': False, }, 'novaclient': { - 'handlers': ['null'], + 'handlers': ['test'], 'propagate': False, }, 'keystoneclient': { - 'handlers': ['null'], + 'handlers': ['test'], + 'propagate': False, + }, + 'glanceclient': { + 'handlers': ['test'], 'propagate': False, }, 'quantum': { - 'handlers': ['null'], + 'handlers': ['test'], 'propagate': False, }, 'nose.plugins.manager': {