+++ /dev/null
-Subject: [PATCH] [WIP] Fix remaining Django 1.9 test failures
- This patch gets Horizon to a passing state in the Django 1.9 tests
-Author: Rob Cresswell <robert.cresswell@outlook.com>
-Date: Thu, 11 Feb 2016 14:45:32 +0000
-Co-Authored-By: Itxaka <iserrano@redhat.com>
-Change-Id: Icbc1a3c039de658faa9fba4a2cdd5027345fe94d
-Partially-Implements: blueprint drop-dj17
-Origin: upstream, https://review.openstack.org/#/c/280222/
-Last-Update: 2016-03-21
-
-diff --git a/horizon/forms/base.py b/horizon/forms/base.py
-index 5364d38..b54aa63 100644
---- a/horizon/forms/base.py
-+++ b/horizon/forms/base.py
-@@ -41,7 +41,7 @@ class SelfHandlingForm(SelfHandlingMixin, forms.Form):
- wish for API errors to appear as errors on the form rather than
- using the messages framework.
- """
-- self._errors[NON_FIELD_ERRORS] = self.error_class([message])
-+ self.add_error(NON_FIELD_ERRORS, message)
-
- def set_warning(self, message):
- """Sets a warning on the form.
-diff --git a/horizon/forms/views.py b/horizon/forms/views.py
-index f6276e3..ec67cb8 100644
---- a/horizon/forms/views.py
-+++ b/horizon/forms/views.py
-@@ -165,6 +165,11 @@ class ModalFormView(ModalBackdropMixin, ModalFormMixin, views.HorizonFormView):
- """Returns an instance of the form to be used in this view."""
- return form_class(self.request, **self.get_form_kwargs())
-
-+ def form_invalid(self, form):
-+ context = super(ModalFormView, self).get_context_data()
-+ context['form'] = form
-+ return self.render_to_response(context)
-+
- def form_valid(self, form):
- try:
- handled = form.handle(self.request, form.cleaned_data)
-diff --git a/horizon/test/settings.py b/horizon/test/settings.py
-index e231348..7f5ab99 100644
---- a/horizon/test/settings.py
-+++ b/horizon/test/settings.py
-@@ -59,7 +59,8 @@ INSTALLED_APPS = (
- 'horizon',
- 'horizon.test',
- 'horizon.test.test_dashboards.cats',
-- 'horizon.test.test_dashboards.dogs'
-+ 'horizon.test.test_dashboards.dogs',
-+ 'openstack_auth'
- )
-
- MIDDLEWARE_CLASSES = (
-diff --git a/horizon/test/tests/base.py b/horizon/test/tests/base.py
-index 12b85e0..53869c1 100644
---- a/horizon/test/tests/base.py
-+++ b/horizon/test/tests/base.py
-@@ -17,6 +17,7 @@
- # License for the specific language governing permissions and limitations
- # under the License.
-
-+import django
- from django.conf import settings
- from django.contrib.auth.models import User # noqa
- from django.core.exceptions import ImproperlyConfigured # noqa
-@@ -308,7 +309,10 @@ class HorizonTests(BaseHorizonTests):
-
- self.client.logout()
- resp = self.client.get(url)
-- self.assertRedirects(resp, redirect_url)
-+ if django.VERSION >= (1, 9):
-+ self.assertRedirects(resp, settings.TESTSERVER + redirect_url)
-+ else:
-+ self.assertRedirects(resp, redirect_url)
-
- # Set SSL settings for test server
- settings.SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL',
-diff --git a/horizon/test/tests/forms.py b/horizon/test/tests/forms.py
-index 16c7cba..7f66415 100644
---- a/horizon/test/tests/forms.py
-+++ b/horizon/test/tests/forms.py
-@@ -27,6 +27,8 @@ class FormMixinTests(test.TestCase):
- view.args = args
- view.kwargs = kwargs
- view.template_name = 'test_template'
-+ # Note(Itxaka): ModalFormView requires a form_class to behave properly
-+ view.form_class = TestForm
- return view
-
- def test_modal_form_mixin_hide_true_if_ajax(self):
-@@ -80,7 +82,9 @@ class FormErrorTests(test.TestCase):
-
- def setUp(self):
- super(FormErrorTests, self).setUp()
-- self.form = TestForm(self.request)
-+ # Note(Itxaka): We pass data to the form so its bound and has the
-+ # proper cleaned_data fields
-+ self.form = TestForm(self.request, data={'fake': 'data'})
-
- def _render_form(self):
- return shortcuts.render(self.request, self.template,
-diff --git a/horizon/test/tests/middleware.py b/horizon/test/tests/middleware.py
-index 694824b..6328ed5 100644
---- a/horizon/test/tests/middleware.py
-+++ b/horizon/test/tests/middleware.py
-@@ -13,8 +13,8 @@
- # License for the specific language governing permissions and limitations
- # under the License.
-
-+import django
- from django.conf import settings
--
- from django.http import HttpResponseRedirect # noqa
- from django.utils import timezone
-
-@@ -41,7 +41,10 @@ class MiddlewareTests(test.TestCase):
- resp = mw.process_exception(request, exceptions.NotAuthorized())
- resp.client = self.client
-
-- self.assertRedirects(resp, url)
-+ if django.VERSION >= (1, 9):
-+ self.assertRedirects(resp, settings.TESTSERVER + url)
-+ else:
-+ self.assertRedirects(resp, url)
-
- def test_process_response_redirect_on_ajax_request(self):
- url = settings.LOGIN_URL
-diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py
-index 951e22f..e64a4dc 100644
---- a/openstack_dashboard/dashboards/admin/networks/tests.py
-+++ b/openstack_dashboard/dashboards/admin/networks/tests.py
-@@ -390,8 +390,9 @@ class NetworkTests(test.BaseAdminViewTests):
- tenant_id = self.tenants.first().id
- network = self.networks.first()
- extensions = self.api_extensions.list()
-- api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn([tenants,
-- False])
-+ api.keystone.tenant_list(
-+ IsA(http.HttpRequest)
-+ ).MultipleTimes().AndReturn([tenants, False])
- api.neutron.list_extensions(
- IsA(http.HttpRequest)).AndReturn(extensions)
- self.mox.ReplayAll()
-@@ -417,8 +418,11 @@ class NetworkTests(test.BaseAdminViewTests):
- tenant_id = self.tenants.first().id
- network = self.networks.first()
- extensions = self.api_extensions.list()
-- api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn([tenants,
-- False])
-+
-+ api.keystone.tenant_list(
-+ IsA(http.HttpRequest)
-+ ).MultipleTimes().AndReturn([tenants, False])
-+
- api.neutron.list_extensions(
- IsA(http.HttpRequest)).AndReturn(extensions)
- self.mox.ReplayAll()
-@@ -447,8 +451,10 @@ class NetworkTests(test.BaseAdminViewTests):
- tenant_id = self.tenants.first().id
- network = self.networks.first()
- extensions = self.api_extensions.list()
-- api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn([tenants,
-- False])
-+ api.keystone.tenant_list(
-+ IsA(http.HttpRequest)
-+ ).MultipleTimes().AndReturn([tenants, False])
-+
- api.neutron.list_extensions(
- IsA(http.HttpRequest)).AndReturn(extensions)
- self.mox.ReplayAll()
-diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py
-index c70d692..c048774 100644
---- a/openstack_dashboard/dashboards/admin/volumes/tests.py
-+++ b/openstack_dashboard/dashboards/admin/volumes/tests.py
-@@ -18,6 +18,7 @@ from django.conf import settings
- from django.core.urlresolvers import reverse
- from django import http
- from django.test.utils import override_settings
-+from django.utils.http import urlunquote # noqa
- from mox3.mox import IsA # noqa
-
- from openstack_dashboard import api
-@@ -92,7 +93,7 @@ class VolumeTests(test.BaseAdminViewTests):
-
- self.mox.ReplayAll()
-
-- res = self.client.get(url)
-+ res = self.client.get(urlunquote(url))
-
- self.assertTemplateUsed(res, 'admin/volumes/index.html')
- self.assertEqual(res.status_code, 200)
-@@ -190,8 +191,8 @@ class VolumeTests(test.BaseAdminViewTests):
- .AndReturn(True)
-
- self.mox.ReplayAll()
-- res = self.client.get(reverse(
-- 'horizon:admin:volumes:volume_types_tab'))
-+ url = reverse('horizon:admin:volumes:volume_types_tab')
-+ res = self.client.get(urlunquote(url))
-
- self.assertEqual(res.status_code, 200)
- self.assertTemplateUsed(
-@@ -216,10 +217,11 @@ class VolumeTests(test.BaseAdminViewTests):
- AndReturn([self.tenants.list(), False])
-
- self.mox.ReplayAll()
-- res = self.client.get(reverse('horizon:admin:volumes:snapshots_tab'))
-+ url = reverse('horizon:admin:volumes:snapshots_tab')
-+ res = self.client.get(urlunquote(url))
-
- self.assertEqual(res.status_code, 200)
-- self.assertTemplateUsed(res, 'horizon/common/_detail_table.html')
-+ self.assertTemplateUsed(res, 'admin/volumes/index.html')
- snapshots = res.context['volume_snapshots_table'].data
- self.assertItemsEqual(snapshots, self.cinder_volume_snapshots.list())
-
-@@ -240,7 +242,7 @@ class VolumeTests(test.BaseAdminViewTests):
-
- self.mox.ReplayAll()
-
-- res = self.client.get(url)
-+ res = self.client.get(urlunquote(url))
-
- self.assertTemplateUsed(res, 'admin/volumes/index.html')
- self.assertEqual(res.status_code, 200)
-diff --git a/openstack_dashboard/dashboards/identity/ngusers/panel.py b/openstack_dashboard/dashboards/identity/ngusers/panel.py
-index e414284..77c4651 100644
---- a/openstack_dashboard/dashboards/identity/ngusers/panel.py
-+++ b/openstack_dashboard/dashboards/identity/ngusers/panel.py
-@@ -16,14 +16,9 @@ from django.utils.translation import ugettext_lazy as _
-
- import horizon
-
--from openstack_dashboard.dashboards.identity import dashboard
--
-
- class NGUsers(horizon.Panel):
- name = _("Users")
- slug = 'ngusers'
- policy_rules = (("identity", "identity:get_user"),
- ("identity", "identity:list_users"))
--
--
--dashboard.Identity.register(NGUsers)
-diff --git a/openstack_dashboard/dashboards/identity/users/tests.py b/openstack_dashboard/dashboards/identity/users/tests.py
-index 504de15..59356d4 100644
---- a/openstack_dashboard/dashboards/identity/users/tests.py
-+++ b/openstack_dashboard/dashboards/identity/users/tests.py
-@@ -18,6 +18,7 @@
-
- from socket import timeout as socket_timeout # noqa
-
-+import django
- from django.core.urlresolvers import reverse
- from django import http
- from django.test.utils import override_settings
-@@ -228,6 +229,19 @@ class UsersViewTests(test.BaseAdminViewTests):
- api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
- api.keystone.get_default_role(IgnoreArg()) \
- .AndReturn(self.roles.first())
-+ if django.VERSION >= (1, 9):
-+ if api.keystone.VERSIONS.active >= 3:
-+ api.keystone.tenant_list(
-+ IgnoreArg(), domain=domain_id).AndReturn(
-+ [self.tenants.list(), False])
-+ else:
-+ api.keystone.tenant_list(
-+ IgnoreArg(), user=None).AndReturn(
-+ [self.tenants.list(), False])
-+
-+ api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
-+ api.keystone.get_default_role(IgnoreArg()) \
-+ .AndReturn(self.roles.first())
-
- self.mox.ReplayAll()
-
-@@ -268,6 +282,19 @@ class UsersViewTests(test.BaseAdminViewTests):
- api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
- api.keystone.get_default_role(IgnoreArg()) \
- .AndReturn(self.roles.first())
-+ if django.VERSION >= (1, 9):
-+ if api.keystone.VERSIONS.active >= 3:
-+ api.keystone.tenant_list(
-+ IgnoreArg(), domain=domain_id).AndReturn(
-+ [self.tenants.list(), False])
-+ else:
-+ api.keystone.tenant_list(
-+ IgnoreArg(), user=None).AndReturn(
-+ [self.tenants.list(), False])
-+
-+ api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
-+ api.keystone.get_default_role(IgnoreArg()) \
-+ .AndReturn(self.roles.first())
-
- self.mox.ReplayAll()
-
-@@ -311,6 +338,19 @@ class UsersViewTests(test.BaseAdminViewTests):
- api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
- api.keystone.get_default_role(IgnoreArg()) \
- .AndReturn(self.roles.first())
-+ if django.VERSION >= (1, 9):
-+ if api.keystone.VERSIONS.active >= 3:
-+ api.keystone.tenant_list(
-+ IgnoreArg(), domain=domain_id).AndReturn(
-+ [self.tenants.list(), False])
-+ else:
-+ api.keystone.tenant_list(
-+ IgnoreArg(), user=None).AndReturn(
-+ [self.tenants.list(), False])
-+
-+ api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
-+ api.keystone.get_default_role(IgnoreArg()) \
-+ .AndReturn(self.roles.first())
-
- self.mox.ReplayAll()
-
-diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py
-index bef6b3c..bcfe3a8 100644
---- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py
-+++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py
-@@ -18,6 +18,7 @@
-
- import cgi
-
-+import django
- from django.conf import settings
- from django.core.urlresolvers import reverse
- from django import http
-@@ -410,6 +411,12 @@ class SecurityGroupsViewTests(test.TestCase):
- IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
- api.network.security_group_list(
- IsA(http.HttpRequest)).AndReturn(sec_group_list)
-+ if django.VERSION >= (1, 9):
-+ api.network.security_group_backend(
-+ IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
-+ api.network.security_group_list(
-+ IsA(http.HttpRequest)).AndReturn(sec_group_list)
-+
- self.mox.ReplayAll()
-
- formData = {'method': 'AddRule',
-@@ -435,6 +442,13 @@ class SecurityGroupsViewTests(test.TestCase):
- IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
- api.network.security_group_list(
- IsA(http.HttpRequest)).AndReturn(sec_group_list)
-+ if django.VERSION >= (1, 9):
-+ for i in range(3):
-+ api.network.security_group_backend(
-+ IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
-+ api.network.security_group_list(
-+ IsA(http.HttpRequest)).AndReturn(sec_group_list)
-+
- self.mox.ReplayAll()
-
- formData = {'method': 'AddRule',
-@@ -490,6 +504,13 @@ class SecurityGroupsViewTests(test.TestCase):
- api.network.security_group_list(
- IsA(http.HttpRequest)).AndReturn(sec_group_list)
-
-+ if django.VERSION >= (1, 9):
-+ for i in range(4):
-+ api.network.security_group_backend(
-+ IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
-+ api.network.security_group_list(
-+ IsA(http.HttpRequest)).AndReturn(sec_group_list)
-+
- self.mox.ReplayAll()
-
- formData = {'method': 'AddRule',
-diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py
-index 11210d6..2598403 100644
---- a/openstack_dashboard/dashboards/project/instances/tests.py
-+++ b/openstack_dashboard/dashboards/project/instances/tests.py
-@@ -4511,7 +4511,8 @@ class InstanceTests(helpers.TestCase):
- confirm_password=pass2,
- disk_config='MANUAL')
-
-- self.assertContains(res, "Passwords do not match.")
-+ self.assertEqual(res.context['form'].errors['__all__'],
-+ ["Passwords do not match."])
-
- @helpers.create_stubs(instance_rebuild_post_stubs)
- def test_rebuild_instance_post_with_empty_string(self):
-diff --git a/openstack_dashboard/dashboards/project/stacks/forms.py b/openstack_dashboard/dashboards/project/stacks/forms.py
-index a42f079..f7dcf51 100644
---- a/openstack_dashboard/dashboards/project/stacks/forms.py
-+++ b/openstack_dashboard/dashboards/project/stacks/forms.py
-@@ -13,6 +13,7 @@
- import json
- import logging
-
-+import django
- from django.conf import settings
- from django.utils import html
- from django.utils.translation import ugettext_lazy as _
-@@ -125,6 +126,13 @@ class TemplateForm(forms.SelfHandlingForm):
- widget=forms.widgets.Textarea(attrs=attributes),
- required=False)
-
-+ if django.VERSION >= (1, 9):
-+ # Note(Itxaka): On django>=1.9 Charfield has an strip option that
-+ # we need to set to False as to not hit
-+ # https://bugs.launchpad.net/python-heatclient/+bug/1546166
-+ environment_data.strip = False
-+ template_data.strip = False
-+
- def __init__(self, *args, **kwargs):
- self.next_view = kwargs.pop('next_view')
- super(TemplateForm, self).__init__(*args, **kwargs)
-@@ -252,6 +260,12 @@ class CreateStackForm(forms.SelfHandlingForm):
- environment_data = forms.CharField(
- widget=forms.widgets.HiddenInput,
- required=False)
-+ if django.VERSION >= (1, 9):
-+ # Note(Itxaka): On django>=1.9 Charfield has an strip option that
-+ # we need to set to False as to not hit
-+ # https://bugs.launchpad.net/python-heatclient/+bug/1546166
-+ environment_data.strip = False
-+
- parameters = forms.CharField(
- widget=forms.widgets.HiddenInput)
- stack_name = forms.RegexField(
-diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html
-index b418edc..12d131a 100644
---- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html
-+++ b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html
-@@ -54,5 +54,5 @@
- {% endblock %}
-
- {% block modal-footer %}
-- <a href="{% url 'horizon:project:stack:index' %}" class="btn btn-default cancel">{% trans "Close" %}</a>
-+ <a href="{% url 'horizon:project:stacks:index' %}" class="btn btn-default cancel">{% trans "Close" %}</a>
- {% endblock %}
-diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tests.py b/openstack_dashboard/dashboards/project/volumes/backups/tests.py
-index 4e5e357..389e909 100644
---- a/openstack_dashboard/dashboards/project/volumes/backups/tests.py
-+++ b/openstack_dashboard/dashboards/project/volumes/backups/tests.py
-@@ -70,21 +70,17 @@ class VolumeBackupsViewTests(test.TestCase):
- AndReturn(volumes)
- api.cinder.volume_backup_delete(IsA(http.HttpRequest), backup.id)
-
-- api.cinder.volume_backup_list_paged(
-- IsA(http.HttpRequest), marker=None, sort_dir='desc',
-- paginate=True).AndReturn([vol_backups, False, False])
-- api.cinder.volume_list(IsA(http.HttpRequest)). \
-- AndReturn(volumes)
- self.mox.ReplayAll()
-
- formData = {'action':
- 'volume_backups__delete__%s' % backup.id}
- res = self.client.post(INDEX_URL +
- "?tab=volumes_and_snapshots__backups_tab",
-- formData, follow=True)
-+ formData)
-
-- self.assertIn("Scheduled deletion of Volume Backup: backup1",
-- [m.message for m in res.context['messages']])
-+ self.assertRedirectsNoFollow(res, INDEX_URL +
-+ "?tab=volumes_and_snapshots__backups_tab")
-+ self.assertMessageCount(success=1)
-
- @test.create_stubs({api.cinder: ('volume_backup_get', 'volume_get')})
- def test_volume_backup_detail_get(self):
-diff --git a/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py b/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py
-index b0eacb3..b608cca 100644
---- a/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py
-+++ b/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py
-@@ -10,8 +10,10 @@
- # License for the specific language governing permissions and limitations
- # under the License.
-
-+import django
- from django.core.urlresolvers import reverse
- from django import http
-+from django.utils.http import urlunquote # noqa
- from mox3.mox import IsA # noqa
-
- from openstack_dashboard.api import cinder
-@@ -19,7 +21,8 @@ from openstack_dashboard.test import helpers as test
-
-
- VOLUME_INDEX_URL = reverse('horizon:project:volumes:index')
--VOLUME_CGROUPS_TAB_URL = reverse('horizon:project:volumes:cgroups_tab')
-+VOLUME_CGROUPS_TAB_URL = urlunquote(reverse(
-+ 'horizon:project:volumes:cgroups_tab'))
-
-
- class ConsistencyGroupTests(test.TestCase):
-@@ -111,8 +114,9 @@ class ConsistencyGroupTests(test.TestCase):
- AndReturn(cgroups)
- cinder.volume_cgroup_delete(IsA(http.HttpRequest), cgroup.id,
- force=False)
-- cinder.volume_cgroup_list_with_vol_type_names(IsA(http.HttpRequest)).\
-- AndReturn(cgroups)
-+ if django.VERSION < (1, 9):
-+ cinder.volume_cgroup_list_with_vol_type_names(
-+ IsA(http.HttpRequest)).AndReturn(cgroups)
-
- self.mox.ReplayAll()
-
-diff --git a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py
-index a8cc741..d7975b5 100644
---- a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py
-+++ b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py
-@@ -108,7 +108,8 @@ class VolumeSnapshotsViewTests(test.TestCase):
- @test.create_stubs({api.cinder: ('volume_snapshot_list_paged',
- 'volume_list',
- 'volume_backup_supported',
-- 'volume_snapshot_delete')})
-+ 'volume_snapshot_delete',
-+ 'tenant_absolute_limits')})
- def test_delete_volume_snapshot(self):
- vol_snapshots = self.cinder_volume_snapshots.list()
- volumes = self.cinder_volumes.list()
-@@ -123,19 +124,14 @@ class VolumeSnapshotsViewTests(test.TestCase):
- AndReturn(volumes)
-
- api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id)
-- api.cinder.volume_snapshot_list_paged(
-- IsA(http.HttpRequest), paginate=True, marker=None,
-- sort_dir='desc').AndReturn([[], False, False])
-- api.cinder.volume_list(IsA(http.HttpRequest)). \
-- AndReturn(volumes)
- self.mox.ReplayAll()
-
- formData = {'action':
- 'volume_snapshots__delete__%s' % snapshot.id}
-- res = self.client.post(VOLUME_SNAPSHOTS_TAB_URL, formData, follow=True)
-+ res = self.client.post(VOLUME_SNAPSHOTS_TAB_URL, formData)
-
-- self.assertIn("Scheduled deletion of Volume Snapshot: test snapshot",
-- [m.message for m in res.context['messages']])
-+ self.assertRedirectsNoFollow(res, VOLUME_SNAPSHOTS_TAB_URL)
-+ self.assertMessageCount(success=1)
-
- @test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')})
- def test_volume_snapshot_detail_get(self):
-diff --git a/openstack_dashboard/dashboards/project/volumes/test.py b/openstack_dashboard/dashboards/project/volumes/test.py
-index b632fbd..1f3ef4a 100644
---- a/openstack_dashboard/dashboards/project/volumes/test.py
-+++ b/openstack_dashboard/dashboards/project/volumes/test.py
-@@ -18,6 +18,7 @@ from django.conf import settings
- from django.core.urlresolvers import reverse
- from django import http
- from django.test.utils import override_settings
-+from django.utils.http import urlunquote # noqa
-
- from mox3.mox import IsA # noqa
-
-@@ -32,8 +33,10 @@ from openstack_dashboard.test import helpers as test
-
-
- INDEX_URL = reverse('horizon:project:volumes:index')
--VOLUME_SNAPSHOTS_TAB_URL = reverse('horizon:project:volumes:snapshots_tab')
--VOLUME_BACKUPS_TAB_URL = reverse('horizon:project:volumes:backups_tab')
-+VOLUME_SNAPSHOTS_TAB_URL = urlunquote(reverse(
-+ 'horizon:project:volumes:snapshots_tab'))
-+VOLUME_BACKUPS_TAB_URL = urlunquote(reverse(
-+ 'horizon:project:volumes:backups_tab'))
-
-
- class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
-@@ -126,7 +129,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
- AndReturn(self.cinder_limits['absolute'])
- self.mox.ReplayAll()
-
-- res = self.client.get(url)
-+ res = self.client.get(urlunquote(url))
- self.assertEqual(res.status_code, 200)
- self.assertTemplateUsed(res, 'project/volumes/index.html')
-
-@@ -223,7 +226,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
- self.cinder_volumes.list())
- self.mox.ReplayAll()
-
-- res = self.client.get(url)
-+ res = self.client.get(urlunquote(url))
- self.assertEqual(res.status_code, 200)
- self.assertTemplateUsed(res, 'project/volumes/index.html')
-
-@@ -312,7 +315,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase):
- self.cinder_volumes.list())
- self.mox.ReplayAll()
-
-- res = self.client.get(url)
-+ res = self.client.get(urlunquote(url))
- self.assertEqual(res.status_code, 200)
- self.assertTemplateUsed(res, 'project/volumes/index.html')
-
-diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py
-index 3eac769..0e07ba8 100644
---- a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py
-+++ b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py
-@@ -16,10 +16,12 @@
- # License for the specific language governing permissions and limitations
- # under the License.
-
-+import django
- from django.core.urlresolvers import reverse
- from django.forms import widgets
- from django import http
- from django.test.utils import override_settings
-+from django.utils.http import urlunquote # noqa
-
- from mox3.mox import IsA # noqa
- import six
-@@ -32,7 +34,8 @@ from openstack_dashboard.usage import quotas
-
-
- VOLUME_INDEX_URL = reverse('horizon:project:volumes:index')
--VOLUME_VOLUMES_TAB_URL = reverse('horizon:project:volumes:volumes_tab')
-+VOLUME_VOLUMES_TAB_URL = urlunquote(reverse(
-+ 'horizon:project:volumes:volumes_tab'))
- SEARCH_OPTS = dict(status=api.cinder.VOLUME_STATE_AVAILABLE)
-
-
-@@ -472,8 +475,9 @@ class VolumeViewTests(test.TestCase):
-
- cinder.volume_type_list(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.list())
-- cinder.volume_type_list(IsA(http.HttpRequest)).\
-- AndReturn(self.volume_types.list())
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_list(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.list())
- cinder.volume_type_default(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.first())
- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-@@ -482,9 +486,13 @@ class VolumeViewTests(test.TestCase):
- str(snapshot.id)).AndReturn(snapshot)
- cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\
- AndReturn(self.cinder_volumes.first())
--
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_default(IsA(http.HttpRequest)). \
-+ AndReturn(self.volume_types.first())
-+ cinder.volume_snapshot_get(IsA(http.HttpRequest),
-+ str(snapshot.id)).AndReturn(snapshot)
-+ cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id). \
-+ AndReturn(self.cinder_volumes.first())
-
- self.mox.ReplayAll()
-
-@@ -649,8 +657,9 @@ class VolumeViewTests(test.TestCase):
-
- cinder.volume_type_list(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.list())
-- cinder.volume_type_list(IsA(http.HttpRequest)).\
-- AndReturn(self.volume_types.list())
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_list(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.list())
- cinder.volume_type_default(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.first())
- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-@@ -661,8 +670,15 @@ class VolumeViewTests(test.TestCase):
- .AndReturn(True)
- cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
- self.cinder_availability_zones.list())
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_default(IsA(http.HttpRequest)). \
-+ AndReturn(self.volume_types.first())
-+ api.glance.image_get(IsA(http.HttpRequest),
-+ str(image.id)).AndReturn(image)
-+ cinder.extension_supported(IsA(http.HttpRequest),
-+ 'AvailabilityZones').AndReturn(True)
-+ cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
-+ self.cinder_availability_zones.list())
-
- self.mox.ReplayAll()
-
-@@ -696,8 +712,9 @@ class VolumeViewTests(test.TestCase):
-
- cinder.volume_type_list(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.list())
-- cinder.volume_type_list(IsA(http.HttpRequest)).\
-- AndReturn(self.volume_types.list())
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_list(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.list())
- cinder.volume_type_default(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.first())
- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-@@ -708,8 +725,15 @@ class VolumeViewTests(test.TestCase):
- .AndReturn(True)
- cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
- self.cinder_availability_zones.list())
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_default(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.first())
-+ api.glance.image_get(IsA(http.HttpRequest),
-+ str(image.id)).AndReturn(image)
-+ cinder.extension_supported(IsA(http.HttpRequest),
-+ 'AvailabilityZones').AndReturn(True)
-+ cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
-+ self.cinder_availability_zones.list())
-
- self.mox.ReplayAll()
-
-@@ -753,8 +777,9 @@ class VolumeViewTests(test.TestCase):
-
- cinder.volume_type_list(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.list())
-- cinder.volume_type_list(IsA(http.HttpRequest)).\
-- AndReturn(self.volume_types.list())
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_list(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.list())
- cinder.volume_type_default(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.first())
- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-@@ -778,8 +803,29 @@ class VolumeViewTests(test.TestCase):
- .AndReturn(True)
- cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
- self.cinder_availability_zones.list())
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_default(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.first())
-+ cinder.volume_snapshot_list(IsA(http.HttpRequest),
-+ search_opts=SEARCH_OPTS). \
-+ AndReturn(self.cinder_volume_snapshots.list())
-+ api.glance.image_list_detailed(
-+ IsA(http.HttpRequest),
-+ filters={'is_public': True, 'status': 'active'}) \
-+ .AndReturn([self.images.list(), False, False])
-+ api.glance.image_list_detailed(
-+ IsA(http.HttpRequest),
-+ filters={'property-owner_id': self.tenant.id,
-+ 'status': 'active'}) \
-+ .AndReturn([[], False, False])
-+ cinder.volume_list(IsA(
-+ http.HttpRequest),
-+ search_opts=SEARCH_OPTS).AndReturn(self.cinder_volumes.list())
-+ cinder.extension_supported(IsA(http.HttpRequest),
-+ 'AvailabilityZones') \
-+ .AndReturn(True)
-+ cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
-+ self.cinder_availability_zones.list())
-
- self.mox.ReplayAll()
-
-@@ -810,8 +856,9 @@ class VolumeViewTests(test.TestCase):
-
- cinder.volume_type_list(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.list())
-- cinder.volume_type_list(IsA(http.HttpRequest)).\
-- AndReturn(self.volume_types.list())
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_list(IsA(http.HttpRequest)).\
-+ AndReturn(self.volume_types.list())
- cinder.volume_type_default(IsA(http.HttpRequest)).\
- AndReturn(self.volume_types.first())
- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-@@ -835,8 +882,29 @@ class VolumeViewTests(test.TestCase):
- .AndReturn(True)
- cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
- self.cinder_availability_zones.list())
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-+ if django.VERSION >= (1, 9):
-+ cinder.volume_type_default(IsA(http.HttpRequest)). \
-+ AndReturn(self.volume_types.first())
-+ cinder.volume_snapshot_list(IsA(http.HttpRequest),
-+ search_opts=SEARCH_OPTS). \
-+ AndReturn(self.cinder_volume_snapshots.list())
-+ api.glance.image_list_detailed(
-+ IsA(http.HttpRequest),
-+ filters={'is_public': True, 'status': 'active'}) \
-+ .AndReturn([self.images.list(), False, False])
-+ api.glance.image_list_detailed(
-+ IsA(http.HttpRequest),
-+ filters={'property-owner_id': self.tenant.id,
-+ 'status': 'active'}) \
-+ .AndReturn([[], False, False])
-+ cinder.volume_list(IsA(
-+ http.HttpRequest),
-+ search_opts=SEARCH_OPTS).AndReturn(self.cinder_volumes.list())
-+ cinder.extension_supported(IsA(http.HttpRequest),
-+ 'AvailabilityZones') \
-+ .AndReturn(True)
-+ cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn(
-+ self.cinder_availability_zones.list())
-
- self.mox.ReplayAll()
-
-@@ -1434,18 +1502,12 @@ class VolumeViewTests(test.TestCase):
- quotas: ('tenant_limit_usages',)})
- def test_extend_volume_with_wrong_size(self):
- volume = self.cinder_volumes.first()
-- usage_limit = {'maxTotalVolumeGigabytes': 100,
-- 'gigabytesUsed': 20,
-- 'volumesUsed': len(self.cinder_volumes.list()),
-- 'maxTotalVolumes': 6}
- formData = {'name': u'A Volume I Am Making',
- 'orig_size': volume.size,
- 'new_size': 10}
-
- cinder.volume_get(IsA(http.HttpRequest), volume.id).\
- AndReturn(self.cinder_volumes.first())
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-
- self.mox.ReplayAll()
-
-@@ -1574,8 +1636,6 @@ class VolumeViewTests(test.TestCase):
- AndReturn(usage_limit)
- cinder.volume_get(IsA(http.HttpRequest), volume.id).\
- AndReturn(self.volumes.first())
-- quotas.tenant_limit_usages(IsA(http.HttpRequest)).\
-- AndReturn(usage_limit)
-
- self.mox.ReplayAll()
-
-diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py
-index 69ab6d0..3e8a588 100644
---- a/openstack_dashboard/settings.py
-+++ b/openstack_dashboard/settings.py
-@@ -28,6 +28,7 @@ from openstack_dashboard import exceptions
- from openstack_dashboard.static_settings import find_static_files # noqa
- from openstack_dashboard.static_settings import get_staticfiles_dirs # noqa
- from openstack_dashboard import theme_settings
-+from openstack_dashboard.utils import settings
-
-
- warnings.formatwarning = lambda message, category, *args, **kwargs: \
-@@ -299,10 +300,10 @@ if os.path.exists(LOCAL_SETTINGS_DIR_PATH):
- for filename in sorted(filenames):
- if filename.endswith(".py"):
- try:
-- execfile(os.path.join(dirpath, filename))
-+ settings.execfile_schim(os.path.join(dirpath, filename))
- except Exception as e:
- logging.exception(
-- "Can not exec settings snippet %s" % (filename))
-+ "Can not exec settings snippet %s" % filename)
-
-
- if not WEBROOT.endswith('/'):
-@@ -363,7 +364,6 @@ if not SECRET_KEY:
- # Load the pluggable dashboard settings
- import openstack_dashboard.enabled
- import openstack_dashboard.local.enabled
--from openstack_dashboard.utils import settings
-
- INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable
- settings.update_dashboards(
-@@ -376,8 +376,14 @@ settings.update_dashboards(
- )
- INSTALLED_APPS[0:0] = ADD_INSTALLED_APPS
-
--from openstack_auth import policy
--POLICY_CHECK_FUNCTION = policy.check
-+
-+def check(actions, request, target=None):
-+ # Note(Itxaka): This is to prevent circular dependencies and apps not ready
-+ # If you do django imports in your settings, you are gonna have a bad time
-+ from openstack_auth import policy
-+ return policy.check(actions, request, target=None)
-+
-+POLICY_CHECK_FUNCTION = check
-
- # This base context objects gets added to the offline context generator
- # for each theme configured.
-@@ -392,11 +398,14 @@ COMPRESS_OFFLINE_CONTEXT = 'horizon.themes.offline_context'
- if DEBUG:
- logging.basicConfig(level=logging.DEBUG)
-
-+# Note(Itxaka): This does not work due the apps not ready when importing
-+# auth_utils
- # during django reloads and an active user is logged in, the monkey
- # patch below will not otherwise be applied in time - resulting in developers
- # appearing to be logged out. In typical production deployments this section
- # below may be omitted, though it should not be harmful
--from openstack_auth import utils as auth_utils
--auth_utils.patch_middleware_get_user()
-+#
-+# from openstack_auth import utils as auth_utils
-+# auth_utils.patch_middleware_get_user()
-
- CSRF_COOKIE_AGE = None
-diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py
-index dfca1e9..2336bfd 100644
---- a/openstack_dashboard/test/helpers.py
-+++ b/openstack_dashboard/test/helpers.py
-@@ -29,6 +29,7 @@ from django.core.handlers import wsgi
- from django.core import urlresolvers
- from django.test.client import RequestFactory # noqa
- from django.test import utils as django_test_utils
-+from django.utils import http
-
- from ceilometerclient.v2 import client as ceilometer_client
- from cinderclient import client as cinder_client
-@@ -232,8 +233,10 @@ class TestCase(horizon_helpers.TestCase):
- processing the view which is redirected to.
- """
- if django.VERSION >= (1, 9):
-- self.assertEqual(response._headers.get('location', None),
-- ('Location', expected_url))
-+ loc = six.text_type(response._headers.get('location', None)[1])
-+ loc = http.urlunquote(loc)
-+ expected_url = http.urlunquote(expected_url)
-+ self.assertEqual(loc, expected_url)
- else:
- self.assertEqual(response._headers.get('location', None),
- ('Location', settings.TESTSERVER + expected_url))
-diff --git a/openstack_dashboard/utils/settings.py b/openstack_dashboard/utils/settings.py
-index 8bf9990..d6f8ad7 100644
---- a/openstack_dashboard/utils/settings.py
-+++ b/openstack_dashboard/utils/settings.py
-@@ -168,3 +168,12 @@ def update_dashboards(modules, horizon_config, installed_apps):
- # so we save the reference to it before we append to installed_apps
- horizon_config.setdefault('plugins', []).extend(apps)
- installed_apps[0:0] = apps
-+
-+
-+def execfile_schim(filename):
-+ if six.PY3:
-+ with open(filename) as f:
-+ code = compile(f.read(), os.path.basename(filename), 'exec')
-+ exec(code)
-+ else:
-+ execfile(filename)