Merge "Temporarily harbouring Horizon Dashboard"
This commit is contained in:
20
horizon_dashboard/.gitignore
vendored
Normal file
20
horizon_dashboard/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.environment_version
|
||||||
|
.selenium_log
|
||||||
|
.coverage*
|
||||||
|
.noseids
|
||||||
|
.venv
|
||||||
|
coverage.xml
|
||||||
|
pep8.txt
|
||||||
|
pylint.txt
|
||||||
|
reports
|
||||||
|
demo_dashboard/local/local_settings.py
|
||||||
|
/static/
|
||||||
|
docs/build/
|
||||||
|
docs/source/sourcecode
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.secret_key_store
|
31
horizon_dashboard/README.rst
Normal file
31
horizon_dashboard/README.rst
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
====================================
|
||||||
|
Horizon Customization Demo Dashboard
|
||||||
|
====================================
|
||||||
|
|
||||||
|
This Django project demonstrates how the `Horizon`_ app can be used to
|
||||||
|
construct customized dashboards (for OpenStack or anything else).
|
||||||
|
|
||||||
|
The ``horizon`` module is pulled down from GitHub during setup
|
||||||
|
(see setup instructions below) and added to the virtual environment.
|
||||||
|
|
||||||
|
.. _Horizon: http://github.com/openstack/horizon
|
||||||
|
|
||||||
|
Setup Instructions
|
||||||
|
==================
|
||||||
|
|
||||||
|
The following should get you started::
|
||||||
|
|
||||||
|
$ git clone https://github.com/stackforge/python-mistralclient.git
|
||||||
|
$ cd python-mistralclient/horizon_dashboard
|
||||||
|
$ cp demo_dashboard/local/local_settings.py.example demo_dashboard/local/local_settings.py
|
||||||
|
|
||||||
|
Edit the ``local_settings.py`` file as needed. Make sure you have changed
|
||||||
|
OPENSTACK_HOST to point to your keystone server and also check all endpoints
|
||||||
|
are accessible. You may want to change OPENSTACK_ENDPOINT_TYPE to "publicURL"
|
||||||
|
if some of your endpoints are inaccessible.
|
||||||
|
|
||||||
|
When you're ready to run the development server::
|
||||||
|
|
||||||
|
$ ./run_tests.sh --runserver
|
||||||
|
|
||||||
|
|
0
horizon_dashboard/demo_dashboard/__init__.py
Normal file
0
horizon_dashboard/demo_dashboard/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from mistralclient.api import client as mistral_client
|
||||||
|
|
||||||
|
|
||||||
|
def mistralclient(request):
|
||||||
|
return mistral_client.Client()
|
@@ -0,0 +1,22 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
|
||||||
|
class Default(horizon.Panel):
|
||||||
|
name = _("Default")
|
||||||
|
slug = 'default'
|
||||||
|
urls = 'demo_dashboard.dashboards.mistral.workbooks.urls'
|
||||||
|
nav = False
|
||||||
|
|
||||||
|
|
||||||
|
class MistralDashboard(horizon.Dashboard):
|
||||||
|
name = _("Mistral")
|
||||||
|
slug = "mistral"
|
||||||
|
panels = ('default', 'workbooks', 'executions',)
|
||||||
|
default_panel = 'default'
|
||||||
|
roles = ('admin',)
|
||||||
|
|
||||||
|
|
||||||
|
horizon.register(MistralDashboard)
|
||||||
|
MistralDashboard.register(Default)
|
@@ -0,0 +1,12 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
from demo_dashboard.dashboards.mistral import dashboard
|
||||||
|
|
||||||
|
|
||||||
|
class Executions(horizon.Panel):
|
||||||
|
name = _("Executions")
|
||||||
|
slug = 'executions'
|
||||||
|
|
||||||
|
|
||||||
|
dashboard.MistralDashboard.register(Executions)
|
@@ -0,0 +1,14 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutionsTable(tables.DataTable):
|
||||||
|
id = tables.Column("id", verbose_name=_("ID"))
|
||||||
|
wb_name = tables.Column("workbook_name", verbose_name=_("Workbook"))
|
||||||
|
state = tables.Column("state", verbose_name=_("State"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "executions"
|
||||||
|
verbose_name = _("Executions")
|
||||||
|
# row_actions = (ExecuteWorkflow,)
|
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Executions" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Workbooks") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{{ table.render }}
|
||||||
|
{% endblock %}
|
@@ -0,0 +1,9 @@
|
|||||||
|
from django.conf.urls import patterns # noqa
|
||||||
|
from django.conf.urls import url # noqa
|
||||||
|
|
||||||
|
from demo_dashboard.dashboards.mistral.executions.views import IndexView
|
||||||
|
|
||||||
|
urlpatterns = patterns(
|
||||||
|
'',
|
||||||
|
url(r'^$', IndexView.as_view(), name='index'),
|
||||||
|
)
|
@@ -0,0 +1,14 @@
|
|||||||
|
from horizon import tables
|
||||||
|
|
||||||
|
from demo_dashboard.dashboards.mistral import api
|
||||||
|
from demo_dashboard.dashboards.mistral.executions.tables import ExecutionsTable
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(tables.DataTableView):
|
||||||
|
table_class = ExecutionsTable
|
||||||
|
template_name = 'mistral/executions/index.html'
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
client = api.mistralclient(self.request)
|
||||||
|
return [item for wb in client.workbooks.list()
|
||||||
|
for item in client.executions.list(wb.name)]
|
@@ -0,0 +1,37 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
from demo_dashboard.dashboards.mistral import api
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteForm(forms.SelfHandlingForm):
|
||||||
|
workbook_name = forms.CharField(label=_("Workbook"),
|
||||||
|
required=True,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={'readonly': 'readonly'}))
|
||||||
|
task = forms.CharField(label=_("Task"),
|
||||||
|
required=True,
|
||||||
|
help_text=_("Name of the task to stop"))
|
||||||
|
context = forms.CharField(label=_("Context"),
|
||||||
|
required=False,
|
||||||
|
initial="{}",
|
||||||
|
widget=forms.widgets.Textarea())
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(ExecuteForm, self).__init__(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
try:
|
||||||
|
ex = api.mistralclient(request).executions.create(**data)
|
||||||
|
|
||||||
|
msg = _('Execution has been created with id "%s".') % ex.id
|
||||||
|
messages.success(request, msg)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
msg = _('Failed to execute workbook "%s".') % data['workbook_name']
|
||||||
|
redirect = reverse('horizon:mistral:workbooks:index')
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
@@ -0,0 +1,13 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
from demo_dashboard.dashboards.mistral import dashboard
|
||||||
|
|
||||||
|
|
||||||
|
class Workbooks(horizon.Panel):
|
||||||
|
name = _("Workbooks")
|
||||||
|
slug = 'workbooks'
|
||||||
|
|
||||||
|
|
||||||
|
dashboard.MistralDashboard.register(Workbooks)
|
@@ -0,0 +1,28 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteWorkflow(tables.LinkAction):
|
||||||
|
name = "execute"
|
||||||
|
verbose_name = _("Execute")
|
||||||
|
url = "horizon:mistral:workbooks:execute"
|
||||||
|
classes = ("ajax-modal", "btn-edit")
|
||||||
|
|
||||||
|
|
||||||
|
def tags_to_string(workbook):
|
||||||
|
return ', '.join(workbook.tags)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkbooksTable(tables.DataTable):
|
||||||
|
name = tables.Column("name", verbose_name=_("Name"))
|
||||||
|
description = tables.Column("description", verbose_name=_("Description"))
|
||||||
|
tags = tables.Column(tags_to_string, verbose_name=_("Tags"))
|
||||||
|
|
||||||
|
def get_object_id(self, datum):
|
||||||
|
return datum.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "workbooks"
|
||||||
|
verbose_name = _("Workbooks")
|
||||||
|
row_actions = (ExecuteWorkflow,)
|
@@ -0,0 +1,25 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load url from future %}
|
||||||
|
|
||||||
|
{% block form_id %}execute_form{% endblock %}
|
||||||
|
{% block form_action %}{% url 'horizon:mistral:workbooks:execute' workbook_name %}{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-header %}{% trans "Execute" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-body %}
|
||||||
|
<div class="left">
|
||||||
|
<fieldset>
|
||||||
|
{% include "horizon/common/_form_fields.html" %}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<h3>{% trans "Description:" %}</h3>
|
||||||
|
<p>{% trans "From here you can execute a workbook." %}</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-footer %}
|
||||||
|
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Execute" %}" />
|
||||||
|
<a href="{% url 'horizon:mistral:workbooks:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
|
{% endblock %}
|
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Execute workbook" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Execute workbook") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include 'mistral/workbooks/_execute.html' %}
|
||||||
|
{% endblock %}
|
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Workbooks" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Workbooks") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{{ table.render }}
|
||||||
|
{% endblock %}
|
@@ -0,0 +1,13 @@
|
|||||||
|
from django.conf.urls import patterns # noqa
|
||||||
|
from django.conf.urls import url # noqa
|
||||||
|
|
||||||
|
from demo_dashboard.dashboards.mistral.workbooks.views \
|
||||||
|
import IndexView, ExecuteView
|
||||||
|
|
||||||
|
WORKBOOKS = r'^(?P<workbook_name>[^/]+)/%s$'
|
||||||
|
|
||||||
|
urlpatterns = patterns(
|
||||||
|
'',
|
||||||
|
url(r'^$', IndexView.as_view(), name='index'),
|
||||||
|
url(WORKBOOKS % 'execute', ExecuteView.as_view(), name='execute'),
|
||||||
|
)
|
@@ -0,0 +1,31 @@
|
|||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
|
||||||
|
from horizon import tables, forms
|
||||||
|
|
||||||
|
from demo_dashboard.dashboards.mistral import api
|
||||||
|
from demo_dashboard.dashboards.mistral.workbooks.tables import WorkbooksTable
|
||||||
|
from demo_dashboard.dashboards.mistral.workbooks.forms import ExecuteForm
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(tables.DataTableView):
|
||||||
|
table_class = WorkbooksTable
|
||||||
|
template_name = 'mistral/workbooks/index.html'
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return api.mistralclient(self.request).workbooks.list()
|
||||||
|
|
||||||
|
|
||||||
|
class ExecuteView(forms.ModalFormView):
|
||||||
|
form_class = ExecuteForm
|
||||||
|
template_name = 'mistral/workbooks/execute.html'
|
||||||
|
success_url = reverse_lazy("horizon:mistral:executions:index")
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(ExecuteView, self).get_context_data(**kwargs)
|
||||||
|
context["workbook_name"] = self.kwargs['workbook_name']
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_initial(self, **kwargs):
|
||||||
|
return {
|
||||||
|
'workbook_name': self.kwargs['workbook_name']
|
||||||
|
}
|
0
horizon_dashboard/demo_dashboard/local/__init__.py
Normal file
0
horizon_dashboard/demo_dashboard/local/__init__.py
Normal file
488
horizon_dashboard/demo_dashboard/local/local_settings.py.example
Normal file
488
horizon_dashboard/demo_dashboard/local/local_settings.py.example
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from openstack_dashboard import exceptions
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
|
# Required for Django 1.5.
|
||||||
|
# If horizon is running in production (DEBUG is False), set this
|
||||||
|
# with the list of host/domain names that the application can serve.
|
||||||
|
# For more information see:
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||||
|
#ALLOWED_HOSTS = ['horizon.example.com', ]
|
||||||
|
|
||||||
|
# Set SSL proxy settings:
|
||||||
|
# For Django 1.4+ pass this header from the proxy after terminating the SSL,
|
||||||
|
# and don't forget to strip it from the client's request.
|
||||||
|
# For more information see:
|
||||||
|
# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header
|
||||||
|
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
|
||||||
|
|
||||||
|
# If Horizon is being served through SSL, then uncomment the following two
|
||||||
|
# settings to better secure the cookies from security exploits
|
||||||
|
#CSRF_COOKIE_SECURE = True
|
||||||
|
#SESSION_COOKIE_SECURE = True
|
||||||
|
|
||||||
|
# Overrides for OpenStack API versions. Use this setting to force the
|
||||||
|
# OpenStack dashboard to use a specific API version for a given service API.
|
||||||
|
# NOTE: The version should be formatted as it appears in the URL for the
|
||||||
|
# service API. For example, The identity service APIs have inconsistent
|
||||||
|
# use of the decimal point, so valid options would be "2.0" or "3".
|
||||||
|
# OPENSTACK_API_VERSIONS = {
|
||||||
|
# "identity": 3,
|
||||||
|
# "volume": 2
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Set this to True if running on multi-domain model. When this is enabled, it
|
||||||
|
# will require user to enter the Domain name in addition to username for login.
|
||||||
|
# OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = False
|
||||||
|
|
||||||
|
# Overrides the default domain used when running on single-domain model
|
||||||
|
# with Keystone V3. All entities will be created in the default domain.
|
||||||
|
# OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'Default'
|
||||||
|
|
||||||
|
# Set Console type:
|
||||||
|
# valid options would be "AUTO", "VNC", "SPICE" or "RDP"
|
||||||
|
# CONSOLE_TYPE = "AUTO"
|
||||||
|
|
||||||
|
# Default OpenStack Dashboard configuration.
|
||||||
|
HORIZON_CONFIG = {
|
||||||
|
'dashboards': ('project', 'admin', 'settings',),
|
||||||
|
'default_dashboard': 'project',
|
||||||
|
'user_home': 'openstack_dashboard.views.get_user_home',
|
||||||
|
'ajax_queue_limit': 10,
|
||||||
|
'auto_fade_alerts': {
|
||||||
|
'delay': 3000,
|
||||||
|
'fade_duration': 1500,
|
||||||
|
'types': ['alert-success', 'alert-info']
|
||||||
|
},
|
||||||
|
'help_url': "http://docs.openstack.org",
|
||||||
|
'exceptions': {'recoverable': exceptions.RECOVERABLE,
|
||||||
|
'not_found': exceptions.NOT_FOUND,
|
||||||
|
'unauthorized': exceptions.UNAUTHORIZED},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Specify a regular expression to validate user passwords.
|
||||||
|
# HORIZON_CONFIG["password_validator"] = {
|
||||||
|
# "regex": '.*',
|
||||||
|
# "help_text": _("Your password does not meet the requirements.")
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Disable simplified floating IP address management for deployments with
|
||||||
|
# multiple floating IP pools or complex network requirements.
|
||||||
|
# HORIZON_CONFIG["simple_ip_management"] = False
|
||||||
|
|
||||||
|
# Turn off browser autocompletion for the login form if so desired.
|
||||||
|
# HORIZON_CONFIG["password_autocomplete"] = "off"
|
||||||
|
|
||||||
|
LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# Set custom secret key:
|
||||||
|
# You can either set it to a specific value or you can let horizion generate a
|
||||||
|
# default secret key that is unique on this machine, e.i. regardless of the
|
||||||
|
# amount of Python WSGI workers (if used behind Apache+mod_wsgi): However, there
|
||||||
|
# may be situations where you would want to set this explicitly, e.g. when
|
||||||
|
# multiple dashboard instances are distributed on different machines (usually
|
||||||
|
# behind a load-balancer). Either you have to make sure that a session gets all
|
||||||
|
# requests routed to the same dashboard instance or you set the same SECRET_KEY
|
||||||
|
# for all of them.
|
||||||
|
from horizon.utils import secret_key
|
||||||
|
SECRET_KEY = secret_key.generate_or_read_from_file(
|
||||||
|
os.path.join(LOCAL_PATH, '.secret_key_store'))
|
||||||
|
|
||||||
|
# We recommend you use memcached for development; otherwise after every reload
|
||||||
|
# of the django development server, you will have to login again. To use
|
||||||
|
# memcached set CACHES to something like
|
||||||
|
# CACHES = {
|
||||||
|
# 'default': {
|
||||||
|
# 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||||
|
# 'LOCATION': '127.0.0.1:11211',
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send email to the console by default
|
||||||
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
|
# Or send them to /dev/null
|
||||||
|
#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
|
||||||
|
|
||||||
|
# Configure these for your outgoing email host
|
||||||
|
# EMAIL_HOST = 'smtp.my-company.com'
|
||||||
|
# EMAIL_PORT = 25
|
||||||
|
# EMAIL_HOST_USER = 'djangomail'
|
||||||
|
# EMAIL_HOST_PASSWORD = 'top-secret!'
|
||||||
|
|
||||||
|
# For multiple regions uncomment this configuration, and add (endpoint, title).
|
||||||
|
# AVAILABLE_REGIONS = [
|
||||||
|
# ('http://cluster1.example.com:5000/v2.0', 'cluster1'),
|
||||||
|
# ('http://cluster2.example.com:5000/v2.0', 'cluster2'),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
OPENSTACK_HOST = "172.16.80.200"
|
||||||
|
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v2.0" % OPENSTACK_HOST
|
||||||
|
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_"
|
||||||
|
|
||||||
|
# Disable SSL certificate checks (useful for self-signed certificates):
|
||||||
|
# OPENSTACK_SSL_NO_VERIFY = True
|
||||||
|
|
||||||
|
# The CA certificate to use to verify SSL connections
|
||||||
|
# OPENSTACK_SSL_CACERT = '/path/to/cacert.pem'
|
||||||
|
|
||||||
|
# The OPENSTACK_KEYSTONE_BACKEND settings can be used to identify the
|
||||||
|
# capabilities of the auth backend for Keystone.
|
||||||
|
# If Keystone has been configured to use LDAP as the auth backend then set
|
||||||
|
# can_edit_user to False and name to 'ldap'.
|
||||||
|
#
|
||||||
|
# TODO(tres): Remove these once Keystone has an API to identify auth backend.
|
||||||
|
OPENSTACK_KEYSTONE_BACKEND = {
|
||||||
|
'name': 'native',
|
||||||
|
'can_edit_user': True,
|
||||||
|
'can_edit_group': True,
|
||||||
|
'can_edit_project': True,
|
||||||
|
'can_edit_domain': True,
|
||||||
|
'can_edit_role': True
|
||||||
|
}
|
||||||
|
|
||||||
|
#Setting this to True, will add a new "Retrieve Password" action on instance,
|
||||||
|
#allowing Admin session password retrieval/decryption.
|
||||||
|
#OPENSTACK_ENABLE_PASSWORD_RETRIEVE = False
|
||||||
|
|
||||||
|
# The Xen Hypervisor has the ability to set the mount point for volumes
|
||||||
|
# attached to instances (other Hypervisors currently do not). Setting
|
||||||
|
# can_set_mount_point to True will add the option to set the mount point
|
||||||
|
# from the UI.
|
||||||
|
OPENSTACK_HYPERVISOR_FEATURES = {
|
||||||
|
'can_set_mount_point': False,
|
||||||
|
'can_set_password': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
# The OPENSTACK_NEUTRON_NETWORK settings can be used to enable optional
|
||||||
|
# services provided by neutron. Options currently available are load
|
||||||
|
# balancer service, security groups, quotas, VPN service.
|
||||||
|
OPENSTACK_NEUTRON_NETWORK = {
|
||||||
|
'enable_lb': False,
|
||||||
|
'enable_firewall': False,
|
||||||
|
'enable_quotas': True,
|
||||||
|
'enable_vpn': False,
|
||||||
|
# The profile_support option is used to detect if an external router can be
|
||||||
|
# configured via the dashboard. When using specific plugins the
|
||||||
|
# profile_support can be turned on if needed.
|
||||||
|
'profile_support': None,
|
||||||
|
#'profile_support': 'cisco',
|
||||||
|
}
|
||||||
|
|
||||||
|
# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features
|
||||||
|
# in the OpenStack Dashboard related to the Image service, such as the list
|
||||||
|
# of supported image formats.
|
||||||
|
# OPENSTACK_IMAGE_BACKEND = {
|
||||||
|
# 'image_formats': [
|
||||||
|
# ('', ''),
|
||||||
|
# ('aki', _('AKI - Amazon Kernel Image')),
|
||||||
|
# ('ami', _('AMI - Amazon Machine Image')),
|
||||||
|
# ('ari', _('ARI - Amazon Ramdisk Image')),
|
||||||
|
# ('iso', _('ISO - Optical Disk Image')),
|
||||||
|
# ('qcow2', _('QCOW2 - QEMU Emulator')),
|
||||||
|
# ('raw', _('Raw')),
|
||||||
|
# ('vdi', _('VDI')),
|
||||||
|
# ('vhd', _('VHD')),
|
||||||
|
# ('vmdk', _('VMDK'))
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
|
||||||
|
# The IMAGE_CUSTOM_PROPERTY_TITLES settings is used to customize the titles for
|
||||||
|
# image custom property attributes that appear on image detail pages.
|
||||||
|
IMAGE_CUSTOM_PROPERTY_TITLES = {
|
||||||
|
"architecture": _("Architecture"),
|
||||||
|
"kernel_id": _("Kernel ID"),
|
||||||
|
"ramdisk_id": _("Ramdisk ID"),
|
||||||
|
"image_state": _("Euca2ools state"),
|
||||||
|
"project_id": _("Project ID"),
|
||||||
|
"image_type": _("Image Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints
|
||||||
|
# in the Keystone service catalog. Use this setting when Horizon is running
|
||||||
|
# external to the OpenStack environment. The default is 'publicURL'.
|
||||||
|
#OPENSTACK_ENDPOINT_TYPE = "publicURL"
|
||||||
|
|
||||||
|
# SECONDARY_ENDPOINT_TYPE specifies the fallback endpoint type to use in the
|
||||||
|
# case that OPENSTACK_ENDPOINT_TYPE is not present in the endpoints
|
||||||
|
# in the Keystone service catalog. Use this setting when Horizon is running
|
||||||
|
# external to the OpenStack environment. The default is None. This
|
||||||
|
# value should differ from OPENSTACK_ENDPOINT_TYPE if used.
|
||||||
|
#SECONDARY_ENDPOINT_TYPE = "publicURL"
|
||||||
|
|
||||||
|
# The number of objects (Swift containers/objects or images) to display
|
||||||
|
# on a single page before providing a paging element (a "more" link)
|
||||||
|
# to paginate results.
|
||||||
|
API_RESULT_LIMIT = 1000
|
||||||
|
API_RESULT_PAGE_SIZE = 20
|
||||||
|
|
||||||
|
# The timezone of the server. This should correspond with the timezone
|
||||||
|
# of your entire OpenStack installation, and hopefully be in UTC.
|
||||||
|
TIME_ZONE = "UTC"
|
||||||
|
|
||||||
|
# When launching an instance, the menu of available flavors is
|
||||||
|
# sorted by RAM usage, ascending. If you would like a different sort order,
|
||||||
|
# you can provide another flavor attribute as sorting key. Alternatively, you
|
||||||
|
# can provide a custom callback method to use for sorting. You can also provide
|
||||||
|
# a flag for reverse sort. For more info, see
|
||||||
|
# http://docs.python.org/2/library/functions.html#sorted
|
||||||
|
# CREATE_INSTANCE_FLAVOR_SORT = {
|
||||||
|
# 'key': 'name',
|
||||||
|
# # or
|
||||||
|
# 'key': my_awesome_callback_method,
|
||||||
|
# 'reverse': False,
|
||||||
|
# }
|
||||||
|
|
||||||
|
# The Horizon Policy Enforcement engine uses these values to load per service
|
||||||
|
# policy rule files. The content of these files should match the files the
|
||||||
|
# OpenStack services are using to determine role based access control in the
|
||||||
|
# target installation.
|
||||||
|
|
||||||
|
# Path to directory containing policy.json files
|
||||||
|
#POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf")
|
||||||
|
# Map of local copy of service policy files
|
||||||
|
#POLICY_FILES = {
|
||||||
|
# 'identity': 'keystone_policy.json',
|
||||||
|
# '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
|
||||||
|
# for loggers specified in this configuration dictionary. Note that
|
||||||
|
# if nothing is specified here and disable_existing_loggers is True,
|
||||||
|
# django.db.backends will still log unless it is disabled explicitly.
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'handlers': {
|
||||||
|
'null': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'class': 'django.utils.log.NullHandler',
|
||||||
|
},
|
||||||
|
'console': {
|
||||||
|
# Set the level to "DEBUG" for verbose output logging.
|
||||||
|
'level': 'INFO',
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
# Logging from django.db.backends is VERY verbose, send to null
|
||||||
|
# by default.
|
||||||
|
'django.db.backends': {
|
||||||
|
'handlers': ['null'],
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'requests': {
|
||||||
|
'handlers': ['null'],
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'horizon': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'openstack_dashboard': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'novaclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'cinderclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'keystoneclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'glanceclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'neutronclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'heatclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'ceilometerclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'troveclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'swiftclient': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'openstack_auth': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'nose.plugins.manager': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'django': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'iso8601': {
|
||||||
|
'handlers': ['null'],
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 'direction' should not be specified for all_tcp/udp/icmp.
|
||||||
|
# It is specified in the form.
|
||||||
|
SECURITY_GROUP_RULES = {
|
||||||
|
'all_tcp': {
|
||||||
|
'name': 'ALL TCP',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '1',
|
||||||
|
'to_port': '65535',
|
||||||
|
},
|
||||||
|
'all_udp': {
|
||||||
|
'name': 'ALL UDP',
|
||||||
|
'ip_protocol': 'udp',
|
||||||
|
'from_port': '1',
|
||||||
|
'to_port': '65535',
|
||||||
|
},
|
||||||
|
'all_icmp': {
|
||||||
|
'name': 'ALL ICMP',
|
||||||
|
'ip_protocol': 'icmp',
|
||||||
|
'from_port': '-1',
|
||||||
|
'to_port': '-1',
|
||||||
|
},
|
||||||
|
'ssh': {
|
||||||
|
'name': 'SSH',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '22',
|
||||||
|
'to_port': '22',
|
||||||
|
},
|
||||||
|
'smtp': {
|
||||||
|
'name': 'SMTP',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '25',
|
||||||
|
'to_port': '25',
|
||||||
|
},
|
||||||
|
'dns': {
|
||||||
|
'name': 'DNS',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '53',
|
||||||
|
'to_port': '53',
|
||||||
|
},
|
||||||
|
'http': {
|
||||||
|
'name': 'HTTP',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '80',
|
||||||
|
'to_port': '80',
|
||||||
|
},
|
||||||
|
'pop3': {
|
||||||
|
'name': 'POP3',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '110',
|
||||||
|
'to_port': '110',
|
||||||
|
},
|
||||||
|
'imap': {
|
||||||
|
'name': 'IMAP',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '143',
|
||||||
|
'to_port': '143',
|
||||||
|
},
|
||||||
|
'ldap': {
|
||||||
|
'name': 'LDAP',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '389',
|
||||||
|
'to_port': '389',
|
||||||
|
},
|
||||||
|
'https': {
|
||||||
|
'name': 'HTTPS',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '443',
|
||||||
|
'to_port': '443',
|
||||||
|
},
|
||||||
|
'smtps': {
|
||||||
|
'name': 'SMTPS',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '465',
|
||||||
|
'to_port': '465',
|
||||||
|
},
|
||||||
|
'imaps': {
|
||||||
|
'name': 'IMAPS',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '993',
|
||||||
|
'to_port': '993',
|
||||||
|
},
|
||||||
|
'pop3s': {
|
||||||
|
'name': 'POP3S',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '995',
|
||||||
|
'to_port': '995',
|
||||||
|
},
|
||||||
|
'ms_sql': {
|
||||||
|
'name': 'MS SQL',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '1433',
|
||||||
|
'to_port': '1433',
|
||||||
|
},
|
||||||
|
'mysql': {
|
||||||
|
'name': 'MYSQL',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '3306',
|
||||||
|
'to_port': '3306',
|
||||||
|
},
|
||||||
|
'rdp': {
|
||||||
|
'name': 'RDP',
|
||||||
|
'ip_protocol': 'tcp',
|
||||||
|
'from_port': '3389',
|
||||||
|
'to_port': '3389',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
FLAVOR_EXTRA_KEYS = {
|
||||||
|
'flavor_keys': [
|
||||||
|
('quota:read_bytes_sec', _('Quota: Read bytes')),
|
||||||
|
('quota:write_bytes_sec', _('Quota: Write bytes')),
|
||||||
|
('quota:cpu_quota', _('Quota: CPU')),
|
||||||
|
('quota:cpu_period', _('Quota: CPU period')),
|
||||||
|
('quota:inbound_average', _('Quota: Inbound average')),
|
||||||
|
('quota:outbound_average', _('Quota: Outbound average')),
|
||||||
|
]
|
||||||
|
}
|
3
horizon_dashboard/demo_dashboard/models.py
Normal file
3
horizon_dashboard/demo_dashboard/models.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||||
|
"""
|
263
horizon_dashboard/demo_dashboard/settings.py
Normal file
263
horizon_dashboard/demo_dashboard/settings.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
# Copyright 2012 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from openstack_dashboard import exceptions
|
||||||
|
|
||||||
|
warnings.formatwarning = lambda message, category, *args, **kwargs: \
|
||||||
|
'%s: %s' % (category.__name__, message)
|
||||||
|
|
||||||
|
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
BIN_DIR = os.path.abspath(os.path.join(ROOT_PATH, '..', 'bin'))
|
||||||
|
|
||||||
|
if ROOT_PATH not in sys.path:
|
||||||
|
sys.path.append(ROOT_PATH)
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
|
SITE_BRANDING = 'OpenStack Dashboard'
|
||||||
|
|
||||||
|
LOGIN_URL = '/auth/login/'
|
||||||
|
LOGOUT_URL = '/auth/logout/'
|
||||||
|
# LOGIN_REDIRECT_URL can be used as an alternative for
|
||||||
|
# HORIZON_CONFIG.user_home, if user_home is not set.
|
||||||
|
# Do not set it to '/home/', as this will cause circular redirect loop
|
||||||
|
LOGIN_REDIRECT_URL = '/'
|
||||||
|
|
||||||
|
MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media'))
|
||||||
|
MEDIA_URL = '/media/'
|
||||||
|
STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static'))
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'openstack_dashboard.urls'
|
||||||
|
|
||||||
|
HORIZON_CONFIG = {
|
||||||
|
'dashboards': ('project', 'admin', 'mistral',
|
||||||
|
'settings', 'router',),
|
||||||
|
'default_dashboard': 'project',
|
||||||
|
'user_home': 'openstack_dashboard.views.get_user_home',
|
||||||
|
'ajax_queue_limit': 10,
|
||||||
|
'auto_fade_alerts': {
|
||||||
|
'delay': 3000,
|
||||||
|
'fade_duration': 1500,
|
||||||
|
'types': ['alert-success', 'alert-info']
|
||||||
|
},
|
||||||
|
'help_url': "http://docs.openstack.org",
|
||||||
|
'exceptions': {'recoverable': exceptions.RECOVERABLE,
|
||||||
|
'not_found': exceptions.NOT_FOUND,
|
||||||
|
'unauthorized': exceptions.UNAUTHORIZED},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set to True to allow users to upload images to glance via Horizon server.
|
||||||
|
# When enabled, a file form field will appear on the create image form.
|
||||||
|
# See documentation for deployment considerations.
|
||||||
|
HORIZON_IMAGES_ALLOW_UPLOAD = True
|
||||||
|
|
||||||
|
# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features
|
||||||
|
# in the OpenStack Dashboard related to the Image service, such as the list
|
||||||
|
# of supported image formats.
|
||||||
|
OPENSTACK_IMAGE_BACKEND = {
|
||||||
|
'image_formats': [
|
||||||
|
('', ''),
|
||||||
|
('aki', _('AKI - Amazon Kernel Image')),
|
||||||
|
('ami', _('AMI - Amazon Machine Image')),
|
||||||
|
('ari', _('ARI - Amazon Ramdisk Image')),
|
||||||
|
('iso', _('ISO - Optical Disk Image')),
|
||||||
|
('qcow2', _('QCOW2 - QEMU Emulator')),
|
||||||
|
('raw', _('Raw')),
|
||||||
|
('vdi', _('VDI')),
|
||||||
|
('vhd', _('VHD')),
|
||||||
|
('vmdk', _('VMDK'))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'horizon.middleware.HorizonMiddleware',
|
||||||
|
'django.middleware.doc.XViewMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
'django.core.context_processors.debug',
|
||||||
|
'django.core.context_processors.i18n',
|
||||||
|
'django.core.context_processors.request',
|
||||||
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.static',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'horizon.context_processors.horizon',
|
||||||
|
'openstack_dashboard.context_processors.openstack',
|
||||||
|
)
|
||||||
|
|
||||||
|
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',
|
||||||
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
|
)
|
||||||
|
|
||||||
|
COMPRESS_PRECOMPILERS = (
|
||||||
|
('text/less', ('lesscpy {infile}')),
|
||||||
|
)
|
||||||
|
|
||||||
|
COMPRESS_CSS_FILTERS = (
|
||||||
|
'compressor.filters.css_default.CssAbsoluteFilter',
|
||||||
|
)
|
||||||
|
|
||||||
|
COMPRESS_ENABLED = True
|
||||||
|
COMPRESS_OUTPUT_DIR = 'dashboard'
|
||||||
|
COMPRESS_CSS_HASHING_METHOD = 'hash'
|
||||||
|
COMPRESS_PARSER = 'compressor.parser.HtmlParser'
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'openstack_dashboard',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'django.contrib.humanize',
|
||||||
|
'compressor',
|
||||||
|
'horizon',
|
||||||
|
'openstack_auth',
|
||||||
|
'demo_dashboard.dashboards.mistral'
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
|
||||||
|
AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
||||||
|
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
|
||||||
|
|
||||||
|
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
|
||||||
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||||
|
SESSION_COOKIE_SECURE = False
|
||||||
|
SESSION_TIMEOUT = 1800
|
||||||
|
|
||||||
|
# When using cookie-based sessions, log error when the session cookie exceeds
|
||||||
|
# the following size (common browsers drop cookies above a certain size):
|
||||||
|
SESSION_COOKIE_MAX_SIZE = 4093
|
||||||
|
|
||||||
|
# when doing upgrades, it may be wise to stick to PickleSerializer
|
||||||
|
# TODO(mrunge): remove after Icehouse
|
||||||
|
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
||||||
|
|
||||||
|
LANGUAGES = (
|
||||||
|
('de', 'German'),
|
||||||
|
('en', 'English'),
|
||||||
|
('en-au', 'Australian English'),
|
||||||
|
('en-gb', 'British English'),
|
||||||
|
('es', 'Spanish'),
|
||||||
|
('fr', 'French'),
|
||||||
|
('hi', 'Hindi'),
|
||||||
|
('ja', 'Japanese'),
|
||||||
|
('ko', 'Korean (Korea)'),
|
||||||
|
('nl', 'Dutch (Netherlands)'),
|
||||||
|
('pl', 'Polish'),
|
||||||
|
('pt-br', 'Portuguese (Brazil)'),
|
||||||
|
('sr', 'Serbian'),
|
||||||
|
('zh-cn', 'Simplified Chinese'),
|
||||||
|
('zh-tw', 'Chinese (Taiwan)'),
|
||||||
|
)
|
||||||
|
LANGUAGE_CODE = 'en'
|
||||||
|
LANGUAGE_COOKIE_NAME = 'horizon_language'
|
||||||
|
USE_I18N = True
|
||||||
|
USE_L10N = True
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
OPENSTACK_KEYSTONE_DEFAULT_ROLE = '_member_'
|
||||||
|
|
||||||
|
DEFAULT_EXCEPTION_REPORTER_FILTER = 'horizon.exceptions.HorizonReporterFilter'
|
||||||
|
|
||||||
|
POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf")
|
||||||
|
# Map of local copy of service policy files
|
||||||
|
POLICY_FILES = {
|
||||||
|
'identity': 'keystone_policy.json',
|
||||||
|
'compute': 'nova_policy.json',
|
||||||
|
'volume': 'cinder_policy.json',
|
||||||
|
'image': 'glance_policy.json',
|
||||||
|
}
|
||||||
|
|
||||||
|
SECRET_KEY = None
|
||||||
|
LOCAL_PATH = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
from local.local_settings import * # noqa
|
||||||
|
except ImportError:
|
||||||
|
logging.warning("No local_settings file found.")
|
||||||
|
|
||||||
|
# Load the pluggable dashboard settings
|
||||||
|
import openstack_dashboard.enabled
|
||||||
|
import openstack_dashboard.local.enabled
|
||||||
|
from openstack_dashboard.utils import settings
|
||||||
|
|
||||||
|
INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable
|
||||||
|
settings.update_dashboards([
|
||||||
|
openstack_dashboard.enabled,
|
||||||
|
openstack_dashboard.local.enabled,
|
||||||
|
], HORIZON_CONFIG, INSTALLED_APPS)
|
||||||
|
|
||||||
|
# Ensure that we always have a SECRET_KEY set, even when no local_settings.py
|
||||||
|
# file is present. See local_settings.py.example for full documentation on the
|
||||||
|
# horizon.utils.secret_key module and its use.
|
||||||
|
if not SECRET_KEY:
|
||||||
|
if not LOCAL_PATH:
|
||||||
|
LOCAL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
'local')
|
||||||
|
|
||||||
|
from horizon.utils import secret_key
|
||||||
|
SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH,
|
||||||
|
'.secret_key_store'))
|
||||||
|
|
||||||
|
from openstack_dashboard import policy
|
||||||
|
POLICY_CHECK_FUNCTION = policy.check
|
||||||
|
|
||||||
|
# Add HORIZON_CONFIG to the context information for offline compression
|
||||||
|
COMPRESS_OFFLINE_CONTEXT = {
|
||||||
|
'STATIC_URL': STATIC_URL,
|
||||||
|
'HORIZON_CONFIG': HORIZON_CONFIG
|
||||||
|
}
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
# during django reloads and an active user is logged in, the monkey
|
||||||
|
# patch below will not otherwise be applied in time - resulting in developers
|
||||||
|
# appearing to be logged out. In typical production deployments this section
|
||||||
|
# below may be ommited, though it should not be harmful
|
||||||
|
from openstack_auth import utils as auth_utils
|
||||||
|
auth_utils.patch_middleware_get_user()
|
22
horizon_dashboard/manage.py
Executable file
22
horizon_dashboard/manage.py
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# 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 os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.core.management import execute_from_command_line # noqa
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo_dashboard.settings")
|
||||||
|
execute_from_command_line(sys.argv)
|
3
horizon_dashboard/requirements.txt
Normal file
3
horizon_dashboard/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-e git+https://github.com/openstack/horizon.git#egg=horizon
|
||||||
|
# -e ../python-mistralclient
|
||||||
|
-e git+https://github.com/stackforge/python-mistralclient.git#egg=mistralclient
|
502
horizon_dashboard/run_tests.sh
Executable file
502
horizon_dashboard/run_tests.sh
Executable file
@@ -0,0 +1,502 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
|
||||||
|
# ---------------UPDATE ME-------------------------------#
|
||||||
|
# Increment me any time the environment should be rebuilt.
|
||||||
|
# This includes dependency changes, directory renames, etc.
|
||||||
|
# Simple integer sequence: 1, 2, 3...
|
||||||
|
environment_version=42
|
||||||
|
#--------------------------------------------------------#
|
||||||
|
|
||||||
|
function usage {
|
||||||
|
echo "Usage: $0 [OPTION]..."
|
||||||
|
echo "Run Horizon's test suite(s)"
|
||||||
|
echo ""
|
||||||
|
echo " -V, --virtual-env Always use virtualenv. Install automatically"
|
||||||
|
echo " if not present"
|
||||||
|
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local"
|
||||||
|
echo " environment"
|
||||||
|
echo " -c, --coverage Generate reports using Coverage"
|
||||||
|
echo " -f, --force Force a clean re-build of the virtual"
|
||||||
|
echo " environment. Useful when dependencies have"
|
||||||
|
echo " been added."
|
||||||
|
echo " -m, --manage Run a Django management command."
|
||||||
|
echo " --makemessages Create/Update English translation files."
|
||||||
|
echo " --compilemessages Compile all translation files."
|
||||||
|
echo " -p, --pep8 Just run pep8"
|
||||||
|
echo " -P, --no-pep8 Don't run pep8 by default"
|
||||||
|
echo " -t, --tabs Check for tab characters in files."
|
||||||
|
echo " -y, --pylint Just run pylint"
|
||||||
|
echo " -q, --quiet Run non-interactively. (Relatively) quiet."
|
||||||
|
echo " Implies -V if -N is not set."
|
||||||
|
echo " --only-selenium Run only the Selenium unit tests"
|
||||||
|
echo " --with-selenium Run unit tests including Selenium tests"
|
||||||
|
echo " --integration Run the integration tests (requires a running "
|
||||||
|
echo " OpenStack environment)"
|
||||||
|
echo " --runserver Run the Django development server for"
|
||||||
|
echo " openstack_dashboard in the virtual"
|
||||||
|
echo " environment."
|
||||||
|
echo " --docs Just build the documentation"
|
||||||
|
echo " --backup-environment Make a backup of the environment on exit"
|
||||||
|
echo " --restore-environment Restore the environment before running"
|
||||||
|
echo " --destroy-environment Destroy the environment and exit"
|
||||||
|
echo " -h, --help Print this usage message"
|
||||||
|
echo ""
|
||||||
|
echo "Note: with no options specified, the script will try to run the tests in"
|
||||||
|
echo " a virtual environment, If no virtualenv is found, the script will ask"
|
||||||
|
echo " if you would like to create one. If you prefer to run tests NOT in a"
|
||||||
|
echo " virtual environment, simply pass the -N option."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# DEFAULTS FOR RUN_TESTS.SH
|
||||||
|
#
|
||||||
|
root=`pwd -P`
|
||||||
|
venv=$root/.venv
|
||||||
|
with_venv=tools/with_venv.sh
|
||||||
|
included_dirs="demo_dashboard"
|
||||||
|
tested_apps="demo_dashboard"
|
||||||
|
settings="demo_dashboard.test.settings"
|
||||||
|
|
||||||
|
always_venv=0
|
||||||
|
backup_env=0
|
||||||
|
command_wrapper=""
|
||||||
|
destroy=0
|
||||||
|
force=0
|
||||||
|
just_pep8=0
|
||||||
|
no_pep8=0
|
||||||
|
just_pylint=0
|
||||||
|
just_docs=0
|
||||||
|
just_tabs=0
|
||||||
|
never_venv=0
|
||||||
|
quiet=0
|
||||||
|
restore_env=0
|
||||||
|
runserver=0
|
||||||
|
only_selenium=0
|
||||||
|
with_selenium=0
|
||||||
|
integration=0
|
||||||
|
testopts=""
|
||||||
|
testargs=""
|
||||||
|
with_coverage=0
|
||||||
|
makemessages=0
|
||||||
|
compilemessages=0
|
||||||
|
manage=0
|
||||||
|
|
||||||
|
# Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default"
|
||||||
|
[ "$JOB_NAME" ] || JOB_NAME="default"
|
||||||
|
|
||||||
|
function process_option {
|
||||||
|
# If running manage command, treat the rest of options as arguments.
|
||||||
|
if [ $manage -eq 1 ]; then
|
||||||
|
testargs="$testargs $1"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
-h|--help) usage;;
|
||||||
|
-V|--virtual-env) always_venv=1; never_venv=0;;
|
||||||
|
-N|--no-virtual-env) always_venv=0; never_venv=1;;
|
||||||
|
-p|--pep8) just_pep8=1;;
|
||||||
|
-P|--no-pep8) no_pep8=1;;
|
||||||
|
-y|--pylint) just_pylint=1;;
|
||||||
|
-f|--force) force=1;;
|
||||||
|
-t|--tabs) just_tabs=1;;
|
||||||
|
-q|--quiet) quiet=1;;
|
||||||
|
-c|--coverage) with_coverage=1;;
|
||||||
|
-m|--manage) manage=1;;
|
||||||
|
--makemessages) makemessages=1;;
|
||||||
|
--compilemessages) compilemessages=1;;
|
||||||
|
--only-selenium) only_selenium=1;;
|
||||||
|
--with-selenium) with_selenium=1;;
|
||||||
|
--integration) integration=1;;
|
||||||
|
--docs) just_docs=1;;
|
||||||
|
--runserver) runserver=1;;
|
||||||
|
--backup-environment) backup_env=1;;
|
||||||
|
--restore-environment) restore_env=1;;
|
||||||
|
--destroy-environment) destroy=1;;
|
||||||
|
-*) testopts="$testopts $1";;
|
||||||
|
*) testargs="$testargs $1"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_management_command {
|
||||||
|
${command_wrapper} python $root/manage.py $testopts $testargs
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_server {
|
||||||
|
echo "Starting Django development server..."
|
||||||
|
${command_wrapper} python $root/manage.py runserver $testopts $testargs
|
||||||
|
echo "Server stopped."
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_pylint {
|
||||||
|
echo "Running pylint ..."
|
||||||
|
PYTHONPATH=$root ${command_wrapper} pylint --rcfile=.pylintrc -f parseable $included_dirs > pylint.txt || true
|
||||||
|
CODE=$?
|
||||||
|
grep Global -A2 pylint.txt
|
||||||
|
if [ $CODE -lt 32 ]; then
|
||||||
|
echo "Completed successfully."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Completed with problems."
|
||||||
|
exit $CODE
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_pep8 {
|
||||||
|
echo "Running flake8 ..."
|
||||||
|
set +o errexit
|
||||||
|
${command_wrapper} python -c "import hacking" 2>/dev/null
|
||||||
|
no_hacking=$?
|
||||||
|
set -o errexit
|
||||||
|
if [ $never_venv -eq 1 -a $no_hacking -eq 1 ]; then
|
||||||
|
echo "**WARNING**:" >&2
|
||||||
|
echo "OpenStack hacking is not installed on your host. Its detection will be missed." >&2
|
||||||
|
echo "Please install or use virtual env if you need OpenStack hacking detection." >&2
|
||||||
|
fi
|
||||||
|
DJANGO_SETTINGS_MODULE=openstack_dashboard.test.settings ${command_wrapper} flake8
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_sphinx {
|
||||||
|
echo "Building sphinx..."
|
||||||
|
export DJANGO_SETTINGS_MODULE=openstack_dashboard.settings
|
||||||
|
${command_wrapper} sphinx-build -b html doc/source doc/build/html
|
||||||
|
echo "Build complete."
|
||||||
|
}
|
||||||
|
|
||||||
|
function tab_check {
|
||||||
|
TAB_VIOLATIONS=`find $included_dirs -type f -regex ".*\.\(css\|js\|py\|html\)" -print0 | xargs -0 awk '/\t/' | wc -l`
|
||||||
|
if [ $TAB_VIOLATIONS -gt 0 ]; then
|
||||||
|
echo "TABS! $TAB_VIOLATIONS of them! Oh no!"
|
||||||
|
HORIZON_FILES=`find $included_dirs -type f -regex ".*\.\(css\|js\|py|\html\)"`
|
||||||
|
for TABBED_FILE in $HORIZON_FILES
|
||||||
|
do
|
||||||
|
TAB_COUNT=`awk '/\t/' $TABBED_FILE | wc -l`
|
||||||
|
if [ $TAB_COUNT -gt 0 ]; then
|
||||||
|
echo "$TABBED_FILE: $TAB_COUNT"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
return $TAB_VIOLATIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroy_venv {
|
||||||
|
echo "Cleaning environment..."
|
||||||
|
echo "Removing virtualenv..."
|
||||||
|
rm -rf $venv
|
||||||
|
echo "Virtualenv removed."
|
||||||
|
rm -f .environment_version
|
||||||
|
echo "Environment cleaned."
|
||||||
|
}
|
||||||
|
|
||||||
|
function environment_check {
|
||||||
|
echo "Checking environment."
|
||||||
|
if [ -f .environment_version ]; then
|
||||||
|
ENV_VERS=`cat .environment_version`
|
||||||
|
if [ $ENV_VERS -eq $environment_version ]; then
|
||||||
|
if [ -e ${venv} ]; then
|
||||||
|
# If the environment exists and is up-to-date then set our variables
|
||||||
|
command_wrapper="${root}/${with_venv}"
|
||||||
|
echo "Environment is up to date."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $always_venv -eq 1 ]; then
|
||||||
|
install_venv
|
||||||
|
else
|
||||||
|
if [ ! -e ${venv} ]; then
|
||||||
|
echo -e "Environment not found. Install? (Y/n) \c"
|
||||||
|
else
|
||||||
|
echo -e "Your environment appears to be out of date. Update? (Y/n) \c"
|
||||||
|
fi
|
||||||
|
read update_env
|
||||||
|
if [ "x$update_env" = "xY" -o "x$update_env" = "x" -o "x$update_env" = "xy" ]; then
|
||||||
|
install_venv
|
||||||
|
else
|
||||||
|
# Set our command wrapper anyway.
|
||||||
|
command_wrapper="${root}/${with_venv}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanity_check {
|
||||||
|
# Anything that should be determined prior to running the tests, server, etc.
|
||||||
|
# Don't sanity-check anything environment-related in -N flag is set
|
||||||
|
if [ $never_venv -eq 0 ]; then
|
||||||
|
if [ ! -e ${venv} ]; then
|
||||||
|
echo "Virtualenv not found at $venv. Did install_venv.py succeed?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Remove .pyc files. This is sanity checking because they can linger
|
||||||
|
# after old files are deleted.
|
||||||
|
find . -name "*.pyc" -exec rm -rf {} \;
|
||||||
|
}
|
||||||
|
|
||||||
|
function backup_environment {
|
||||||
|
if [ $backup_env -eq 1 ]; then
|
||||||
|
echo "Backing up environment \"$JOB_NAME\"..."
|
||||||
|
if [ ! -e ${venv} ]; then
|
||||||
|
echo "Environment not installed. Cannot back up."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -d /tmp/.horizon_environment/$JOB_NAME ]; then
|
||||||
|
mv /tmp/.horizon_environment/$JOB_NAME /tmp/.horizon_environment/$JOB_NAME.old
|
||||||
|
rm -rf /tmp/.horizon_environment/$JOB_NAME
|
||||||
|
fi
|
||||||
|
mkdir -p /tmp/.horizon_environment/$JOB_NAME
|
||||||
|
cp -r $venv /tmp/.horizon_environment/$JOB_NAME/
|
||||||
|
cp .environment_version /tmp/.horizon_environment/$JOB_NAME/
|
||||||
|
# Remove the backup now that we've completed successfully
|
||||||
|
rm -rf /tmp/.horizon_environment/$JOB_NAME.old
|
||||||
|
echo "Backup completed"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function restore_environment {
|
||||||
|
if [ $restore_env -eq 1 ]; then
|
||||||
|
echo "Restoring environment from backup..."
|
||||||
|
if [ ! -d /tmp/.horizon_environment/$JOB_NAME ]; then
|
||||||
|
echo "No backup to restore from."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp -r /tmp/.horizon_environment/$JOB_NAME/.venv ./ || true
|
||||||
|
cp -r /tmp/.horizon_environment/$JOB_NAME/.environment_version ./ || true
|
||||||
|
|
||||||
|
echo "Environment restored successfully."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function install_venv {
|
||||||
|
# Install with install_venv.py
|
||||||
|
export PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE-/tmp/.pip_download_cache}
|
||||||
|
export PIP_USE_MIRRORS=true
|
||||||
|
if [ $quiet -eq 1 ]; then
|
||||||
|
export PIP_NO_INPUT=true
|
||||||
|
fi
|
||||||
|
echo "Fetching new src packages..."
|
||||||
|
rm -rf $venv/src
|
||||||
|
python tools/install_venv.py
|
||||||
|
command_wrapper="$root/${with_venv}"
|
||||||
|
# Make sure it worked and record the environment version
|
||||||
|
sanity_check
|
||||||
|
chmod -R 754 $venv
|
||||||
|
echo $environment_version > .environment_version
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_tests {
|
||||||
|
sanity_check
|
||||||
|
|
||||||
|
if [ $with_selenium -eq 1 ]; then
|
||||||
|
export WITH_SELENIUM=1
|
||||||
|
elif [ $only_selenium -eq 1 ]; then
|
||||||
|
export WITH_SELENIUM=1
|
||||||
|
export SKIP_UNITTESTS=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $with_selenium -eq 0 -a $integration -eq 0 ]; then
|
||||||
|
testopts="$testopts --exclude-dir=openstack_dashboard/test/integration_tests"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$testargs" ]; then
|
||||||
|
run_tests_all
|
||||||
|
else
|
||||||
|
run_tests_subset
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_tests_subset {
|
||||||
|
project=`echo $testargs | awk -F. '{print $1}'`
|
||||||
|
${command_wrapper} python $root/manage.py test --settings=$project.test.settings $testopts $testargs
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_tests_all {
|
||||||
|
echo "Running Horizon application tests"
|
||||||
|
export NOSE_XUNIT_FILE=horizon/nosetests.xml
|
||||||
|
if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then
|
||||||
|
export NOSE_HTML_OUT_FILE='horizon_nose_results.html'
|
||||||
|
fi
|
||||||
|
if [ $with_coverage -eq 1 ]; then
|
||||||
|
${command_wrapper} coverage erase
|
||||||
|
coverage_run="coverage run -p"
|
||||||
|
fi
|
||||||
|
${command_wrapper} ${coverage_run} $root/manage.py test horizon --settings=horizon.test.settings $testopts
|
||||||
|
# get results of the Horizon tests
|
||||||
|
HORIZON_RESULT=$?
|
||||||
|
|
||||||
|
echo "Running openstack_dashboard tests"
|
||||||
|
export NOSE_XUNIT_FILE=openstack_dashboard/nosetests.xml
|
||||||
|
if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then
|
||||||
|
export NOSE_HTML_OUT_FILE='dashboard_nose_results.html'
|
||||||
|
fi
|
||||||
|
${command_wrapper} ${coverage_run} $root/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings $testopts
|
||||||
|
# get results of the openstack_dashboard tests
|
||||||
|
DASHBOARD_RESULT=$?
|
||||||
|
|
||||||
|
if [ $with_coverage -eq 1 ]; then
|
||||||
|
echo "Generating coverage reports"
|
||||||
|
${command_wrapper} coverage combine
|
||||||
|
${command_wrapper} coverage xml -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*'
|
||||||
|
${command_wrapper} coverage html -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' -d reports
|
||||||
|
fi
|
||||||
|
# Remove the leftover coverage files from the -p flag earlier.
|
||||||
|
rm -f .coverage.*
|
||||||
|
|
||||||
|
PEP8_RESULT=0
|
||||||
|
if [ $no_pep8 -eq 0 ] && [ $only_selenium -eq 0 ]; then
|
||||||
|
run_pep8
|
||||||
|
PEP8_RESULT=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
TEST_RESULT=$(($HORIZON_RESULT || $DASHBOARD_RESULT || $PEP8_RESULT))
|
||||||
|
if [ $TEST_RESULT -eq 0 ]; then
|
||||||
|
echo "Tests completed successfully."
|
||||||
|
else
|
||||||
|
echo "Tests failed."
|
||||||
|
fi
|
||||||
|
exit $TEST_RESULT
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_integration_tests {
|
||||||
|
export INTEGRATION_TESTS=1
|
||||||
|
|
||||||
|
echo "Running Horizon integration tests..."
|
||||||
|
${command_wrapper} nosetests openstack_dashboard/test/integration_tests/tests
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_makemessages {
|
||||||
|
OPTS="-l en --no-obsolete"
|
||||||
|
DASHBOARD_OPTS="--extension=html,txt,csv --ignore=openstack"
|
||||||
|
echo -n "horizon: "
|
||||||
|
cd horizon
|
||||||
|
${command_wrapper} $root/manage.py makemessages $OPTS
|
||||||
|
HORIZON_PY_RESULT=$?
|
||||||
|
echo -n "horizon javascript: "
|
||||||
|
${command_wrapper} $root/manage.py makemessages -d djangojs $OPTS
|
||||||
|
HORIZON_JS_RESULT=$?
|
||||||
|
echo -n "openstack_dashboard: "
|
||||||
|
cd ../openstack_dashboard
|
||||||
|
${command_wrapper} $root/manage.py makemessages $DASHBOARD_OPTS $OPTS
|
||||||
|
DASHBOARD_RESULT=$?
|
||||||
|
cd ..
|
||||||
|
exit $(($HORIZON_PY_RESULT || $HORIZON_JS_RESULT || $DASHBOARD_RESULT))
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_compilemessages {
|
||||||
|
cd horizon
|
||||||
|
${command_wrapper} $root/manage.py compilemessages
|
||||||
|
HORIZON_PY_RESULT=$?
|
||||||
|
cd ../openstack_dashboard
|
||||||
|
${command_wrapper} $root/manage.py compilemessages
|
||||||
|
DASHBOARD_RESULT=$?
|
||||||
|
cd ..
|
||||||
|
# English is the source language, so compiled catalogs are unnecessary.
|
||||||
|
rm -vf horizon/locale/en/LC_MESSAGES/django*.mo
|
||||||
|
rm -vf openstack_dashboard/locale/en/LC_MESSAGES/django.mo
|
||||||
|
exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------PREPARE THE ENVIRONMENT------------ #
|
||||||
|
|
||||||
|
# PROCESS ARGUMENTS, OVERRIDE DEFAULTS
|
||||||
|
for arg in "$@"; do
|
||||||
|
process_option $arg
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $quiet -eq 1 ] && [ $never_venv -eq 0 ] && [ $always_venv -eq 0 ]
|
||||||
|
then
|
||||||
|
always_venv=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If destroy is set, just blow it away and exit.
|
||||||
|
if [ $destroy -eq 1 ]; then
|
||||||
|
destroy_venv
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ignore all of this if the -N flag was set
|
||||||
|
if [ $never_venv -eq 0 ]; then
|
||||||
|
|
||||||
|
# Restore previous environment if desired
|
||||||
|
if [ $restore_env -eq 1 ]; then
|
||||||
|
restore_environment
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove the virtual environment if --force used
|
||||||
|
if [ $force -eq 1 ]; then
|
||||||
|
destroy_venv
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Then check if it's up-to-date
|
||||||
|
environment_check
|
||||||
|
|
||||||
|
# Create a backup of the up-to-date environment if desired
|
||||||
|
if [ $backup_env -eq 1 ]; then
|
||||||
|
backup_environment
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------EXERCISE THE CODE------------ #
|
||||||
|
|
||||||
|
# Run management commands
|
||||||
|
if [ $manage -eq 1 ]; then
|
||||||
|
run_management_command
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the docs
|
||||||
|
if [ $just_docs -eq 1 ]; then
|
||||||
|
run_sphinx
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update translation files
|
||||||
|
if [ $makemessages -eq 1 ]; then
|
||||||
|
run_makemessages
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile translation files
|
||||||
|
if [ $compilemessages -eq 1 ]; then
|
||||||
|
run_compilemessages
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PEP8
|
||||||
|
if [ $just_pep8 -eq 1 ]; then
|
||||||
|
run_pep8
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pylint
|
||||||
|
if [ $just_pylint -eq 1 ]; then
|
||||||
|
run_pylint
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tab checker
|
||||||
|
if [ $just_tabs -eq 1 ]; then
|
||||||
|
tab_check
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Integration tests
|
||||||
|
if [ $integration -eq 1 ]; then
|
||||||
|
run_integration_tests
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Django development server
|
||||||
|
if [ $runserver -eq 1 ]; then
|
||||||
|
run_server
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Full test suite
|
||||||
|
run_tests || exit
|
8
horizon_dashboard/test-requirements.txt
Normal file
8
horizon_dashboard/test-requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
coverage
|
||||||
|
django-nose
|
||||||
|
mox
|
||||||
|
netaddr
|
||||||
|
nose
|
||||||
|
pep8
|
||||||
|
pylint
|
||||||
|
sphinx
|
71
horizon_dashboard/tools/install_venv.py
Normal file
71
horizon_dashboard/tools/install_venv.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Copyright 2010 OpenStack Foundation
|
||||||
|
# Copyright 2013 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 sys
|
||||||
|
import install_venv_common as install_venv # noqa
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def print_help(venv, root):
|
||||||
|
help = """
|
||||||
|
Openstack development environment setup is complete.
|
||||||
|
|
||||||
|
Openstack development uses virtualenv to track and manage Python
|
||||||
|
dependencies while in development and testing.
|
||||||
|
|
||||||
|
To activate the Openstack virtualenv for the extent of your current shell
|
||||||
|
session you can run:
|
||||||
|
|
||||||
|
$ source %s/bin/activate
|
||||||
|
|
||||||
|
Or, if you prefer, you can run commands in the virtualenv on a case by case
|
||||||
|
basis by running:
|
||||||
|
|
||||||
|
$ %s/tools/with_venv.sh <your command>
|
||||||
|
|
||||||
|
Also, make test will automatically use the virtualenv.
|
||||||
|
"""
|
||||||
|
print(help % (venv, root))
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
if os.environ.get('tools_path'):
|
||||||
|
root = os.environ['tools_path']
|
||||||
|
venv = os.path.join(root, '.venv')
|
||||||
|
if os.environ.get('venv'):
|
||||||
|
venv = os.environ['venv']
|
||||||
|
|
||||||
|
pip_requires = os.path.join(root, 'requirements.txt')
|
||||||
|
test_requires = os.path.join(root, 'test-requirements.txt')
|
||||||
|
py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
|
||||||
|
project = 'Openstack'
|
||||||
|
install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
|
||||||
|
py_version, project)
|
||||||
|
options = install.parse_args(argv)
|
||||||
|
install.check_python_version()
|
||||||
|
install.check_dependencies()
|
||||||
|
install.create_virtualenv(no_site_packages=options.no_site_packages)
|
||||||
|
install.install_dependencies()
|
||||||
|
print_help(venv, root)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(sys.argv)
|
172
horizon_dashboard/tools/install_venv_common.py
Normal file
172
horizon_dashboard/tools/install_venv_common.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# Copyright 2013 OpenStack Foundation
|
||||||
|
# Copyright 2013 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Provides methods needed by installation script for OpenStack development
|
||||||
|
virtual environments.
|
||||||
|
|
||||||
|
Since this script is used to bootstrap a virtualenv from the system's Python
|
||||||
|
environment, it should be kept strictly compatible with Python 2.6.
|
||||||
|
|
||||||
|
Synced in from openstack-common
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class InstallVenv(object):
|
||||||
|
|
||||||
|
def __init__(self, root, venv, requirements,
|
||||||
|
test_requirements, py_version,
|
||||||
|
project):
|
||||||
|
self.root = root
|
||||||
|
self.venv = venv
|
||||||
|
self.requirements = requirements
|
||||||
|
self.test_requirements = test_requirements
|
||||||
|
self.py_version = py_version
|
||||||
|
self.project = project
|
||||||
|
|
||||||
|
def die(self, message, *args):
|
||||||
|
print(message % args, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def check_python_version(self):
|
||||||
|
if sys.version_info < (2, 6):
|
||||||
|
self.die("Need Python Version >= 2.6")
|
||||||
|
|
||||||
|
def run_command_with_code(self, cmd, redirect_output=True,
|
||||||
|
check_exit_code=True):
|
||||||
|
"""Runs a command in an out-of-process shell.
|
||||||
|
|
||||||
|
Returns the output of that command. Working directory is self.root.
|
||||||
|
"""
|
||||||
|
if redirect_output:
|
||||||
|
stdout = subprocess.PIPE
|
||||||
|
else:
|
||||||
|
stdout = None
|
||||||
|
|
||||||
|
proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
|
||||||
|
output = proc.communicate()[0]
|
||||||
|
if check_exit_code and proc.returncode != 0:
|
||||||
|
self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
|
||||||
|
return (output, proc.returncode)
|
||||||
|
|
||||||
|
def run_command(self, cmd, redirect_output=True, check_exit_code=True):
|
||||||
|
return self.run_command_with_code(cmd, redirect_output,
|
||||||
|
check_exit_code)[0]
|
||||||
|
|
||||||
|
def get_distro(self):
|
||||||
|
if (os.path.exists('/etc/fedora-release') or
|
||||||
|
os.path.exists('/etc/redhat-release')):
|
||||||
|
return Fedora(
|
||||||
|
self.root, self.venv, self.requirements,
|
||||||
|
self.test_requirements, self.py_version, self.project)
|
||||||
|
else:
|
||||||
|
return Distro(
|
||||||
|
self.root, self.venv, self.requirements,
|
||||||
|
self.test_requirements, self.py_version, self.project)
|
||||||
|
|
||||||
|
def check_dependencies(self):
|
||||||
|
self.get_distro().install_virtualenv()
|
||||||
|
|
||||||
|
def create_virtualenv(self, no_site_packages=True):
|
||||||
|
"""Creates the virtual environment and installs PIP.
|
||||||
|
|
||||||
|
Creates the virtual environment and installs PIP only into the
|
||||||
|
virtual environment.
|
||||||
|
"""
|
||||||
|
if not os.path.isdir(self.venv):
|
||||||
|
print('Creating venv...', end=' ')
|
||||||
|
if no_site_packages:
|
||||||
|
self.run_command(['virtualenv', '-q', '--no-site-packages',
|
||||||
|
self.venv])
|
||||||
|
else:
|
||||||
|
self.run_command(['virtualenv', '-q', self.venv])
|
||||||
|
print('done.')
|
||||||
|
else:
|
||||||
|
print("venv already exists...")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def pip_install(self, *args):
|
||||||
|
self.run_command(['tools/with_venv.sh',
|
||||||
|
'pip', 'install', '--upgrade'] + list(args),
|
||||||
|
redirect_output=False)
|
||||||
|
|
||||||
|
def install_dependencies(self):
|
||||||
|
print('Installing dependencies with pip (this can take a while)...')
|
||||||
|
|
||||||
|
# First things first, make sure our venv has the latest pip and
|
||||||
|
# setuptools and pbr
|
||||||
|
self.pip_install('pip>=1.4')
|
||||||
|
self.pip_install('setuptools')
|
||||||
|
self.pip_install('pbr')
|
||||||
|
|
||||||
|
self.pip_install('-r', self.requirements, '-r', self.test_requirements)
|
||||||
|
|
||||||
|
def parse_args(self, argv):
|
||||||
|
"""Parses command-line arguments."""
|
||||||
|
parser = optparse.OptionParser()
|
||||||
|
parser.add_option('-n', '--no-site-packages',
|
||||||
|
action='store_true',
|
||||||
|
help="Do not inherit packages from global Python "
|
||||||
|
"install")
|
||||||
|
return parser.parse_args(argv[1:])[0]
|
||||||
|
|
||||||
|
|
||||||
|
class Distro(InstallVenv):
|
||||||
|
|
||||||
|
def check_cmd(self, cmd):
|
||||||
|
return bool(self.run_command(['which', cmd],
|
||||||
|
check_exit_code=False).strip())
|
||||||
|
|
||||||
|
def install_virtualenv(self):
|
||||||
|
if self.check_cmd('virtualenv'):
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.check_cmd('easy_install'):
|
||||||
|
print('Installing virtualenv via easy_install...', end=' ')
|
||||||
|
if self.run_command(['easy_install', 'virtualenv']):
|
||||||
|
print('Succeeded')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print('Failed')
|
||||||
|
|
||||||
|
self.die('ERROR: virtualenv not found.\n\n%s development'
|
||||||
|
' requires virtualenv, please install it using your'
|
||||||
|
' favorite package management tool' % self.project)
|
||||||
|
|
||||||
|
|
||||||
|
class Fedora(Distro):
|
||||||
|
"""This covers all Fedora-based distributions.
|
||||||
|
|
||||||
|
Includes: Fedora, RHEL, CentOS, Scientific Linux
|
||||||
|
"""
|
||||||
|
|
||||||
|
def check_pkg(self, pkg):
|
||||||
|
return self.run_command_with_code(['rpm', '-q', pkg],
|
||||||
|
check_exit_code=False)[1] == 0
|
||||||
|
|
||||||
|
def install_virtualenv(self):
|
||||||
|
if self.check_cmd('virtualenv'):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.check_pkg('python-virtualenv'):
|
||||||
|
self.die("Please install 'python-virtualenv'.")
|
||||||
|
|
||||||
|
super(Fedora, self).install_virtualenv()
|
32
horizon_dashboard/tools/requirements_style_check.sh
Executable file
32
horizon_dashboard/tools/requirements_style_check.sh
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Enforce the requirement that dependencies are listed in the input
|
||||||
|
# files in alphabetical order.
|
||||||
|
|
||||||
|
# FIXME(dhellmann): This doesn't deal with URL requirements very
|
||||||
|
# well. We should probably sort those on the egg-name, rather than the
|
||||||
|
# full line.
|
||||||
|
|
||||||
|
function check_file() {
|
||||||
|
typeset f=$1
|
||||||
|
|
||||||
|
# We don't care about comment lines.
|
||||||
|
grep -v '^#' $f > ${f}.unsorted
|
||||||
|
sort -i -f ${f}.unsorted > ${f}.sorted
|
||||||
|
diff -c ${f}.unsorted ${f}.sorted
|
||||||
|
rc=$?
|
||||||
|
rm -f ${f}.sorted ${f}.unsorted
|
||||||
|
return $rc
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_code=0
|
||||||
|
for filename in $@
|
||||||
|
do
|
||||||
|
check_file $filename
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
echo "Please list requirements in $filename in alphabetical order" 1>&2
|
||||||
|
exit_code=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit $exit_code
|
7
horizon_dashboard/tools/with_venv.sh
Executable file
7
horizon_dashboard/tools/with_venv.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)}
|
||||||
|
VENV_PATH=${VENV_PATH:-${TOOLS_PATH}}
|
||||||
|
VENV_DIR=${VENV_NAME:-/../.venv}
|
||||||
|
TOOLS=${TOOLS_PATH}
|
||||||
|
VENV=${VENV:-${VENV_PATH}/${VENV_DIR}}
|
||||||
|
source ${VENV}/bin/activate && "$@"
|
Reference in New Issue
Block a user