From 28bde7a23059e0426e1d7e6855375345299c1f3f Mon Sep 17 00:00:00 2001 From: Kirill Izotov Date: Tue, 10 Jun 2014 18:41:02 +0700 Subject: [PATCH] Move dashboard from python-mistralclient Also: - add license headers - set up tox for flake8 checks and venv - remove run_tests.sh, tools/ and other stuff we don't need at the moment - add basic sphinx setup for later use Change-Id: I0d34fb9fd0bfd351d20474da88715b30ff3c3d61 --- .gitignore | 42 ++ LICENSE | 175 +++++++ README.md | 4 - README.rst | 33 ++ doc/source/conf.py | 62 +++ doc/source/index.rst | 1 + manage.py | 23 + mistraldashboard/__init__.py | 0 mistraldashboard/dashboards/__init__.py | 0 .../dashboards/mistral/__init__.py | 0 mistraldashboard/dashboards/mistral/api.py | 37 ++ .../dashboards/mistral/dashboard.py | 38 ++ .../dashboards/mistral/executions/__init__.py | 0 .../dashboards/mistral/executions/panel.py | 28 + .../dashboards/mistral/executions/tables.py | 42 ++ .../templates/executions/index.html | 11 + .../dashboards/mistral/executions/urls.py | 29 ++ .../dashboards/mistral/executions/views.py | 43 ++ .../dashboards/mistral/workbooks/__init__.py | 0 .../dashboards/mistral/workbooks/forms.py | 63 +++ .../dashboards/mistral/workbooks/panel.py | 29 ++ .../dashboards/mistral/workbooks/tables.py | 44 ++ .../templates/workbooks/_execute.html | 25 + .../templates/workbooks/execute.html | 11 + .../workbooks/templates/workbooks/index.html | 11 + .../dashboards/mistral/workbooks/urls.py | 29 ++ .../dashboards/mistral/workbooks/views.py | 48 ++ mistraldashboard/local/__init__.py | 0 .../local/local_settings.py.example | 488 ++++++++++++++++++ mistraldashboard/models.py | 3 + mistraldashboard/settings.py | 263 ++++++++++ requirements.txt | 3 + setup.cfg | 29 ++ setup.py | 30 ++ test-requirements.txt | 4 + tox.ini | 26 + 36 files changed, 1670 insertions(+), 4 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE delete mode 100644 README.md create mode 100644 README.rst create mode 100644 doc/source/conf.py create mode 100644 doc/source/index.rst create mode 100755 manage.py create mode 100644 mistraldashboard/__init__.py create mode 100644 mistraldashboard/dashboards/__init__.py create mode 100644 mistraldashboard/dashboards/mistral/__init__.py create mode 100644 mistraldashboard/dashboards/mistral/api.py create mode 100644 mistraldashboard/dashboards/mistral/dashboard.py create mode 100644 mistraldashboard/dashboards/mistral/executions/__init__.py create mode 100644 mistraldashboard/dashboards/mistral/executions/panel.py create mode 100644 mistraldashboard/dashboards/mistral/executions/tables.py create mode 100644 mistraldashboard/dashboards/mistral/executions/templates/executions/index.html create mode 100644 mistraldashboard/dashboards/mistral/executions/urls.py create mode 100644 mistraldashboard/dashboards/mistral/executions/views.py create mode 100644 mistraldashboard/dashboards/mistral/workbooks/__init__.py create mode 100644 mistraldashboard/dashboards/mistral/workbooks/forms.py create mode 100644 mistraldashboard/dashboards/mistral/workbooks/panel.py create mode 100644 mistraldashboard/dashboards/mistral/workbooks/tables.py create mode 100644 mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/_execute.html create mode 100644 mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/execute.html create mode 100644 mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/index.html create mode 100644 mistraldashboard/dashboards/mistral/workbooks/urls.py create mode 100644 mistraldashboard/dashboards/mistral/workbooks/views.py create mode 100644 mistraldashboard/local/__init__.py create mode 100644 mistraldashboard/local/local_settings.py.example create mode 100644 mistraldashboard/models.py create mode 100644 mistraldashboard/settings.py create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test-requirements.txt create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10e4d87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +*.py[cod] +*.sqlite + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +.venv +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject +.idea +.DS_Store +mistraldashboard/local/local_settings.py +mistraldashboard/local/.secret_key_store +static diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/README.md b/README.md deleted file mode 100644 index 48dcf68..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -mistral-dashboard -================= - -OpenStack dashboard for Mistral project diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..b3abb0b --- /dev/null +++ b/README.rst @@ -0,0 +1,33 @@ +================= +Mistral Dashboard +================= + +Horizon plugin for Mistral. + +Setup Instructions +================== + +The following should get you started:: + + $ git clone https://github.com/stackforge/mistral-dashboard.git + $ cd mistral-dashboard + $ cp mistraldashboard/local/local_settings.py.example \ + mistraldashboard/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. + +You may also need to add a service and endpoints to keystone:: + + $ MISTRAL_URL="http://[host]:[port]/v1" + $ keystone service-create --name mistral --type workflow + $ keystone endpoint-create --service_id mistral --publicurl $MISTRAL_URL \ + --adminurl $MISTRAL_URL --internalurl $MISTRAL_URL + +When you're ready to run the development server:: + + $ tox -evenv -- python manage.py runserver + + diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 0000000..59ec8bd --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# 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 + +sys.path.insert(0, os.path.abspath('../..')) +# -- General configuration ---------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'oslosphinx', +] + +# autodoc generation is a bit aggressive and a nuisance when doing heavy +# text edit cycles. +# execute "export SPHINX_DEBUG=1" in your terminal to disable + +# The suffix of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'mistral-dashboard' +copyright = u'2014, OpenStack Foundation' + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +add_module_names = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# -- Options for HTML output -------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +# html_theme_path = ["."] +# html_theme = '_theme' +# html_static_path = [] + +# Output file base name for HTML help builder. +htmlhelp_basename = '%sdoc' % project diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..38ba804 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1 @@ +.. include:: ../../README.rst \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..4ce8bb5 --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/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", + "mistraldashboard.settings") + execute_from_command_line(sys.argv) diff --git a/mistraldashboard/__init__.py b/mistraldashboard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/dashboards/__init__.py b/mistraldashboard/dashboards/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/dashboards/mistral/__init__.py b/mistraldashboard/dashboards/mistral/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/dashboards/mistral/api.py b/mistraldashboard/dashboards/mistral/api.py new file mode 100644 index 0000000..32b80ff --- /dev/null +++ b/mistraldashboard/dashboards/mistral/api.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf import settings + +from mistralclient.api import client as mistral_client + +SERVICE_TYPE = 'workflow' + + +def mistralclient(request): + return mistral_client.Client( + username=request.user.username, + auth_token=request.user.token.id, + project_id=request.user.tenant_id, + # Ideally, we should get it from identity endpoint, but since + # python-mistralclient is not supporting v2.0 API it might create + # additional troubles for those who still rely on v2.0 stack-wise. + auth_url=getattr(settings, 'OPENSTACK_KEYSTONE_URL'), + # Todo: add SECONDARY_ENDPOINT_TYPE support + endpoint_type=getattr(settings, + 'OPENSTACK_ENDPOINT_TYPE', + 'internalURL'), + service_type=SERVICE_TYPE) diff --git a/mistraldashboard/dashboards/mistral/dashboard.py b/mistraldashboard/dashboards/mistral/dashboard.py new file mode 100644 index 0000000..b0e34d2 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/dashboard.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.utils.translation import ugettext_lazy as _ + +import horizon + + +class Default(horizon.Panel): + name = _("Default") + slug = 'default' + urls = 'mistraldashboard.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) diff --git a/mistraldashboard/dashboards/mistral/executions/__init__.py b/mistraldashboard/dashboards/mistral/executions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/dashboards/mistral/executions/panel.py b/mistraldashboard/dashboards/mistral/executions/panel.py new file mode 100644 index 0000000..e42a595 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/executions/panel.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.utils.translation import ugettext_lazy as _ + +import horizon +from mistraldashboard.dashboards.mistral import dashboard + + +class Executions(horizon.Panel): + name = _("Executions") + slug = 'executions' + + +dashboard.MistralDashboard.register(Executions) diff --git a/mistraldashboard/dashboards/mistral/executions/tables.py b/mistraldashboard/dashboards/mistral/executions/tables.py new file mode 100644 index 0000000..4549331 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/executions/tables.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.utils.translation import ugettext_lazy as _ + +from horizon import tables + + +class ExecutionsTable(tables.DataTable): + id = tables.Column("id", + verbose_name=_("ID"), + link=("horizon:mistral:executions:tasks")) + wb_name = tables.Column("workbook_name", verbose_name=_("Workbook")) + state = tables.Column("state", verbose_name=_("State")) + + class Meta: + name = "executions" + verbose_name = _("Executions") + + +class TaskTable(tables.DataTable): + id = tables.Column("id", verbose_name=_("ID")) + name = tables.Column("name", verbose_name=_("Name")) + action = tables.Column("action", verbose_name=_("Action")) + state = tables.Column("state", verbose_name=_("State")) + + class Meta: + name = "tasks" + verbose_name = _("Tasks") diff --git a/mistraldashboard/dashboards/mistral/executions/templates/executions/index.html b/mistraldashboard/dashboards/mistral/executions/templates/executions/index.html new file mode 100644 index 0000000..864a4d4 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/executions/templates/executions/index.html @@ -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=_("Executions") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %} diff --git a/mistraldashboard/dashboards/mistral/executions/urls.py b/mistraldashboard/dashboards/mistral/executions/urls.py new file mode 100644 index 0000000..8d9b875 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/executions/urls.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf.urls import patterns # noqa +from django.conf.urls import url # noqa + +from mistraldashboard.dashboards.mistral.executions.views import IndexView +from mistraldashboard.dashboards.mistral.executions.views import TaskView + +EXECUTIONS = r'^(?P[^/]+)/%s$' + +urlpatterns = patterns( + '', + url(r'^$', IndexView.as_view(), name='index'), + url(EXECUTIONS % 'tasks', TaskView.as_view(), name='tasks'), +) diff --git a/mistraldashboard/dashboards/mistral/executions/views.py b/mistraldashboard/dashboards/mistral/executions/views.py new file mode 100644 index 0000000..f8ecc5f --- /dev/null +++ b/mistraldashboard/dashboards/mistral/executions/views.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from horizon import tables + +from mistraldashboard.dashboards.mistral import api +from mistraldashboard.dashboards.mistral.executions.tables \ + import ExecutionsTable +from mistraldashboard.dashboards.mistral.executions.tables import TaskTable + + +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)] + + +class TaskView(tables.DataTableView): + table_class = TaskTable + 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.tasks.list(wb.name, + self.kwargs['execution_id'])] diff --git a/mistraldashboard/dashboards/mistral/workbooks/__init__.py b/mistraldashboard/dashboards/mistral/workbooks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/dashboards/mistral/workbooks/forms.py b/mistraldashboard/dashboards/mistral/workbooks/forms.py new file mode 100644 index 0000000..5e4a261 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/forms.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse +import yaml + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from mistraldashboard.dashboards.mistral import api + + +class ExecuteForm(forms.SelfHandlingForm): + workbook_name = forms.CharField(label=_("Workbook"), + required=True, + widget=forms.TextInput( + attrs={'readonly': 'readonly'})) + task = forms.ChoiceField(label=_("Task"), + required=True, + help_text=_("Task to start the execution")) + 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) + client = api.mistralclient(request) + workbook_definition = client.workbooks.get_definition( + kwargs['initial']['workbook_name']) + workbook = yaml.safe_load(workbook_definition) + + task_choices = [('', _("Select a task"))] + for task in workbook['Workflow']['tasks']: + task_choices.append((task, task)) + self.fields['task'].choices = task_choices + + 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) diff --git a/mistraldashboard/dashboards/mistral/workbooks/panel.py b/mistraldashboard/dashboards/mistral/workbooks/panel.py new file mode 100644 index 0000000..9065a57 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/panel.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.utils.translation import ugettext_lazy as _ + +import horizon + +from mistraldashboard.dashboards.mistral import dashboard + + +class Workbooks(horizon.Panel): + name = _("Workbooks") + slug = 'workbooks' + + +dashboard.MistralDashboard.register(Workbooks) diff --git a/mistraldashboard/dashboards/mistral/workbooks/tables.py b/mistraldashboard/dashboards/mistral/workbooks/tables.py new file mode 100644 index 0000000..25b3319 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/tables.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.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,) diff --git a/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/_execute.html b/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/_execute.html new file mode 100644 index 0000000..94d3267 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/_execute.html @@ -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 %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description:" %}

+

{% trans "From here you can execute a workbook." %}

+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/execute.html b/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/execute.html new file mode 100644 index 0000000..848d6c9 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/execute.html @@ -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 %} diff --git a/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/index.html b/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/index.html new file mode 100644 index 0000000..a7094d5 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/templates/workbooks/index.html @@ -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 %} diff --git a/mistraldashboard/dashboards/mistral/workbooks/urls.py b/mistraldashboard/dashboards/mistral/workbooks/urls.py new file mode 100644 index 0000000..a262a40 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/urls.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf.urls import patterns # noqa +from django.conf.urls import url # noqa + +from mistraldashboard.dashboards.mistral.workbooks.views import IndexView +from mistraldashboard.dashboards.mistral.workbooks.views import ExecuteView + +WORKBOOKS = r'^(?P[^/]+)/%s$' + +urlpatterns = patterns( + '', + url(r'^$', IndexView.as_view(), name='index'), + url(WORKBOOKS % 'execute', ExecuteView.as_view(), name='execute'), +) diff --git a/mistraldashboard/dashboards/mistral/workbooks/views.py b/mistraldashboard/dashboards/mistral/workbooks/views.py new file mode 100644 index 0000000..71f1840 --- /dev/null +++ b/mistraldashboard/dashboards/mistral/workbooks/views.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2014 - StackStorm, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.core.urlresolvers import reverse_lazy + +from horizon import tables +from horizon import forms + +from mistraldashboard.dashboards.mistral import api +from mistraldashboard.dashboards.mistral.workbooks.tables import WorkbooksTable +from mistraldashboard.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'] + } diff --git a/mistraldashboard/local/__init__.py b/mistraldashboard/local/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mistraldashboard/local/local_settings.py.example b/mistraldashboard/local/local_settings.py.example new file mode 100644 index 0000000..d9960d0 --- /dev/null +++ b/mistraldashboard/local/local_settings.py.example @@ -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')), + ] +} diff --git a/mistraldashboard/models.py b/mistraldashboard/models.py new file mode 100644 index 0000000..1b3d5f9 --- /dev/null +++ b/mistraldashboard/models.py @@ -0,0 +1,3 @@ +""" +Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 +""" diff --git a/mistraldashboard/settings.py b/mistraldashboard/settings.py new file mode 100644 index 0000000..aeadbac --- /dev/null +++ b/mistraldashboard/settings.py @@ -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', + 'mistraldashboard.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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..16f9b48 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +-e git+https://github.com/openstack/horizon.git#egg=horizon +-e git+https://github.com/stackforge/python-mistralclient.git#egg=python-mistralclient +PyYAML>=3.1.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..a4206e9 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,29 @@ +[metadata] +name = mistral-dashboard +summary = Mistral dashboard +description-file = README.rst +license = Apache License, Version 2.0 +classifiers = + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux +author = Openstack Mistral Team +author-email = openstack-dev@lists.openstack.org + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[files] +packages = + mistraldashboard + +[build_sphinx] +source-dir = doc/source +build-dir = doc/build +all_files = 1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..10ec121 --- /dev/null +++ b/setup.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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. + +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools + +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 +try: + import multiprocessing # noqa +except ImportError: + pass + +setuptools.setup( + setup_requires=['pbr'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..2fa2aa7 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,4 @@ +pep8==1.4.5 +flake8==2.0 +sphinx>=1.1.2 +oslosphinx diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..94a3f58 --- /dev/null +++ b/tox.ini @@ -0,0 +1,26 @@ +[tox] +envlist = pep8 +minversion = 1.6 +skipsdist = True + +[testenv] +usedevelop = True +setenv = VIRTUAL_ENV={envdir} + DJANGO_SETTINGS_MODULE=mistraldashboard.settings +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:pep8] +commands = flake8 {posargs} + +[testenv:venv] +commands = {posargs} + +[testenv:docs] +commands = python setup.py build_sphinx + +[flake8] +show-source = true +ignore = H101,H302,H306,H803 +builtins = _ +exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools,local_settings.py