From 7c3897a4bd12bf9a73c27d874cabb08bd5d9bed7 Mon Sep 17 00:00:00 2001 From: Chad Roberts Date: Wed, 23 Apr 2014 09:53:52 -0400 Subject: [PATCH] Adding plugins panel for Sahara The plugins panel is used to show the various hadoop flavors/versions that are supported by the underlying Sahara API. * also added basic panel tests This code was originally from: https://github.com/openstack/sahara-dashboard Change-Id: Ib0c95334a5122529fddbfd9c0cd0d0c06e0c8ad3 Partial-Implements: blueprint merge-sahara-dashboard Co-Authored-By: Nikita Konovalov Co-Authored-By: Dmitry Mescheryakov --- .../dashboards/admin/info/tests.py | 3 +- .../dashboards/project/dashboard.py | 9 +++- .../project/data_processing/__init__.py | 0 .../data_processing/data_plugins/__init__.py | 0 .../data_processing/data_plugins/panel.py | 26 ++++++++++ .../data_processing/data_plugins/tables.py | 43 ++++++++++++++++ .../data_processing/data_plugins/tabs.py | 46 +++++++++++++++++ .../_details.html | 25 ++++++++++ .../_versions_list.html | 5 ++ .../data_processing.data_plugins/details.html | 15 ++++++ .../data_processing.data_plugins/plugins.html | 15 ++++++ .../data_processing/data_plugins/tests.py | 50 +++++++++++++++++++ .../data_processing/data_plugins/urls.py | 25 ++++++++++ .../data_processing/data_plugins/views.py | 47 +++++++++++++++++ openstack_dashboard/test/helpers.py | 10 ++++ .../test/test_data/keystone_data.py | 10 +++- .../test/test_data/sahara_data.py | 30 +++++++++++ openstack_dashboard/test/test_data/utils.py | 2 + 18 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 openstack_dashboard/dashboards/project/data_processing/__init__.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/__init__.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/panel.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/tables.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/tabs.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_details.html create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_versions_list.html create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/details.html create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/plugins.html create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/tests.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/urls.py create mode 100644 openstack_dashboard/dashboards/project/data_processing/data_plugins/views.py create mode 100644 openstack_dashboard/test/test_data/sahara_data.py diff --git a/openstack_dashboard/dashboards/admin/info/tests.py b/openstack_dashboard/dashboards/admin/info/tests.py index 1fd14b2771..cf92085d8e 100644 --- a/openstack_dashboard/dashboards/admin/info/tests.py +++ b/openstack_dashboard/dashboards/admin/info/tests.py @@ -63,7 +63,8 @@ class SystemInfoViewTests(test.BaseAdminViewTests): '', '', '', - '']) + '', + '', ]) network_agents_tab = res.context['tab_group'].get_tab('network_agents') self.assertQuerysetEqual( diff --git a/openstack_dashboard/dashboards/project/dashboard.py b/openstack_dashboard/dashboards/project/dashboard.py index 7bcdcffe5b..21d4917f91 100644 --- a/openstack_dashboard/dashboards/project/dashboard.py +++ b/openstack_dashboard/dashboards/project/dashboard.py @@ -57,6 +57,12 @@ class DatabasePanels(horizon.PanelGroup): 'database_backups',) +class DataProcessingPanels(horizon.PanelGroup): + name = _("Data Processing") + slug = "data_processing" + panels = ('data_processing.data_plugins',) + + class Project(horizon.Dashboard): name = _("Project") slug = "project" @@ -65,7 +71,8 @@ class Project(horizon.Dashboard): NetworkPanels, ObjectStorePanels, OrchestrationPanels, - DatabasePanels,) + DatabasePanels, + DataProcessingPanels,) default_panel = 'overview' supports_tenants = True diff --git a/openstack_dashboard/dashboards/project/data_processing/__init__.py b/openstack_dashboard/dashboards/project/data_processing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/__init__.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/panel.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/panel.py new file mode 100644 index 0000000000..c3ef6edb91 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/panel.py @@ -0,0 +1,26 @@ +# 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 openstack_dashboard.dashboards.project import dashboard + + +class PluginsPanel(horizon.Panel): + name = _("Plugins") + slug = 'data_processing.data_plugins' + permissions = ('openstack.services.data_processing',) + +dashboard.Project.register(PluginsPanel) diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/tables.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/tables.py new file mode 100644 index 0000000000..d1045ee3ed --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/tables.py @@ -0,0 +1,43 @@ +# 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 import template +from django.utils.translation import ugettext_lazy as _ + +from horizon import tables + +LOG = logging.getLogger(__name__) + + +def render_versions(plugin): + template_name = 'project/data_processing.data_plugins/_versions_list.html' + context = {"plugin": plugin} + return template.loader.render_to_string(template_name, context) + + +class PluginsTable(tables.DataTable): + title = tables.Column("title", + verbose_name=_("Title"), + link=("horizon:project:data_processing.data_plugins:details")) + + versions = tables.Column(render_versions, + verbose_name=_("Supported Versions")) + + description = tables.Column("description", + verbose_name=_("Description")) + + class Meta: + name = "plugins" + verbose_name = _("Plugins") diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/tabs.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/tabs.py new file mode 100644 index 0000000000..46cb321b5b --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/tabs.py @@ -0,0 +1,46 @@ +# 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.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from openstack_dashboard.api import sahara as saharaclient + +LOG = logging.getLogger(__name__) + + +class DetailsTab(tabs.Tab): + name = _("Details") + slug = "plugin_details_tab" + template_name = ("project/data_processing.data_plugins/_details.html") + + def get_context_data(self, request): + plugin_id = self.tab_group.kwargs['plugin_id'] + plugin = None + try: + plugin = saharaclient.plugin_get(request, plugin_id) + except Exception as e: + LOG.error("Unable to get plugin with plugin_id %s (%s)" % + (plugin_id, str(e))) + exceptions.handle(self.tab_group.request, + _('Unable to retrieve plugin.')) + return {"plugin": plugin} + + +class PluginDetailsTabs(tabs.TabGroup): + slug = "cluster_details" + tabs = (DetailsTab,) + sticky = True diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_details.html b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_details.html new file mode 100644 index 0000000000..57635d1e5a --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_details.html @@ -0,0 +1,25 @@ +{% load i18n %} + +

