Temporarily harbouring Horizon Dashboard
Change-Id: I46a1771d5ad5d0f6ac1ce8d28ac1e522cebf10bc
This commit is contained in:
parent
f403e29d5c
commit
54b4c60ffd
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 && "$@"
|
Loading…
Reference in New Issue
Block a user