Temporarily harbouring Horizon Dashboard

Change-Id: I46a1771d5ad5d0f6ac1ce8d28ac1e522cebf10bc
This commit is contained in:
Kirill Izotov 2014-04-25 16:05:11 +07:00
parent f403e29d5c
commit 54b4c60ffd
34 changed files with 1878 additions and 0 deletions

20
horizon_dashboard/.gitignore vendored Normal file
View 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

View 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

View File

@ -0,0 +1,5 @@
from mistralclient.api import client as mistral_client
def mistralclient(request):
return mistral_client.Client()

View File

@ -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)

View File

@ -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)

View File

@ -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,)

View File

@ -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 %}

View File

@ -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'),
)

View File

@ -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)]

View File

@ -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)

View File

@ -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)

View File

@ -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,)

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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'),
)

View File

@ -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']
}

View 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')),
]
}

View File

@ -0,0 +1,3 @@
"""
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
"""

View 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
View 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)

View 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
View 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

View File

@ -0,0 +1,8 @@
coverage
django-nose
mox
netaddr
nose
pep8
pylint
sphinx

View 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)

View 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()

View 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

View 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 && "$@"