{% trans "Data Processing Plugin Overview" %}

+ +
+

{% trans "Info" %}

+
+
+
{% trans "Name" %}
+
{{ plugin.name }}
+
{% trans "Title" %}
+
{{ plugin.title }}
+
{% trans "Description" %}
+
{{ plugin.description }}
+
{% trans "Supported Versions" %}
+
+
    + {% for version in plugin.versions %} +
  • {{ version }}
  • + {% endfor %} +
+
+ +
+
\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_versions_list.html b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_versions_list.html new file mode 100644 index 0000000000..f0db801ea3 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/_versions_list.html @@ -0,0 +1,5 @@ +
    + {% for version in plugin.versions %} +
  • {{ version }}
  • + {% endfor %} +
\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/details.html b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/details.html new file mode 100644 index 0000000000..85558cbb7f --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/details.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Data Processing Plugin Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Data Processing Plugin Details") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} \ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/plugins.html b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/plugins.html new file mode 100644 index 0000000000..ec247eb8b2 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/templates/data_processing.data_plugins/plugins.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Data Processing" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Data Processing Plugins") %} +{% endblock page_header %} + +{% block main %} + +
+ {{ plugins_table.render }} +
+ +{% endblock %} \ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/tests.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/tests.py new file mode 100644 index 0000000000..ea749dc8c2 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/tests.py @@ -0,0 +1,50 @@ +# 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 import http + +from mox import IsA # noqa + +from openstack_dashboard import api +from openstack_dashboard.test import helpers as test + + +INDEX_URL = reverse( + 'horizon:project:data_processing.data_plugins:index') +DETAILS_URL = reverse( + 'horizon:project:data_processing.data_plugins:details', args=['id']) + + +class DataProcessingPluginsTests(test.TestCase): + @test.create_stubs({api.sahara: ('plugin_list',)}) + def test_index(self): + api.sahara.plugin_list(IsA(http.HttpRequest)) \ + .AndReturn(self.plugins.list()) + self.mox.ReplayAll() + res = self.client.get(INDEX_URL) + self.assertTemplateUsed(res, + 'project/data_processing.data_plugins/plugins.html') + self.assertContains(res, 'vanilla') + self.assertContains(res, 'plugin') + + @test.create_stubs({api.sahara: ('plugin_get',)}) + def test_details(self): + api.sahara.plugin_get(IsA(http.HttpRequest), IsA(unicode)) \ + .AndReturn(self.plugins.list()[0]) + self.mox.ReplayAll() + res = self.client.get(DETAILS_URL) + self.assertTemplateUsed(res, + 'project/data_processing.data_plugins/details.html') + self.assertContains(res, 'vanilla') + self.assertContains(res, 'plugin') + self.assertContains(res, 'Plugin Overview') diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/urls.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/urls.py new file mode 100644 index 0000000000..bcb7a69de8 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/urls.py @@ -0,0 +1,25 @@ +# 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 openstack_dashboard.dashboards.project.\ + data_processing.data_plugins import views + + +urlpatterns = patterns('', + url(r'^$', views.PluginsView.as_view(), name='index'), + url(r'^(?P[^/]+)$', + views.PluginDetailsView.as_view(), name='details'), + ) diff --git a/openstack_dashboard/dashboards/project/data_processing/data_plugins/views.py b/openstack_dashboard/dashboards/project/data_processing/data_plugins/views.py new file mode 100644 index 0000000000..659656a252 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/data_plugins/views.py @@ -0,0 +1,47 @@ +# 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.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import tables +from horizon import tabs + +from openstack_dashboard.api import sahara as saharaclient +import openstack_dashboard.dashboards.project.data_processing. \ + data_plugins.tables as p_tables +import openstack_dashboard.dashboards.project.data_processing. \ + data_plugins.tabs as p_tabs + +LOG = logging.getLogger(__name__) + + +class PluginsView(tables.DataTableView): + table_class = p_tables.PluginsTable + template_name = 'project/data_processing.data_plugins/plugins.html' + + def get_data(self): + try: + plugins = saharaclient.plugin_list(self.request) + except Exception: + plugins = [] + msg = _('Unable to retrieve data processing plugins.') + exceptions.handle(self.request, msg) + return plugins + + +class PluginDetailsView(tabs.TabView): + tab_group_class = p_tabs.PluginDetailsTabs + template_name = 'project/data_processing.data_plugins/details.html' diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py index 273be119c7..8954a17ecf 100644 --- a/openstack_dashboard/test/helpers.py +++ b/openstack_dashboard/test/helpers.py @@ -36,6 +36,7 @@ from heatclient import client as heat_client from keystoneclient.v2_0 import client as keystone_client from neutronclient.v2_0 import client as neutron_client from novaclient.v1_1 import client as nova_client +from saharaclient import client as sahara_client from swiftclient import client as swift_client from troveclient import client as trove_client @@ -262,6 +263,7 @@ class APITestCase(TestCase): self._original_heatclient = api.heat.heatclient self._original_ceilometerclient = api.ceilometer.ceilometerclient self._original_troveclient = api.trove.troveclient + self._original_saharaclient = api.sahara.client # Replace the clients with our stubs. api.glance.glanceclient = lambda request: self.stub_glanceclient() @@ -273,6 +275,7 @@ class APITestCase(TestCase): api.ceilometer.ceilometerclient = lambda request: \ self.stub_ceilometerclient() api.trove.troveclient = lambda request: self.stub_troveclient() + api.sahara.client = lambda request: self.stub_saharaclient() def tearDown(self): super(APITestCase, self).tearDown() @@ -284,6 +287,7 @@ class APITestCase(TestCase): api.heat.heatclient = self._original_heatclient api.ceilometer.ceilometerclient = self._original_ceilometerclient api.trove.troveclient = self._original_troveclient + api.sahara.client = self._original_saharaclient def stub_novaclient(self): if not hasattr(self, "novaclient"): @@ -357,6 +361,12 @@ class APITestCase(TestCase): self.troveclient = self.mox.CreateMock(trove_client.Client) return self.troveclient + def stub_saharaclient(self): + if not hasattr(self, "saharaclient"): + self.mox.StubOutWithMock(sahara_client, 'Client') + self.saharaclient = self.mox.CreateMock(sahara_client.Client) + return self.saharaclient + @unittest.skipUnless(os.environ.get('WITH_SELENIUM', False), "The WITH_SELENIUM env variable is not set.") diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py index 210454b4b2..296b809123 100644 --- a/openstack_dashboard/test/test_data/keystone_data.py +++ b/openstack_dashboard/test/test_data/keystone_data.py @@ -122,7 +122,15 @@ SERVICE_CATALOG = [ {"region": "RegionOne", "adminURL": "http://admin.trove.example.com:8779/v1.0", "publicURL": "http://public.trove.example.com:8779/v1.0", - "internalURL": "http://int.trove.example.com:8779/v1.0"}]} + "internalURL": "http://int.trove.example.com:8779/v1.0"}]}, + {"type": "data_processing", + "name": "Sahara", + "endpoints_links": [], + "endpoints": [ + {"region": "RegionOne", + "adminURL": "http://admin.sahara.example.com:8386/v1.1", + "publicURL": "http://public.sahara.example.com:8386/v1.1", + "internalURL": "http://int.sahara.example.com:8386/v1.1"}]} ] diff --git a/openstack_dashboard/test/test_data/sahara_data.py b/openstack_dashboard/test/test_data/sahara_data.py new file mode 100644 index 0000000000..382937e6ab --- /dev/null +++ b/openstack_dashboard/test/test_data/sahara_data.py @@ -0,0 +1,30 @@ +# 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 openstack_dashboard.test.test_data import utils + +from saharaclient.api import plugins + + +def data(TEST): + TEST.plugins = utils.TestDataContainer() + + plugin1_dict = { + "description": "vanilla plugin", + "name": "vanilla", + "title": "Vanilla Apache Hadoop", + "versions": ["2.3.0", "1.2.1"] + } + + plugin1 = plugins.Plugin(plugins.PluginManager(None), plugin1_dict) + + TEST.plugins.add(plugin1) diff --git a/openstack_dashboard/test/test_data/utils.py b/openstack_dashboard/test/test_data/utils.py index c80fd834d2..771cb55aa4 100644 --- a/openstack_dashboard/test/test_data/utils.py +++ b/openstack_dashboard/test/test_data/utils.py @@ -22,6 +22,7 @@ def load_test_data(load_onto=None): from openstack_dashboard.test.test_data import keystone_data from openstack_dashboard.test.test_data import neutron_data from openstack_dashboard.test.test_data import nova_data + from openstack_dashboard.test.test_data import sahara_data from openstack_dashboard.test.test_data import swift_data from openstack_dashboard.test.test_data import trove_data @@ -37,6 +38,7 @@ def load_test_data(load_onto=None): heat_data.data, ceilometer_data.data, trove_data.data, + sahara_data.data, ) if load_onto: for data_func in loaders: