[python-django-horizon/f18] Updated patches from f18-patches

Matthias Runge mrunge at fedoraproject.org
Mon Jan 21 10:23:50 UTC 2013


commit e4a5087d1e8e897bd071a0f0065d7295967da3d4
Author: Matthias Runge <mrunge at redhat.com>
Date:   Mon Jan 21 10:26:37 2013 +0100

    Updated patches from f18-patches

 ...-floating-ips-table-action-column-s-width.patch |   26 +++
 ...-inherit-from-base.html-in-500-error-page.patch |  221 ++++++++++++++++++
 0007-Allow-setting-nova-quotas-to-unlimited.patch  |   46 ++++
 ...d-cinder-calls-when-cinder-is-unavailable.patch |  236 ++++++++++++++++++++
 ...patch => 0009-disable-debug-move-web-root.patch |    0
 0010-Add-a-check-for-unlimited-quotas.patch        |   90 ++++++++
 ...-set-mount-point-as-it-s-unsupported-here.patch |    0
 ...patch => 0012-disable-debug-move-web-root.patch |    2 +-
 0013-take-variables-out-of-compressed-output.patch |   26 +++
 python-django-horizon.spec                         |   18 ++-
 10 files changed, 662 insertions(+), 3 deletions(-)
---
diff --git a/0005-Specify-floating-ips-table-action-column-s-width.patch b/0005-Specify-floating-ips-table-action-column-s-width.patch
new file mode 100644
index 0000000..17c08b2
--- /dev/null
+++ b/0005-Specify-floating-ips-table-action-column-s-width.patch
@@ -0,0 +1,26 @@
+From 2f959c6efeffad6f4413335dfd3c9e330c3d8e31 Mon Sep 17 00:00:00 2001
+From: Jiang Yong <jiangyong.hn at gmail.com>
+Date: Wed, 5 Dec 2012 18:15:47 +0800
+Subject: [PATCH] Specify floating ips table action column's width
+
+Fixes bug 1081875
+
+Change-Id: Id92a1c7afa829ef2ff8e973919900cf7cb7c59e9
+---
+ openstack_dashboard/static/dashboard/less/horizon.less | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/openstack_dashboard/static/dashboard/less/horizon.less b/openstack_dashboard/static/dashboard/less/horizon.less
+index 0bfe7d7..db788d2 100644
+--- a/openstack_dashboard/static/dashboard/less/horizon.less
++++ b/openstack_dashboard/static/dashboard/less/horizon.less
+@@ -823,6 +823,9 @@ td.actions_column {
+   min-width: 140px;
+   min-height: 20px;
+ }
++#floating_ips td.actions_column {
++  width: 180px;
++}
+ 
+ td.actions_column .row_actions a,
+ td.actions_column .row_actions input,
diff --git a/0006-Don-t-inherit-from-base.html-in-500-error-page.patch b/0006-Don-t-inherit-from-base.html-in-500-error-page.patch
new file mode 100644
index 0000000..8dbb0a5
--- /dev/null
+++ b/0006-Don-t-inherit-from-base.html-in-500-error-page.patch
@@ -0,0 +1,221 @@
+From 0b1c5536dfd80f071d8e3e92b71dc7eca0b1d325 Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Wed, 14 Nov 2012 18:30:39 +1100
+Subject: [PATCH] Don't inherit from base.html in 500 error page
+
+For server errors, the context passed to the template is empty,
+so things like STATIC_URL and context processors don't work.
+
+Fixes bug 1067206 for the folsom branch.
+
+This has been adapted to folsom release and is a backport from master.
+
+Change-Id: I5cd60523360d71b088226e4d0f87d88bac8a8df3
+---
+ horizon/templatetags/horizon.py               |   6 ++
+ openstack_dashboard/templates/500.html        | 103 ++++++++++++++++++++------
+ openstack_dashboard/test/error_pages_urls.py  |   7 ++
+ openstack_dashboard/test/tests/error_pages.py |  34 +++++++++
+ openstack_dashboard/urls.py                   |   5 ++
+ 5 files changed, 131 insertions(+), 24 deletions(-)
+ create mode 100644 openstack_dashboard/test/error_pages_urls.py
+ create mode 100644 openstack_dashboard/test/tests/error_pages.py
+
+diff --git a/horizon/templatetags/horizon.py b/horizon/templatetags/horizon.py
+index 13169d4..f17a252 100644
+--- a/horizon/templatetags/horizon.py
++++ b/horizon/templatetags/horizon.py
+@@ -21,6 +21,7 @@ from django.utils.datastructures import SortedDict
+ from django.utils.translation import ugettext as _
+ 
+ from horizon.base import Horizon
++from django.conf import settings
+ 
+ 
+ register = template.Library()
+@@ -135,3 +136,8 @@ def jstemplate(parser, token):
+     nodelist = parser.parse(('endjstemplate',))
+     parser.delete_first_token()
+     return JSTemplateNode(nodelist)
++
++
++ at register.assignment_tag
++def load_config():
++    return getattr(settings, 'HORIZON_CONFIG', {})
+diff --git a/openstack_dashboard/templates/500.html b/openstack_dashboard/templates/500.html
+index d4d6747..fb51211 100644
+--- a/openstack_dashboard/templates/500.html
++++ b/openstack_dashboard/templates/500.html
+@@ -1,27 +1,82 @@
+-{% extends "base.html" %}
+-{% load i18n %}
++{% load branding i18n staticfiles %}
++{% load load_config from horizon %}
+ 
+-{% block title %} - {% trans "Internal Server Error" %}{% endblock %}
++{% load_config as HORIZON_CONFIG %}
+ 
+-{% block content %}
+-  <div id="right_content">
+-    <div id="page_head">
+-      <h2 id="page_heading">{% trans "Internal Server Error" %}</h2>
+-      <p id="page_description">{% trans "An unexpected error occurred while processing your request.  Please try your request again." %}</p>
++
++{% comment %}
++
++    NB: The context for 500 pages is an empty dict.
++        Don't add any content here that depends on things from
++        the context.
++
++{% endcomment %}
++
++<!DOCTYPE html>
++<html>
++  <head>
++    <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
++    <link rel="shortcut icon" href="{% static "dashboard/img/favicon.ico" %}"/>
++    <title>{% trans "Server error" %} - {% site_branding %}</title>
++    {% block css %}
++    <style>
++    a {
++        color: #43a1d6;
++        text-decoration: none;
++    }
++    body {
++      color: rgb(106, 106, 106);
++      text-align: center;
++      font-weight: normal;
++      background: none repeat scroll 0% 0% rgb(250, 250, 250);
++    }
++    div#container {
++      position: absolute;
++      top: 80px;
++      padding-top: 170px;
++      margin: 0px 0px 0px -196px;
++      left: 50%;
++      width: 390px;
++
++      background: url("{% static "dashboard/img/logo.png" %}") no-repeat scroll center 35px padding-box rgb(255, 255, 255);
++      border: 1px solid rgb(225, 225, 225);
++
++      -webkit-border-radius: 6px;
++      -moz-border-radius: 6px;
++      border-radius: 6px;
++
++      box-shadow: 0px 3px 7px rgba(0, 0, 0, 0.3);
++      -webkit-box-shadow: 0px 3px 7px rgba(0, 0, 0, 0.3);
++      -moz-box-shadow: 0px 3px 7px rgba(0, 0, 0, 0.3);
++    }
++    h2 {
++      font-weight: normal;
++    }
++    div#container > div {
++      padding: 25px;
++    }
++    </style>
++    {% endblock %}
++  </head>
++  <body id="{% block body_id %}{% endblock %}">
++    {% block page_header %}{% endblock %}
++    {% block content %}
++    <div id="container">
++      <div id="text">
++        {% block text %}
++        <h2>{% trans "Something went wrong!" %}</h2>
++        <p>{% trans "An unexpected error has occurred. Try refreshing the page. If that doesn't help, contact your local administrator." %}</p>
++        {% endblock %}
++      </div>
++      <div id="links">
++        {% block links %}
++        <p><a href="/">{% trans "Home" %}</a></p>
++        <p><a href="{{ HORIZON_CONFIG.help_url }}">{% trans "Help" %}</a></p>
++        {% endblock %}
++      </div>
+     </div>
+-  </div>
+-{% endblock %}
+-
+-{% block sidebar %}
+-  <div id="sidebar">
+-    <ul id="navigation">
+-      {% block nav_home %}
+-      <li><h3><a href="{% url index %}">{% trans "Home" %}</a></h3></li>
+-      {% endblock %}
+-
+-      {% block nav_projects %}
+-      <li><h3><a href="{% url index %}">{% trans "Projects" %}</a></h3></li>
+-      {% endblock %}
+-    </ul>
+-  </div> <!-- end sidebar -->
+-{% endblock %}
++    {% endblock %}
++    {% block footer %}{% endblock %}
++    {% block js %}{% endblock %}
++  </body>
++</html>
+diff --git a/openstack_dashboard/test/error_pages_urls.py b/openstack_dashboard/test/error_pages_urls.py
+new file mode 100644
+index 0000000..cc28840
+--- /dev/null
++++ b/openstack_dashboard/test/error_pages_urls.py
+@@ -0,0 +1,7 @@
++from django.conf.urls import patterns, url, include
++
++from openstack_dashboard.urls import urlpatterns
++
++urlpatterns += patterns('',
++    (r'^500/$', 'django.views.defaults.server_error')
++)
+diff --git a/openstack_dashboard/test/tests/error_pages.py b/openstack_dashboard/test/tests/error_pages.py
+new file mode 100644
+index 0000000..44cb8da
+--- /dev/null
++++ b/openstack_dashboard/test/tests/error_pages.py
+@@ -0,0 +1,34 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright (c) 2012 OpenStack, LLC.
++# All Rights Reserved.
++#
++#    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 os import path
++
++from django.conf import settings
++
++from horizon import exceptions
++from openstack_dashboard.test import helpers as test
++
++
++class ErrorPageTests(test.TestCase):
++    """ Tests for error pages """
++    urls = 'openstack_dashboard.test.error_pages_urls'
++
++    def test_500_error(self):
++        TEMPLATE_DIRS = (path.join(settings.ROOT_PATH, 'templates'),)
++        with self.settings(TEMPLATE_DIRS=TEMPLATE_DIRS):
++            response = self.client.get('/500/')
++            self.assertTrue('Server error' in response.content)
+diff --git a/openstack_dashboard/urls.py b/openstack_dashboard/urls.py
+index 6d1a55e..aa1654d 100644
+--- a/openstack_dashboard/urls.py
++++ b/openstack_dashboard/urls.py
+@@ -42,3 +42,8 @@ urlpatterns += staticfiles_urlpatterns()
+ # development. Only active if DEBUG==True and the URL prefix is a local
+ # path. Production media should NOT be served by Django.
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
++
++if settings.DEBUG:
++    urlpatterns += patterns('',
++        url(r'^500/$', 'django.views.defaults.server_error')
++    )
diff --git a/0007-Allow-setting-nova-quotas-to-unlimited.patch b/0007-Allow-setting-nova-quotas-to-unlimited.patch
new file mode 100644
index 0000000..1eb93b6
--- /dev/null
+++ b/0007-Allow-setting-nova-quotas-to-unlimited.patch
@@ -0,0 +1,46 @@
+From e19a2189fd8c1ea6413e23049152edcce25c304c Mon Sep 17 00:00:00 2001
+From: Joe Gordon <jogo at cloudscaling.com>
+Date: Wed, 9 Jan 2013 12:15:04 -0800
+Subject: [PATCH] Allow setting nova quotas to unlimited
+
+To set a quota to unlimited it is set to -1
+
+Fix bug 1082489
+
+Change-Id: I2e1e53f6a5a33f2b248decc9d94f5a430f383508
+---
+ horizon/dashboards/syspanel/projects/workflows.py | 20 +++++++++++---------
+ 1 file changed, 11 insertions(+), 9 deletions(-)
+
+diff --git a/horizon/dashboards/syspanel/projects/workflows.py b/horizon/dashboards/syspanel/projects/workflows.py
+index 6d6e81a..e1c6645 100644
+--- a/horizon/dashboards/syspanel/projects/workflows.py
++++ b/horizon/dashboards/syspanel/projects/workflows.py
+@@ -33,16 +33,18 @@ ADD_USER_URL = "horizon:syspanel:projects:create_user"
+ 
+ class UpdateProjectQuotaAction(workflows.Action):
+     ifcb_label = _("Injected File Content Bytes")
+-    metadata_items = forms.IntegerField(min_value=0, label=_("Metadata Items"))
+-    cores = forms.IntegerField(min_value=0, label=_("VCPUs"))
+-    instances = forms.IntegerField(min_value=0, label=_("Instances"))
+-    injected_files = forms.IntegerField(min_value=0, label=_("Injected Files"))
+-    injected_file_content_bytes = forms.IntegerField(min_value=0,
++    metadata_items = forms.IntegerField(min_value=-1,
++            label=_("Metadata Items"))
++    cores = forms.IntegerField(min_value=-1, label=_("VCPUs"))
++    instances = forms.IntegerField(min_value=-1, label=_("Instances"))
++    injected_files = forms.IntegerField(min_value=-1,
++            label=_("Injected Files"))
++    injected_file_content_bytes = forms.IntegerField(min_value=-1,
+                                                      label=ifcb_label)
+-    volumes = forms.IntegerField(min_value=0, label=_("Volumes"))
+-    gigabytes = forms.IntegerField(min_value=0, label=_("Gigabytes"))
+-    ram = forms.IntegerField(min_value=0, label=_("RAM (MB)"))
+-    floating_ips = forms.IntegerField(min_value=0, label=_("Floating IPs"))
++    volumes = forms.IntegerField(min_value=-1, label=_("Volumes"))
++    gigabytes = forms.IntegerField(min_value=-1, label=_("Gigabytes"))
++    ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)"))
++    floating_ips = forms.IntegerField(min_value=-1, label=_("Floating IPs"))
+ 
+     class Meta:
+         name = _("Quota")
diff --git a/0008-Avoid-cinder-calls-when-cinder-is-unavailable.patch b/0008-Avoid-cinder-calls-when-cinder-is-unavailable.patch
new file mode 100644
index 0000000..122e1c8
--- /dev/null
+++ b/0008-Avoid-cinder-calls-when-cinder-is-unavailable.patch
@@ -0,0 +1,236 @@
+From 476072d49a41cd8461aa302591f828627760b67e Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Tue, 18 Dec 2012 14:00:48 +0100
+Subject: [PATCH] Avoid cinder calls, when cinder is unavailable
+
+When a volume service is not available, then a cinder client can not
+be created. This patch skips the calls, so that dashboard doesn't
+break any more.
+
+This is a backport to Folsom release, as this turned out to be a major
+issue.
+Fixes bug 1084137
+
+Change-Id: I8f2f8b0b131b4bb5319d74f6da48671f146d7e00
+---
+ horizon/api/base.py                                |  9 ++++
+ horizon/api/nova.py                                | 22 +++++++--
+ .../dashboards/nova/images_and_snapshots/views.py  | 14 ++++--
+ horizon/test.py                                    | 10 ++++
+ horizon/tests/api_tests/cinder_tests.py            | 56 ++++++++++++++++++++++
+ 5 files changed, 101 insertions(+), 10 deletions(-)
+ create mode 100644 horizon/tests/api_tests/cinder_tests.py
+
+diff --git a/horizon/api/base.py b/horizon/api/base.py
+index 15f9f9e..929221c 100644
+--- a/horizon/api/base.py
++++ b/horizon/api/base.py
+@@ -116,3 +116,12 @@ def url_for(request, service_type, admin=False, endpoint_type=None):
+             raise exceptions.ServiceCatalogException(service_type)
+     else:
+         raise exceptions.ServiceCatalogException(service_type)
++
++
++def is_service_enabled(request, service_type, service_name=None):
++    service = get_service_from_catalog(request.user.service_catalog,
++                                       service_type)
++    if service and service_name:
++        return service['name'] == service_name
++    else:
++        return service is not None
+diff --git a/horizon/api/nova.py b/horizon/api/nova.py
+index 3bf209f..6861f5a 100644
+--- a/horizon/api/nova.py
++++ b/horizon/api/nova.py
+@@ -210,15 +210,21 @@ def novaclient(request):
+ 
+ def cinderclient(request):
+     insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
++    cinder_url = ""
++    try:
++        cinder_url = url_for(request, 'volume')
++    except exceptions.ServiceCatalogException:
++        LOG.debug('no volume service configured.')
++        return None
+     LOG.debug('cinderclient connection created using token "%s" and url "%s"' %
+-              (request.user.token.id, url_for(request, 'volume')))
++              (request.user.token.id, cinder_url))
+     c = cinder_client.Client(request.user.username,
+                              request.user.token.id,
+                              project_id=request.user.tenant_id,
+-                             auth_url=url_for(request, 'volume'),
++                             auth_url=cinder_url,
+                              insecure=insecure)
+     c.client.auth_token = request.user.token.id
+-    c.client.management_url = url_for(request, 'volume')
++    c.client.management_url = cinder_url
+     return c
+ 
+ 
+@@ -513,7 +519,10 @@ def volume_list(request, search_opts=None):
+     To see all volumes in the cloud as an admin you can pass in a special
+     search option: {'all_tenants': 1}
+     """
+-    return cinderclient(request).volumes.list(search_opts=search_opts)
++    c_client = cinderclient(request)
++    if c_client is None:
++        return []
++    return c_client.volumes.list(search_opts=search_opts)
+ 
+ 
+ def volume_get(request, volume_id):
+@@ -565,7 +574,10 @@ def volume_snapshot_get(request, snapshot_id):
+ 
+ 
+ def volume_snapshot_list(request):
+-    return cinderclient(request).volume_snapshots.list()
++    c_client = cinderclient(request)
++    if c_client is None:
++        return []
++    return c_client.volume_snapshots.list()
+ 
+ 
+ def volume_snapshot_create(request, volume_id, name, description):
+diff --git a/horizon/dashboards/nova/images_and_snapshots/views.py b/horizon/dashboards/nova/images_and_snapshots/views.py
+index bade47e..b382265 100644
+--- a/horizon/dashboards/nova/images_and_snapshots/views.py
++++ b/horizon/dashboards/nova/images_and_snapshots/views.py
+@@ -28,6 +28,7 @@ import logging
+ from django.utils.translation import ugettext_lazy as _
+ 
+ from horizon import api
++from horizon.api.base import is_service_enabled
+ from horizon import exceptions
+ from horizon import tables
+ from horizon import tabs
+@@ -74,12 +75,15 @@ class IndexView(tables.MultiTableView):
+         return snaps
+ 
+     def get_volume_snapshots_data(self):
+-        try:
+-            snapshots = api.volume_snapshot_list(self.request)
+-        except:
++        if is_service_enabled(self.request, 'volume'):
++            try:
++                snapshots = api.volume_snapshot_list(self.request)
++            except:
++                snapshots = []
++                exceptions.handle(self.request, _("Unable to retrieve "
++                                                  "volume snapshots."))
++        else:
+             snapshots = []
+-            exceptions.handle(self.request, _("Unable to retrieve "
+-                                              "volume snapshots."))
+         return snapshots
+ 
+ 
+diff --git a/horizon/test.py b/horizon/test.py
+index f2977b1..d6cfa51 100644
+--- a/horizon/test.py
++++ b/horizon/test.py
+@@ -36,6 +36,7 @@ from keystoneclient.v2_0 import client as keystone_client
+ from novaclient.v1_1 import client as nova_client
+ from quantumclient.v2_0 import client as quantum_client
+ from swiftclient import client as swift_client
++from cinderclient import client as cinder_client
+ 
+ from selenium.webdriver.firefox.webdriver import WebDriver
+ 
+@@ -293,12 +294,14 @@ class APITestCase(TestCase):
+         self._original_keystoneclient = api.keystone.keystoneclient
+         self._original_novaclient = api.nova.novaclient
+         self._original_quantumclient = api.quantum.quantumclient
++        self._original_cinderclient = api.nova.cinderclient
+ 
+         # Replace the clients with our stubs.
+         api.glance.glanceclient = lambda request: self.stub_glanceclient()
+         api.keystone.keystoneclient = fake_keystoneclient
+         api.nova.novaclient = lambda request: self.stub_novaclient()
+         api.quantum.quantumclient = lambda request: self.stub_quantumclient()
++        api.nova.cinderclient = lambda request: self.stub_cinderclient()
+ 
+     def tearDown(self):
+         super(APITestCase, self).tearDown()
+@@ -306,6 +309,7 @@ class APITestCase(TestCase):
+         api.nova.novaclient = self._original_novaclient
+         api.keystone.keystoneclient = self._original_keystoneclient
+         api.quantum.quantumclient = self._original_quantumclient
++        api.nova.cinderclient = self._original_cinderclient
+ 
+     def stub_novaclient(self):
+         if not hasattr(self, "novaclient"):
+@@ -313,6 +317,12 @@ class APITestCase(TestCase):
+             self.novaclient = self.mox.CreateMock(nova_client.Client)
+         return self.novaclient
+ 
++    def stub_cinderclient(self):
++        if not hasattr(self, "cinderclient"):
++            self.mox.StubOutWithMock(cinder_client, 'Client')
++            self.cinderclient = self.mox.CreateMock(cinder_client.Client)
++        return self.cinderclient
++
+     def stub_keystoneclient(self):
+         if not hasattr(self, "keystoneclient"):
+             self.mox.StubOutWithMock(keystone_client, 'Client')
+diff --git a/horizon/tests/api_tests/cinder_tests.py b/horizon/tests/api_tests/cinder_tests.py
+new file mode 100644
+index 0000000..ab3ce75
+--- /dev/null
++++ b/horizon/tests/api_tests/cinder_tests.py
+@@ -0,0 +1,56 @@
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright 2012 Red Hat, 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 horizon import api
++from horizon import test
++
++
++class CinderApiTests(test.APITestCase):
++    def test_volume_list(self):
++        search_opts = {'all_tenants': 1}
++        volumes = self.volumes.list()
++        cinderclient = self.stub_cinderclient()
++        cinderclient.volumes = self.mox.CreateMockAnything()
++        cinderclient.volumes.list(search_opts=search_opts,).AndReturn(volumes)
++        self.mox.ReplayAll()
++
++        # No assertions are necessary. Verification is handled by mox.
++        api.nova.volume_list(self.request, search_opts=search_opts)
++
++    def test_volume_snapshot_list(self):
++        volume_snapshots = self.volume_snapshots.list()
++        cinderclient = self.stub_cinderclient()
++        cinderclient.volume_snapshots = self.mox.CreateMockAnything()
++        cinderclient.volume_snapshots.list().AndReturn(volume_snapshots)
++        self.mox.ReplayAll()
++
++        api.nova.volume_snapshot_list(self.request)
++
++    def test_volume_snapshot_list_no_volume_configured(self):
++        # remove volume from service catalog
++        catalog = self.service_catalog
++        for service in catalog:
++            if service["type"] == "volume":
++                self.service_catalog.remove(service)
++        volume_snapshots = self.volume_snapshots.list()
++
++        cinderclient = self.stub_cinderclient()
++        cinderclient.volume_snapshots = self.mox.CreateMockAnything()
++        cinderclient.volume_snapshots.list().AndReturn(volume_snapshots)
++        self.mox.ReplayAll()
++
++        api.nova.volume_snapshot_list(self.request)
diff --git a/0005-disable-debug-move-web-root.patch b/0009-disable-debug-move-web-root.patch
similarity index 100%
copy from 0005-disable-debug-move-web-root.patch
copy to 0009-disable-debug-move-web-root.patch
diff --git a/0010-Add-a-check-for-unlimited-quotas.patch b/0010-Add-a-check-for-unlimited-quotas.patch
new file mode 100644
index 0000000..68238a6
--- /dev/null
+++ b/0010-Add-a-check-for-unlimited-quotas.patch
@@ -0,0 +1,90 @@
+From ebc5e6db042cbd3ba16fc06ddfb8a785dea238cb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Victoria=20Mart=C3=ADnez=20de=20la=20Cruz?=
+ <vickymsee at gmail.com>
+Date: Fri, 11 Jan 2013 17:43:35 -0300
+Subject: [PATCH] Add a check for unlimited quotas
+
+This extra check will prevent Horizon to
+misunderstand "-1" value when tallying quotas,
+setting the "Available" field to inf
+instead of 0.
+
+Change-Id: Idfeb9a10b8ce8eea1a39b3d48dffe08d6450fadd
+Fixes: bug #1084976
+---
+ horizon/api/nova.py                   |  2 +-
+ horizon/tests/api_tests/nova_tests.py | 51 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 52 insertions(+), 1 deletion(-)
+
+diff --git a/horizon/api/nova.py b/horizon/api/nova.py
+index 3bf209f..43618e2 100644
+--- a/horizon/api/nova.py
++++ b/horizon/api/nova.py
+@@ -455,7 +455,7 @@ def tenant_quota_usages(request):
+ 
+         usages[usage]['quota'] = getattr(quotas, usage)
+ 
+-        if usages[usage]['quota'] is None:
++        if usages[usage]['quota'] is None or usages[usage]['quota'] == -1:
+             usages[usage]['quota'] = float("inf")
+             usages[usage]['available'] = float("inf")
+         elif type(usages[usage]['quota']) is str:
+diff --git a/horizon/tests/api_tests/nova_tests.py b/horizon/tests/api_tests/nova_tests.py
+index 1952519..5131a3a 100644
+--- a/horizon/tests/api_tests/nova_tests.py
++++ b/horizon/tests/api_tests/nova_tests.py
+@@ -204,3 +204,54 @@ class ComputeApiTests(test.APITestCase):
+                                 'quota': 10}}
+ 
+         self.assertEquals(quota_usages, expected_output)
++
++    @test.create_stubs({api.nova: ('volume_list',
++                                   'server_list',
++                                   'flavor_list',
++                                   'tenant_floating_ip_list',
++                                   'tenant_quota_get',)})
++    def test_tenant_quota_usages_unlimited_quota(self):
++        inf_quota = self.quotas.first()
++        inf_quota.ram = -1
++
++        api.nova.flavor_list(IsA(http.HttpRequest)) \
++            .AndReturn(self.flavors.list())
++        api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
++            .AndReturn(inf_quota)
++        api.nova.tenant_floating_ip_list(IsA(http.HttpRequest)) \
++            .AndReturn(self.floating_ips.list())
++        api.nova.server_list(IsA(http.HttpRequest)) \
++            .AndReturn(self.servers.list())
++        api.nova.volume_list(IsA(http.HttpRequest)) \
++            .AndReturn(self.volumes.list())
++
++        self.mox.ReplayAll()
++
++        quota_usages = api.tenant_quota_usages(self.request)
++        expected_output = {'gigabytes': {
++                                'used': 80,
++                                'flavor_fields': [],
++                                'quota': 1000},
++                           'ram': {
++                                'available': float("inf"),
++                                'used': 1024,
++                                'flavor_fields': ['ram'],
++                                'quota': float("inf")},
++                           'floating_ips': {
++                                'used': 2,
++                                'flavor_fields': [],
++                                'quota': 1},
++                           'instances': {
++                                'used': 2,
++                                'flavor_fields': [],
++                                'quota': 10},
++                           'volumes': {
++                                'used': 3,
++                                'flavor_fields': [],
++                                'quota': 1},
++                           'cores': {
++                                'used': 2,
++                                'flavor_fields': ['vcpus'],
++                                'quota': 10}}
++
++        self.assertEquals(quota_usages, expected_output)
diff --git a/0006-disable-to-set-mount-point-as-it-s-unsupported-here.patch b/0011-disable-to-set-mount-point-as-it-s-unsupported-here.patch
similarity index 100%
rename from 0006-disable-to-set-mount-point-as-it-s-unsupported-here.patch
rename to 0011-disable-to-set-mount-point-as-it-s-unsupported-here.patch
diff --git a/0005-disable-debug-move-web-root.patch b/0012-disable-debug-move-web-root.patch
similarity index 97%
rename from 0005-disable-debug-move-web-root.patch
rename to 0012-disable-debug-move-web-root.patch
index a390677..8453d80 100644
--- a/0005-disable-debug-move-web-root.patch
+++ b/0012-disable-debug-move-web-root.patch
@@ -1,4 +1,4 @@
-From 43f98c4d393daefa6b45cd5253699676ef877328 Mon Sep 17 00:00:00 2001
+From e852e4afb29bc04567fe77cf0dfa0871215e6181 Mon Sep 17 00:00:00 2001
 From: Matthias Runge <mrunge at redhat.com>
 Date: Tue, 4 Dec 2012 13:46:22 +0100
 Subject: [PATCH] disable debug, move web root
diff --git a/0013-take-variables-out-of-compressed-output.patch b/0013-take-variables-out-of-compressed-output.patch
new file mode 100644
index 0000000..5daf241
--- /dev/null
+++ b/0013-take-variables-out-of-compressed-output.patch
@@ -0,0 +1,26 @@
+From fcd27175f278caebbdaff92553dc1fe3f8957f66 Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Mon, 21 Jan 2013 10:19:23 +0100
+Subject: [PATCH] take variables out of compressed output
+
+---
+ horizon/templates/horizon/_conf.html | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/horizon/templates/horizon/_conf.html b/horizon/templates/horizon/_conf.html
+index 2dc70fa..2ed6382 100644
+--- a/horizon/templates/horizon/_conf.html
++++ b/horizon/templates/horizon/_conf.html
+@@ -3,6 +3,7 @@
+ {% compress js %}
+ <script src='{{ STATIC_URL }}horizon/js/horizon.js' type='text/javascript' charset='utf-8'></script>
+ <script src='{{ STATIC_URL }}horizon/js/horizon.conf.js' type='text/javascript' charset='utf-8'></script>
++{% endcompress %}
+ <script type='text/javascript' charset='utf-8'>
+ /* Storage for backend configuration variables which the frontend
+  * should be aware of.
+@@ -13,4 +14,3 @@ horizon.conf.ajax = {
+   queue_limit: {{ HORIZON_CONFIG.ajax_queue_limit|default:"null" }}
+ };
+ </script>
+-{% endcompress %}
diff --git a/python-django-horizon.spec b/python-django-horizon.spec
index 68840a1..6803478 100644
--- a/python-django-horizon.spec
+++ b/python-django-horizon.spec
@@ -20,8 +20,15 @@ Patch0001: 0001-Revert-Temp-fix-for-api-keystone.py.patch
 Patch0002: 0002-Fix-bug-1055929-Can-not-display-usage-data-for-Quota.patch
 Patch0003: 0003-Bump-next-version-to-2012.2.2.patch
 Patch0004: 0004-Pin-docutils-to-0.9.1-fix-pep8-errors.patch
-Patch0005: 0005-disable-debug-move-web-root.patch
-Patch0006: 0006-disable-to-set-mount-point-as-it-s-unsupported-here.patch
+Patch0005: 0005-Specify-floating-ips-table-action-column-s-width.patch
+Patch0006: 0006-Don-t-inherit-from-base.html-in-500-error-page.patch
+Patch0007: 0007-Allow-setting-nova-quotas-to-unlimited.patch
+Patch0008: 0008-Avoid-cinder-calls-when-cinder-is-unavailable.patch
+Patch0009: 0009-disable-debug-move-web-root.patch
+Patch0010: 0010-Add-a-check-for-unlimited-quotas.patch
+Patch0011: 0011-disable-to-set-mount-point-as-it-s-unsupported-here.patch
+Patch0012: 0012-disable-debug-move-web-root.patch
+Patch0013: 0013-take-variables-out-of-compressed-output.patch
 
 
 %if 0%{?rhel}<7 || 0%{?fedora} < 18
@@ -126,6 +133,13 @@ Documentation for the Django Horizon application for talking with Openstack
 %patch0004 -p1
 %patch0005 -p1
 %patch0006 -p1
+%patch0007 -p1
+%patch0008 -p1
+%patch0009 -p1
+%patch0010 -p1
+%patch0011 -p1
+%patch0012 -p1
+%patch0013 -p1
 # remove unnecessary .po files
 find . -name "django*.po" -exec rm -f '{}' \;
 


More information about the scm-commits mailing list