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 <nkonovalov@mirantis.com>
Co-Authored-By: Dmitry Mescheryakov <dmescheryakov@mirantis.com>
This commit is contained in:
Chad Roberts 2014-04-23 09:53:52 -04:00
parent f712ed04cc
commit 7c3897a4bd
18 changed files with 358 additions and 3 deletions

View File

@ -63,7 +63,8 @@ class SystemInfoViewTests(test.BaseAdminViewTests):
'<Service: ec2>', '<Service: ec2>',
'<Service: metering>', '<Service: metering>',
'<Service: orchestration>', '<Service: orchestration>',
'<Service: database>']) '<Service: database>',
'<Service: data_processing>', ])
network_agents_tab = res.context['tab_group'].get_tab('network_agents') network_agents_tab = res.context['tab_group'].get_tab('network_agents')
self.assertQuerysetEqual( self.assertQuerysetEqual(

View File

@ -57,6 +57,12 @@ class DatabasePanels(horizon.PanelGroup):
'database_backups',) 'database_backups',)
class DataProcessingPanels(horizon.PanelGroup):
name = _("Data Processing")
slug = "data_processing"
panels = ('data_processing.data_plugins',)
class Project(horizon.Dashboard): class Project(horizon.Dashboard):
name = _("Project") name = _("Project")
slug = "project" slug = "project"
@ -65,7 +71,8 @@ class Project(horizon.Dashboard):
NetworkPanels, NetworkPanels,
ObjectStorePanels, ObjectStorePanels,
OrchestrationPanels, OrchestrationPanels,
DatabasePanels,) DatabasePanels,
DataProcessingPanels,)
default_panel = 'overview' default_panel = 'overview'
supports_tenants = True supports_tenants = True

View File

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

View File

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

View File

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

View File

@ -0,0 +1,25 @@
{% load i18n %}
<h3>{% trans "Data Processing Plugin Overview" %}</h3>
<div class="status row-fluid detail">
<h4>{% trans "Info" %}</h4>
<hr class="header_rule">
<dl>
<dt>{% trans "Name" %}</dt>
<dd>{{ plugin.name }}</dd>
<dt>{% trans "Title" %}</dt>
<dd>{{ plugin.title }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ plugin.description }}</dd>
<dt>{% trans "Supported Versions" %}</dt>
<dd>
<ul>
{% for version in plugin.versions %}
<li>{{ version }}</li>
{% endfor %}
</ul>
</dd>
</dl>
</div>

View File

@ -0,0 +1,5 @@
<ul>
{% for version in plugin.versions %}
<li>{{ version }}</li>
{% endfor %}
</ul>

View File

@ -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 %}
<div class="row-fluid">
<div class="span12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -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 %}
<div class="plugins">
{{ plugins_table.render }}
</div>
{% endblock %}

View File

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

View File

@ -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<plugin_id>[^/]+)$',
views.PluginDetailsView.as_view(), name='details'),
)

View File

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

View File

@ -36,6 +36,7 @@ from heatclient import client as heat_client
from keystoneclient.v2_0 import client as keystone_client from keystoneclient.v2_0 import client as keystone_client
from neutronclient.v2_0 import client as neutron_client from neutronclient.v2_0 import client as neutron_client
from novaclient.v1_1 import client as nova_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 swiftclient import client as swift_client
from troveclient import client as trove_client from troveclient import client as trove_client
@ -262,6 +263,7 @@ class APITestCase(TestCase):
self._original_heatclient = api.heat.heatclient self._original_heatclient = api.heat.heatclient
self._original_ceilometerclient = api.ceilometer.ceilometerclient self._original_ceilometerclient = api.ceilometer.ceilometerclient
self._original_troveclient = api.trove.troveclient self._original_troveclient = api.trove.troveclient
self._original_saharaclient = api.sahara.client
# Replace the clients with our stubs. # Replace the clients with our stubs.
api.glance.glanceclient = lambda request: self.stub_glanceclient() api.glance.glanceclient = lambda request: self.stub_glanceclient()
@ -273,6 +275,7 @@ class APITestCase(TestCase):
api.ceilometer.ceilometerclient = lambda request: \ api.ceilometer.ceilometerclient = lambda request: \
self.stub_ceilometerclient() self.stub_ceilometerclient()
api.trove.troveclient = lambda request: self.stub_troveclient() api.trove.troveclient = lambda request: self.stub_troveclient()
api.sahara.client = lambda request: self.stub_saharaclient()
def tearDown(self): def tearDown(self):
super(APITestCase, self).tearDown() super(APITestCase, self).tearDown()
@ -284,6 +287,7 @@ class APITestCase(TestCase):
api.heat.heatclient = self._original_heatclient api.heat.heatclient = self._original_heatclient
api.ceilometer.ceilometerclient = self._original_ceilometerclient api.ceilometer.ceilometerclient = self._original_ceilometerclient
api.trove.troveclient = self._original_troveclient api.trove.troveclient = self._original_troveclient
api.sahara.client = self._original_saharaclient
def stub_novaclient(self): def stub_novaclient(self):
if not hasattr(self, "novaclient"): if not hasattr(self, "novaclient"):
@ -357,6 +361,12 @@ class APITestCase(TestCase):
self.troveclient = self.mox.CreateMock(trove_client.Client) self.troveclient = self.mox.CreateMock(trove_client.Client)
return self.troveclient 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), @unittest.skipUnless(os.environ.get('WITH_SELENIUM', False),
"The WITH_SELENIUM env variable is not set.") "The WITH_SELENIUM env variable is not set.")

View File

@ -122,7 +122,15 @@ SERVICE_CATALOG = [
{"region": "RegionOne", {"region": "RegionOne",
"adminURL": "http://admin.trove.example.com:8779/v1.0", "adminURL": "http://admin.trove.example.com:8779/v1.0",
"publicURL": "http://public.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"}]}
] ]

View File

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

View File

@ -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 keystone_data
from openstack_dashboard.test.test_data import neutron_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 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 swift_data
from openstack_dashboard.test.test_data import trove_data from openstack_dashboard.test.test_data import trove_data
@ -37,6 +38,7 @@ def load_test_data(load_onto=None):
heat_data.data, heat_data.data,
ceilometer_data.data, ceilometer_data.data,
trove_data.data, trove_data.data,
sahara_data.data,
) )
if load_onto: if load_onto:
for data_func in loaders: for data_func in loaders: