diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..d928527 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,16 @@ +If you would like to contribute to the development of OpenStack, +you must follow the steps in this page: + + http://docs.openstack.org/infra/manual/developers.html + +Once those steps have been completed, changes to OpenStack +should be submitted for review via the Gerrit tool, following +the workflow documented at: + + http://docs.openstack.org/infra/manual/developers.html#development-workflow + +Pull requests submitted through GitHub will be ignored. + +Bugs should be filed on Launchpad, not GitHub: + + https://bugs.launchpad.net/cerberus-dashboard diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 0000000..9566857 --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,4 @@ +cerberus-dashboard Style Commandments +=============================================== + +Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + + 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/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..c978a52 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include AUTHORS +include ChangeLog +exclude .gitignore +exclude .gitreview + +global-exclude *.pyc diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..2b9de29 --- /dev/null +++ b/README.rst @@ -0,0 +1,15 @@ +=============================== +cerberus-dashboard +=============================== + +Dashboard for Openstack Cerberus + +* Free software: Apache license +* Documentation: http://docs.openstack.org/developer/cerberus-dashboard +* Source: http://git.openstack.org/cgit/openstack/cerberus-dashboard +* Bugs: http://bugs.launchpad.net/replace with the name of the project on launchpad + +Features +-------- + +* TODO diff --git a/_cerberus.py.example b/_cerberus.py.example new file mode 100644 index 0000000..9f9a3e2 --- /dev/null +++ b/_cerberus.py.example @@ -0,0 +1,9 @@ +from cerberusdashboard import exceptions + +DASHBOARD = 'security' +ADD_INSTALLED_APPS = ['cerberusdashboard'] +DEFAULT = True +ADD_EXCEPTIONS = { + 'recoverable': exceptions.RECOVERABLE, + 'not_found': exceptions.NOT_FOUND, +} diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 0000000..15cd6cb --- /dev/null +++ b/babel.cfg @@ -0,0 +1,2 @@ +[python: **.py] + diff --git a/cerberusdashboard/__init__.py b/cerberusdashboard/__init__.py new file mode 100644 index 0000000..b7745b2 --- /dev/null +++ b/cerberusdashboard/__init__.py @@ -0,0 +1,19 @@ +# -*- 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 pbr.version + + +__version__ = pbr.version.VersionInfo( + 'cerberus-dashboard').version_string() diff --git a/cerberusdashboard/api/__init__.py b/cerberusdashboard/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/api/cerberus.py b/cerberusdashboard/api/cerberus.py new file mode 100644 index 0000000..bf14052 --- /dev/null +++ b/cerberusdashboard/api/cerberus.py @@ -0,0 +1,132 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +from cerberusclient import client as cerberus_client +from horizon.utils.memoized import memoized # noqa +from openstack_dashboard.api import base +from sticksclient import client as sticks_client + +from cerberusdashboard.utils import importutils + +keystone = importutils.import_any('openstack_dashboard.api.keystone', + 'horizon.api.keystone') + +LOG = logging.getLogger(__name__) + + +@memoized +def cerberusclient(request): + """Initialization of Cerberus client.""" + cerberus_endpoint = base.url_for(request, 'security') + return cerberus_client.Client('1', + cerberus_endpoint, + tenant_id=request.user.tenant_id, + token=request.user.token.id) + + +@memoized +def sticksclient(request): + """Initialization of Cerberus client.""" + sticks_endpoint = base.url_for(request, 'helpdesk') + return sticks_client.Client('1', + sticks_endpoint, + tenant_id=request.user.tenant_id, + token=request.user.token.id) + + +def plugin_list(request): + """List plugins.""" + return cerberusclient(request).plugins.list() + + +def plugin_get(request, plugin_id): + """Get plugin information.""" + return cerberusclient(request).plugins.get(plugin_id) + + +def task_list(request): + """List tasks.""" + return cerberusclient(request).tasks.list() + + +def task_get(request, task_id): + """Get specific task.""" + return cerberusclient(request).tasks.get(task_id) + + +def task_create(request, plugin_id, method, name='unknown', type='unique', + period=None): + """Create a task.""" + return cerberusclient(request).tasks.create( + plugin_id, method, name, type, period) + + +def task_stop(request, task_id): + """Stop specific task.""" + return cerberusclient(request).tasks.stop(task_id) + + +def task_restart(request, task_id): + """Stop specific task.""" + return cerberusclient(request).tasks.restart(task_id) + + +def task_delete(request, task_id): + """Delete specific task.""" + return cerberusclient(request).tasks.delete(task_id) + + +def task_force_delete(request, task_id): + """Force delete specific task.""" + return cerberusclient(request).tasks.force_delete(task_id) + + +def security_report_list(request): + """List the security reports.""" + return cerberusclient(request).security_reports.list() + + +def security_report_get(request, sr_id): + """Get a security report.""" + return cerberusclient(request).security_reports.get(sr_id) + + +def security_report_put_ticket_id(request, sr_id, ticket_id): + """Update a security report by adding its associated ticket id.""" + return cerberusclient(request).security_reports.put(sr_id, ticket_id) + + +def security_alarm_list(request): + """List the security alarms.""" + return cerberusclient(request).security_alarms.list() + + +def security_alarm_get(request, security_alarm_id): + """Get a security alarm.""" + + return cerberusclient(request).security_alarms.get(security_alarm_id) + + +def security_alarm_put_ticket_id(request, sa_id, ticket_id): + """Update a security report by adding its associated ticket id.""" + return cerberusclient(request).security_alarms.put(sa_id, ticket_id) + + +def ticket_create(request, data): + """Create a ticket from a security report.""" + return sticksclient(request).tickets.create(data) diff --git a/cerberusdashboard/dashboard.py b/cerberusdashboard/dashboard.py new file mode 100644 index 0000000..ebe3214 --- /dev/null +++ b/cerberusdashboard/dashboard.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 as _ + +import horizon + + +class Cerberus(horizon.Dashboard): + name = _("Security") + slug = "security" + panels = ("security_reports", "tasks", "plugins", "security_alarms") + default_panel = 'security_reports' + nav = True + supports_tenants = True + +horizon.register(Cerberus) diff --git a/cerberusdashboard/exceptions.py b/cerberusdashboard/exceptions.py new file mode 100644 index 0000000..6d30602 --- /dev/null +++ b/cerberusdashboard/exceptions.py @@ -0,0 +1,23 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 cerberusclient.common import exceptions as cerberusclient +from sticksclient.common import exceptions as sticksclient + +NOT_FOUND = (cerberusclient.HTTPNotFound,) +# HTTPInternalServerError is thrown by Redmine when project does not exist +# This error may change in the future (refer to sticks client) +RECOVERABLE = (sticksclient.HTTPInternalServerError,) diff --git a/cerberusdashboard/openstack/__init__.py b/cerberusdashboard/openstack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/openstack/common/__init__.py b/cerberusdashboard/openstack/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/openstack/common/importutils.py b/cerberusdashboard/openstack/common/importutils.py new file mode 100644 index 0000000..d75f69f --- /dev/null +++ b/cerberusdashboard/openstack/common/importutils.py @@ -0,0 +1,73 @@ +# Copyright 2011 OpenStack Foundation. +# All Rights Reserved. +# +# 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 related utilities and helper functions. +""" + +import sys +import traceback + + +def import_class(import_str): + """Returns a class from a string including module and class.""" + mod_str, _sep, class_str = import_str.rpartition('.') + try: + __import__(mod_str) + return getattr(sys.modules[mod_str], class_str) + except (ValueError, AttributeError): + raise ImportError('Class %s cannot be found (%s)' % + (class_str, + traceback.format_exception(*sys.exc_info()))) + + +def import_object(import_str, *args, **kwargs): + """Import a class and return an instance of it.""" + return import_class(import_str)(*args, **kwargs) + + +def import_object_ns(name_space, import_str, *args, **kwargs): + """Tries to import object from default namespace. + + Imports a class and return an instance of it, first by trying + to find the class in a default namespace, then failing back to + a full path if not found in the default namespace. + """ + import_value = "%s.%s" % (name_space, import_str) + try: + return import_class(import_value)(*args, **kwargs) + except ImportError: + return import_class(import_str)(*args, **kwargs) + + +def import_module(import_str): + """Import a module.""" + __import__(import_str) + return sys.modules[import_str] + + +def import_versioned_module(version, submodule=None): + module = 'solumdashboard.v%s' % version + if submodule: + module = '.'.join((module, submodule)) + return import_module(module) + + +def try_import(import_str, default=None): + """Try to import a module and if it fails return default.""" + try: + return import_module(import_str) + except ImportError: + return default diff --git a/cerberusdashboard/plugins/__init__.py b/cerberusdashboard/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/plugins/panel.py b/cerberusdashboard/plugins/panel.py new file mode 100644 index 0000000..54d25ce --- /dev/null +++ b/cerberusdashboard/plugins/panel.py @@ -0,0 +1,28 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 cerberusdashboard import dashboard + + +class PluginsPanel(horizon.Panel): + name = _("Plugins") + slug = 'plugins' + permissions = ('openstack.roles.admin',) + +dashboard.Cerberus.register(PluginsPanel) diff --git a/cerberusdashboard/plugins/tables.py b/cerberusdashboard/plugins/tables.py new file mode 100644 index 0000000..8b6b058 --- /dev/null +++ b/cerberusdashboard/plugins/tables.py @@ -0,0 +1,38 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 PluginsTable(tables.DataTable): + uuid = tables.Column("uuid", + link=("horizon:security:plugins:details"), + verbose_name="uuid",) + name = tables.Column("name", verbose_name=_("Name")) + version = tables.Column("version", verbose_name=_("Version")) + provider = tables.Column("provider", verbose_name=_("Provider")) + type = tables.Column("type", verbose_name=_("Type")) + description = tables.Column("description", verbose_name=_("Description"), + truncate=30) + tool_name = tables.Column("tool_name", verbose_name=_("Tool name")) + + def get_object_id(self, datum): + return datum.uuid + + class Meta: + name = "plugins" + verbose_name = _("Plugins") diff --git a/cerberusdashboard/plugins/tabs.py b/cerberusdashboard/plugins/tabs.py new file mode 100644 index 0000000..9c74c1c --- /dev/null +++ b/cerberusdashboard/plugins/tabs.py @@ -0,0 +1,33 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 tabs + + +class OverviewTab(tabs.Tab): + name = _("Overview") + slug = "overview" + template_name = "plugins/_detail_overview.html" + + def get_context_data(self, request): + return {"plugin": self.tab_group.kwargs['plugin']} + + +class PluginDetailTabs(tabs.TabGroup): + slug = "plugin_details" + tabs = (OverviewTab,) diff --git a/cerberusdashboard/plugins/urls.py b/cerberusdashboard/plugins/urls.py new file mode 100644 index 0000000..ac88e06 --- /dev/null +++ b/cerberusdashboard/plugins/urls.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 +from django.conf.urls import url + +import cerberusdashboard.plugins.views as views + + +urlpatterns = patterns('', + url(r'^$', views.IndexView.as_view(), + name='index'), + url(r'^$', views.IndexView.as_view(), + name='plugins'), + url(r'^(?P[^/]+)$', + views.DetailView.as_view(), + name='details'),) diff --git a/cerberusdashboard/plugins/views.py b/cerberusdashboard/plugins/views.py new file mode 100644 index 0000000..178cc35 --- /dev/null +++ b/cerberusdashboard/plugins/views.py @@ -0,0 +1,78 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 django.utils.translation import ugettext_lazy as _ +import logging + +from horizon import exceptions +from horizon import tables +from horizon import tabs + +from cerberusdashboard import api +from cerberusdashboard.plugins import tables as p_tables +from cerberusdashboard.plugins import tabs as p_tabs + + +LOG = logging.getLogger(__name__) + + +class IndexView(tables.DataTableView): + table_class = p_tables.PluginsTable + template_name = 'plugins/index.html' + + @staticmethod + def get_redirect_url(): + return reverse_lazy('horizon:security:plugins:index') + + def get_data(self): + try: + plugins = api.cerberus.plugin_list(self.request) + except Exception as e: + LOG.exception(e) + exceptions.handle(self.request, + _('Unable to retrieve plugins details.'), + redirect=self.get_redirect_url()) + return plugins + + +class DetailView(tabs.TabView): + tab_group_class = p_tabs.PluginDetailTabs + template_name = 'plugins/detail.html' + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + plugin = self.get_data() + context["plugin"] = plugin + return context + + def get_data(self): + try: + return api.cerberus.plugin_get(self.request, + self.kwargs['plugin_id']) + except Exception as e: + LOG.exception(e) + exceptions.handle(self.request, + _('Unable to retrieve plugin details.'), + redirect=self.get_redirect_url()) + + @staticmethod + def get_redirect_url(): + return reverse_lazy('horizon:security:plugins:index') + + def get_tabs(self, request, *args, **kwargs): + plugin = self.get_data() + return self.tab_group_class(request, plugin=plugin, **kwargs) diff --git a/cerberusdashboard/security_alarms/__init__.py b/cerberusdashboard/security_alarms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/security_alarms/panel.py b/cerberusdashboard/security_alarms/panel.py new file mode 100644 index 0000000..4a4393f --- /dev/null +++ b/cerberusdashboard/security_alarms/panel.py @@ -0,0 +1,28 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 cerberusdashboard import dashboard + + +class SecurityAlarmsPanel(horizon.Panel): + name = _("Security Alarms") + slug = 'security_alarms' + permissions = ('openstack.roles.admin',) + +dashboard.Cerberus.register(SecurityAlarmsPanel) diff --git a/cerberusdashboard/security_alarms/tables.py b/cerberusdashboard/security_alarms/tables.py new file mode 100644 index 0000000..9713b5e --- /dev/null +++ b/cerberusdashboard/security_alarms/tables.py @@ -0,0 +1,113 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +from django.core import urlresolvers +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import tables + +from cerberusdashboard import api + +LOG = logging.getLogger(__name__) + + +def is_associated(alarm): + ticket_id = getattr(alarm, "ticket_id", None) + if not ticket_id: + return False + else: + return True + + +class CreateTicket(tables.BatchAction): + name = "create_ticket" + action_present = _("Create") + action_past = _("%(data_type)s created") + data_type_singular = _("Ticket") + data_type_plural = _("Tickets") + classes = ('btn-danger', 'btn-terminate') + failure_url = urlresolvers.reverse_lazy( + 'horizon:security:security_alarms:index') + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} + + def allowed(self, request, alarm=None): + """Allow terminate action if instance not currently being deleted.""" + return not(is_associated(alarm)) + + def action(self, request, alarm_id): + alarm = api.cerberus.security_alarm_get(request, alarm_id) + if alarm.project_id is not None: + data = {'project': alarm.project_id, 'title': alarm.summary} + else: + data = {'project': 'infra', 'title': alarm.summary} + try: + ticket = api.cerberus.ticket_create(request, data) + api.cerberus.security_alarm_put_ticket_id(request, + alarm.alarm_id, + ticket.id) + except Exception as e: + LOG.exception(e) + exceptions.handle(request, + _("Unable to create ticket."), + redirect=self.failure_url) + + +class ShowTicket(tables.LinkAction): + name = "show_ticket" + verbose_name = _("Show ticket") + url = "horizon:helpdesk:tickets:details" + classes = ("btn-edit",) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} + + def allowed(self, request, alarm=None): + """Allow terminate action if instance not currently being deleted.""" + return is_associated(alarm) + + def get_link_url(self, alarm=None): + return urlresolvers.reverse(self.url, args=(alarm.ticket_id,)) + + +class SecurityAlarmsTable(tables.DataTable): + alarm_id = tables.Column("alarm_id", + link=("horizon:security:security_alarms:" + "details"), + verbose_name="id",) + summary = tables.Column("summary", verbose_name=_("Summary")) + description = tables.Column("description", verbose_name=_("Description"), + truncate=30) + project_id = tables.Column("project_id", verbose_name=_("Project")) + plugin_id = tables.Column("plugin_id", verbose_name=_("Plugin")) + received_at = tables.Column("timestamp", verbose_name=_("Received at")) + + def get_object_id(self, datum): + return datum.alarm_id + + class Meta: + name = "security_alarms" + verbose_name = _("Security Alarms") + row_actions = (CreateTicket, ShowTicket) diff --git a/cerberusdashboard/security_alarms/tabs.py b/cerberusdashboard/security_alarms/tabs.py new file mode 100644 index 0000000..afadd4b --- /dev/null +++ b/cerberusdashboard/security_alarms/tabs.py @@ -0,0 +1,34 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 tabs + + +class OverviewTab(tabs.Tab): + name = _("Overview") + slug = "overview" + template_name = ("security_alarms/_detail_overview.html") + + def get_context_data(self, request, **kwargs): + return {"security_alarm": self.tab_group.kwargs['security_alarm']} + + +class DetailsTabs(tabs.TabGroup): + slug = "security_alarm_details" + sticky = True + tabs = (OverviewTab,) diff --git a/cerberusdashboard/security_alarms/urls.py b/cerberusdashboard/security_alarms/urls.py new file mode 100644 index 0000000..c2fa6d7 --- /dev/null +++ b/cerberusdashboard/security_alarms/urls.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 +from django.conf.urls import url + +import cerberusdashboard.security_alarms.views as views + + +urlpatterns = patterns('', + url(r'^$', views.IndexView.as_view(), + name='index'), + url(r'^$', views.IndexView.as_view(), + name='security_alarms'), + url(r'^(?P[^/]+)$', + views.DetailView.as_view(), + name='details'),) diff --git a/cerberusdashboard/security_alarms/views.py b/cerberusdashboard/security_alarms/views.py new file mode 100644 index 0000000..8748b63 --- /dev/null +++ b/cerberusdashboard/security_alarms/views.py @@ -0,0 +1,77 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +from django.core.urlresolvers import reverse_lazy +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import tables +from horizon import tabs + +from cerberusdashboard.api import cerberus +from cerberusdashboard.security_alarms import tables as s_tables +from cerberusdashboard.security_alarms import tabs as s_tabs + + +LOG = logging.getLogger(__name__) + + +class IndexView(tables.DataTableView): + table_class = s_tables.SecurityAlarmsTable + template_name = 'security_alarms/index.html' + + def get_data(self): + try: + security_alarms = cerberus.security_alarm_list(self.request) + except Exception: + raise + return security_alarms + + +class DetailView(tabs.TabbedTableView): + tab_group_class = s_tabs.DetailsTabs + template_name = 'security_alarms/detail.html' + redirect_url = 'security_alarms:index' + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + context["security_alarm"] = self.get_data() + return context + + def get_data(self): + alarm_id = self.kwargs['alarm_id'] + try: + security_alarm = cerberus.security_alarm_get( + self.request, alarm_id) + except Exception as e: + LOG.exception(e) + redirect = reverse_lazy(self.redirect_url) + exceptions.handle(self.request, + _('Unable to retrieve details for ' + 'security alarm "%s".') % alarm_id, + redirect=redirect) + # Not all exception types handled above will result in a redirect. + # Need to raise here just in case. + raise exceptions.Http302(redirect) + + return security_alarm + + def get_tabs(self, request, *args, **kwargs): + security_alarm = self.get_data() + return self.tab_group_class(request, + security_alarm=security_alarm, + **kwargs) diff --git a/cerberusdashboard/security_reports/__init__.py b/cerberusdashboard/security_reports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/security_reports/panel.py b/cerberusdashboard/security_reports/panel.py new file mode 100644 index 0000000..fde7c5c --- /dev/null +++ b/cerberusdashboard/security_reports/panel.py @@ -0,0 +1,27 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 cerberusdashboard import dashboard + + +class SecurityReportsPanel(horizon.Panel): + name = _("Security Reports") + slug = 'security_reports' + +dashboard.Cerberus.register(SecurityReportsPanel) diff --git a/cerberusdashboard/security_reports/tables.py b/cerberusdashboard/security_reports/tables.py new file mode 100644 index 0000000..37eb46a --- /dev/null +++ b/cerberusdashboard/security_reports/tables.py @@ -0,0 +1,137 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +from django.core import urlresolvers +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import tables + +from cerberusdashboard import api + + +LOG = logging.getLogger(__name__) + + +def is_associated(report): + ticket_id = getattr(report, "ticket_id", None) + if not ticket_id: + return False + else: + return True + + +class CreateTicket(tables.BatchAction): + name = "create_ticket" + action_present = _("Create") + action_past = _("%(data_type)s created") + data_type_singular = _("Ticket") + data_type_plural = _("Tickets") + classes = ('btn-danger', 'btn-terminate') + failure_url = urlresolvers.reverse_lazy( + 'horizon:security:security_reports:index') + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} + + def allowed(self, request, report=None): + """Allow terminate action if instance not currently being deleted.""" + return not(is_associated(report)) + + def action(self, request, report_id): + report = api.cerberus.security_report_get(request, report_id) + data = {'project': report.project_id, 'title': report.title} + try: + ticket = api.cerberus.ticket_create(request, data) + api.cerberus.security_report_put_ticket_id(request, + report_id, + ticket.id) + except Exception as e: + LOG.exception(e) + exceptions.handle(request, + _("Unable to create ticket."), + redirect=self.failure_url) + + +class ShowTicket(tables.LinkAction): + name = "show_ticket" + verbose_name = _("Show ticket") + url = "horizon:helpdesk:tickets:details" + classes = ("btn-edit",) + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} + + def allowed(self, request, report=None): + """Allow terminate action if instance not currently being deleted.""" + return is_associated(report) + + def get_link_url(self, report=None): + return urlresolvers.reverse(self.url, args=(report.ticket_id,)) + + +class SecurityReportsTable(tables.DataTable): + report_id = tables.Column("id", + link=("horizon:security:security_reports:" + "details"), + verbose_name="id",) + title = tables.Column("title", verbose_name=_("Title")) + description = tables.Column("description", verbose_name=_("Description")) + plugin_id = tables.Column("plugin_id", verbose_name=_("Plugin")) + component_name = tables.Column("component_name", + verbose_name=_("Component name")) + description = tables.Column("description", verbose_name=_("Description"), + truncate=30) + vulns = tables.Column("vulnerabilities_number", + verbose_name=_("Number of vulnerabilities")) + security_rating = tables.Column("security_rating", + verbose_name=_("Security rating")) + last_date = tables.Column("last_report_date", verbose_name=_("Scan date")) + project_id = tables.Column("project_id", verbose_name=_("Project")) + + class Meta: + name = "security_reports" + verbose_name = _("Security Reports") + row_actions = (CreateTicket, ShowTicket) + + +class VulnerabilitiesTable(tables.DataTable): + service = tables.Column("service", + verbose_name=_("Service")) + + family = tables.Column("family", verbose_name=_("Family")) + + name = tables.Column("name", verbose_name=_("Name")) + + risk = tables.Column("score", + verbose_name=_("Risk")) + + vuln_state = tables.Column('vuln_state', + verbose_name=_("State")) + + def get_object_id(self, vulnerability): + return vulnerability.get('id', None) + + class Meta: + name = "vulnerabilities" + verbose_name = _("Vulnerabilities") diff --git a/cerberusdashboard/security_reports/tabs.py b/cerberusdashboard/security_reports/tabs.py new file mode 100644 index 0000000..9232e3a --- /dev/null +++ b/cerberusdashboard/security_reports/tabs.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 json + +from horizon import tabs + +from cerberusdashboard.security_reports import tables + + +class OverviewTab(tabs.Tab): + name = _("Overview") + slug = "overview" + template_name = ("security_reports/_detail_overview.html") + + def get_context_data(self, request, **kwargs): + return {"security_report": self.tab_group.kwargs['security_report']} + + +class VulnerabilitiesTab(tabs.TableTab): + table_classes = (tables.VulnerabilitiesTable,) + name = _("Vulnerabilities") + slug = "vulnerabilities" + template_name = "horizon/common/_detail_table.html" + + def get_vulnerabilities_data(self): + vulnerabilities = self.tab_group.kwargs[ + 'security_report'].vulnerabilities + + return json.loads(vulnerabilities).values() + + +class DetailsTabs(tabs.TabGroup): + slug = "security_report_details" + sticky = True + tabs = (OverviewTab, VulnerabilitiesTab,) diff --git a/cerberusdashboard/security_reports/urls.py b/cerberusdashboard/security_reports/urls.py new file mode 100644 index 0000000..f138cf1 --- /dev/null +++ b/cerberusdashboard/security_reports/urls.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 +from django.conf.urls import url + +import cerberusdashboard.security_reports.views as views + + +urlpatterns = patterns('', + url(r'^$', views.IndexView.as_view(), + name='index'), + url(r'^$', views.IndexView.as_view(), + name='security_reports'), + url(r'^(?P[^/]+)$', + views.DetailView.as_view(), + name='details'),) diff --git a/cerberusdashboard/security_reports/views.py b/cerberusdashboard/security_reports/views.py new file mode 100644 index 0000000..55b4e5a --- /dev/null +++ b/cerberusdashboard/security_reports/views.py @@ -0,0 +1,76 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import logging + +from django.core.urlresolvers import reverse_lazy +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import tables +from horizon import tabs + +from cerberusdashboard.api import cerberus +from cerberusdashboard.security_reports import tables as s_tables +from cerberusdashboard.security_reports import tabs as s_tabs + + +LOG = logging.getLogger(__name__) + + +class IndexView(tables.DataTableView): + table_class = s_tables.SecurityReportsTable + template_name = 'security_reports/index.html' + + def get_data(self): + try: + security_reports = cerberus.security_report_list(self.request) + except Exception: + raise + return security_reports + + +class DetailView(tabs.TabbedTableView): + tab_group_class = s_tabs.DetailsTabs + template_name = 'security_reports/detail.html' + redirect_url = 'horizon/security/security_reports:index' + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + context["security_report"] = self.get_data() + return context + + def get_data(self): + report_id = self.kwargs['report_id'] + try: + security_report = cerberus.security_report_get( + self.request, report_id) + except Exception as e: + LOG.exception(e) + redirect = reverse_lazy(self.redirect_url) + exceptions.handle(self.request, + _('Unable to retrieve details for ' + 'security report "%s".') % report_id, + redirect=redirect) + # Not all exception types handled above will result in a redirect. + # Need to raise here just in case. + raise exceptions.Http302(redirect) + + return security_report + + def get_tabs(self, request, *args, **kwargs): + security_report = self.get_data() + return self.tab_group_class(request, + security_report=security_report, + **kwargs) diff --git a/cerberusdashboard/tasks/__init__.py b/cerberusdashboard/tasks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/tasks/panel.py b/cerberusdashboard/tasks/panel.py new file mode 100644 index 0000000..08e02f4 --- /dev/null +++ b/cerberusdashboard/tasks/panel.py @@ -0,0 +1,28 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 cerberusdashboard import dashboard + + +class TasksPanel(horizon.Panel): + name = _("Tasks") + slug = 'tasks' + permissions = ('openstack.roles.admin',) + +dashboard.Cerberus.register(TasksPanel) diff --git a/cerberusdashboard/tasks/tables.py b/cerberusdashboard/tasks/tables.py new file mode 100644 index 0000000..ab63151 --- /dev/null +++ b/cerberusdashboard/tasks/tables.py @@ -0,0 +1,132 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +from django.core import urlresolvers +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import tables + +from cerberusdashboard import api + + +LOG = logging.getLogger(__name__) + + +def is_recurrent(task): + type = getattr(task, "type", None) + if not type: + return False + return type.lower() == "recurrent" + + +def is_stopped(task): + state = getattr(task, "state", None) + if not state: + return False + return state.lower() == "stopped" + + +class DeleteTasks(tables.BatchAction): + name = "delete" + action_present = _("Delete") + action_past = _("%(data_type)s deleted") + data_type_singular = _("Task") + data_type_plural = _("Tasks") + classes = ('btn-danger') + failure_url = urlresolvers.reverse_lazy( + 'horizon:security:tasks:index') + + def allowed(self, request, task=None): + """Allow terminate action if instance not currently being deleted.""" + return is_recurrent(task) + + def action(self, request, obj_id): + try: + api.cerberus.task_delete(request, obj_id) + except Exception as e: + LOG.exception(e) + exceptions.handle(request, + _("Unable to delete ticket."), + redirect=self.failure_url) + + +class RestartTasks(tables.BatchAction): + name = "restart" + action_present = _("Restart") + action_past = _("%(data_type)s restarted") + data_type_singular = _("Task") + data_type_plural = _("Tasks") + classes = ('btn-danger') + failure_url = urlresolvers.reverse_lazy( + 'horizon:security:tasks:index') + + def allowed(self, request, task=None): + """Allow terminate action if instance not currently being deleted.""" + return is_recurrent(task) and is_stopped(task) + + def action(self, request, obj_id): + try: + api.cerberus.task_restart(request, obj_id) + except Exception as e: + LOG.exception(e) + exceptions.handle(request, + _("Unable to restart ticket."), + redirect=self.failure_url) + + +class StopTasks(tables.BatchAction): + name = "stop" + action_present = _("Stop") + action_past = _("%(data_type)s stopped") + data_type_singular = _("Task") + data_type_plural = _("Tasks") + classes = ('btn-danger', 'btn-terminate') + failure_url = urlresolvers.reverse_lazy( + 'horizon:security:tasks:index') + + def get_policy_target(self, request, datum=None): + project_id = None + if datum: + project_id = getattr(datum, 'tenant_id', None) + return {"project_id": project_id} + + def action(self, request, obj_id): + try: + api.cerberus.task_stop(request, obj_id) + except Exception as e: + LOG.exception(e) + exceptions.handle(request, + _("Unable to stop task."), + redirect=self.failure_url) + + +class TasksTable(tables.DataTable): + id = tables.Column("id", + link=("horizon:security:tasks:details"), + verbose_name="id",) + name = tables.Column("name", verbose_name=_("Name")) + type = tables.Column("type", verbose_name=_("Type")) + period = tables.Column("period", verbose_name=_("Period")) + state = tables.Column("state", verbose_name=_("State")) + plugin_id = tables.Column("plugin_id", verbose_name=_("Plugin")) + + class Meta: + name = "tasks" + verbose_name = _("Tasks") + table_actions = (DeleteTasks, StopTasks, RestartTasks) + row_actions = (DeleteTasks, StopTasks, RestartTasks) diff --git a/cerberusdashboard/tasks/tabs.py b/cerberusdashboard/tasks/tabs.py new file mode 100644 index 0000000..b91174e --- /dev/null +++ b/cerberusdashboard/tasks/tabs.py @@ -0,0 +1,33 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 tabs + + +class OverviewTab(tabs.Tab): + name = _("Overview") + slug = "overview" + template_name = ("tasks/_detail_overview.html") + + def get_context_data(self, request, **kwargs): + return {"task": self.tab_group.kwargs['task']} + + +class DetailsTabs(tabs.TabGroup): + slug = "task_details" + sticky = True + tabs = (OverviewTab,) diff --git a/cerberusdashboard/tasks/urls.py b/cerberusdashboard/tasks/urls.py new file mode 100644 index 0000000..d101a16 --- /dev/null +++ b/cerberusdashboard/tasks/urls.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 +from django.conf.urls import url + +import cerberusdashboard.tasks.views as views + + +urlpatterns = patterns('', + url(r'^$', views.IndexView.as_view(), + name='index'), + url(r'^$', views.IndexView.as_view(), + name='tasks'), + url(r'^(?P[^/]+)$', + views.DetailView.as_view(), + name='details'),) diff --git a/cerberusdashboard/tasks/views.py b/cerberusdashboard/tasks/views.py new file mode 100644 index 0000000..e31fde9 --- /dev/null +++ b/cerberusdashboard/tasks/views.py @@ -0,0 +1,73 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 +from django.utils.translation import ugettext_lazy as _ +import logging + +from horizon import exceptions +from horizon import tables +from horizon import tabs + +from cerberusdashboard import api +from cerberusdashboard.tasks import tables as t_tables +from cerberusdashboard.tasks import tabs as t_tabs + + +LOG = logging.getLogger(__name__) + + +class IndexView(tables.DataTableView): + table_class = t_tables.TasksTable + template_name = 'tasks/index.html' + + def get_data(self): + try: + tasks = api.cerberus.task_list(self.request) + except Exception: + raise + return tasks + + +class DetailView(tabs.TabbedTableView): + tab_group_class = t_tabs.DetailsTabs + template_name = 'tasks/detail.html' + redirect_url = 'horizon:security:tasks:index' + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + context["task"] = self.get_data() + return context + + def get_data(self): + id = self.kwargs['id'] + try: + task = api.cerberus.task_get(self.request, id) + except Exception as e: + LOG.exception(e) + redirect = reverse(self.redirect_url) + exceptions.handle(self.request, + _('Unable to retrieve details for ' + 'task "%s".') % id, + redirect=redirect) + # Not all exception types handled above will result in a redirect. + # Need to raise here just in case. + raise exceptions.Http302(redirect) + + return task + + def get_tabs(self, request, *args, **kwargs): + task = self.get_data() + return self.tab_group_class(request, task=task, **kwargs) diff --git a/cerberusdashboard/templates/plugins/_detail_overview.html b/cerberusdashboard/templates/plugins/_detail_overview.html new file mode 100644 index 0000000..e84b926 --- /dev/null +++ b/cerberusdashboard/templates/plugins/_detail_overview.html @@ -0,0 +1,27 @@ +{% load i18n sizeformat %} +{% load url from future %} + +

{% trans "Plugin" %}

+ +
+

{% trans "Overview" %}

+
+
+
{% trans "Name" %}
+
{{plugin.name}}
+
{% trans "Version" %}
+
{{plugin.version}}
+
{% trans "uuid" %}
+
{{plugin.uuid}}
+
{% trans "Description" %}
+
{{plugin.description}}
+
{% trans "Subscribed events" %}
+ {% for event in plugin.subscribed_events %} +
{{event}}
+ {% endfor %} +
{% trans "Task methods" %}
+ {% for method in plugin.methods %} +
{{method}}
+ {% endfor %} +
+
\ No newline at end of file diff --git a/cerberusdashboard/templates/plugins/detail.html b/cerberusdashboard/templates/plugins/detail.html new file mode 100644 index 0000000..72c5fc8 --- /dev/null +++ b/cerberusdashboard/templates/plugins/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Plugin Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Plugin Details:")%} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/cerberusdashboard/templates/plugins/index.html b/cerberusdashboard/templates/plugins/index.html new file mode 100644 index 0000000..977b5ad --- /dev/null +++ b/cerberusdashboard/templates/plugins/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Plugins" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Plugins") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %} diff --git a/cerberusdashboard/templates/security_alarms/_detail_overview.html b/cerberusdashboard/templates/security_alarms/_detail_overview.html new file mode 100644 index 0000000..9da5925 --- /dev/null +++ b/cerberusdashboard/templates/security_alarms/_detail_overview.html @@ -0,0 +1,28 @@ +{% load i18n sizeformat %} +{% load url from future %} + +

{% trans "Security alarm" %}

+ +
+

{% trans "Overview" %}

+
+
+
{% trans "ID" %}
+
{{ security_alarm.id }}
+
{% trans "status" %}
+
{{ security_alarm.status }}
+
{% trans "severity" %}
+
{{ security_alarm.severity }}
+
{% trans "Summary" %}
+
{{ security_alarm.summary }}
+
{% trans "Description" %}
+
{{ security_alarm.description }}
+
{% trans "Component id" %}
+
{{ security_alarm.component_id }}
+
{% trans "Received at" %}
+
{{ security_alarm.timestamp }}
+
{% trans "Plugin" %}
+
+ {{ security_alarm.plugin_id }}
+
+
\ No newline at end of file diff --git a/cerberusdashboard/templates/security_alarms/detail.html b/cerberusdashboard/templates/security_alarms/detail.html new file mode 100644 index 0000000..a7365d5 --- /dev/null +++ b/cerberusdashboard/templates/security_alarms/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Security Alarm Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Security Alarm Details: ")|add:security_alarm.id %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/cerberusdashboard/templates/security_alarms/index.html b/cerberusdashboard/templates/security_alarms/index.html new file mode 100644 index 0000000..7a1c1bb --- /dev/null +++ b/cerberusdashboard/templates/security_alarms/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Security Alarms" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Security Alarms") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %} diff --git a/cerberusdashboard/templates/security_reports/_detail_overview.html b/cerberusdashboard/templates/security_reports/_detail_overview.html new file mode 100644 index 0000000..7bc595f --- /dev/null +++ b/cerberusdashboard/templates/security_reports/_detail_overview.html @@ -0,0 +1,41 @@ +{% load i18n sizeformat %} +{% load url from future %} + +

{% trans "Security report" %}

+ +
+

{% trans "Overview" %}

+
+
+
{% trans "ID" %}
+
{{ security_report.id }}
+
{% trans "Title" %}
+
{{ security_report.title }}
+
{% trans "Description" %}
+
{{ security_report.description }}
+
{% trans "Component type" %}
+
{{ security_report.component_type }}
+ {% if security_report.component_type == 'instance' %} + {% url 'horizon:project:instances:detail' security_report.component_id as component_url %} + {% endif %} +
{% trans "Component id" %}
+
+ {% if security_report.component_type == 'instance' %} + {{ security_report.component_id }} + {% else %} + {{ security_report.component_id }} + {% endif %} + +
+
{% trans "Security rating" %}
+
{{ security_report.security_rating }}
+
{% trans "Plugin" %}
+
+ {{ security_report.plugin_id }}
+
{% trans "Associated ticket" %}
+ {% if security_report.ticket_id != None %} +
+ {{ security_report.ticket_id }}
+ {% endif %} +
+
\ No newline at end of file diff --git a/cerberusdashboard/templates/security_reports/detail.html b/cerberusdashboard/templates/security_reports/detail.html new file mode 100644 index 0000000..82f2fe4 --- /dev/null +++ b/cerberusdashboard/templates/security_reports/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Security Report Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Security Report Details: ")|add:security_report.id %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/cerberusdashboard/templates/security_reports/index.html b/cerberusdashboard/templates/security_reports/index.html new file mode 100644 index 0000000..ef6a126 --- /dev/null +++ b/cerberusdashboard/templates/security_reports/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Security Reports" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Security Reports") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %} diff --git a/cerberusdashboard/templates/tasks/_detail_overview.html b/cerberusdashboard/templates/tasks/_detail_overview.html new file mode 100644 index 0000000..df348e3 --- /dev/null +++ b/cerberusdashboard/templates/tasks/_detail_overview.html @@ -0,0 +1,19 @@ +{% load i18n sizeformat %} +{% load url from future %} + +

{% trans "Task" %}

+ +
+

{% trans "Overview" %}

+
+
+
{% trans "ID" %}
+
{{ task.id }}
+
{% trans "Type" %}
+
{{ task.type }}
+
{% trans "Plugin" %}
+
{{ task.plugin_id }}
+
{% trans "State" %}
+
{{ task.state }}
+
+
\ No newline at end of file diff --git a/cerberusdashboard/templates/tasks/detail.html b/cerberusdashboard/templates/tasks/detail.html new file mode 100644 index 0000000..3559272 --- /dev/null +++ b/cerberusdashboard/templates/tasks/detail.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Task Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Task Details:") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} diff --git a/cerberusdashboard/templates/tasks/index.html b/cerberusdashboard/templates/tasks/index.html new file mode 100644 index 0000000..545814d --- /dev/null +++ b/cerberusdashboard/templates/tasks/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Tasks" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Tasks") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %} diff --git a/cerberusdashboard/tests/__init__.py b/cerberusdashboard/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/tests/base.py b/cerberusdashboard/tests/base.py new file mode 100644 index 0000000..1c30cdb --- /dev/null +++ b/cerberusdashboard/tests/base.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +# Copyright 2010-2011 OpenStack Foundation +# 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. + +from oslotest import base + + +class TestCase(base.BaseTestCase): + + """Test case base class for all unit tests.""" diff --git a/cerberusdashboard/tests/test_cerberus-dashboard.py b/cerberusdashboard/tests/test_cerberus-dashboard.py new file mode 100644 index 0000000..426e4c8 --- /dev/null +++ b/cerberusdashboard/tests/test_cerberus-dashboard.py @@ -0,0 +1,28 @@ +# -*- 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. + +""" +test_cerberus-dashboard +---------------------------------- + +Tests for `cerberus-dashboard` module. +""" + +from cerberusdashboard.tests import base + + +class TestCerberusDashboard(base.TestCase): + + def test_something(self): + pass diff --git a/cerberusdashboard/utils/__init__.py b/cerberusdashboard/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cerberusdashboard/utils/importutils.py b/cerberusdashboard/utils/importutils.py new file mode 100644 index 0000000..6abf18f --- /dev/null +++ b/cerberusdashboard/utils/importutils.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2015 EUROGICIEL +# +# 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 cerberusdashboard.openstack.common import importutils + + +def import_any(*args): + if not args: + raise RuntimeError('No module is specified for import') + + for module_name in args: + module = importutils.try_import(module_name) + if module is not None: + return module + + raise RuntimeError('Unable to import any modules from the list %s' % + str(args)) diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100755 index 0000000..32b59af --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,75 @@ +# -*- 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'cerberus-dashboard' +copyright = u'2013, 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 = ['static'] + +# Output file base name for HTML help builder. +htmlhelp_basename = '%sdoc' % project + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', + '%s.tex' % project, + u'%s Documentation' % project, + u'OpenStack Foundation', 'manual'), +] + +# Example configuration for intersphinx: refer to the Python standard library. +#intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst new file mode 100644 index 0000000..1728a61 --- /dev/null +++ b/doc/source/contributing.rst @@ -0,0 +1,4 @@ +============ +Contributing +============ +.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..be12121 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,25 @@ +.. cerberus-dashboard documentation master file, created by + sphinx-quickstart on Tue Jul 9 22:26:36 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to cerberus-dashboard's documentation! +======================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + readme + installation + usage + contributing + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/source/installation.rst b/doc/source/installation.rst new file mode 100644 index 0000000..197c78f --- /dev/null +++ b/doc/source/installation.rst @@ -0,0 +1,12 @@ +============ +Installation +============ + +At the command line:: + + $ pip install cerberus-dashboard + +Or, if you have virtualenvwrapper installed:: + + $ mkvirtualenv cerberus-dashboard + $ pip install cerberus-dashboard diff --git a/doc/source/readme.rst b/doc/source/readme.rst new file mode 100644 index 0000000..a6210d3 --- /dev/null +++ b/doc/source/readme.rst @@ -0,0 +1 @@ +.. include:: ../../README.rst diff --git a/doc/source/usage.rst b/doc/source/usage.rst new file mode 100644 index 0000000..56676b2 --- /dev/null +++ b/doc/source/usage.rst @@ -0,0 +1,7 @@ +======== +Usage +======== + +To use cerberus-dashboard in a project:: + + import cerberus-dashboard diff --git a/openstack-common.conf b/openstack-common.conf new file mode 100644 index 0000000..6bb7b5c --- /dev/null +++ b/openstack-common.conf @@ -0,0 +1,6 @@ +[DEFAULT] + +# The list of modules to copy from oslo-incubator.git + +# The base module to hold the copy of openstack.common +base=cerberusdashboard diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..95137a6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +pbr>=0.6,!=0.7,<1.0 +Babel>=1.3 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..82d6692 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,47 @@ +[metadata] +name = cerberus-dashboard +summary = Dashboard for Openstack Cerberus +description-file = + README.rst +author = OpenStack +author-email = openstack-dev@lists.openstack.org +home-page = http://www.openstack.org/ +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 2.6 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.3 + Programming Language :: Python :: 3.4 + +[files] +packages = + cerberusdashboard + +[build_sphinx] +source-dir = doc/source +build-dir = doc/build +all_files = 1 + +[upload_sphinx] +upload-dir = doc/build/html + +[compile_catalog] +directory = cerberus-dashboard/locale +domain = cerberus-dashboard + +[update_catalog] +domain = cerberus-dashboard +output_dir = cerberus-dashboard/locale +input_file = cerberus-dashboard/locale/cerberus-dashboard.pot + +[extract_messages] +keywords = _ gettext ngettext l_ lazy_gettext +mapping_file = babel.cfg +output_file = cerberus-dashboard/locale/cerberus-dashboard.pot diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..7363757 --- /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..5f7faa7 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,16 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +hacking<0.11,>=0.10.0 + +coverage>=3.6 +discover +python-subunit>=0.0.18 +oslotest>=1.2.0 # Apache-2.0 +testrepository>=0.0.18 +testscenarios>=0.4 +testtools>=0.9.36,!=1.2.0 +# Doc requirements +sphinx>=1.1.2,<1.1.999 +oslosphinx<=2.5.0 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..8b9ca84 --- /dev/null +++ b/tox.ini @@ -0,0 +1,35 @@ +[tox] +minversion = 1.6 +envlist = py33,py34,py26,py27,pypy,pep8 +skipsdist = True + +[testenv] +usedevelop = True +install_command = pip install -U {opts} {packages} +setenv = + VIRTUAL_ENV={envdir} +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:pep8] +commands = flake8 + +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = + nosetests --with-xunit --with-xcoverage --cover-package=cerberus --nocapture --cover-tests --cover-branches --cover-min-percentage=50 + +[testenv:docs] +commands = python setup.py build_sphinx + +[testenv:debug] +commands = oslo_debug_helper {posargs} + +[flake8] + +show-source = True +ignore = H101,H238,H302,H803 +builtins = _ +exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build