[python-django-horizon] Update to havana-3 snapshot
Matthias Runge
mrunge at fedoraproject.org
Mon Sep 9 12:31:54 UTC 2013
commit 92cf4a7753189127ae91be69b3b951947f3921a5
Author: Matthias Runge <mrunge at redhat.com>
Date: Mon Sep 9 14:14:21 2013 +0200
Update to havana-3 snapshot
.gitignore | 1 +
...-Don-t-access-the-net-while-building-docs.patch | 4 +-
0002-disable-debug-move-web-root.patch | 35 +-
...file-location-to-tmp-and-also-add-localho.patch | 4 +-
...-Add-a-customization-module-based-on-RHOS.patch | 260 +++
...eystone-test-data-to-match-openstack_auth.patch | 91 -
...-Add-a-customization-module-based-on-RHOS.patch | 1716 --------------
0005-Revert-Adding-panels-for-trove.patch | 2375 ++++++++++++++++++++
...oslo.sphinx-and-remove-local-copy-of-doc-.patch | 1348 +++++++++++
...olicy-files-and-checks-to-etc-openstack-d.patch | 32 +
python-django-horizon.spec | 8 +-
sources | 2 +-
12 files changed, 4042 insertions(+), 1834 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 12b8945..73cd073 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@
/horizon-2013.1.1.tar.gz
/horizon-2013.2.b1.tar.gz
/horizon-2013.2.b2.tar.gz
+/horizon-2013.2.b3.tar.gz
diff --git a/0001-Don-t-access-the-net-while-building-docs.patch b/0001-Don-t-access-the-net-while-building-docs.patch
index 7ca49df..5863678 100644
--- a/0001-Don-t-access-the-net-while-building-docs.patch
+++ b/0001-Don-t-access-the-net-while-building-docs.patch
@@ -1,4 +1,4 @@
-From 4d9ab732c9a60c07158b799117db7d5fbf4fa05a Mon Sep 17 00:00:00 2001
+From e843606f059684957482b85db841b2623eddeda2 Mon Sep 17 00:00:00 2001
From: Matthias Runge <mrunge at redhat.com>
Date: Fri, 5 Apr 2013 10:16:19 +0200
Subject: [PATCH] Don't access the net while building docs (Note this hasn't
@@ -9,7 +9,7 @@ Subject: [PATCH] Don't access the net while building docs (Note this hasn't
1 file changed, 1 deletion(-)
diff --git a/doc/source/conf.py b/doc/source/conf.py
-index 297a688..adb3d14 100644
+index b708972..0a7f677 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -141,7 +141,6 @@ write_autodoc_index()
diff --git a/0002-disable-debug-move-web-root.patch b/0002-disable-debug-move-web-root.patch
index 9c0b0c6..0c4cbd0 100644
--- a/0002-disable-debug-move-web-root.patch
+++ b/0002-disable-debug-move-web-root.patch
@@ -1,15 +1,15 @@
-From 796abb7ce7b94ff98aacf5233870f0afcfe786c4 Mon Sep 17 00:00:00 2001
+From c1ad47c868f41c4e7f2a8c94b02242741c487534 Mon Sep 17 00:00:00 2001
From: Matthias Runge <mrunge at redhat.com>
Date: Fri, 5 Apr 2013 10:07:53 +0200
Subject: [PATCH] disable debug, move web root
---
- openstack_dashboard/local/local_settings.py.example | 4 ++--
- openstack_dashboard/settings.py | 12 +++++++-----
- 2 files changed, 9 insertions(+), 7 deletions(-)
+ openstack_dashboard/local/local_settings.py.example | 8 ++++++--
+ openstack_dashboard/settings.py | 10 ++++++----
+ 2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example
-index 4494b69..3f5da3d 100644
+index 9c3f714..fa4bb18 100644
--- a/openstack_dashboard/local/local_settings.py.example
+++ b/openstack_dashboard/local/local_settings.py.example
@@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _
@@ -21,17 +21,21 @@ index 4494b69..3f5da3d 100644
TEMPLATE_DEBUG = DEBUG
# Required for Django 1.5.
-@@ -147,7 +147,7 @@ OPENSTACK_KEYSTONE_BACKEND = {
+@@ -150,7 +150,11 @@ OPENSTACK_KEYSTONE_BACKEND = {
}
OPENSTACK_HYPERVISOR_FEATURES = {
- 'can_set_mount_point': True,
+ 'can_set_mount_point': False,
++
++ # NOTE: as of Grizzly this is not yet supported in Nova so enabling this
++ # setting will not do anything useful
++ 'can_encrypt_volumes': False
+ }
- # NOTE: as of Grizzly this is not yet supported in Nova so enabling this
- # setting will not do anything useful
+ # The OPENSTACK_NEUTRON_NETWORK settings can be used to enable optional
diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py
-index 9613f58..02ac8ba 100644
+index 378917b..9d1ee84 100644
--- a/openstack_dashboard/settings.py
+++ b/openstack_dashboard/settings.py
@@ -29,7 +29,7 @@ warnings.formatwarning = lambda message, category, *args, **kwargs: \
@@ -43,7 +47,7 @@ index 9613f58..02ac8ba 100644
if ROOT_PATH not in sys.path:
sys.path.append(ROOT_PATH)
-@@ -39,12 +39,13 @@ TEMPLATE_DEBUG = DEBUG
+@@ -47,12 +47,13 @@ SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH,
SITE_BRANDING = 'OpenStack Dashboard'
@@ -60,16 +64,7 @@ index 9613f58..02ac8ba 100644
MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media'))
MEDIA_URL = '/media/'
-@@ -112,7 +113,7 @@ STATICFILES_FINDERS = (
- 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
- )
-
--less_binary = os.path.join(BIN_DIR, 'less', 'lessc')
-+less_binary = os.path.join(BIN_DIR, 'lessc')
- COMPRESS_PRECOMPILERS = (
- ('text/less', (less_binary + ' {infile} {outfile}')),
- )
-@@ -125,6 +126,7 @@ COMPRESS_ENABLED = True
+@@ -132,6 +133,7 @@ COMPRESS_ENABLED = True
COMPRESS_OUTPUT_DIR = 'dashboard'
COMPRESS_CSS_HASHING_METHOD = 'hash'
COMPRESS_PARSER = 'compressor.parser.HtmlParser'
diff --git a/0003-change-lockfile-location-to-tmp-and-also-add-localho.patch b/0003-change-lockfile-location-to-tmp-and-also-add-localho.patch
index eaf0186..b85326f 100644
--- a/0003-change-lockfile-location-to-tmp-and-also-add-localho.patch
+++ b/0003-change-lockfile-location-to-tmp-and-also-add-localho.patch
@@ -1,4 +1,4 @@
-From fcf8b00597925b908f565bd27b29b440f41b3e78 Mon Sep 17 00:00:00 2001
+From 8d98fac2f2de942662822b495c1d8b36761c8603 Mon Sep 17 00:00:00 2001
From: Matthias Runge <mrunge at redhat.com>
Date: Thu, 25 Jul 2013 11:32:38 +0200
Subject: [PATCH] change lockfile location to '/tmp' and also add localhost to
@@ -9,7 +9,7 @@ Subject: [PATCH] change lockfile location to '/tmp' and also add localhost to
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example
-index 3f5da3d..52f1656 100644
+index fa4bb18..9e4675c 100644
--- a/openstack_dashboard/local/local_settings.py.example
+++ b/openstack_dashboard/local/local_settings.py.example
@@ -12,7 +12,7 @@ TEMPLATE_DEBUG = DEBUG
diff --git a/0004-Add-a-customization-module-based-on-RHOS.patch b/0004-Add-a-customization-module-based-on-RHOS.patch
new file mode 100644
index 0000000..faa0188
--- /dev/null
+++ b/0004-Add-a-customization-module-based-on-RHOS.patch
@@ -0,0 +1,260 @@
+From cdbb878b1c43671873e21836dd8ba65e8a9a4750 Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Thu, 14 Feb 2013 12:55:54 +0100
+Subject: [PATCH] Add a customization module based on RHOS
+
+Change-Id: I8622ffc8e31cf553315077070b2a696de1bd5081
+
+Conflicts:
+ openstack_dashboard/settings.py
+---
+ openstack_dashboard/settings.py | 12 ++++-
+ openstack_dashboard_theme/__init__.py | 0
+ openstack_dashboard_theme/models.py | 0
+ .../static/dashboard/less/rhtheme.less | 55 ++++++++++++++++++++++
+ .../templates/_stylesheets.html | 7 +++
+ .../templates/auth/_login.html | 29 ++++++++++++
+ .../templates/auth/login.html | 10 ++++
+ .../templates/horizon/common/_sidebar.html | 35 ++++++++++++++
+ openstack_dashboard_theme/templates/splash.html | 17 +++++++
+ 9 files changed, 163 insertions(+), 2 deletions(-)
+ create mode 100644 openstack_dashboard_theme/__init__.py
+ create mode 100644 openstack_dashboard_theme/models.py
+ create mode 100644 openstack_dashboard_theme/static/dashboard/less/rhtheme.less
+ create mode 100644 openstack_dashboard_theme/templates/_stylesheets.html
+ create mode 100644 openstack_dashboard_theme/templates/auth/_login.html
+ create mode 100644 openstack_dashboard_theme/templates/auth/login.html
+ create mode 100644 openstack_dashboard_theme/templates/horizon/common/_sidebar.html
+ create mode 100644 openstack_dashboard_theme/templates/splash.html
+
+diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py
+index 9d1ee84..7bad6f8 100644
+--- a/openstack_dashboard/settings.py
++++ b/openstack_dashboard/settings.py
+@@ -107,14 +107,13 @@ TEMPLATE_CONTEXT_PROCESSORS = (
+ )
+
+ TEMPLATE_LOADERS = (
+- 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+ 'horizon.loaders.TemplateLoader'
+ )
+
+ TEMPLATE_DIRS = (
+ os.path.join(ROOT_PATH, 'templates'),
+-)
++)
+
+ STATICFILES_FINDERS = (
+ 'compressor.finders.CompressorFinder',
+@@ -152,6 +151,15 @@ INSTALLED_APPS = (
+ 'openstack_dashboard.dashboards.router',
+ )
+
++THEME_APP = 'openstack_dashboard_theme'
++
++try:
++ __import__(THEME_APP)
++ INSTALLED_APPS = (THEME_APP,) + INSTALLED_APPS
++except:
++ pass
++
++
+ TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
+ AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
+ MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
+diff --git a/openstack_dashboard_theme/__init__.py b/openstack_dashboard_theme/__init__.py
+new file mode 100644
+index 0000000..e69de29
+diff --git a/openstack_dashboard_theme/models.py b/openstack_dashboard_theme/models.py
+new file mode 100644
+index 0000000..e69de29
+diff --git a/openstack_dashboard_theme/static/dashboard/less/rhtheme.less b/openstack_dashboard_theme/static/dashboard/less/rhtheme.less
+new file mode 100644
+index 0000000..5350db3
+--- /dev/null
++++ b/openstack_dashboard_theme/static/dashboard/less/rhtheme.less
+@@ -0,0 +1,55 @@
++ at import "/static/dashboard/less/horizon.less";
++
++/* Login Splash Page */
++
++#splash .rhlogin {
++ background: #fff url(/static/dashboard/img/rh-logo.png) no-repeat center 35px;
++ position: absolute;
++ top: 80px;
++ left: 50%;
++ margin: 0 0 0 -195px;
++ padding-top: 170px;
++ width: 390px;
++ border: 1px solid #e1e1e1;
++ max-height: none;
++ -webkit-border-radius: 6px;
++ -moz-border-radius: 6px;
++ border-radius: 6px;
++ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
++ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
++ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
++ -webkit-background-clip: padding-box;
++ -moz-background-clip: padding-box;
++ background-clip: padding-box;
++
++ form {
++ .error {
++ width: 100%;
++ }
++ input {
++ width: 350px;
++ }
++ select {
++ width: 360px;
++ }
++ }
++}
++
++h1.rhos {
++ width: 100%;
++ margin: 0;
++ background-color: #f5f5f5;
++ padding-bottom: 40px;
++}
++
++h1.rhos a {
++ background: url(/static/dashboard/img/rh-logo.png) top left no-repeat;
++ display: block;
++ float: left;
++ width: 116px;
++ height: 123px;
++ text-indent: -9999px;
++ margin-left: 56px;
++ margin-top: 15px;
++ margin-bottom: 25px;
++}
+diff --git a/openstack_dashboard_theme/templates/_stylesheets.html b/openstack_dashboard_theme/templates/_stylesheets.html
+new file mode 100644
+index 0000000..88463e1
+--- /dev/null
++++ b/openstack_dashboard_theme/templates/_stylesheets.html
+@@ -0,0 +1,7 @@
++{% load compress %}
++
++{% compress css %}
++<link href='{{ STATIC_URL }}dashboard/less/rhtheme.less' type='text/less' media='screen' rel='stylesheet' />
++{% endcompress %}
++
++<link rel="shortcut icon" href="{{ STATIC_URL }}dashboard/img/rhfavicon.ico"/>
+diff --git a/openstack_dashboard_theme/templates/auth/_login.html b/openstack_dashboard_theme/templates/auth/_login.html
+new file mode 100644
+index 0000000..1cb6036
+--- /dev/null
++++ b/openstack_dashboard_theme/templates/auth/_login.html
+@@ -0,0 +1,29 @@
++{% extends "horizon/common/_modal_form.html" %}
++{% load i18n %}
++{% load url from future %}
++
++{% block modal-header %}{% trans "Log In" %}{% endblock %}
++{% block modal_class %}rhlogin{% if hide %}modal hide{% endif %}{% endblock %}
++
++{% block form_action %}{% url 'login' %}{% endblock %}
++{% block autocomplete %}{{ HORIZON_CONFIG.password_autocomplete }}{% endblock %}
++
++{% block modal-body %}
++ <fieldset>
++ {% if request.user.is_authenticated and 'next' in request.GET %}
++ <div class="control-group clearfix error">
++ <span class="help-inline"><p>{% trans "You don't have permissions to access:" %}</p>
++ <p><b>{{ request.GET.next }}</b></p>
++ <p>{% trans "Login as different user or go back to" %}
++ <a href="{% url 'horizon:user_home' %}">{% trans "home page" %}</a></p>
++ </span>
++ </div>
++ {% endif %}
++ {% if next %}<input type="hidden" name="{{ redirect_field_name }}" value="{{ next }}" />{% endif %}
++ {% include "horizon/common/_form_fields.html" %}
++ </fieldset>
++{% endblock %}
++
++{% block modal-footer %}
++ <button type="submit" class="btn btn-primary pull-right">{% trans "Sign In" %}</button>
++{% endblock %}
+diff --git a/openstack_dashboard_theme/templates/auth/login.html b/openstack_dashboard_theme/templates/auth/login.html
+new file mode 100644
+index 0000000..6fa7746
+--- /dev/null
++++ b/openstack_dashboard_theme/templates/auth/login.html
+@@ -0,0 +1,10 @@
++{% extends "base.html" %}
++{% load i18n %}
++
++{% block title %}{% trans "Login" %}{% endblock %}
++
++{% block body_id %}splash{% endblock %}
++
++{% block content %}
++ {% include 'auth/_login.html' %}
++{% endblock %}
+diff --git a/openstack_dashboard_theme/templates/horizon/common/_sidebar.html b/openstack_dashboard_theme/templates/horizon/common/_sidebar.html
+new file mode 100644
+index 0000000..5672846
+--- /dev/null
++++ b/openstack_dashboard_theme/templates/horizon/common/_sidebar.html
+@@ -0,0 +1,35 @@
++{% load branding horizon i18n %}
++
++<div class='sidebar'>
++ <h1 class="rhos clearfix"><a href="{% url horizon:user_home %}">{% site_branding %}</a></h1>
++
++ {% horizon_main_nav %}
++
++ {% if request.horizon.dashboard.supports_tenants %}
++ <div id="tenant_switcher" class="dropdown switcher_bar" tabindex="1">
++ {% with num_of_tenants=authorized_tenants|length %}
++ {% if num_of_tenants > 1 %}
++ <a class="dropdown-toggle" data-toggle="dropdown" href="#tenant_switcher">
++ {% endif %}
++ <h4>{% trans "Current Project" %}</h4>
++ <h3>{{ request.user.tenant_name }}</h3>
++ {% if num_of_tenants > 1 %}
++ </a>
++ {% endif %}
++
++ {% if num_of_tenants > 1 %}
++ <ul id="tenant_list" class="dropdown-menu">
++ <li class='divider'></li>
++ {% for tenant in authorized_tenants %}
++ {% if tenant.enabled and tenant.id != request.user.tenant_id %}
++ <li><a href="{% url switch_tenants tenant.id %}">{{ tenant.name }}</a></li>
++ {% endif %}
++ {% endfor %}
++ </ul>
++ {% endif %}
++ {% endwith %}
++ </div>
++ {% endif %}
++
++ {% horizon_dashboard_nav %}
++</div>
+diff --git a/openstack_dashboard_theme/templates/splash.html b/openstack_dashboard_theme/templates/splash.html
+new file mode 100644
+index 0000000..4a5cfe1
+--- /dev/null
++++ b/openstack_dashboard_theme/templates/splash.html
+@@ -0,0 +1,17 @@
++{% load i18n branding %}
++
++<!DOCTYPE html>
++<html lang="en" xml:lang="en">
++ <head>
++ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
++ <title>{% trans "Login" %} - {% site_branding %}</title>
++ {% include "_stylesheets.html" %}
++ </head>
++ <body id="splash">
++ <div class="container">
++ <div class="row large-rounded">
++ {% include 'auth/_login.html' %}
++ </div>
++ </div>
++ </body>
++</html>
diff --git a/0005-Revert-Adding-panels-for-trove.patch b/0005-Revert-Adding-panels-for-trove.patch
new file mode 100644
index 0000000..8f4f3c9
--- /dev/null
+++ b/0005-Revert-Adding-panels-for-trove.patch
@@ -0,0 +1,2375 @@
+From 74bae0b89a51ab55477e9a13e515f69a78fafb07 Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Mon, 9 Sep 2013 12:50:57 +0200
+Subject: [PATCH] Revert "Adding panels for trove"
+
+This reverts commit 8c1bc54f9338e0e9ebf06b33e665e0e9e35aee87.
+---
+ openstack_dashboard/api/__init__.py | 2 -
+ openstack_dashboard/api/trove.py | 127 -----------
+ openstack_dashboard/dashboards/admin/info/tests.py | 3 +-
+ .../dashboards/project/dashboard.py | 13 +-
+ .../project/database_backups/__init__.py | 0
+ .../dashboards/project/database_backups/panel.py | 31 ---
+ .../dashboards/project/database_backups/tables.py | 122 -----------
+ .../database_backups/_backup_details_help.html | 3 -
+ .../templates/database_backups/backup.html | 11 -
+ .../templates/database_backups/details.html | 53 -----
+ .../templates/database_backups/index.html | 11 -
+ .../dashboards/project/database_backups/tests.py | 97 ---------
+ .../dashboards/project/database_backups/urls.py | 28 ---
+ .../dashboards/project/database_backups/views.py | 100 ---------
+ .../project/database_backups/workflows/__init__.py | 3 -
+ .../database_backups/workflows/create_backup.py | 88 --------
+ .../dashboards/project/databases/__init__.py | 0
+ .../dashboards/project/databases/panel.py | 30 ---
+ .../dashboards/project/databases/tables.py | 236 ---------------------
+ .../dashboards/project/databases/tabs.py | 111 ----------
+ .../templates/databases/_detail_overview.html | 34 ---
+ .../templates/databases/_detail_users.html | 3 -
+ .../templates/databases/_launch_details_help.html | 53 -----
+ .../databases/_launch_initialize_help.html | 19 --
+ .../templates/databases/_launch_restore_help.html | 4 -
+ .../databases/templates/databases/detail.html | 15 --
+ .../databases/templates/databases/index.html | 11 -
+ .../databases/templates/databases/launch.html | 11 -
+ .../databases/templates/databases/update.html | 11 -
+ .../dashboards/project/databases/tests.py | 197 -----------------
+ .../dashboards/project/databases/urls.py | 29 ---
+ .../dashboards/project/databases/views.py | 120 -----------
+ .../project/databases/workflows/__init__.py | 3 -
+ .../project/databases/workflows/create_instance.py | 222 -------------------
+ openstack_dashboard/exceptions.py | 10 +-
+ .../local/local_settings.py.example | 7 -
+ openstack_dashboard/test/helpers.py | 10 -
+ openstack_dashboard/test/test_data/exceptions.py | 7 -
+ .../test/test_data/keystone_data.py | 10 +-
+ openstack_dashboard/test/test_data/trove_data.py | 79 -------
+ openstack_dashboard/test/test_data/utils.py | 4 +-
+ requirements.txt | 1 -
+ 42 files changed, 7 insertions(+), 1922 deletions(-)
+ delete mode 100644 openstack_dashboard/api/trove.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/__init__.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/panel.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/tables.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/templates/database_backups/_backup_details_help.html
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/templates/database_backups/backup.html
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/templates/database_backups/details.html
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/templates/database_backups/index.html
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/tests.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/urls.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/views.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/workflows/__init__.py
+ delete mode 100644 openstack_dashboard/dashboards/project/database_backups/workflows/create_backup.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/__init__.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/panel.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/tables.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/tabs.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/_detail_users.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/_launch_details_help.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/_launch_initialize_help.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/_launch_restore_help.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/detail.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/index.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/launch.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/templates/databases/update.html
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/tests.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/urls.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/views.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/workflows/__init__.py
+ delete mode 100644 openstack_dashboard/dashboards/project/databases/workflows/create_instance.py
+ delete mode 100644 openstack_dashboard/test/test_data/trove_data.py
+
+diff --git a/openstack_dashboard/api/__init__.py b/openstack_dashboard/api/__init__.py
+index 602f585..cc2a6bb 100644
+--- a/openstack_dashboard/api/__init__.py
++++ b/openstack_dashboard/api/__init__.py
+@@ -44,7 +44,6 @@ from openstack_dashboard.api import network
+ from openstack_dashboard.api import neutron
+ from openstack_dashboard.api import nova
+ from openstack_dashboard.api import swift
+-from openstack_dashboard.api import trove
+
+ assert base
+ assert cinder
+@@ -57,4 +56,3 @@ assert neutron
+ assert lbaas
+ assert swift
+ assert ceilometer
+-assert trove
+diff --git a/openstack_dashboard/api/trove.py b/openstack_dashboard/api/trove.py
+deleted file mode 100644
+index 36e8237..0000000
+--- a/openstack_dashboard/api/trove.py
++++ /dev/null
+@@ -1,127 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting.
+-#
+-# 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 django.conf import settings # noqa
+-
+-from troveclient import auth
+-from troveclient import client
+-
+-
+-class TokenAuth(object):
+- """Simple Token Authentication handler for trove api"""
+-
+- def __init__(self, client, auth_strategy, auth_url, username, password,
+- tenant, region, service_type, service_name, service_url):
+- # TODO(rmyers): handle some of these other args
+- self.username = username
+- self.service_type = service_type
+- self.service_name = service_name
+-
+- def authenticate(self):
+- catalog = {
+- 'access': {
+- 'serviceCatalog': self.username.service_catalog,
+- 'token': {
+- 'id': self.username.token.id,
+- }
+- }
+- }
+- return auth.ServiceCatalog(catalog,
+- service_type=self.service_type,
+- service_name=self.service_name)
+-
+-
+-def troveclient(request):
+- return client.Dbaas(username=request.user,
+- api_key=None,
+- auth_strategy=TokenAuth)
+-
+-
+-def instance_list(request, marker=None):
+- default_page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20)
+- page_size = request.session.get('horizon_pagesize', default_page_size)
+- return troveclient(request).instances.list(limit=page_size, marker=marker)
+-
+-
+-def instance_get(request, instance_id):
+- return troveclient(request).instances.get(instance_id)
+-
+-
+-def instance_delete(request, instance_id):
+- return troveclient(request).instances.delete(instance_id)
+-
+-
+-def instance_create(request, name, volume, flavor, databases=None,
+- users=None, restore_point=None):
+- return troveclient(request).instances.create(
+- name,
+- flavor,
+- {'size': volume},
+- databases=databases,
+- users=users,
+- restorePoint=restore_point)
+-
+-
+-def instance_backups(request, instance_id):
+- return troveclient(request).instances.backups(instance_id)
+-
+-
+-def instance_restart(request, instance_id):
+- return troveclient(request).instances.restart(instance_id)
+-
+-
+-def database_list(request, instance_id):
+- return troveclient(request).databases.list(instance_id)
+-
+-
+-def database_delete(request, instance_id, db_name):
+- return troveclient(request).databases.delete(instance_id, db_name)
+-
+-
+-def backup_list(request):
+- return troveclient(request).backups.list()
+-
+-
+-def backup_get(request, backup_id):
+- return troveclient(request).backups.get(backup_id)
+-
+-
+-def backup_delete(request, backup_id):
+- return troveclient(request).backups.delete(backup_id)
+-
+-
+-def backup_create(request, name, instance_id, description=None):
+- return troveclient(request).backups.create(name, instance_id, description)
+-
+-
+-def flavor_list(request):
+- return troveclient(request).flavors.list()
+-
+-
+-def flavor_get(request, flavor_id):
+- return troveclient(request).flavors.get(flavor_id)
+-
+-
+-def users_list(request, instance_id):
+- return troveclient(request).users.list(instance_id)
+-
+-
+-def user_delete(request, instance_id, user):
+- return troveclient(request).users.delete(instance_id, user)
+-
+-
+-def user_list_access(request, instance_id, user):
+- return troveclient(request).users.list_access(instance_id, user)
+diff --git a/openstack_dashboard/dashboards/admin/info/tests.py b/openstack_dashboard/dashboards/admin/info/tests.py
+index 46cc43b..60c6cc1 100644
+--- a/openstack_dashboard/dashboards/admin/info/tests.py
++++ b/openstack_dashboard/dashboards/admin/info/tests.py
+@@ -56,8 +56,7 @@ class SystemInfoViewTests(test.BaseAdminViewTests):
+ '<Service: network>',
+ '<Service: ec2>',
+ '<Service: metering>',
+- '<Service: orchestration>',
+- '<Service: database>'])
++ '<Service: orchestration>'])
+
+ zones_tab = res.context['tab_group'].get_tab('zones')
+ self.assertQuerysetEqual(zones_tab._tables['zones'].data,
+diff --git a/openstack_dashboard/dashboards/project/dashboard.py b/openstack_dashboard/dashboards/project/dashboard.py
+index eabc97a..a18fea4 100644
+--- a/openstack_dashboard/dashboards/project/dashboard.py
++++ b/openstack_dashboard/dashboards/project/dashboard.py
+@@ -50,22 +50,11 @@ class OrchestrationPanels(horizon.PanelGroup):
+ panels = ('stacks',)
+
+
+-class DatabasePanels(horizon.PanelGroup):
+- name = _("Manage Databases")
+- slug = "database"
+- panels = ('databases',
+- 'database_backups',)
+-
+-
+ class Project(horizon.Dashboard):
+ name = _("Project")
+ slug = "project"
+ panels = (
+- BasePanels,
+- NetworkPanels,
+- ObjectStorePanels,
+- OrchestrationPanels,
+- DatabasePanels,)
++ BasePanels, NetworkPanels, ObjectStorePanels, OrchestrationPanels)
+ default_panel = 'overview'
+ supports_tenants = True
+
+diff --git a/openstack_dashboard/dashboards/project/database_backups/__init__.py b/openstack_dashboard/dashboards/project/database_backups/__init__.py
+deleted file mode 100644
+index e69de29..0000000
+diff --git a/openstack_dashboard/dashboards/project/database_backups/panel.py b/openstack_dashboard/dashboards/project/database_backups/panel.py
+deleted file mode 100644
+index 3779e53..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/panel.py
++++ /dev/null
+@@ -1,31 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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 django.utils.translation import ugettext_lazy as _ # noqa
+-
+-import horizon
+-
+-from openstack_dashboard.dashboards.project import dashboard
+-
+-
+-class Backups(horizon.Panel):
+- name = _("Database Backups")
+- slug = 'database_backups'
+- permissions = ('openstack.services.database',
+- 'openstack.services.object-store',)
+-
+-
+-dashboard.Project.register(Backups)
+diff --git a/openstack_dashboard/dashboards/project/database_backups/tables.py b/openstack_dashboard/dashboards/project/database_backups/tables.py
+deleted file mode 100644
+index 877544c..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/tables.py
++++ /dev/null
+@@ -1,122 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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.
+-
+-import logging
+-
+-from django.core.urlresolvers import reverse # noqa
+-from django.template.defaultfilters import title # noqa
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-
+-from horizon import tables
+-from horizon.utils import filters
+-
+-from openstack_dashboard import api
+-
+-
+-LOG = logging.getLogger(__name__)
+-
+-STATUS_CHOICES = (
+- ("BUILDING", None),
+- ("COMPLETED", True),
+- ("DELETE_FAILED", False),
+- ("FAILED", False),
+- ("NEW", None),
+- ("SAVING", None),
+-)
+-
+-
+-class LaunchLink(tables.LinkAction):
+- name = "create"
+- verbose_name = _("Create Backup")
+- url = "horizon:project:database_backups:create"
+- classes = ("btn-launch", "ajax-modal")
+-
+-
+-class RestoreLink(tables.LinkAction):
+- name = "restore"
+- verbose_name = _("Restore Backup")
+- url = "horizon:project:databases:launch"
+- classes = ("btn-launch", "ajax-modal")
+-
+- def get_link_url(self, datam):
+- url = reverse(self.url)
+- return url + '?backup=%s' % datam.id
+-
+-
+-class DeleteBackup(tables.BatchAction):
+- name = "delete"
+- action_present = _("Delete")
+- action_past = _("Scheduled deletion of")
+- data_type_singular = _("Backup")
+- data_type_plural = _("Backups")
+- classes = ('btn-danger', 'btn-terminate')
+-
+- def action(self, request, obj_id):
+- api.trove.backup_delete(request, obj_id)
+-
+-
+-class UpdateRow(tables.Row):
+- ajax = True
+-
+- def get_data(self, request, backup_id):
+- backup = api.trove.backup_get(request, backup_id)
+- try:
+- backup.instance = api.trove.instance_get(request,
+- backup.instance_id)
+- except Exception:
+- pass
+- return backup
+-
+-
+-def db_link(obj):
+- if not hasattr(obj, 'instance'):
+- return
+- if hasattr(obj.instance, 'name'):
+- return reverse(
+- 'horizon:project:databases:detail',
+- kwargs={'instance_id': obj.instance_id})
+-
+-
+-def db_name(obj):
+- if hasattr(obj.instance, 'name'):
+- return obj.instance.name
+- return obj.instance_id
+-
+-
+-class BackupsTable(tables.DataTable):
+- name = tables.Column("name",
+- link=("horizon:project:database_backups:detail"),
+- verbose_name=_("Name"))
+- created = tables.Column("created", verbose_name=_("Created At"),
+- filters=[filters.parse_isotime])
+- location = tables.Column(lambda obj: _("Download"),
+- link=lambda obj: obj.locationRef,
+- verbose_name=_("Backup File"))
+- instance = tables.Column(db_name, link=db_link,
+- verbose_name=_("Database"))
+- status = tables.Column("status",
+- filters=(title, filters.replace_underscores),
+- verbose_name=_("Status"),
+- status=True,
+- status_choices=STATUS_CHOICES)
+-
+- class Meta:
+- name = "backups"
+- verbose_name = _("Backups")
+- status_columns = ["status"]
+- row_class = UpdateRow
+- table_actions = (LaunchLink, DeleteBackup)
+- row_actions = (RestoreLink, DeleteBackup)
+diff --git a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/_backup_details_help.html b/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/_backup_details_help.html
+deleted file mode 100644
+index d428033..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/_backup_details_help.html
++++ /dev/null
+@@ -1,3 +0,0 @@
+-{% load i18n %}
+-
+-<p>{% blocktrans %}Specify the details for the backup.{% endblocktrans %}</p>
+diff --git a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/backup.html b/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/backup.html
+deleted file mode 100644
+index 5053701..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/backup.html
++++ /dev/null
+@@ -1,11 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n %}
+-{% block title %}{% trans "Backup Database" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title=_("Backup Database") %}
+-{% endblock page_header %}
+-
+-{% block main %}
+- {% include 'horizon/common/_workflow.html' %}
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/details.html b/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/details.html
+deleted file mode 100644
+index 783b582..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/details.html
++++ /dev/null
+@@ -1,53 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n sizeformat %}
+-{% block title %}{% trans "Backup Detail" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title="Backup Detail: "|add:backup.name %}
+-{% endblock page_header %}
+-
+-{% block main %}
+-<div class="row-fluid">
+- <div class="span12">
+- <h3>{% trans "Backup Overview" %}</h3>
+-
+- <div class="status row-fluid detail">
+- <h4>{% trans "Info" %}</h4>
+- <hr class="header_rule">
+- <dl>
+- <dt>{% trans "Name" %}</dt>
+- <dd>{{ backup.name }}</dd>
+- <dt>{% trans "Description" %}</dt>
+- <dd>{{ backup.description|linebreaksbr }}</dd>
+- <dt>{% trans "ID" %}</dt>
+- <dd>{{ backup.id }}</dd>
+- <dt>{% trans "Status" %}</dt>
+- <dd>{{ backup.status|title }}</dd>
+- <dt>{% trans "Backup File Location" %}</dt>
+- <dd>{{ backup.locationRef }}</dd>
+- <dt>{% trans "Initial Volume Size" %}</dt>
+- <dd>{{ backup.size }} {% trans "GB" %}</dd>
+- <dt>{% trans "Created On" %}</dt>
+- <dd>{{ backup.updated_at|date:"N jS, Y P" }}</dd>
+- <dt>{% trans "Backup Duration" %}</dt>
+- <dd>{{ backup.duration }}</dd>
+- </dl>
+- </div>
+-
+- {% if instance %}
+- <div class="addresses row-fluid detail">
+- <h4>{% trans "Database Info" %}</h4>
+- <hr class="header_rule">
+- <dl>
+- <dt>{% trans "Name" %}</dt>
+- <dd>{{ instance.name }}</dd>
+- <dt>{% trans "ID" %}</dt>
+- <dd>{{ instance.id }}</dd>
+- <dt>{% trans "Status" %}</dt>
+- <dd>{{ instance.status|title }}</dd>
+- </dl>
+- </div>
+- {% endif %}
+- </div>
+-</div>
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/index.html b/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/index.html
+deleted file mode 100644
+index aed5ee7..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/templates/database_backups/index.html
++++ /dev/null
+@@ -1,11 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n %}
+-{% block title %}{% trans "Database Backups" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title=_("Backups") %}
+-{% endblock page_header %}
+-
+-{% block main %}
+- {{ table.render }}
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/database_backups/tests.py b/openstack_dashboard/dashboards/project/database_backups/tests.py
+deleted file mode 100644
+index 2d02cb6..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/tests.py
++++ /dev/null
+@@ -1,97 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Mirantis 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 django.core.urlresolvers import reverse # noqa
+-from django import http
+-from mox import IsA # noqa
+-
+-from openstack_dashboard import api
+-from openstack_dashboard.test import helpers as test
+-
+-INDEX_URL = reverse('horizon:project:database_backups:index')
+-BACKUP_URL = reverse('horizon:project:database_backups:create')
+-DETAILS_URL = reverse('horizon:project:database_backups:detail', args=['id'])
+-
+-
+-class DatabasesBackupsTests(test.TestCase):
+-
+- @test.create_stubs({api.trove: ('backup_list', )})
+- def test_index(self):
+- api.trove.backup_list(IsA(http.HttpRequest))\
+- .AndReturn(self.database_backups.list())
+-
+- self.mox.ReplayAll()
+-
+- res = self.client.get(INDEX_URL)
+-
+- self.assertTemplateUsed(res, 'project/database_backups/index.html')
+-
+- @test.create_stubs({api.trove: ('backup_list',)})
+- def test_index_exception(self):
+- api.trove.backup_list(IsA(http.HttpRequest))\
+- .AndRaise(self.exceptions.trove)
+-
+- self.mox.ReplayAll()
+-
+- res = self.client.get(INDEX_URL)
+-
+- self.assertTemplateUsed(
+- res, 'project/database_backups/index.html')
+- self.assertEqual(res.status_code, 200)
+- self.assertMessageCount(res, error=1)
+-
+- @test.create_stubs({api.trove: ('instance_list',)})
+- def test_launch_backup(self):
+- api.trove.instance_list(IsA(http.HttpRequest))\
+- .AndReturn([])
+-
+- self.mox.ReplayAll()
+-
+- res = self.client.get(BACKUP_URL)
+- self.assertTemplateUsed(res, 'project/database_backups/backup.html')
+-
+- @test.create_stubs({api.trove: ('instance_list',)})
+- def test_launch_backup_exception(self):
+- api.trove.instance_list(IsA(http.HttpRequest))\
+- .AndRaise(self.exceptions.trove)
+-
+- self.mox.ReplayAll()
+-
+- res = self.client.get(BACKUP_URL)
+- self.assertMessageCount(res, error=1)
+- self.assertTemplateUsed(res, 'project/database_backups/backup.html')
+-
+- @test.create_stubs({api.trove: ('backup_get',)})
+- def test_detail_backup(self):
+- api.trove.backup_get(IsA(http.HttpRequest),
+- IsA(unicode))\
+- .AndReturn(self.database_backups.first())
+-
+- self.mox.ReplayAll()
+- res = self.client.get(DETAILS_URL)
+-
+- self.assertTemplateUsed(res, 'project/database_backups/details.html')
+-
+- @test.create_stubs({api.trove: ('backup_get',)})
+- def test_detail_backup_notfound(self):
+- api.trove.backup_get(IsA(http.HttpRequest),
+- IsA(unicode))\
+- .AndRaise(self.exceptions.trove)
+-
+- self.mox.ReplayAll()
+- res = self.client.get(DETAILS_URL)
+-
+- self.assertRedirectsNoFollow(res, INDEX_URL)
+diff --git a/openstack_dashboard/dashboards/project/database_backups/urls.py b/openstack_dashboard/dashboards/project/database_backups/urls.py
+deleted file mode 100644
+index 5200050..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/urls.py
++++ /dev/null
+@@ -1,28 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-#
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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 django.conf.urls.defaults import patterns # noqa
+-from django.conf.urls.defaults import url # noqa
+-
+-from openstack_dashboard.dashboards.project.database_backups import views
+-
+-urlpatterns = patterns(
+- '',
+- url(r'^$', views.IndexView.as_view(), name='index'),
+- url(r'^create$', views.BackupView.as_view(), name='create'),
+- url(r'^(?P<backup_id>[^/]+)/$', views.DetailView.as_view(),
+- name='detail'),
+-)
+diff --git a/openstack_dashboard/dashboards/project/database_backups/views.py b/openstack_dashboard/dashboards/project/database_backups/views.py
+deleted file mode 100644
+index fc223fe..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/views.py
++++ /dev/null
+@@ -1,100 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-#
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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.
+-
+-"""
+-Views for displaying database backups.
+-"""
+-import logging
+-
+-from django.core.urlresolvers import reverse # noqa
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-
+-from horizon import exceptions
+-from horizon import tables as horizon_tables
+-from horizon.utils import filters
+-from horizon import views as horizon_views
+-from horizon import workflows as horizon_workflows
+-
+-from openstack_dashboard import api
+-from openstack_dashboard.dashboards.project.database_backups import tables
+-from openstack_dashboard.dashboards.project.database_backups import workflows
+-
+-LOG = logging.getLogger(__name__)
+-
+-
+-class IndexView(horizon_tables.DataTableView):
+- table_class = tables.BackupsTable
+- template_name = 'project/database_backups/index.html'
+-
+- def _get_extra_data(self, backup):
+- """Apply extra info to the backup."""
+- instance_id = backup.instance_id
+- if not hasattr(self, '_instances'):
+- self._instances = {}
+- instance = self._instances.get(instance_id)
+- if instance is None:
+- try:
+- instance = api.trove.instance_get(self.request, instance_id)
+- except Exception:
+- instance = _('Not Found')
+- backup.instance = instance
+- return backup
+-
+- def get_data(self):
+- # TODO(rmyers) Add pagination support after it is available
+- # https://blueprints.launchpad.net/trove/+spec/paginate-backup-list
+- try:
+- backups = api.trove.backup_list(self.request)
+- backups = map(self._get_extra_data, backups)
+- except Exception:
+- backups = []
+- msg = _('Error getting database backup list.')
+- exceptions.handle(self.request, msg)
+- return backups
+-
+-
+-class BackupView(horizon_workflows.WorkflowView):
+- workflow_class = workflows.CreateBackup
+- template_name = "project/database_backups/backup.html"
+-
+- def get_context_data(self, **kwargs):
+- context = super(BackupView, self).get_context_data(**kwargs)
+- context["instance_id"] = kwargs.get("instance_id")
+- self._instance = context['instance_id']
+- return context
+-
+-
+-class DetailView(horizon_views.APIView):
+- template_name = "project/database_backups/details.html"
+-
+- def get_data(self, request, context, *args, **kwargs):
+- backup_id = kwargs.get("backup_id")
+- try:
+- backup = api.trove.backup_get(request, backup_id)
+- backup.created_at = filters.parse_isotime(backup.created)
+- backup.updated_at = filters.parse_isotime(backup.updated)
+- backup.duration = backup.updated_at - backup.created_at
+- except Exception:
+- redirect = reverse('horizon:project:database_backups:index')
+- msg = _('Unable to retrieve details for backup: %s') % backup_id
+- exceptions.handle(self.request, msg, redirect=redirect)
+- try:
+- instance = api.trove.instance_get(request, backup.instance_id)
+- except Exception:
+- instance = None
+- context['backup'] = backup
+- context['instance'] = instance
+- return context
+diff --git a/openstack_dashboard/dashboards/project/database_backups/workflows/__init__.py b/openstack_dashboard/dashboards/project/database_backups/workflows/__init__.py
+deleted file mode 100644
+index 5564c53..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/workflows/__init__.py
++++ /dev/null
+@@ -1,3 +0,0 @@
+-from create_backup import CreateBackup
+-
+-assert CreateBackup
+diff --git a/openstack_dashboard/dashboards/project/database_backups/workflows/create_backup.py b/openstack_dashboard/dashboards/project/database_backups/workflows/create_backup.py
+deleted file mode 100644
+index 7d1029c..0000000
+--- a/openstack_dashboard/dashboards/project/database_backups/workflows/create_backup.py
++++ /dev/null
+@@ -1,88 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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.
+-
+-import logging
+-
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-
+-from horizon import exceptions
+-from horizon import forms
+-from horizon import workflows
+-
+-from openstack_dashboard import api
+-
+-LOG = logging.getLogger(__name__)
+-
+-
+-class BackupDetailsAction(workflows.Action):
+- name = forms.CharField(max_length=80, label=_("Name"))
+- instance = forms.ChoiceField(label=_("Database Instance"))
+- description = forms.CharField(max_length=512, label=_("Description"),
+- widget=forms.TextInput(),
+- required=False,
+- help_text=_("Optional Backup Description"))
+-
+- class Meta:
+- name = _("Details")
+- help_text_template = \
+- "project/database_backups/_backup_details_help.html"
+-
+- def populate_instance_choices(self, request, context):
+- LOG.info("Obtaining list of instances.")
+- try:
+- instances = api.trove.instance_list(request)
+- except Exception:
+- instances = []
+- msg = _("Unable to list database instance to backup.")
+- exceptions.handle(request, msg)
+- return [(i.id, i.name) for i in instances]
+-
+-
+-class SetBackupDetails(workflows.Step):
+- action_class = BackupDetailsAction
+- contributes = ["name", "description", "instance"]
+-
+-
+-class CreateBackup(workflows.Workflow):
+- slug = "create_backup"
+- name = _("Backup Database")
+- finalize_button_name = _("Backup")
+- success_message = _('Scheduled backup "%(name)s".')
+- failure_message = _('Unable to launch %(count)s named "%(name)s".')
+- success_url = "horizon:project:database_backups:index"
+- default_steps = [SetBackupDetails]
+-
+- def get_initial(self):
+- initial = super(CreateBackup, self).get_initial()
+- initial['instance_id']
+-
+- def format_status_message(self, message):
+- name = self.context.get('name', 'unknown instance')
+- return message % {"count": _("instance"), "name": name}
+-
+- def handle(self, request, context):
+- try:
+- LOG.info("Creating backup")
+- api.trove.backup_create(request,
+- context['name'],
+- context['instance'],
+- context['description'])
+- return True
+- except Exception:
+- LOG.exception("Exception while creating backup")
+- msg = _('Error creating database backup.')
+- exceptions.handle(request, msg)
+- return False
+diff --git a/openstack_dashboard/dashboards/project/databases/__init__.py b/openstack_dashboard/dashboards/project/databases/__init__.py
+deleted file mode 100644
+index e69de29..0000000
+diff --git a/openstack_dashboard/dashboards/project/databases/panel.py b/openstack_dashboard/dashboards/project/databases/panel.py
+deleted file mode 100644
+index 08bcdb5..0000000
+--- a/openstack_dashboard/dashboards/project/databases/panel.py
++++ /dev/null
+@@ -1,30 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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 django.utils.translation import ugettext_lazy as _ # noqa
+-
+-import horizon
+-
+-from openstack_dashboard.dashboards.project import dashboard
+-
+-
+-class Databases(horizon.Panel):
+- name = _("Database Instances")
+- slug = 'databases'
+- permissions = ('openstack.services.database',)
+-
+-
+-dashboard.Project.register(Databases)
+diff --git a/openstack_dashboard/dashboards/project/databases/tables.py b/openstack_dashboard/dashboards/project/databases/tables.py
+deleted file mode 100644
+index 4f44ca5..0000000
+--- a/openstack_dashboard/dashboards/project/databases/tables.py
++++ /dev/null
+@@ -1,236 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2012 Nebula, Inc.
+-#
+-# Licensed under the Apache License, Version 2.0 (the "License"); you may
+-# not use this file except in compliance with the License. You may obtain
+-# a copy of the License at
+-#
+-# http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+-# License for the specific language governing permissions and limitations
+-# under the License.
+-
+-import logging
+-
+-from django.core import urlresolvers
+-from django.template.defaultfilters import title # noqa
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-
+-from horizon import exceptions
+-from horizon import tables
+-from horizon.templatetags import sizeformat
+-from horizon.utils import filters
+-
+-from openstack_dashboard import api
+-from openstack_dashboard.dashboards.project.database_backups \
+- import tables as backup_tables
+-
+-
+-LOG = logging.getLogger(__name__)
+-
+-ACTIVE_STATES = ("ACTIVE",)
+-
+-
+-class TerminateInstance(tables.BatchAction):
+- name = "terminate"
+- action_present = _("Terminate")
+- action_past = _("Scheduled termination of")
+- data_type_singular = _("Instance")
+- data_type_plural = _("Instances")
+- classes = ('btn-danger', 'btn-terminate')
+-
+- def action(self, request, obj_id):
+- api.trove.instance_delete(request, obj_id)
+-
+-
+-class RestartInstance(tables.BatchAction):
+- name = "restart"
+- action_present = _("Restart")
+- action_past = _("Restarted")
+- data_type_singular = _("Database")
+- data_type_plural = _("Databases")
+- classes = ('btn-danger', 'btn-reboot')
+-
+- def allowed(self, request, instance=None):
+- return ((instance.status in ACTIVE_STATES
+- or instance.status == 'SHUTOFF'))
+-
+- def action(self, request, obj_id):
+- api.trove.instance_restart(request, obj_id)
+-
+-
+-class DeleteUser(tables.DeleteAction):
+- name = "delete"
+- action_present = _("Delete")
+- action_past = _("Deleted")
+- data_type_singular = _("User")
+- data_type_plural = _("Users")
+-
+- def delete(self, request, obj_id):
+- datum = self.table.get_object_by_id(obj_id)
+- try:
+- api.trove.users_delete(request, datum.instance.id, datum.name)
+- except Exception:
+- msg = _('Error deleting database user.')
+- exceptions.handle(request, msg)
+-
+-
+-class DeleteDatabase(tables.DeleteAction):
+- name = "delete"
+- action_present = _("Delete")
+- action_past = _("Deleted")
+- data_type_singular = _("Database")
+- data_type_plural = _("Databases")
+-
+- def delete(self, request, obj_id):
+- datum = self.table.get_object_by_id(obj_id)
+- try:
+- api.trove.database_delete(request, datum.instance.id, datum.name)
+- except Exception:
+- msg = _('Error deleting database on instance.')
+- exceptions.handle(request, msg)
+-
+-
+-class LaunchLink(tables.LinkAction):
+- name = "launch"
+- verbose_name = _("Launch Instance")
+- url = "horizon:project:databases:launch"
+- classes = ("btn-launch", "ajax-modal")
+-
+-
+-class CreateBackup(tables.LinkAction):
+- name = "backup"
+- verbose_name = _("Create Backup")
+- url = "horizon:project:database_backups:create"
+- classes = ("ajax-modal", "btn-camera")
+-
+- def allowed(self, request, instance=None):
+- return request.user.has_perm('openstack.services.object-store')
+-
+- def get_link_url(self, datam):
+- url = urlresolvers.reverse(self.url)
+- return url + "?instance=%s" % datam.id
+-
+-
+-class UpdateRow(tables.Row):
+- ajax = True
+-
+- def get_data(self, request, instance_id):
+- instance = api.trove.instance_get(request, instance_id)
+- try:
+- flavor_id = instance.flavor['id']
+- instance.full_flavor = api.trove.flavor_get(request, flavor_id)
+- except Exception:
+- pass
+- return instance
+-
+-
+-def get_ips(instance):
+- if hasattr(instance, "ip"):
+- if len(instance.ip):
+- return instance.ip[0]
+- return _("Not Assigned")
+-
+-
+-def get_size(instance):
+- if hasattr(instance, "full_flavor"):
+- size_string = _("%(name)s | %(RAM)s RAM")
+- vals = {'name': instance.full_flavor.name,
+- 'RAM': sizeformat.mbformat(instance.full_flavor.ram)}
+- return size_string % vals
+- return _("Not available")
+-
+-
+-def get_databases(user):
+- if hasattr(user, "access"):
+- databases = [db.name for db in user.access]
+- databases.sort()
+- return ', '.join(databases)
+- return _("-")
+-
+-
+-class InstancesTable(tables.DataTable):
+- STATUS_CHOICES = (
+- ("active", True),
+- ("shutoff", True),
+- ("suspended", True),
+- ("paused", True),
+- ("error", False),
+- )
+- name = tables.Column("name",
+- link=("horizon:project:databases:detail"),
+- verbose_name=_("Database Name"))
+- ip = tables.Column(get_ips, verbose_name=_("IP Address"))
+- size = tables.Column(get_size,
+- verbose_name=_("Size"),
+- attrs={'data-type': 'size'})
+- status = tables.Column("status",
+- filters=(title, filters.replace_underscores),
+- verbose_name=_("Status"),
+- status=True,
+- status_choices=STATUS_CHOICES)
+-
+- class Meta:
+- name = "databases"
+- verbose_name = _("Databases")
+- status_columns = ["status"]
+- row_class = UpdateRow
+- table_actions = (LaunchLink, TerminateInstance)
+- row_actions = (CreateBackup,
+- RestartInstance, TerminateInstance)
+-
+-
+-class UsersTable(tables.DataTable):
+- name = tables.Column("name", verbose_name=_("User Name"))
+- host = tables.Column("host", verbose_name=_("Allowed Hosts"))
+- databases = tables.Column(get_databases, verbose_name=_("Databases"))
+-
+- class Meta:
+- name = "users"
+- verbose_name = _("Database Instance Users")
+- table_actions = [DeleteUser]
+- row_actions = [DeleteUser]
+-
+- def get_object_id(self, datum):
+- return datum.name
+-
+-
+-class DatabaseTable(tables.DataTable):
+- name = tables.Column("name", verbose_name=_("Database Name"))
+-
+- class Meta:
+- name = "databases"
+- verbose_name = _("Databases")
+- table_actions = [DeleteDatabase]
+- row_actions = [DeleteDatabase]
+-
+- def get_object_id(self, datum):
+- return datum.name
+-
+-
+-class InstanceBackupsTable(tables.DataTable):
+- name = tables.Column("name",
+- link=("horizon:project:database_backups:detail"),
+- verbose_name=_("Name"))
+- created = tables.Column("created", verbose_name=_("Created At"),
+- filters=[filters.parse_isotime])
+- location = tables.Column(lambda obj: _("Download"),
+- link=lambda obj: obj.locationRef,
+- verbose_name=_("Backup File"))
+- status = tables.Column("status",
+- filters=(title, filters.replace_underscores),
+- verbose_name=_("Status"),
+- status=True,
+- status_choices=backup_tables.STATUS_CHOICES)
+-
+- class Meta:
+- name = "backups"
+- verbose_name = _("Backups")
+- status_columns = ["status"]
+- row_class = UpdateRow
+- table_actions = (backup_tables.LaunchLink, backup_tables.DeleteBackup)
+- row_actions = (backup_tables.RestoreLink, backup_tables.DeleteBackup)
+diff --git a/openstack_dashboard/dashboards/project/databases/tabs.py b/openstack_dashboard/dashboards/project/databases/tabs.py
+deleted file mode 100644
+index a49e1b5..0000000
+--- a/openstack_dashboard/dashboards/project/databases/tabs.py
++++ /dev/null
+@@ -1,111 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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 django.conf import settings # noqa
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-
+-from horizon import tabs
+-
+-from openstack_dashboard import api
+-from openstack_dashboard.dashboards.project.databases import tables
+-
+-
+-class OverviewTab(tabs.Tab):
+- name = _("Overview")
+- slug = "overview"
+- template_name = ("project/databases/_detail_overview.html")
+-
+- def get_context_data(self, request):
+- return {"instance": self.tab_group.kwargs['instance']}
+-
+-
+-class UserTab(tabs.TableTab):
+- table_classes = [tables.UsersTable]
+- name = _("Users")
+- slug = "users_tab"
+- instance = None
+- template_name = "horizon/common/_detail_table.html"
+- preload = False
+-
+- def get_users_data(self):
+- instance = self.tab_group.kwargs['instance']
+- try:
+- data = api.trove.users_list(self.request, instance.id)
+- for user in data:
+- user.instance = instance
+- user.access = api.trove.user_list_access(self.request,
+- instance.id,
+- user.name)
+- except Exception:
+- data = []
+- return data
+-
+- def allowed(self, request):
+- perms = getattr(settings, 'TROVE_ADD_USER_PERMS', [])
+- if perms:
+- return request.user.has_perms(perms)
+- return True
+-
+-
+-class DatabaseTab(tabs.TableTab):
+- table_classes = [tables.DatabaseTable]
+- name = _("Databases")
+- slug = "database_tab"
+- instance = None
+- template_name = "horizon/common/_detail_table.html"
+- preload = False
+-
+- def get_databases_data(self):
+- instance = self.tab_group.kwargs['instance']
+- try:
+- data = api.trove.database_list(self.request, instance.id)
+- add_instance = lambda d: setattr(d, 'instance', instance)
+- map(add_instance, data)
+- except Exception:
+- data = []
+- return data
+-
+- def allowed(self, request):
+- perms = getattr(settings, 'TROVE_ADD_DATABASE_PERMS', [])
+- if perms:
+- return request.user.has_perms(perms)
+- return True
+-
+-
+-class BackupsTab(tabs.TableTab):
+- table_classes = [tables.InstanceBackupsTable]
+- name = _("Backups")
+- slug = "backups_tab"
+- instance = None
+- template_name = "horizon/common/_detail_table.html"
+- preload = False
+-
+- def get_backups_data(self):
+- instance = self.tab_group.kwargs['instance']
+- try:
+- data = api.trove.instance_backups(self.request, instance.id)
+- except Exception:
+- data = []
+- return data
+-
+- def allowed(self, request):
+- return request.user.has_perm('openstack.services.object-store')
+-
+-
+-class InstanceDetailTabs(tabs.TabGroup):
+- slug = "instance_details"
+- tabs = (OverviewTab, UserTab, DatabaseTab, BackupsTab)
+- sticky = True
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview.html b/openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview.html
+deleted file mode 100644
+index 6cc0e1b..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview.html
++++ /dev/null
+@@ -1,34 +0,0 @@
+-{% load i18n sizeformat %}
+-
+-<h3>{% trans "Instance Overview" %}</h3>
+-
+-<div class="status row-fluid detail">
+- <h4>{% trans "Info" %}</h4>
+- <hr class="header_rule">
+- <dl>
+- <dt>{% trans "Name" %}</dt>
+- <dd>{{ instance.name }}</dd>
+- <dt>{% trans "ID" %}</dt>
+- <dd>{{ instance.id }}</dd>
+- <dt>{% trans "Status" %}</dt>
+- <dd>{{ instance.status|title }}</dd>
+- <dt>{% trans "RAM" %}</dt>
+- <dd>{{ instance.full_flavor.ram|mbformat }}</dd>
+- </dl>
+-</div>
+-
+-<div class="addresses row-fluid detail">
+- <h4>{% trans "Connection Info" %}</h4>
+- <hr class="header_rule">
+- <dl>
+- {% with instance.ip.0 as ipaddress %}
+- <dt>{% trans "Instance IP Address" %}</dt>
+- <dd>{{ ipaddress }}</dd>
+- <dt>{% trans "Database Port" %}</dt>
+- <dd>3306</dd> {# TODO: This should be a config #}
+- <dt>{% trans "Connection Examples" %}</dt>
+- <dd>mysql -h {{ ipaddress }} -u USERNAME -p</dd>
+- <dd>mysql://USERNAME:PASSWORD@{{ ipaddress }}:3306/DATABASE</dd>
+- {% endwith %}
+- </dl>
+-</div>
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/_detail_users.html b/openstack_dashboard/dashboards/project/databases/templates/databases/_detail_users.html
+deleted file mode 100644
+index 001cef2..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/_detail_users.html
++++ /dev/null
+@@ -1,3 +0,0 @@
+-{% load i18n %}
+-
+-{{ table.render }}
+\ No newline at end of file
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_details_help.html b/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_details_help.html
+deleted file mode 100644
+index 93a2154..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_details_help.html
++++ /dev/null
+@@ -1,53 +0,0 @@
+-{% load i18n horizon humanize %}
+-
+-<p>{% blocktrans %}Specify the details for launching an instance.{% endblocktrans %}</p>
+-<p>{% blocktrans %}The chart below shows the resources used by this project in relation to the project's quotas.{% endblocktrans %}</p>
+-
+-<h4>{% trans "Flavor Details" %}</h4>
+-<table class="flavor_table table-striped">
+- <tbody>
+- <tr><td class="flavor_name">{% trans "Name" %}</td><td><span id="flavor_name"></span></td></tr>
+- <tr><td class="flavor_name">{% trans "VCPUs" %}</td><td><span id="flavor_vcpus"></span></td></tr>
+- <tr><td class="flavor_name">{% trans "Root Disk" %}</td><td><span id="flavor_disk"> </span> {% trans "GB" %}</td></tr>
+- <tr><td class="flavor_name">{% trans "Ephemeral Disk" %}</td><td><span id="flavor_ephemeral"></span> {% trans "GB" %}</td></tr>
+- <tr><td class="flavor_name">{% trans "Total Disk" %}</td><td><span id="flavor_disk_total"></span> {% trans "GB" %}</td></tr>
+- <tr><td class="flavor_name">{% trans "RAM" %}</td><td><span id="flavor_ram"></span> {% trans "MB" %}</td></tr>
+- </tbody>
+-</table>
+-
+-<div class="quota-dynamic">
+- <h4>{% trans "Project Quotas" %}</h4>
+- <div class="quota_title clearfix">
+- <strong>{% trans "Number of Instances" %} <span>({{ usages.instances.used|intcomma }})</span></strong>
+- <p>{{ usages.instances.available|quota|intcomma }}</p>
+- </div>
+- <div id="quota_instances" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.instances.quota }}" data-quota-used="{{ usages.instances.used }}">
+- {% horizon_progress_bar usages.instances.used usages.instances.quota %}
+- </div>
+-
+- <div class="quota_title clearfix">
+- <strong>{% trans "Number of VCPUs" %} <span>({{ usages.cores.used|intcomma }})</span></strong>
+- <p>{{ usages.cores.available|quota|intcomma }}</p>
+- </div>
+- <div id="quota_vcpus" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.cores.quota }}" data-quota-used="{{ usages.cores.used }}">
+- {% horizon_progress_bar usages.cores.used usages.cores.quota %}
+- </div>
+-
+- <div class="quota_title clearfix">
+- <strong>{% trans "Total RAM" %} <span>({{ usages.ram.used|intcomma }} {% trans "MB" %})</span></strong>
+- <p>{{ usages.ram.available|quota:"MB"|intcomma }}</p>
+- </div>
+- <div id="quota_ram" data-progress-indicator-flavor data-quota-limit="{{ usages.ram.quota }}" data-quota-used="{{ usages.ram.used }}" class="quota_bar">
+- {% horizon_progress_bar usages.ram.used usages.ram.quota %}
+- </div>
+-</div>
+-
+-<script type="text/javascript" charset="utf-8">
+- if(typeof horizon.Quota !== 'undefined') {
+- horizon.Quota.initWithFlavors({{ flavors|safe|default:"{}" }});
+- } else {
+- addHorizonLoadEvent(function() {
+- horizon.Quota.initWithFlavors({{ flavors|safe|default:"{}" }});
+- });
+- }
+-</script>
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_initialize_help.html b/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_initialize_help.html
+deleted file mode 100644
+index 2be87b5..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_initialize_help.html
++++ /dev/null
+@@ -1,19 +0,0 @@
+-{% load i18n horizon humanize %}
+-
+-<p>{% blocktrans %}Create an initial database and/or add initial users.{% endblocktrans %}</p>
+-
+-<h4>{% trans "Create Initial Databases" %}</h4>
+-<p>{% trans "Optionally provide a comma separated list of databases to create:" %}</p>
+-<pre>database1, database2, database3</pre>
+-
+-<h4>{% trans "Create Initial Admin User" %}</h4>
+-<p>{% blocktrans %}Create an optional initial user.
+- This user will have access to all databases you create.{% endblocktrans %}</p>
+-<ul>
+- <li><strong>{% trans "Username (required)" %}</strong></li>
+- <li><strong>{% trans "Password (required)" %}</strong></li>
+- <li><strong>{% trans "Host (optional)" %}</strong>
+- <em>{% blocktrans %}Allow the user to connect from this host
+- only. If not provided this use will be allowed to connect from anywhere.
+- {% endblocktrans %}</em></li>
+-</ul>
+\ No newline at end of file
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_restore_help.html b/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_restore_help.html
+deleted file mode 100644
+index ce82f68..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/_launch_restore_help.html
++++ /dev/null
+@@ -1,4 +0,0 @@
+-{% load i18n horizon humanize %}
+-
+-<p>{% blocktrans %}Create this database from a previous backup.{% endblocktrans %}</p>
+-
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/detail.html b/openstack_dashboard/dashboards/project/databases/templates/databases/detail.html
+deleted file mode 100644
+index 2f48557..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/detail.html
++++ /dev/null
+@@ -1,15 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n sizeformat %}
+-{% block title %}{% trans "Database Detail" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title="Database Detail: "|add:instance.name %}
+-{% endblock page_header %}
+-
+-{% block main %}
+-<div class="row-fluid">
+- <div class="span12">
+- {{ tab_group.render }}
+- </div>
+-</div>
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/index.html b/openstack_dashboard/dashboards/project/databases/templates/databases/index.html
+deleted file mode 100644
+index 0bc3171..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/index.html
++++ /dev/null
+@@ -1,11 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n %}
+-{% block title %}{% trans "Databases" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title=_("Databases") %}
+-{% endblock page_header %}
+-
+-{% block main %}
+- {{ table.render }}
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/launch.html b/openstack_dashboard/dashboards/project/databases/templates/databases/launch.html
+deleted file mode 100644
+index 8cf2f48..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/launch.html
++++ /dev/null
+@@ -1,11 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n %}
+-{% block title %}{% trans "Launch Instance" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title=_("Launch Database") %}
+-{% endblock page_header %}
+-
+-{% block main %}
+- {% include 'horizon/common/_workflow.html' %}
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/databases/templates/databases/update.html b/openstack_dashboard/dashboards/project/databases/templates/databases/update.html
+deleted file mode 100644
+index e3aa915..0000000
+--- a/openstack_dashboard/dashboards/project/databases/templates/databases/update.html
++++ /dev/null
+@@ -1,11 +0,0 @@
+-{% extends 'base.html' %}
+-{% load i18n %}
+-{% block title %}{% trans "Edit Instance" %}{% endblock %}
+-
+-{% block page_header %}
+- {% include "horizon/common/_page_header.html" with title=_("Edit Instance") %}
+-{% endblock page_header %}
+-
+-{% block main %}
+- {% include 'horizon/common/_workflow.html' %}
+-{% endblock %}
+diff --git a/openstack_dashboard/dashboards/project/databases/tests.py b/openstack_dashboard/dashboards/project/databases/tests.py
+deleted file mode 100644
+index 2c58428..0000000
+--- a/openstack_dashboard/dashboards/project/databases/tests.py
++++ /dev/null
+@@ -1,197 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Mirantis Inc.
+-# Copyright 2013 Rackspace Hosting.
+-#
+-# 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 django.core.urlresolvers import reverse # noqa
+-from django import http
+-
+-from mox import IsA # noqa
+-
+-from openstack_dashboard import api
+-from openstack_dashboard.test import helpers as test
+-from troveclient import common
+-
+-
+-INDEX_URL = reverse('horizon:project:databases:index')
+-LAUNCH_URL = reverse('horizon:project:databases:launch')
+-DETAILS_URL = reverse('horizon:project:databases:detail', args=['id'])
+-
+-
+-class DatabaseTests(test.TestCase):
+-
+- @test.create_stubs(
+- {api.trove: ('instance_list', 'flavor_list')})
+- def test_index(self):
+- # Mock database instances
+- databases = common.Paginated(self.databases.list())
+- api.trove.instance_list(IsA(http.HttpRequest), marker=None)\
+- .AndReturn(databases)
+- # Mock flavors
+- api.trove.flavor_list(IsA(http.HttpRequest))\
+- .AndReturn(self.flavors.list())
+-
+- self.mox.ReplayAll()
+- res = self.client.get(INDEX_URL)
+- self.assertTemplateUsed(res, 'project/databases/index.html')
+-
+- @test.create_stubs(
+- {api.trove: ('instance_list', 'flavor_list')})
+- def test_index_flavor_exception(self):
+- # Mock database instances
+- databases = common.Paginated(self.databases.list())
+- api.trove.instance_list(IsA(http.HttpRequest), marker=None)\
+- .AndReturn(databases)
+- # Mock flavors
+- api.trove.flavor_list(IsA(http.HttpRequest))\
+- .AndRaise(self.exceptions.trove)
+-
+- self.mox.ReplayAll()
+- res = self.client.get(INDEX_URL)
+- self.assertTemplateUsed(res, 'project/databases/index.html')
+- self.assertMessageCount(res, error=1)
+-
+- @test.create_stubs(
+- {api.trove: ('instance_list',)})
+- def test_index_list_exception(self):
+- # Mock database instances
+- api.trove.instance_list(IsA(http.HttpRequest), marker=None)\
+- .AndRaise(self.exceptions.trove)
+-
+- self.mox.ReplayAll()
+- res = self.client.get(INDEX_URL)
+- self.assertTemplateUsed(res, 'project/databases/index.html')
+- self.assertMessageCount(res, error=1)
+-
+- @test.create_stubs(
+- {api.trove: ('instance_list', 'flavor_list')})
+- def test_index_pagination(self):
+- # Mock database instances
+- databases = common.Paginated(self.databases.list(), next_marker="foo")
+- api.trove.instance_list(IsA(http.HttpRequest), marker=None)\
+- .AndReturn(databases)
+- # Mock flavors
+- api.trove.flavor_list(IsA(http.HttpRequest))\
+- .AndReturn(self.flavors.list())
+-
+- self.mox.ReplayAll()
+- res = self.client.get(INDEX_URL)
+- self.assertTemplateUsed(res, 'project/databases/index.html')
+- self.assertContains(
+- res, 'marker=6ddc36d9-73db-4e23-b52e-368937d72719')
+-
+- @test.create_stubs(
+- {api.trove: ('instance_list', 'flavor_list')})
+- def test_index_flavor_list_exception(self):
+- #Mocking instances
+- databases = common.Paginated(self.databases.list())
+- api.trove.instance_list(IsA(http.HttpRequest), marker=None)\
+- .AndReturn(databases)
+- #Mocking flavor list with raising an exception
+- api.trove.flavor_list(IsA(http.HttpRequest))\
+- .AndRaise(self.exceptions.trove)
+-
+- self.mox.ReplayAll()
+-
+- res = self.client.get(INDEX_URL)
+-
+- self.assertTemplateUsed(res, 'project/databases/index.html')
+- self.assertMessageCount(res, error=1)
+-
+- @test.create_stubs({
+- api.nova: ('flavor_list', 'tenant_absolute_limits'),
+- api.trove: ('backup_list',)})
+- def test_launch_instance(self):
+- api.nova.flavor_list(IsA(http.HttpRequest))\
+- .AndReturn(self.flavors.list())
+- api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
+- .AndReturn([])
+- api.trove.backup_list(IsA(http.HttpRequest))\
+- .AndReturn(self.database_backups.list())
+-
+- self.mox.ReplayAll()
+- res = self.client.get(LAUNCH_URL)
+- self.assertTemplateUsed(res, 'project/databases/launch.html')
+-
+- @test.create_stubs({
+- api.nova: ('flavor_list',),
+- api.trove: ('backup_list', 'instance_create',)})
+- def test_create_simple_instance(self):
+- api.nova.flavor_list(IsA(http.HttpRequest))\
+- .AndReturn(self.flavors.list())
+- api.trove.backup_list(IsA(http.HttpRequest))\
+- .AndReturn(self.database_backups.list())
+-
+- # Actual create database call
+- api.trove.instance_create(
+- IsA(http.HttpRequest),
+- IsA(unicode),
+- IsA(int),
+- IsA(unicode),
+- databases=None,
+- restore_point=None,
+- users=None).AndReturn(self.databases.first())
+-
+- self.mox.ReplayAll()
+- post = {
+- 'name': "MyDB",
+- 'volume': '1',
+- 'flavor': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
+- }
+-
+- res = self.client.post(LAUNCH_URL, post)
+- self.assertRedirectsNoFollow(res, INDEX_URL)
+-
+- @test.create_stubs({
+- api.nova: ('flavor_list',),
+- api.trove: ('backup_list', 'instance_create',)})
+- def test_create_simple_instance_exception(self):
+- trove_exception = self.exceptions.nova
+- api.nova.flavor_list(IsA(http.HttpRequest))\
+- .AndReturn(self.flavors.list())
+- api.trove.backup_list(IsA(http.HttpRequest))\
+- .AndReturn(self.database_backups.list())
+-
+- # Actual create database call
+- api.trove.instance_create(
+- IsA(http.HttpRequest),
+- IsA(unicode),
+- IsA(int),
+- IsA(unicode),
+- databases=None,
+- restore_point=None,
+- users=None).AndRaise(trove_exception)
+-
+- self.mox.ReplayAll()
+- post = {
+- 'name': "MyDB",
+- 'volume': '1',
+- 'flavor': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
+- }
+-
+- res = self.client.post(LAUNCH_URL, post)
+- self.assertRedirectsNoFollow(res, INDEX_URL)
+-
+- @test.create_stubs(
+- {api.trove: ('instance_get', 'flavor_get',)})
+- def test_details(self):
+- api.trove.instance_get(IsA(http.HttpRequest), IsA(unicode))\
+- .AndReturn(self.databases.first())
+- api.trove.flavor_get(IsA(http.HttpRequest), IsA(str))\
+- .AndReturn(self.flavors.first())
+-
+- self.mox.ReplayAll()
+- res = self.client.get(DETAILS_URL)
+- self.assertTemplateUsed(res, 'project/databases/detail.html')
+diff --git a/openstack_dashboard/dashboards/project/databases/urls.py b/openstack_dashboard/dashboards/project/databases/urls.py
+deleted file mode 100644
+index 0a2a7a3..0000000
+--- a/openstack_dashboard/dashboards/project/databases/urls.py
++++ /dev/null
+@@ -1,29 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-#
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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 django.conf.urls.defaults import patterns # noqa
+-from django.conf.urls.defaults import url # noqa
+-
+-from openstack_dashboard.dashboards.project.databases import views
+-
+-
+-urlpatterns = patterns(
+- '',
+- url(r'^$', views.IndexView.as_view(), name='index'),
+- url(r'^launch$', views.LaunchInstanceView.as_view(), name='launch'),
+- url(r'^(?P<instance_id>[^/]+)/$', views.DetailView.as_view(),
+- name='detail'),
+-)
+diff --git a/openstack_dashboard/dashboards/project/databases/views.py b/openstack_dashboard/dashboards/project/databases/views.py
+deleted file mode 100644
+index c2c2f48..0000000
+--- a/openstack_dashboard/dashboards/project/databases/views.py
++++ /dev/null
+@@ -1,120 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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.
+-
+-"""
+-Views for managing database instances.
+-"""
+-import logging
+-
+-from django.core.urlresolvers import reverse # noqa
+-from django.utils.datastructures import SortedDict # noqa
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-
+-from horizon import exceptions
+-from horizon import tables as horizon_tables
+-from horizon import tabs as horizon_tabs
+-from horizon import workflows as horizon_workflows
+-
+-from openstack_dashboard import api
+-from openstack_dashboard.dashboards.project.databases import tables
+-from openstack_dashboard.dashboards.project.databases import tabs
+-from openstack_dashboard.dashboards.project.databases import workflows
+-
+-
+-LOG = logging.getLogger(__name__)
+-
+-
+-class IndexView(horizon_tables.DataTableView):
+- table_class = tables.InstancesTable
+- template_name = 'project/databases/index.html'
+-
+- def has_more_data(self, table):
+- return self._more
+-
+- def _extra_data(self, instance):
+- if not hasattr(self, '_flavors'):
+- try:
+- flavors = api.trove.flavor_list(self.request)
+- except Exception:
+- flavors = []
+- msg = _('Unable to retrieve database size information.')
+- exceptions.handle(self.request, msg)
+- self._flavors = SortedDict([(unicode(flavor.id), flavor)
+- for flavor in flavors])
+- flavor = self._flavors.get(instance.flavor["id"])
+- if flavor is not None:
+- instance.full_flavor = flavor
+- return instance
+-
+- def get_data(self):
+- marker = self.request.GET.get(
+- tables.InstancesTable._meta.pagination_param)
+- # Gather our instances
+- try:
+- instances = api.trove.instance_list(self.request, marker=marker)
+- self._more = instances.next or False
+- except Exception:
+- self._more = False
+- instances = []
+- msg = _('Unable to retrieve database instances.')
+- exceptions.handle(self.request, msg)
+- map(self._extra_data, instances)
+- return instances
+-
+-
+-class LaunchInstanceView(horizon_workflows.WorkflowView):
+- workflow_class = workflows.LaunchInstance
+- template_name = "project/databases/launch.html"
+-
+- def get_initial(self):
+- initial = super(LaunchInstanceView, self).get_initial()
+- initial['project_id'] = self.request.user.project_id
+- initial['user_id'] = self.request.user.id
+- return initial
+-
+-
+-class DetailView(horizon_tabs.TabbedTableView):
+- tab_group_class = tabs.InstanceDetailTabs
+- template_name = 'project/databases/detail.html'
+-
+- def get_context_data(self, **kwargs):
+- context = super(DetailView, self).get_context_data(**kwargs)
+- context["instance"] = self.get_data()
+- return context
+-
+- def get_data(self):
+- if not hasattr(self, "_instance"):
+- try:
+- LOG.info("Obtaining instance for detailed view ")
+- instance_id = self.kwargs['instance_id']
+- instance = api.trove.instance_get(self.request, instance_id)
+- except Exception:
+- redirect = reverse('horizon:project:databases:index')
+- msg = _('Unable to retrieve details '
+- 'for database instance: %s') % instance_id
+- exceptions.handle(self.request, msg, redirect=redirect)
+- try:
+- instance.full_flavor = api.trove.flavor_get(
+- self.request, instance.flavor["id"])
+- except Exception:
+- LOG.error('Unable to retrieve flavor details'
+- ' for database instance: %s') % instance_id
+- self._instance = instance
+- return self._instance
+-
+- def get_tabs(self, request, *args, **kwargs):
+- instance = self.get_data()
+- return self.tab_group_class(request, instance=instance, **kwargs)
+diff --git a/openstack_dashboard/dashboards/project/databases/workflows/__init__.py b/openstack_dashboard/dashboards/project/databases/workflows/__init__.py
+deleted file mode 100644
+index 1c7134f..0000000
+--- a/openstack_dashboard/dashboards/project/databases/workflows/__init__.py
++++ /dev/null
+@@ -1,3 +0,0 @@
+-from create_instance import LaunchInstance
+-
+-assert LaunchInstance
+diff --git a/openstack_dashboard/dashboards/project/databases/workflows/create_instance.py b/openstack_dashboard/dashboards/project/databases/workflows/create_instance.py
+deleted file mode 100644
+index 28b7b84..0000000
+--- a/openstack_dashboard/dashboards/project/databases/workflows/create_instance.py
++++ /dev/null
+@@ -1,222 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting
+-#
+-# 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.
+-
+-import logging
+-import simplejson as json
+-
+-from django.conf import settings # noqa
+-from django.utils.translation import ugettext_lazy as _ # noqa
+-from horizon import exceptions
+-from horizon import forms
+-from horizon import workflows
+-
+-from openstack_dashboard import api
+-
+-LOG = logging.getLogger(__name__)
+-
+-
+-class SetInstanceDetailsAction(workflows.Action):
+- name = forms.CharField(max_length=80, label=_("Database Name"))
+- flavor = forms.ChoiceField(label=_("Flavor"),
+- help_text=_("Size of image to launch."))
+- volume = forms.IntegerField(label=_("Volume Size"),
+- min_value=1,
+- initial=1,
+- help_text=_("Size of the volume in GB."))
+-
+- class Meta:
+- name = _("Details")
+- help_text_template = ("project/instances/_launch_details_help.html")
+-
+- def flavors(self, request):
+- if not hasattr(self, '_flavors'):
+- try:
+- self._flavors = api.nova.flavor_list(request)
+- except Exception:
+- LOG.exception("Exception while obtaining flavors list")
+- self._flavors = []
+- return self._flavors
+-
+- def populate_flavor_choices(self, request, context):
+- flavor_list = [(f.id, "%s" % f.name) for f in self.flavors(request)]
+- return sorted(flavor_list)
+-
+- def get_help_text(self):
+- flavors = json.dumps([f._info for f in self.flavors(self.request)])
+- extra = {'flavors': flavors}
+- try:
+- LOG.debug("Obtaining absolute tenant limits")
+- extra['usages'] = api.nova.tenant_absolute_limits(self.request)
+- extra['usages_json'] = json.dumps(extra['usages'])
+- except Exception:
+- exceptions.handle(self.request,
+- _("Unable to retrieve quota information."))
+- return super(SetInstanceDetailsAction, self).get_help_text(extra)
+-
+-
+-TROVE_ADD_USER_PERMS = getattr(settings, 'TROVE_ADD_USER_PERMS', [])
+-TROVE_ADD_DATABASE_PERMS = getattr(settings, 'TROVE_ADD_DATABASE_PERMS', [])
+-TROVE_ADD_PERMS = TROVE_ADD_USER_PERMS + TROVE_ADD_DATABASE_PERMS
+-
+-
+-class SetInstanceDetails(workflows.Step):
+- action_class = SetInstanceDetailsAction
+- contributes = ("name", "volume", "flavor")
+-
+-
+-class AddDatabasesAction(workflows.Action):
+- """
+- Initialize the database with users/databases. This tab will honor
+- the settings which should be a list of permissions required:
+-
+- * TROVE_ADD_USER_PERMS = []
+- * TROVE_ADD_DATABASE_PERMS = []
+- """
+- databases = forms.CharField(label=_('Initial Database'),
+- required=False,
+- help_text=_('Comma separated list of '
+- 'databases to create'))
+- user = forms.CharField(label=_('Initial Admin User'),
+- required=False,
+- help_text=_("Initial admin user to add"))
+- password = forms.CharField(widget=forms.PasswordInput(),
+- label=_("Password"),
+- required=False)
+- host = forms.CharField(label=_("Host (optional)"),
+- required=False,
+- help_text=_("Host or IP that the user is allowed "
+- "to connect through."))
+-
+- class Meta:
+- name = _("Initialize Databases")
+- permissions = TROVE_ADD_PERMS
+- help_text_template = "project/databases/_launch_initialize_help.html"
+-
+- def clean(self):
+- cleaned_data = super(AddDatabasesAction, self).clean()
+- if cleaned_data.get('user'):
+- if not cleaned_data.get('password'):
+- msg = _('You must specify a password if you create a user.')
+- self._errors["password"] = self.error_class([msg])
+- if not cleaned_data.get('databases'):
+- msg = _('You must specify at least one database if '
+- 'you create a user.')
+- self._errors["databases"] = self.error_class([msg])
+- return cleaned_data
+-
+-
+-class InitializeDatabase(workflows.Step):
+- action_class = AddDatabasesAction
+- contributes = ["databases", 'user', 'password', 'host']
+-
+-
+-class RestoreAction(workflows.Action):
+- backup = forms.ChoiceField(label=_("Backup"),
+- required=False,
+- help_text=_('Select a backup to Restore'))
+-
+- class Meta:
+- name = _("Restore From Backup")
+- permissions = ('openstack.services.object-store',)
+- help_text_template = "project/databases/_launch_restore_help.html"
+-
+- def populate_backup_choices(self, request, context):
+- empty = [('', '-')]
+- try:
+- backups = api.trove.backup_list(request)
+- backup_list = [(b.id, b.name) for b in backups]
+- except Exception:
+- backup_list = []
+- return empty + backup_list
+-
+- def clean_backup(self):
+- backup = self.cleaned_data['backup']
+- if backup:
+- try:
+- # Make sure the user is not "hacking" the form
+- # and that they have access to this backup_id
+- LOG.debug("Obtaining backups")
+- bkup = api.trove.backup_get(self.request, backup)
+- self.cleaned_data['backup'] = bkup.id
+- except Exception:
+- raise forms.ValidationError(_("Unable to find backup!"))
+- return backup
+-
+-
+-class RestoreBackup(workflows.Step):
+- action_class = RestoreAction
+- contributes = ['backup']
+-
+-
+-class LaunchInstance(workflows.Workflow):
+- slug = "launch_database"
+- name = _("Launch Database")
+- finalize_button_name = _("Launch")
+- success_message = _('Launched %(count)s named "%(name)s".')
+- failure_message = _('Unable to launch %(count)s named "%(name)s".')
+- success_url = "horizon:project:databases:index"
+- default_steps = (SetInstanceDetails, InitializeDatabase, RestoreBackup)
+-
+- def format_status_message(self, message):
+- name = self.context.get('name', 'unknown instance')
+- return message % {"count": _("instance"), "name": name}
+-
+- def _get_databases(self, context):
+- """Returns the initial databases for this instance."""
+- databases = None
+- if context.get('databases'):
+- dbs = context['databases']
+- databases = [{'name': d.strip()} for d in dbs.split(',')]
+- return databases
+-
+- def _get_users(self, context):
+- users = None
+- if context.get('user'):
+- user = {
+- 'name': context['user'],
+- 'password': context['password'],
+- 'databases': self._get_databases(context)
+- }
+- if context['host']:
+- user['host'] = context['host']
+- users = [user]
+- return users
+-
+- def _get_backup(self, context):
+- backup = None
+- if context.get('backup'):
+- backup = {'backupRef': context['backup']}
+- return backup
+-
+- def handle(self, request, context):
+- try:
+- LOG.info("Launching instance with parameters "
+- "{name=%s, volume=%s, flavor=%s, dbs=%s, users=%s, "
+- "backups=%s}",
+- context['name'], context['volume'], context['flavor'],
+- self._get_databases(context), self._get_users(context),
+- self._get_backup(context))
+- api.trove.instance_create(request,
+- context['name'],
+- context['volume'],
+- context['flavor'],
+- databases=self._get_databases(context),
+- users=self._get_users(context),
+- restore_point=self._get_backup(context))
+- return True
+- except Exception:
+- exceptions.handle(request)
+- return False
+diff --git a/openstack_dashboard/exceptions.py b/openstack_dashboard/exceptions.py
+index 9934b73..e5dc97d 100644
+--- a/openstack_dashboard/exceptions.py
++++ b/openstack_dashboard/exceptions.py
+@@ -25,7 +25,6 @@ from keystoneclient import exceptions as keystoneclient
+ from neutronclient.common import exceptions as neutronclient
+ from novaclient import exceptions as novaclient
+ from swiftclient import client as swiftclient
+-from troveclient import exceptions as troveclient
+
+
+ UNAUTHORIZED = (keystoneclient.Unauthorized,
+@@ -38,8 +37,7 @@ UNAUTHORIZED = (keystoneclient.Unauthorized,
+ neutronclient.Unauthorized,
+ neutronclient.Forbidden,
+ heatclient.HTTPUnauthorized,
+- heatclient.HTTPForbidden,
+- troveclient.Unauthorized)
++ heatclient.HTTPForbidden)
+
+ NOT_FOUND = (keystoneclient.NotFound,
+ cinderclient.NotFound,
+@@ -47,8 +45,7 @@ NOT_FOUND = (keystoneclient.NotFound,
+ glanceclient.NotFound,
+ neutronclient.NetworkNotFoundClient,
+ neutronclient.PortNotFoundClient,
+- heatclient.HTTPNotFound,
+- troveclient.NotFound)
++ heatclient.HTTPNotFound)
+
+ # NOTE(gabriel): This is very broad, and may need to be dialed in.
+ RECOVERABLE = (keystoneclient.ClientException,
+@@ -66,5 +63,4 @@ RECOVERABLE = (keystoneclient.ClientException,
+ neutronclient.AlreadyAttachedClient,
+ neutronclient.StateInvalidClient,
+ swiftclient.ClientException,
+- heatclient.HTTPException,
+- troveclient.ClientException)
++ heatclient.HTTPException)
+diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example
+index 9e4675c..0e8cc55 100644
+--- a/openstack_dashboard/local/local_settings.py.example
++++ b/openstack_dashboard/local/local_settings.py.example
+@@ -234,13 +234,6 @@ TIME_ZONE = "UTC"
+ # 'compute': 'nova_policy.json'
+ #}
+
+-# Trove user and database extension support. By default support for
+-# creating users and databases on database instances is turned on.
+-# To disable these extensions set the permission here to something
+-# unusable such as ["!"].
+-# TROVE_ADD_USER_PERMS = []
+-# TROVE_ADD_DATABASE_PERMS = []
+-
+ LOGGING = {
+ 'version': 1,
+ # When set to True this will disable all logging except
+diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py
+index 0f86317..2fa9e80 100644
+--- a/openstack_dashboard/test/helpers.py
++++ b/openstack_dashboard/test/helpers.py
+@@ -38,7 +38,6 @@ from keystoneclient.v2_0 import client as keystone_client
+ from neutronclient.v2_0 import client as neutron_client
+ from novaclient.v1_1 import client as nova_client
+ from swiftclient import client as swift_client
+-from troveclient import client as trove_client
+
+ import httplib2
+ import mox
+@@ -265,7 +264,6 @@ class APITestCase(TestCase):
+ self._original_cinderclient = api.cinder.cinderclient
+ self._original_heatclient = api.heat.heatclient
+ self._original_ceilometerclient = api.ceilometer.ceilometerclient
+- self._original_troveclient = api.trove.troveclient
+
+ # Replace the clients with our stubs.
+ api.glance.glanceclient = lambda request: self.stub_glanceclient()
+@@ -276,7 +274,6 @@ class APITestCase(TestCase):
+ api.heat.heatclient = lambda request: self.stub_heatclient()
+ api.ceilometer.ceilometerclient = lambda request: \
+ self.stub_ceilometerclient()
+- api.trove.troveclient = lambda request: self.stub_troveclient()
+
+ def tearDown(self):
+ super(APITestCase, self).tearDown()
+@@ -287,7 +284,6 @@ class APITestCase(TestCase):
+ api.cinder.cinderclient = self._original_cinderclient
+ api.heat.heatclient = self._original_heatclient
+ api.ceilometer.ceilometerclient = self._original_ceilometerclient
+- api.trove.troveclient = self._original_troveclient
+
+ def stub_novaclient(self):
+ if not hasattr(self, "novaclient"):
+@@ -353,12 +349,6 @@ class APITestCase(TestCase):
+ CreateMock(ceilometer_client.Client)
+ return self.ceilometerclient
+
+- def stub_troveclient(self):
+- if not hasattr(self, "troveclient"):
+- self.mox.StubOutWithMock(trove_client, 'Client')
+- self.troveclient = self.mox.CreateMock(trove_client.Client)
+- return self.troveclient
+-
+
+ @unittest.skipUnless(os.environ.get('WITH_SELENIUM', False),
+ "The WITH_SELENIUM env variable is not set.")
+diff --git a/openstack_dashboard/test/test_data/exceptions.py b/openstack_dashboard/test/test_data/exceptions.py
+index 272c426..fe7a946 100644
+--- a/openstack_dashboard/test/test_data/exceptions.py
++++ b/openstack_dashboard/test/test_data/exceptions.py
+@@ -19,7 +19,6 @@ from keystoneclient import exceptions as keystone_exceptions
+ from neutronclient.common import exceptions as neutron_exceptions
+ from novaclient import exceptions as nova_exceptions
+ from swiftclient import client as swift_exceptions
+-from troveclient import exceptions as trove_exceptions
+
+ from openstack_dashboard.test.test_data import utils
+
+@@ -73,9 +72,3 @@ def data(TEST):
+
+ cinder_exception = cinder_exceptions.BadRequest
+ TEST.exceptions.cinder = create_stubbed_exception(cinder_exception)
+-
+- trove_exception = trove_exceptions.ClientException
+- TEST.exceptions.trove = create_stubbed_exception(trove_exception)
+-
+- trove_auth = trove_exceptions.Unauthorized
+- TEST.exceptions.trove_unauthorized = create_stubbed_exception(trove_auth)
+diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py
+index aefc896..ec62fd1 100644
+--- a/openstack_dashboard/test/test_data/keystone_data.py
++++ b/openstack_dashboard/test/test_data/keystone_data.py
+@@ -113,15 +113,7 @@ SERVICE_CATALOG = [
+ {"region": "RegionOne",
+ "adminURL": "http://admin.heat.example.com:8004/v1",
+ "publicURL": "http://public.heat.example.com:8004/v1",
+- "internalURL": "http://int.heat.example.com:8004/v1"}]},
+- {"type": "database",
+- "name": "Trove",
+- "endpoints_links": [],
+- "endpoints": [
+- {"region": "RegionOne",
+- "adminURL": "http://admin.trove.example.com:8779/v1.0",
+- "publicURL": "http://public.trove.example.com:8779/v1.0",
+- "internalURL": "http://int.trove.example.com:8779/v1.0"}]}
++ "internalURL": "http://int.heat.example.com:8004/v1"}]}
+ ]
+
+
+diff --git a/openstack_dashboard/test/test_data/trove_data.py b/openstack_dashboard/test/test_data/trove_data.py
+deleted file mode 100644
+index cc848a4..0000000
+--- a/openstack_dashboard/test/test_data/trove_data.py
++++ /dev/null
+@@ -1,79 +0,0 @@
+-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+-
+-# Copyright 2013 Rackspace Hosting.
+-#
+-# 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 troveclient import backups
+-from troveclient import instances
+-
+-from openstack_dashboard.test.test_data import utils
+-
+-
+-DATABASE_DATA = {
+- "status": "ACTIVE",
+- "updated": "2013-08-12T22:00:09",
+- "name": "Test Database",
+- "links": [],
+- "created": "2013-08-12T22:00:03",
+- "ip": [
+- "10.0.0.3"
+- ],
+- "volume": {
+- "used": 0.13,
+- "size": 1
+- },
+- "flavor": {
+- "id": "1",
+- "links": []
+- },
+- "id": "6ddc36d9-73db-4e23-b52e-368937d72719"
+-}
+-
+-
+-BACKUP_ONE = {
+- "instance_id": "6ddc36d9-73db-4e23-b52e-368937d72719",
+- "status": "COMPLETED",
+- "updated": "2013-08-13T19:39:38",
+- "locationRef": "http://swift/v1/AUTH/database_backups/0edb.tar.gz",
+- "name": "backup1",
+- "created": "2013-08-15T18:10:14",
+- "size": 0.13,
+- "id": "0edb3c14-8919-4583-9add-00df9e524081",
+- "description": "Long description of backup"
+-}
+-
+-
+-BACKUP_TWO = {
+- "instance_id": "4d7b3f57-44f5-41d2-8e86-36b88cad572a",
+- "status": "COMPLETED",
+- "updated": "2013-08-10T20:20:44",
+- "locationRef": "http://swift/v1/AUTH/database_backups/e460.tar.gz",
+- "name": "backup2",
+- "created": "2013-08-10T20:20:37",
+- "size": 0.13,
+- "id": "e4602a3c-2bca-478f-b059-b6c215510fb4",
+- "description": "Longer description of backup"
+-}
+-
+-
+-def data(TEST):
+- database = instances.Instance(instances.Instances(None), DATABASE_DATA)
+- bkup1 = backups.Backup(backups.Backups(None), BACKUP_ONE)
+- bkup2 = backups.Backup(backups.Backups(None), BACKUP_TWO)
+-
+- TEST.databases = utils.TestDataContainer()
+- TEST.database_backups = utils.TestDataContainer()
+- TEST.databases.add(database)
+- TEST.database_backups.add(bkup1)
+- TEST.database_backups.add(bkup2)
+diff --git a/openstack_dashboard/test/test_data/utils.py b/openstack_dashboard/test/test_data/utils.py
+index 18a8757..6a65b12 100644
+--- a/openstack_dashboard/test/test_data/utils.py
++++ b/openstack_dashboard/test/test_data/utils.py
+@@ -23,7 +23,6 @@ def load_test_data(load_onto=None):
+ from openstack_dashboard.test.test_data import neutron_data
+ from openstack_dashboard.test.test_data import nova_data
+ from openstack_dashboard.test.test_data import swift_data
+- from openstack_dashboard.test.test_data import trove_data
+
+ # The order of these loaders matters, some depend on others.
+ loaders = (exceptions.data,
+@@ -34,8 +33,7 @@ def load_test_data(load_onto=None):
+ neutron_data.data,
+ swift_data.data,
+ heat_data.data,
+- ceilometer_data.data,
+- trove_data.data)
++ ceilometer_data.data)
+ if load_onto:
+ for data_func in loaders:
+ data_func(load_onto)
+diff --git a/requirements.txt b/requirements.txt
+index f4426df..313124f 100644
+--- a/requirements.txt
++++ b/requirements.txt
+@@ -16,7 +16,6 @@ python-novaclient>=2.12.0
+ python-neutronclient>=2.3.0,<3
+ python-swiftclient>=1.2
+ python-ceilometerclient>=1.0.2
+-python-troveclient
+ pytz>=2010h
+ # Horizon Utility Requirements
+ # for SECURE_KEY generation
diff --git a/0006-Revert-Use-oslo.sphinx-and-remove-local-copy-of-doc-.patch b/0006-Revert-Use-oslo.sphinx-and-remove-local-copy-of-doc-.patch
new file mode 100644
index 0000000..fcbe766
--- /dev/null
+++ b/0006-Revert-Use-oslo.sphinx-and-remove-local-copy-of-doc-.patch
@@ -0,0 +1,1348 @@
+From 597dc95e0455195a7e97b3432472531f1498c0ca Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Mon, 9 Sep 2013 13:51:19 +0200
+Subject: [PATCH] Revert "Use oslo.sphinx and remove local copy of doc theme"
+
+This reverts commit b6f7d8318bf909cc9989809127fc108e405fa400.
+---
+ doc/source/_static/.gitignore | 0
+ doc/source/_static/basic.css | 416 +++++++++++++++++++++++++++++++++++++
+ doc/source/_static/default.css | 230 ++++++++++++++++++++
+ doc/source/_static/jquery.tweet.js | 154 ++++++++++++++
+ doc/source/_static/nature.css | 245 ++++++++++++++++++++++
+ doc/source/_static/tweaks.css | 95 +++++++++
+ doc/source/_templates/.placeholder | 0
+ doc/source/_theme/layout.html | 83 ++++++++
+ doc/source/_theme/theme.conf | 4 +
+ doc/source/conf.py | 13 +-
+ test-requirements.txt | 1 -
+ 11 files changed, 1234 insertions(+), 7 deletions(-)
+ create mode 100644 doc/source/_static/.gitignore
+ create mode 100644 doc/source/_static/basic.css
+ create mode 100644 doc/source/_static/default.css
+ create mode 100644 doc/source/_static/jquery.tweet.js
+ create mode 100644 doc/source/_static/nature.css
+ create mode 100644 doc/source/_static/tweaks.css
+ create mode 100644 doc/source/_templates/.placeholder
+ create mode 100644 doc/source/_theme/layout.html
+ create mode 100644 doc/source/_theme/theme.conf
+
+diff --git a/doc/source/_static/.gitignore b/doc/source/_static/.gitignore
+new file mode 100644
+index 0000000..e69de29
+diff --git a/doc/source/_static/basic.css b/doc/source/_static/basic.css
+new file mode 100644
+index 0000000..d909ce3
+--- /dev/null
++++ b/doc/source/_static/basic.css
+@@ -0,0 +1,416 @@
++/**
++ * Sphinx stylesheet -- basic theme
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++
++/* -- main layout ----------------------------------------------------------- */
++
++div.clearer {
++ clear: both;
++}
++
++/* -- relbar ---------------------------------------------------------------- */
++
++div.related {
++ width: 100%;
++ font-size: 90%;
++}
++
++div.related h3 {
++ display: none;
++}
++
++div.related ul {
++ margin: 0;
++ padding: 0 0 0 10px;
++ list-style: none;
++}
++
++div.related li {
++ display: inline;
++}
++
++div.related li.right {
++ float: right;
++ margin-right: 5px;
++}
++
++/* -- sidebar --------------------------------------------------------------- */
++
++div.sphinxsidebarwrapper {
++ padding: 10px 5px 0 10px;
++}
++
++div.sphinxsidebar {
++ float: left;
++ width: 230px;
++ margin-left: -100%;
++ font-size: 90%;
++}
++
++div.sphinxsidebar ul {
++ list-style: none;
++}
++
++div.sphinxsidebar ul ul,
++div.sphinxsidebar ul.want-points {
++ margin-left: 20px;
++ list-style: square;
++}
++
++div.sphinxsidebar ul ul {
++ margin-top: 0;
++ margin-bottom: 0;
++}
++
++div.sphinxsidebar form {
++ margin-top: 10px;
++}
++
++div.sphinxsidebar input {
++ border: 1px solid #98dbcc;
++ font-family: sans-serif;
++ font-size: 1em;
++}
++
++img {
++ border: 0;
++}
++
++/* -- search page ----------------------------------------------------------- */
++
++ul.search {
++ margin: 10px 0 0 20px;
++ padding: 0;
++}
++
++ul.search li {
++ padding: 5px 0 5px 20px;
++ background-image: url(file.png);
++ background-repeat: no-repeat;
++ background-position: 0 7px;
++}
++
++ul.search li a {
++ font-weight: bold;
++}
++
++ul.search li div.context {
++ color: #888;
++ margin: 2px 0 0 30px;
++ text-align: left;
++}
++
++ul.keywordmatches li.goodmatch a {
++ font-weight: bold;
++}
++
++/* -- index page ------------------------------------------------------------ */
++
++table.contentstable {
++ width: 90%;
++}
++
++table.contentstable p.biglink {
++ line-height: 150%;
++}
++
++a.biglink {
++ font-size: 1.3em;
++}
++
++span.linkdescr {
++ font-style: italic;
++ padding-top: 5px;
++ font-size: 90%;
++}
++
++/* -- general index --------------------------------------------------------- */
++
++table.indextable td {
++ text-align: left;
++ vertical-align: top;
++}
++
++table.indextable dl, table.indextable dd {
++ margin-top: 0;
++ margin-bottom: 0;
++}
++
++table.indextable tr.pcap {
++ height: 10px;
++}
++
++table.indextable tr.cap {
++ margin-top: 10px;
++ background-color: #f2f2f2;
++}
++
++img.toggler {
++ margin-right: 3px;
++ margin-top: 3px;
++ cursor: pointer;
++}
++
++/* -- general body styles --------------------------------------------------- */
++
++a.headerlink {
++ visibility: hidden;
++}
++
++h1:hover > a.headerlink,
++h2:hover > a.headerlink,
++h3:hover > a.headerlink,
++h4:hover > a.headerlink,
++h5:hover > a.headerlink,
++h6:hover > a.headerlink,
++dt:hover > a.headerlink {
++ visibility: visible;
++}
++
++div.body p.caption {
++ text-align: inherit;
++}
++
++div.body td {
++ text-align: left;
++}
++
++.field-list ul {
++ padding-left: 1em;
++}
++
++.first {
++}
++
++p.rubric {
++ margin-top: 30px;
++ font-weight: bold;
++}
++
++/* -- sidebars -------------------------------------------------------------- */
++
++div.sidebar {
++ margin: 0 0 0.5em 1em;
++ border: 1px solid #ddb;
++ padding: 7px 7px 0 7px;
++ background-color: #ffe;
++ width: 40%;
++ float: right;
++}
++
++p.sidebar-title {
++ font-weight: bold;
++}
++
++/* -- topics ---------------------------------------------------------------- */
++
++div.topic {
++ border: 1px solid #ccc;
++ padding: 7px 7px 0 7px;
++ margin: 10px 0 10px 0;
++}
++
++p.topic-title {
++ font-size: 1.1em;
++ font-weight: bold;
++ margin-top: 10px;
++}
++
++/* -- admonitions ----------------------------------------------------------- */
++
++div.admonition {
++ margin-top: 10px;
++ margin-bottom: 10px;
++ padding: 7px;
++}
++
++div.admonition dt {
++ font-weight: bold;
++}
++
++div.admonition dl {
++ margin-bottom: 0;
++}
++
++p.admonition-title {
++ margin: 0px 10px 5px 0px;
++ font-weight: bold;
++}
++
++div.body p.centered {
++ text-align: center;
++ margin-top: 25px;
++}
++
++/* -- tables ---------------------------------------------------------------- */
++
++table.docutils {
++ border: 0;
++ border-collapse: collapse;
++}
++
++table.docutils td, table.docutils th {
++ padding: 1px 8px 1px 0;
++ border-top: 0;
++ border-left: 0;
++ border-right: 0;
++ border-bottom: 1px solid #aaa;
++}
++
++table.field-list td, table.field-list th {
++ border: 0 !important;
++}
++
++table.footnote td, table.footnote th {
++ border: 0 !important;
++}
++
++th {
++ text-align: left;
++ padding-right: 5px;
++}
++
++/* -- other body styles ----------------------------------------------------- */
++
++dl {
++ margin-bottom: 15px;
++}
++
++dd p {
++ margin-top: 0px;
++}
++
++dd ul, dd table {
++ margin-bottom: 10px;
++}
++
++dd {
++ margin-top: 3px;
++ margin-bottom: 10px;
++ margin-left: 30px;
++}
++
++dt:target, .highlight {
++ background-color: #fbe54e;
++}
++
++dl.glossary dt {
++ font-weight: bold;
++ font-size: 1.1em;
++}
++
++.field-list ul {
++ margin: 0;
++ padding-left: 1em;
++}
++
++.field-list p {
++ margin: 0;
++}
++
++.refcount {
++ color: #060;
++}
++
++.optional {
++ font-size: 1.3em;
++}
++
++.versionmodified {
++ font-style: italic;
++}
++
++.system-message {
++ background-color: #fda;
++ padding: 5px;
++ border: 3px solid red;
++}
++
++.footnote:target {
++ background-color: #ffa
++}
++
++.line-block {
++ display: block;
++ margin-top: 1em;
++ margin-bottom: 1em;
++}
++
++.line-block .line-block {
++ margin-top: 0;
++ margin-bottom: 0;
++ margin-left: 1.5em;
++}
++
++/* -- code displays --------------------------------------------------------- */
++
++pre {
++ overflow: auto;
++}
++
++td.linenos pre {
++ padding: 5px 0px;
++ border: 0;
++ background-color: transparent;
++ color: #aaa;
++}
++
++table.highlighttable {
++ margin-left: 0.5em;
++}
++
++table.highlighttable td {
++ padding: 0 0.5em 0 0.5em;
++}
++
++tt.descname {
++ background-color: transparent;
++ font-weight: bold;
++ font-size: 1.2em;
++}
++
++tt.descclassname {
++ background-color: transparent;
++}
++
++tt.xref, a tt {
++ background-color: transparent;
++ font-weight: bold;
++}
++
++h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
++ background-color: transparent;
++}
++
++/* -- math display ---------------------------------------------------------- */
++
++img.math {
++ vertical-align: middle;
++}
++
++div.body div.math p {
++ text-align: center;
++}
++
++span.eqno {
++ float: right;
++}
++
++/* -- printout stylesheet --------------------------------------------------- */
++
++ at media print {
++ div.document,
++ div.documentwrapper,
++ div.bodywrapper {
++ margin: 0 !important;
++ width: 100%;
++ }
++
++ div.sphinxsidebar,
++ div.related,
++ div.footer,
++ #top-link {
++ display: none;
++ }
++}
+diff --git a/doc/source/_static/default.css b/doc/source/_static/default.css
+new file mode 100644
+index 0000000..c8091ec
+--- /dev/null
++++ b/doc/source/_static/default.css
+@@ -0,0 +1,230 @@
++/**
++ * Sphinx stylesheet -- default theme
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++
++ at import url("basic.css");
++
++/* -- page layout ----------------------------------------------------------- */
++
++body {
++ font-family: sans-serif;
++ font-size: 100%;
++ background-color: #11303d;
++ color: #000;
++ margin: 0;
++ padding: 0;
++}
++
++div.document {
++ background-color: #1c4e63;
++}
++
++div.documentwrapper {
++ float: left;
++ width: 100%;
++}
++
++div.bodywrapper {
++ margin: 0 0 0 230px;
++}
++
++div.body {
++ background-color: #ffffff;
++ color: #000000;
++ padding: 0 20px 30px 20px;
++}
++
++div.footer {
++ color: #ffffff;
++ width: 100%;
++ padding: 9px 0 9px 0;
++ text-align: center;
++ font-size: 75%;
++}
++
++div.footer a {
++ color: #ffffff;
++ text-decoration: underline;
++}
++
++div.related {
++ background-color: #133f52;
++ line-height: 30px;
++ color: #ffffff;
++}
++
++div.related a {
++ color: #ffffff;
++}
++
++div.sphinxsidebar {
++}
++
++div.sphinxsidebar h3 {
++ font-family: 'Trebuchet MS', sans-serif;
++ color: #ffffff;
++ font-size: 1.4em;
++ font-weight: normal;
++ margin: 0;
++ padding: 0;
++}
++
++div.sphinxsidebar h3 a {
++ color: #ffffff;
++}
++
++div.sphinxsidebar h4 {
++ font-family: 'Trebuchet MS', sans-serif;
++ color: #ffffff;
++ font-size: 1.3em;
++ font-weight: normal;
++ margin: 5px 0 0 0;
++ padding: 0;
++}
++
++div.sphinxsidebar p {
++ color: #ffffff;
++}
++
++div.sphinxsidebar p.topless {
++ margin: 5px 10px 10px 10px;
++}
++
++div.sphinxsidebar ul {
++ margin: 10px;
++ padding: 0;
++ color: #ffffff;
++}
++
++div.sphinxsidebar a {
++ color: #98dbcc;
++}
++
++div.sphinxsidebar input {
++ border: 1px solid #98dbcc;
++ font-family: sans-serif;
++ font-size: 1em;
++}
++
++/* -- body styles ----------------------------------------------------------- */
++
++a {
++ color: #355f7c;
++ text-decoration: none;
++}
++
++a:hover {
++ text-decoration: underline;
++}
++
++div.body p, div.body dd, div.body li {
++ text-align: left;
++ line-height: 130%;
++}
++
++div.body h1,
++div.body h2,
++div.body h3,
++div.body h4,
++div.body h5,
++div.body h6 {
++ font-family: 'Trebuchet MS', sans-serif;
++ background-color: #f2f2f2;
++ font-weight: normal;
++ color: #20435c;
++ border-bottom: 1px solid #ccc;
++ margin: 20px -20px 10px -20px;
++ padding: 3px 0 3px 10px;
++}
++
++div.body h1 { margin-top: 0; font-size: 200%; }
++div.body h2 { font-size: 160%; }
++div.body h3 { font-size: 140%; }
++div.body h4 { font-size: 120%; }
++div.body h5 { font-size: 110%; }
++div.body h6 { font-size: 100%; }
++
++a.headerlink {
++ color: #c60f0f;
++ font-size: 0.8em;
++ padding: 0 4px 0 4px;
++ text-decoration: none;
++}
++
++a.headerlink:hover {
++ background-color: #c60f0f;
++ color: white;
++}
++
++div.body p, div.body dd, div.body li {
++ text-align: left;
++ line-height: 130%;
++}
++
++div.admonition p.admonition-title + p {
++ display: inline;
++}
++
++div.admonition p {
++ margin-bottom: 5px;
++}
++
++div.admonition pre {
++ margin-bottom: 5px;
++}
++
++div.admonition ul, div.admonition ol {
++ margin-bottom: 5px;
++}
++
++div.note {
++ background-color: #eee;
++ border: 1px solid #ccc;
++}
++
++div.seealso {
++ background-color: #ffc;
++ border: 1px solid #ff6;
++}
++
++div.topic {
++ background-color: #eee;
++}
++
++div.warning {
++ background-color: #ffe4e4;
++ border: 1px solid #f66;
++}
++
++p.admonition-title {
++ display: inline;
++}
++
++p.admonition-title:after {
++ content: ":";
++}
++
++pre {
++ padding: 5px;
++ background-color: #eeffcc;
++ color: #333333;
++ line-height: 120%;
++ border: 1px solid #ac9;
++ border-left: none;
++ border-right: none;
++}
++
++tt {
++ background-color: #ecf0f3;
++ padding: 0 1px 0 1px;
++ font-size: 0.95em;
++}
++
++.warning tt {
++ background: #efc2c2;
++}
++
++.note tt {
++ background: #d6d6d6;
++}
+diff --git a/doc/source/_static/jquery.tweet.js b/doc/source/_static/jquery.tweet.js
+new file mode 100644
+index 0000000..79bf0bd
+--- /dev/null
++++ b/doc/source/_static/jquery.tweet.js
+@@ -0,0 +1,154 @@
++(function($) {
++
++ $.fn.tweet = function(o){
++ var s = {
++ username: ["seaofclouds"], // [string] required, unless you want to display our tweets. :) it can be an array, just do ["username1","username2","etc"]
++ list: null, //[string] optional name of list belonging to username
++ avatar_size: null, // [integer] height and width of avatar if displayed (48px max)
++ count: 3, // [integer] how many tweets to display?
++ intro_text: null, // [string] do you want text BEFORE your your tweets?
++ outro_text: null, // [string] do you want text AFTER your tweets?
++ join_text: null, // [string] optional text in between date and tweet, try setting to "auto"
++ auto_join_text_default: "i said,", // [string] auto text for non verb: "i said" bullocks
++ auto_join_text_ed: "i", // [string] auto text for past tense: "i" surfed
++ auto_join_text_ing: "i am", // [string] auto tense for present tense: "i was" surfing
++ auto_join_text_reply: "i replied to", // [string] auto tense for replies: "i replied to" @someone "with"
++ auto_join_text_url: "i was looking at", // [string] auto tense for urls: "i was looking at" http:...
++ loading_text: null, // [string] optional loading text, displayed while tweets load
++ query: null // [string] optional search query
++ };
++
++ if(o) $.extend(s, o);
++
++ $.fn.extend({
++ linkUrl: function() {
++ var returning = [];
++ var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
++ this.each(function() {
++ returning.push(this.replace(regexp,"<a href=\"$1\">$1</a>"));
++ });
++ return $(returning);
++ },
++ linkUser: function() {
++ var returning = [];
++ var regexp = /[\@]+([A-Za-z0-9-_]+)/gi;
++ this.each(function() {
++ returning.push(this.replace(regexp,"<a href=\"http://twitter.com/$1\">@$1</a>"));
++ });
++ return $(returning);
++ },
++ linkHash: function() {
++ var returning = [];
++ var regexp = / [\#]+([A-Za-z0-9-_]+)/gi;
++ this.each(function() {
++ returning.push(this.replace(regexp, ' <a href="http://search.twitter.com/search?q=&tag=$1&lang=all&from='+s.username.join("%2BOR%2B")+'">#$1</a>'));
++ });
++ return $(returning);
++ },
++ capAwesome: function() {
++ var returning = [];
++ this.each(function() {
++ returning.push(this.replace(/\b(awesome)\b/gi, '<span class="awesome">$1</span>'));
++ });
++ return $(returning);
++ },
++ capEpic: function() {
++ var returning = [];
++ this.each(function() {
++ returning.push(this.replace(/\b(epic)\b/gi, '<span class="epic">$1</span>'));
++ });
++ return $(returning);
++ },
++ makeHeart: function() {
++ var returning = [];
++ this.each(function() {
++ returning.push(this.replace(/(<)+[3]/gi, "<tt class='heart'>♥</tt>"));
++ });
++ return $(returning);
++ }
++ });
++
++ function relative_time(time_value) {
++ var parsed_date = Date.parse(time_value);
++ var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
++ var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
++ var pluralize = function (singular, n) {
++ return '' + n + ' ' + singular + (n == 1 ? '' : 's');
++ };
++ if(delta < 60) {
++ return 'less than a minute ago';
++ } else if(delta < (45*60)) {
++ return 'about ' + pluralize("minute", parseInt(delta / 60)) + ' ago';
++ } else if(delta < (24*60*60)) {
++ return 'about ' + pluralize("hour", parseInt(delta / 3600)) + ' ago';
++ } else {
++ return 'about ' + pluralize("day", parseInt(delta / 86400)) + ' ago';
++ }
++ }
++
++ function build_url() {
++ var proto = ('https:' == document.location.protocol ? 'https:' : 'http:');
++ if (s.list) {
++ return proto+"//api.twitter.com/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?per_page="+s.count+"&callback=?";
++ } else if (s.query == null && s.username.length == 1) {
++ return proto+'//twitter.com/status/user_timeline/'+s.username[0]+'.json?count='+s.count+'&callback=?';
++ } else {
++ var query = (s.query || 'from:'+s.username.join('%20OR%20from:'));
++ return proto+'//search.twitter.com/search.json?&q='+query+'&rpp='+s.count+'&callback=?';
++ }
++ }
++
++ return this.each(function(){
++ var list = $('<ul class="tweet_list">').appendTo(this);
++ var intro = '<p class="tweet_intro">'+s.intro_text+'</p>';
++ var outro = '<p class="tweet_outro">'+s.outro_text+'</p>';
++ var loading = $('<p class="loading">'+s.loading_text+'</p>');
++
++ if(typeof(s.username) == "string"){
++ s.username = [s.username];
++ }
++
++ if (s.loading_text) $(this).append(loading);
++ $.getJSON(build_url(), function(data){
++ if (s.loading_text) loading.remove();
++ if (s.intro_text) list.before(intro);
++ $.each((data.results || data), function(i,item){
++ // auto join text based on verb tense and content
++ if (s.join_text == "auto") {
++ if (item.text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) {
++ var join_text = s.auto_join_text_reply;
++ } else if (item.text.match(/(^\w+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+) .*/i)) {
++ var join_text = s.auto_join_text_url;
++ } else if (item.text.match(/^((\w+ed)|just) .*/im)) {
++ var join_text = s.auto_join_text_ed;
++ } else if (item.text.match(/^(\w*ing) .*/i)) {
++ var join_text = s.auto_join_text_ing;
++ } else {
++ var join_text = s.auto_join_text_default;
++ }
++ } else {
++ var join_text = s.join_text;
++ };
++
++ var from_user = item.from_user || item.user.screen_name;
++ var profile_image_url = item.profile_image_url || item.user.profile_image_url;
++ var join_template = '<span class="tweet_join"> '+join_text+' </span>';
++ var join = ((s.join_text) ? join_template : ' ');
++ var avatar_template = '<a class="tweet_avatar" href="http://twitter.com/'+from_user+'"><img src="'+profile_image_url+'" height="'+s.avatar_size+'" width="'+s.avatar_size+'" alt="'+from_user+'\'s avatar" title="'+from_user+'\'s avatar" border="0"/></a>';
++ var avatar = (s.avatar_size ? avatar_template : '');
++ var date = '<a href="http://twitter.com/'+from_user+'/statuses/'+item.id+'" title="view tweet on twitter">'+relative_time(item.created_at)+'</a>';
++ var text = '<span class="tweet_text">' +$([item.text]).linkUrl().linkUser().linkHash().makeHeart().capAwesome().capEpic()[0]+ '</span>';
++
++ // until we create a template option, arrange the items below to alter a tweet's display.
++ list.append('<li>' + avatar + date + join + text + '</li>');
++
++ list.children('li:first').addClass('tweet_first');
++ list.children('li:odd').addClass('tweet_even');
++ list.children('li:even').addClass('tweet_odd');
++ });
++ if (s.outro_text) list.after(outro);
++ });
++
++ });
++ };
++})(jQuery);
+\ No newline at end of file
+diff --git a/doc/source/_static/nature.css b/doc/source/_static/nature.css
+new file mode 100644
+index 0000000..a98bd42
+--- /dev/null
++++ b/doc/source/_static/nature.css
+@@ -0,0 +1,245 @@
++/*
++ * nature.css_t
++ * ~~~~~~~~~~~~
++ *
++ * Sphinx stylesheet -- nature theme.
++ *
++ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
++ * :license: BSD, see LICENSE for details.
++ *
++ */
++
++ at import url("basic.css");
++
++/* -- page layout ----------------------------------------------------------- */
++
++body {
++ font-family: Arial, sans-serif;
++ font-size: 100%;
++ background-color: #111;
++ color: #555;
++ margin: 0;
++ padding: 0;
++}
++
++div.documentwrapper {
++ float: left;
++ width: 100%;
++}
++
++div.bodywrapper {
++ margin: 0 0 0 {{ theme_sidebarwidth|toint }}px;
++}
++
++hr {
++ border: 1px solid #B1B4B6;
++}
++
++div.document {
++ background-color: #eee;
++}
++
++div.body {
++ background-color: #ffffff;
++ color: #3E4349;
++ padding: 0 30px 30px 30px;
++ font-size: 0.9em;
++}
++
++div.footer {
++ color: #555;
++ width: 100%;
++ padding: 13px 0;
++ text-align: center;
++ font-size: 75%;
++}
++
++div.footer a {
++ color: #444;
++ text-decoration: underline;
++}
++
++div.related {
++ background-color: #6BA81E;
++ line-height: 32px;
++ color: #fff;
++ text-shadow: 0px 1px 0 #444;
++ font-size: 0.9em;
++}
++
++div.related a {
++ color: #E2F3CC;
++}
++
++div.sphinxsidebar {
++ font-size: 0.75em;
++ line-height: 1.5em;
++}
++
++div.sphinxsidebarwrapper{
++ padding: 20px 0;
++}
++
++div.sphinxsidebar h3,
++div.sphinxsidebar h4 {
++ font-family: Arial, sans-serif;
++ color: #222;
++ font-size: 1.2em;
++ font-weight: normal;
++ margin: 0;
++ padding: 5px 10px;
++ background-color: #ddd;
++ text-shadow: 1px 1px 0 white
++}
++
++div.sphinxsidebar h4{
++ font-size: 1.1em;
++}
++
++div.sphinxsidebar h3 a {
++ color: #444;
++}
++
++
++div.sphinxsidebar p {
++ color: #888;
++ padding: 5px 20px;
++}
++
++div.sphinxsidebar p.topless {
++}
++
++div.sphinxsidebar ul {
++ margin: 10px 20px;
++ padding: 0;
++ color: #000;
++}
++
++div.sphinxsidebar a {
++ color: #444;
++}
++
++div.sphinxsidebar input {
++ border: 1px solid #ccc;
++ font-family: sans-serif;
++ font-size: 1em;
++}
++
++div.sphinxsidebar input[type=text]{
++ margin-left: 20px;
++}
++
++/* -- body styles ----------------------------------------------------------- */
++
++a {
++ color: #005B81;
++ text-decoration: none;
++}
++
++a:hover {
++ color: #E32E00;
++ text-decoration: underline;
++}
++
++div.body h1,
++div.body h2,
++div.body h3,
++div.body h4,
++div.body h5,
++div.body h6 {
++ font-family: Arial, sans-serif;
++ background-color: #BED4EB;
++ font-weight: normal;
++ color: #212224;
++ margin: 30px 0px 10px 0px;
++ padding: 5px 0 5px 10px;
++ text-shadow: 0px 1px 0 white
++}
++
++div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
++div.body h2 { font-size: 150%; background-color: #C8D5E3; }
++div.body h3 { font-size: 120%; background-color: #D8DEE3; }
++div.body h4 { font-size: 110%; background-color: #D8DEE3; }
++div.body h5 { font-size: 100%; background-color: #D8DEE3; }
++div.body h6 { font-size: 100%; background-color: #D8DEE3; }
++
++a.headerlink {
++ color: #c60f0f;
++ font-size: 0.8em;
++ padding: 0 4px 0 4px;
++ text-decoration: none;
++}
++
++a.headerlink:hover {
++ background-color: #c60f0f;
++ color: white;
++}
++
++div.body p, div.body dd, div.body li {
++ line-height: 1.5em;
++}
++
++div.admonition p.admonition-title + p {
++ display: inline;
++}
++
++div.highlight{
++ background-color: white;
++}
++
++div.note {
++ background-color: #eee;
++ border: 1px solid #ccc;
++}
++
++div.seealso {
++ background-color: #ffc;
++ border: 1px solid #ff6;
++}
++
++div.topic {
++ background-color: #eee;
++}
++
++div.warning {
++ background-color: #ffe4e4;
++ border: 1px solid #f66;
++}
++
++p.admonition-title {
++ display: inline;
++}
++
++p.admonition-title:after {
++ content: ":";
++}
++
++pre {
++ padding: 10px;
++ background-color: White;
++ color: #222;
++ line-height: 1.2em;
++ border: 1px solid #C6C9CB;
++ font-size: 1.1em;
++ margin: 1.5em 0 1.5em 0;
++ -webkit-box-shadow: 1px 1px 1px #d8d8d8;
++ -moz-box-shadow: 1px 1px 1px #d8d8d8;
++}
++
++tt {
++ background-color: #ecf0f3;
++ color: #222;
++ /* padding: 1px 2px; */
++ font-size: 1.1em;
++ font-family: monospace;
++}
++
++.viewcode-back {
++ font-family: Arial, sans-serif;
++}
++
++div.viewcode-block:target {
++ background-color: #f4debf;
++ border-top: 1px solid #ac9;
++ border-bottom: 1px solid #ac9;
++}
+diff --git a/doc/source/_static/tweaks.css b/doc/source/_static/tweaks.css
+new file mode 100644
+index 0000000..9977c65
+--- /dev/null
++++ b/doc/source/_static/tweaks.css
+@@ -0,0 +1,95 @@
++body {
++ background: #fff url(../_static/header_bg.jpg) top left no-repeat;
++}
++
++#header {
++ width: 950px;
++ margin: 0 auto;
++ height: 102px;
++}
++
++#header h1#logo {
++ background: url(../_static/openstack_logo.png) top left no-repeat;
++ display: block;
++ float: left;
++ text-indent: -9999px;
++ width: 175px;
++ height: 55px;
++}
++
++#navigation {
++ background: url(../_static/header-line.gif) repeat-x 0 bottom;
++ display: block;
++ float: left;
++ margin: 27px 0 0 25px;
++ padding: 0;
++}
++
++#navigation li{
++ float: left;
++ display: block;
++ margin-right: 25px;
++}
++
++#navigation li a {
++ display: block;
++ font-weight: normal;
++ text-decoration: none;
++ background-position: 50% 0;
++ padding: 20px 0 5px;
++ color: #353535;
++ font-size: 14px;
++}
++
++#navigation li a.current, #navigation li a.section {
++ border-bottom: 3px solid #cf2f19;
++ color: #cf2f19;
++}
++
++div.related {
++ background-color: #cde2f8;
++ border: 1px solid #b0d3f8;
++}
++
++div.related a {
++ color: #4078ba;
++ text-shadow: none;
++}
++
++div.sphinxsidebarwrapper {
++ padding-top: 0;
++ overflow: hidden;
++}
++
++pre {
++ color: #555;
++}
++
++div.documentwrapper h1, div.documentwrapper h2, div.documentwrapper h3, div.documentwrapper h4, div.documentwrapper h5, div.documentwrapper h6 {
++ font-family: 'PT Sans', sans-serif !important;
++ color: #264D69;
++ border-bottom: 1px dotted #C5E2EA;
++ padding: 0;
++ background: none;
++ padding-bottom: 5px;
++}
++
++div.documentwrapper h3 {
++ color: #CF2F19;
++}
++
++a.headerlink {
++ color: #fff !important;
++ margin-left: 5px;
++ background: #CF2F19 !important;
++}
++
++div.body {
++ margin-top: -25px;
++ margin-left: 230px;
++}
++
++div.document {
++ width: 960px;
++ margin: 0 auto;
++}
+diff --git a/doc/source/_templates/.placeholder b/doc/source/_templates/.placeholder
+new file mode 100644
+index 0000000..e69de29
+diff --git a/doc/source/_theme/layout.html b/doc/source/_theme/layout.html
+new file mode 100644
+index 0000000..750b782
+--- /dev/null
++++ b/doc/source/_theme/layout.html
+@@ -0,0 +1,83 @@
++{% extends "basic/layout.html" %}
++{% set css_files = css_files + ['_static/tweaks.css'] %}
++{% set script_files = script_files + ['_static/jquery.tweet.js'] %}
++
++{%- macro sidebar() %}
++ {%- if not embedded %}{% if not theme_nosidebar|tobool %}
++ <div class="sphinxsidebar">
++ <div class="sphinxsidebarwrapper">
++ {%- block sidebarlogo %}
++ {%- if logo %}
++ <p class="logo"><a href="{{ pathto(master_doc) }}">
++ <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
++ </a></p>
++ {%- endif %}
++ {%- endblock %}
++ {%- block sidebartoc %}
++ {%- if display_toc %}
++ <h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
++ {{ toc }}
++ {%- endif %}
++ {%- endblock %}
++ {%- block sidebarrel %}
++ {%- if prev %}
++ <h4>{{ _('Previous topic') }}</h4>
++ <p class="topless"><a href="{{ prev.link|e }}"
++ title="{{ _('previous chapter') }}">{{ prev.title }}</a></p>
++ {%- endif %}
++ {%- if next %}
++ <h4>{{ _('Next topic') }}</h4>
++ <p class="topless"><a href="{{ next.link|e }}"
++ title="{{ _('next chapter') }}">{{ next.title }}</a></p>
++ {%- endif %}
++ {%- endblock %}
++ {%- block sidebarsourcelink %}
++ {%- if show_source and has_source and sourcename %}
++ <h3>{{ _('This Page') }}</h3>
++ <ul class="this-page-menu">
++ <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}"
++ rel="nofollow">{{ _('Show Source') }}</a></li>
++ </ul>
++ {%- endif %}
++ {%- endblock %}
++ {%- if customsidebar %}
++ {% include customsidebar %}
++ {%- endif %}
++ {%- block sidebarsearch %}
++ {%- if pagename != "search" %}
++ <div id="searchbox" style="display: none">
++ <h3>{{ _('Quick search') }}</h3>
++ <form class="search" action="{{ pathto('search') }}" method="get">
++ <input type="text" name="q" size="18" />
++ <input type="submit" value="{{ _('Go') }}" />
++ <input type="hidden" name="check_keywords" value="yes" />
++ <input type="hidden" name="area" value="default" />
++ </form>
++ <p class="searchtip" style="font-size: 90%">
++ {{ _('Enter search terms or a module, class or function name.') }}
++ </p>
++ </div>
++ <script type="text/javascript">$('#searchbox').show(0);</script>
++ {%- endif %}
++ {%- endblock %}
++ </div>
++ </div>
++ {%- endif %}{% endif %}
++{%- endmacro %}
++
++{% block relbar1 %}{% endblock relbar1 %}
++
++{% block header %}
++ <div id="header">
++ <h1 id="logo"><a href="http://www.openstack.org/">OpenStack</a></h1>
++ <ul id="navigation">
++ <li><a href="http://www.openstack.org/" title="Go to the Home page" class="link">Home</a></li>
++ <li><a href="http://www.openstack.org/projects/" title="Go to the OpenStack Projects page">Projects</a></li>
++ <li><a href="http://www.openstack.org/user-stories/" title="Go to the User Stories page" class="link">User Stories</a></li>
++ <li><a href="http://www.openstack.org/community/" title="Go to the Community page" class="link">Community</a></li>
++ <li><a href="http://www.openstack.org/blog/" title="Go to the OpenStack Blog">Blog</a></li>
++ <li><a href="http://wiki.openstack.org/" title="Go to the OpenStack Wiki">Wiki</a></li>
++ <li><a href="http://docs.openstack.org/" title="Go to OpenStack Documentation" class="current">Documentation</a></li>
++ </ul>
++ </div>
++{% endblock %}
+\ No newline at end of file
+diff --git a/doc/source/_theme/theme.conf b/doc/source/_theme/theme.conf
+new file mode 100644
+index 0000000..1cc4004
+--- /dev/null
++++ b/doc/source/_theme/theme.conf
+@@ -0,0 +1,4 @@
++[theme]
++inherit = basic
++stylesheet = nature.css
++pygments_style = tango
+diff --git a/doc/source/conf.py b/doc/source/conf.py
+index 0a7f677..adb3d14 100644
+--- a/doc/source/conf.py
++++ b/doc/source/conf.py
+@@ -144,12 +144,13 @@ extensions = ['sphinx.ext.autodoc',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.pngmath',
+- 'sphinx.ext.viewcode',
+- 'oslo.sphinx',
+- ]
++ 'sphinx.ext.viewcode']
+
+ # Add any paths that contain templates here, relative to this directory.
+-templates_path = ['_templates']
++if os.getenv('HUDSON_PUBLISH_DOCS'):
++ templates_path = ['_ga', '_templates']
++else:
++ templates_path = ['_templates']
+
+ # The suffix of source filenames.
+ source_suffix = '.rst'
+@@ -216,8 +217,8 @@ nitpicky = False
+
+ # The theme to use for HTML and HTML Help pages. See the documentation for
+ # a list of builtin themes.
+-# html_theme_path = ['.']
+-# html_theme = '_theme'
++html_theme_path = ['.']
++html_theme = '_theme'
+
+ # Theme options are theme-specific and customize the look and feel of a theme
+ # further. For a list of options available for each theme, see the
+diff --git a/test-requirements.txt b/test-requirements.txt
+index e634373..727d09e 100644
+--- a/test-requirements.txt
++++ b/test-requirements.txt
+@@ -14,4 +14,3 @@ selenium
+ sphinx>=1.1.2
+ # for bug 1091333, remove after sphinx >1.1.3 is released.
+ docutils==0.9.1
+-oslo.sphinx
diff --git a/0007-move-RBAC-policy-files-and-checks-to-etc-openstack-d.patch b/0007-move-RBAC-policy-files-and-checks-to-etc-openstack-d.patch
new file mode 100644
index 0000000..0ab0c0d
--- /dev/null
+++ b/0007-move-RBAC-policy-files-and-checks-to-etc-openstack-d.patch
@@ -0,0 +1,32 @@
+From 76e15a3cd419a0038b44687effec95b41e656d79 Mon Sep 17 00:00:00 2001
+From: Matthias Runge <mrunge at redhat.com>
+Date: Mon, 9 Sep 2013 14:13:07 +0200
+Subject: [PATCH] move RBAC policy files and checks to /etc/openstack-dashboard
+
+---
+ openstack_dashboard/local/local_settings.py.example | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example
+index 0e8cc55..d0f0ad4 100644
+--- a/openstack_dashboard/local/local_settings.py.example
++++ b/openstack_dashboard/local/local_settings.py.example
+@@ -227,12 +227,13 @@ TIME_ZONE = "UTC"
+ # target installation.
+
+ # Path to directory containing policy.json files
+-#POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf")
++# POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf")
++POLICY_FILES_PATH = '/etc/openstack-dashboard'
+ # Map of local copy of service policy files
+-#POLICY_FILES = {
+-# 'identity': 'keystone_policy.json',
+-# 'compute': 'nova_policy.json'
+-#}
++POLICY_FILES = {
++ 'identity': 'keystone_policy.json',
++ 'compute': 'nova_policy.json'
++}
+
+ LOGGING = {
+ 'version': 1,
diff --git a/python-django-horizon.spec b/python-django-horizon.spec
index dbefaa8..811d65c 100644
--- a/python-django-horizon.spec
+++ b/python-django-horizon.spec
@@ -25,8 +25,10 @@ Source11: rh-logo.png
Patch0001: 0001-Don-t-access-the-net-while-building-docs.patch
Patch0002: 0002-disable-debug-move-web-root.patch
Patch0003: 0003-change-lockfile-location-to-tmp-and-also-add-localho.patch
-Patch0004: 0004-Fixed-keystone-test-data-to-match-openstack_auth.patch
-Patch0005: 0005-Add-a-customization-module-based-on-RHOS.patch
+Patch0004: 0004-Add-a-customization-module-based-on-RHOS.patch
+Patch0005: 0005-Revert-Adding-panels-for-trove.patch
+Patch0006: 0006-Revert-Use-oslo.sphinx-and-remove-local-copy-of-doc-.patch
+Patch0007: 0007-move-RBAC-policy-files-and-checks-to-etc-openstack-d.patch
# patch will be included in 2013.2.b3
@@ -154,6 +156,8 @@ Customization module for OpenStack Dashboard to provide a branded logo.
%patch0003 -p1
%patch0004 -p1
%patch0005 -p1
+%patch0006 -p1
+%patch0007 -p1
# remove unnecessary .po files
find . -name "django*.po" -exec rm -f '{}' \;
diff --git a/sources b/sources
index 92b50e6..a78bc9a 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-5da3751e5f5dd03e566063db506c9407 horizon-2013.2.b2.tar.gz
+289914748dff0f4c96f34e0aacf2261c horizon-2013.2.b3.tar.gz
More information about the scm-commits
mailing list