Change-Id: Icbc1a3c039de658faa9fba4a2cdd5027345fe94d
Partially-Implements: blueprint drop-dj17
Origin: upstream, https://review.openstack.org/#/c/280222/
-Last-Update: 2016-02-19
+Last-Update: 2016-03-21
-Index: horizon/horizon/forms/base.py
-===================================================================
---- horizon.orig/horizon/forms/base.py
-+++ horizon/horizon/forms/base.py
-@@ -41,7 +41,7 @@ class SelfHandlingForm(SelfHandlingMixin
+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.
"""
def set_warning(self, message):
"""Sets a warning on the form.
-Index: horizon/horizon/test/settings.py
-===================================================================
---- horizon.orig/horizon/test/settings.py
-+++ horizon/horizon/test/settings.py
+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',
)
MIDDLEWARE_CLASSES = (
-Index: horizon/horizon/test/tests/base.py
-===================================================================
---- horizon.orig/horizon/test/tests/base.py
-+++ horizon/horizon/test/tests/base.py
+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.
# Set SSL settings for test server
settings.SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL',
-Index: horizon/horizon/test/tests/forms.py
-===================================================================
---- horizon.orig/horizon/test/tests/forms.py
-+++ horizon/horizon/test/tests/forms.py
+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
def _render_form(self):
return shortcuts.render(self.request, self.template,
-Index: horizon/horizon/test/tests/middleware.py
-===================================================================
---- horizon.orig/horizon/test/tests/middleware.py
-+++ horizon/horizon/test/tests/middleware.py
+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.
def test_process_response_redirect_on_ajax_request(self):
url = settings.LOGIN_URL
-Index: horizon/openstack_dashboard/dashboards/admin/networks/tests.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/admin/networks/tests.py
-+++ horizon/openstack_dashboard/dashboards/admin/networks/tests.py
-@@ -390,8 +390,9 @@ class NetworkTests(test.BaseAdminViewTes
+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.neutron.list_extensions(
IsA(http.HttpRequest)).AndReturn(extensions)
self.mox.ReplayAll()
-@@ -417,8 +418,11 @@ class NetworkTests(test.BaseAdminViewTes
+@@ -417,8 +418,11 @@ class NetworkTests(test.BaseAdminViewTests):
tenant_id = self.tenants.first().id
network = self.networks.first()
extensions = self.api_extensions.list()
api.neutron.list_extensions(
IsA(http.HttpRequest)).AndReturn(extensions)
self.mox.ReplayAll()
-@@ -447,8 +451,10 @@ class NetworkTests(test.BaseAdminViewTes
+@@ -447,8 +451,10 @@ class NetworkTests(test.BaseAdminViewTests):
tenant_id = self.tenants.first().id
network = self.networks.first()
extensions = self.api_extensions.list()
api.neutron.list_extensions(
IsA(http.HttpRequest)).AndReturn(extensions)
self.mox.ReplayAll()
-Index: horizon/openstack_dashboard/dashboards/admin/volumes/tabs.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/admin/volumes/tabs.py
-+++ horizon/openstack_dashboard/dashboards/admin/volumes/tabs.py
-@@ -35,7 +35,7 @@ class VolumeTab(volumes_tabs.PagedTableM
- name = _("Volumes")
- slug = "volumes_tab"
- template_name = "admin/volumes/volumes/volumes_tables.html"
-- preload = False
-+ preload = True
-
- def get_volumes_data(self):
- volumes = self._get_volumes(search_opts={'all_tenants': True})
-@@ -68,7 +68,7 @@ class VolumeTypesTab(tabs.TableTab, volu
- name = _("Volume Types")
- slug = "volume_types_tab"
- template_name = "admin/volumes/volume_types/volume_types_tables.html"
-- preload = False
-+ preload = True
-
- def get_volume_types_data(self):
- try:
-@@ -115,7 +115,7 @@ class SnapshotTab(volumes_tabs.PagedTabl
- name = _("Volume Snapshots")
- slug = "snapshots_tab"
- template_name = ("horizon/common/_detail_table.html")
-- preload = False
-+ preload = True
-
- def get_volume_snapshots_data(self):
- if cinder.is_volume_service_enabled(self.request):
-Index: horizon/openstack_dashboard/dashboards/admin/volumes/tests.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/admin/volumes/tests.py
-+++ horizon/openstack_dashboard/dashboards/admin/volumes/tests.py
-@@ -180,6 +180,11 @@ class VolumeTests(test.BaseAdminViewTest
- self.assertItemsEqual(qos_specs, self.cinder_qos_specs.list())
-
- @test.create_stubs({cinder: ('volume_list',
-+ 'volume_list_paged',
-+ 'volume_snapshot_list',
-+ 'volume_type_list_with_qos_associations',
-+ 'volume_encryption_type_list',
-+ 'qos_spec_list',
- 'volume_snapshot_list_paged',),
- keystone: ('tenant_list',)})
- def test_snapshots_tab(self):
-@@ -202,6 +207,11 @@ class VolumeTests(test.BaseAdminViewTest
+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())
- @test.create_stubs({cinder: ('volume_list',
-+ 'volume_list_paged',
-+ 'volume_snapshot_list',
-+ 'volume_type_list_with_qos_associations',
-+ 'volume_encryption_type_list',
-+ 'qos_spec_list',
- 'volume_snapshot_list_paged',),
- keystone: ('tenant_list',)})
- def _test_snapshots_index_paginated(self, marker, sort_dir, snapshots, url,
-Index: horizon/openstack_dashboard/dashboards/project/stacks/forms.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/project/stacks/forms.py
-+++ horizon/openstack_dashboard/dashboards/project/stacks/forms.py
+@@ -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
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.SelfHandlingFor
+@@ -125,6 +126,13 @@ class TemplateForm(forms.SelfHandlingForm):
widget=forms.widgets.Textarea(attrs=attributes),
required=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.SelfHandling
+@@ -252,6 +260,12 @@ class CreateStackForm(forms.SelfHandlingForm):
environment_data = forms.CharField(
widget=forms.widgets.HiddenInput,
required=False)
parameters = forms.CharField(
widget=forms.widgets.HiddenInput)
stack_name = forms.RegexField(
-Index: horizon/openstack_dashboard/dashboards/project/volumes/backups/tests.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/project/volumes/backups/tests.py
-+++ horizon/openstack_dashboard/dashboards/project/volumes/backups/tests.py
-@@ -70,21 +70,17 @@ class VolumeBackupsViewTests(test.TestCa
+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)
@test.create_stubs({api.cinder: ('volume_backup_get', 'volume_get')})
def test_volume_backup_detail_get(self):
-Index: horizon/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py
-+++ horizon/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py
-@@ -108,7 +108,8 @@ class VolumeSnapshotsViewTests(test.Test
+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',
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.Test
+@@ -123,19 +124,14 @@ class VolumeSnapshotsViewTests(test.TestCase):
AndReturn(volumes)
api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id)
@test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')})
def test_volume_snapshot_detail_get(self):
-Index: horizon/openstack_dashboard/dashboards/project/volumes/tabs.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/project/volumes/tabs.py
-+++ horizon/openstack_dashboard/dashboards/project/volumes/tabs.py
-@@ -119,7 +119,7 @@ class VolumeTab(PagedTableMixin, tabs.Ta
- name = _("Volumes")
- slug = "volumes_tab"
- template_name = ("horizon/common/_detail_table.html")
-- preload = False
-+ preload = True
-
- def get_volumes_data(self):
- volumes = self._get_volumes()
-@@ -135,7 +135,7 @@ class SnapshotTab(PagedTableMixin, tabs.
- name = _("Volume Snapshots")
- slug = "snapshots_tab"
- template_name = ("horizon/common/_detail_table.html")
-- preload = False
-+ preload = True
-
- def get_volume_snapshots_data(self):
- snapshots = []
-@@ -156,7 +156,6 @@ class SnapshotTab(PagedTableMixin, tabs.
- for snapshot in snapshots:
- volume = volumes.get(snapshot.volume_id)
- setattr(snapshot, '_volume', volume)
--
- return snapshots
-
-
-@@ -165,7 +164,7 @@ class BackupsTab(PagedTableMixin, tabs.T
- name = _("Volume Backups")
- slug = "backups_tab"
- template_name = ("horizon/common/_detail_table.html")
-- preload = False
-+ preload = True
-
- def allowed(self, request):
- return api.cinder.volume_backup_supported(self.request)
-Index: horizon/openstack_dashboard/dashboards/project/volumes/test.py
-===================================================================
---- horizon.orig/openstack_dashboard/dashboards/project/volumes/test.py
-+++ horizon/openstack_dashboard/dashboards/project/volumes/test.py
-@@ -191,12 +191,22 @@ class VolumeAndSnapshotsAndBackupsTests(
- 'volume_snapshot_list_paged',
- 'volume_list',
- 'volume_backup_supported',
-+ 'volume_snapshot_list',
-+ 'volume_type_list_with_qos_associations',
-+ 'volume_encryption_type_list',
-+ 'qos_spec_list',
-+ 'volume_snapshot_list_paged'
-+ 'volume_snapshot_list',
-+ 'volume_type_list_with_qos_associations',
-+ 'volume_encryption_type_list',
-+ 'qos_spec_list',
- ),
- api.nova: ('server_list',)})
- def _test_snapshots_index_paginated(self, marker, sort_dir, snapshots, url,
- has_more, has_prev):
- backup_supported = True
--
-+ api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\
-+ MultipleTimes().AndReturn(self.cinder_limits['absolute'])
- api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\
- MultipleTimes().AndReturn(backup_supported)
- api.cinder.volume_snapshot_list_paged(
-@@ -285,7 +295,8 @@ class VolumeAndSnapshotsAndBackupsTests(
- def _test_backups_index_paginated(self, marker, sort_dir, backups, url,
- has_more, has_prev):
- backup_supported = True
+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())
-
-+ api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\
-+ MultipleTimes().AndReturn(self.cinder_limits['absolute'])
- api.cinder.volume_backup_supported(IsA(http.HttpRequest)).\
- MultipleTimes().AndReturn(backup_supported)
- api.cinder.volume_backup_list_paged(
-Index: horizon/openstack_dashboard/settings.py
-===================================================================
---- horizon.orig/openstack_dashboard/settings.py
-+++ horizon/openstack_dashboard/settings.py
+- 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
# This base context objects gets added to the offline context generator
# for each theme configured.
-@@ -392,11 +398,14 @@ COMPRESS_OFFLINE_CONTEXT = 'horizon.them
+@@ -392,11 +398,14 @@ COMPRESS_OFFLINE_CONTEXT = 'horizon.themes.offline_context'
if DEBUG:
logging.basicConfig(level=logging.DEBUG)
+# auth_utils.patch_middleware_get_user()
CSRF_COOKIE_AGE = None
-Index: horizon/openstack_dashboard/test/helpers.py
-===================================================================
---- horizon.orig/openstack_dashboard/test/helpers.py
-+++ horizon/openstack_dashboard/test/helpers.py
+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 ceilometerclient.v2 import client as ceilometer_client
from cinderclient import client as cinder_client
-@@ -232,8 +233,10 @@ class TestCase(horizon_helpers.TestCase)
+@@ -232,8 +233,10 @@ class TestCase(horizon_helpers.TestCase):
processing the view which is redirected to.
"""
if django.VERSION >= (1, 9):
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)