From 5d38344d75c970686780de113a22db3b7ccf7141 Mon Sep 17 00:00:00 2001 From: Tzu-Mainn Chen Date: Thu, 31 Jul 2014 18:09:57 +0200 Subject: [PATCH] Add History panel This essentially moves the Log table views out of the overcloud panel and into the new history panel. Change-Id: Id65d6a5b204444d51351703467c4246b0cd1a784 --- tuskar_ui/api/heat.py | 14 +++++++ tuskar_ui/infrastructure/dashboard.py | 1 + tuskar_ui/infrastructure/history/__init__.py | 0 tuskar_ui/infrastructure/history/panel.py | 27 +++++++++++++ tuskar_ui/infrastructure/history/tables.py | 37 ++++++++++++++++++ .../history/templates/history/index.html | 17 ++++++++ tuskar_ui/infrastructure/history/tests.py | 39 +++++++++++++++++++ tuskar_ui/infrastructure/history/urls.py | 23 +++++++++++ tuskar_ui/infrastructure/history/views.py | 30 ++++++++++++++ tuskar_ui/infrastructure/overcloud/tables.py | 20 ---------- tuskar_ui/infrastructure/overcloud/tabs.py | 16 +------- tuskar_ui/infrastructure/overcloud/tests.py | 37 ------------------ 12 files changed, 190 insertions(+), 71 deletions(-) create mode 100644 tuskar_ui/infrastructure/history/__init__.py create mode 100644 tuskar_ui/infrastructure/history/panel.py create mode 100644 tuskar_ui/infrastructure/history/tables.py create mode 100644 tuskar_ui/infrastructure/history/templates/history/index.html create mode 100644 tuskar_ui/infrastructure/history/tests.py create mode 100644 tuskar_ui/infrastructure/history/urls.py create mode 100644 tuskar_ui/infrastructure/history/views.py diff --git a/tuskar_ui/api/heat.py b/tuskar_ui/api/heat.py index d7ceec598..f2dded8a3 100644 --- a/tuskar_ui/api/heat.py +++ b/tuskar_ui/api/heat.py @@ -112,6 +112,20 @@ class Stack(base.APIResourceWrapper): if stack.id == stack_id: return stack + @classmethod + @handle_errors(_("Unable to retrieve stack")) + def get_by_plan(cls, request, plan): + """Return the Heat Stack associated with an OvercloudPlan + + :return: Heat Stack associated with the plan; or None + if no Stack is associated, or no Stack can be + found + :rtype: tuskar_ui.api.heat.Stack or None + """ + for stack in Stack.list(request): + if stack.plan and (stack.plan.id == plan.id): + return stack + @memoized.memoized def resources(self, with_joins=True): """Return a list of all Resources associated with the Stack diff --git a/tuskar_ui/infrastructure/dashboard.py b/tuskar_ui/infrastructure/dashboard.py index 93ad1c01b..b63e3f558 100644 --- a/tuskar_ui/infrastructure/dashboard.py +++ b/tuskar_ui/infrastructure/dashboard.py @@ -24,6 +24,7 @@ class BasePanels(horizon.PanelGroup): 'plans', 'nodes', 'flavors', + 'history', ) diff --git a/tuskar_ui/infrastructure/history/__init__.py b/tuskar_ui/infrastructure/history/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tuskar_ui/infrastructure/history/panel.py b/tuskar_ui/infrastructure/history/panel.py new file mode 100644 index 000000000..006190e08 --- /dev/null +++ b/tuskar_ui/infrastructure/history/panel.py @@ -0,0 +1,27 @@ +# -*- coding: utf8 -*- +# +# 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 tuskar_ui.infrastructure import dashboard + + +class History(horizon.Panel): + name = _("History") + slug = "history" + + +dashboard.Infrastructure.register(History) diff --git a/tuskar_ui/infrastructure/history/tables.py b/tuskar_ui/infrastructure/history/tables.py new file mode 100644 index 000000000..e26355a05 --- /dev/null +++ b/tuskar_ui/infrastructure/history/tables.py @@ -0,0 +1,37 @@ +# -*- coding: utf8 -*- +# +# 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 HistoryTable(tables.DataTable): + + timestamp = tables.Column('event_time', + verbose_name=_("Timestamp"), + attrs={'data-type': 'timestamp'}) + resource_name = tables.Column('resource_name', + verbose_name=_("Resource Name")) + resource_status = tables.Column('resource_status', + verbose_name=_("Status")) + resource_status_reason = tables.Column('resource_status_reason', + verbose_name=_("Reason")) + + class Meta: + name = "log" + verbose_name = _("Log") + multi_select = False + table_actions = () + row_actions = () diff --git a/tuskar_ui/infrastructure/history/templates/history/index.html b/tuskar_ui/infrastructure/history/templates/history/index.html new file mode 100644 index 000000000..573a58c79 --- /dev/null +++ b/tuskar_ui/infrastructure/history/templates/history/index.html @@ -0,0 +1,17 @@ +{% extends 'infrastructure/base.html' %} +{% load i18n %} +{% block title %}{% trans 'History' %}{% endblock %} + +{% block page_header %} + {% include 'horizon/common/_page_header.html' with title=_('History') %} +{% endblock page_header %} + +{% block main %} +
+
+

{% trans "History" %}

+ {{ table.render }} +
+
+ +{% endblock %} diff --git a/tuskar_ui/infrastructure/history/tests.py b/tuskar_ui/infrastructure/history/tests.py new file mode 100644 index 000000000..7d4568050 --- /dev/null +++ b/tuskar_ui/infrastructure/history/tests.py @@ -0,0 +1,39 @@ +# -*- coding: utf8 -*- +# +# 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 import urlresolvers + +from mock import patch, call # noqa + +from openstack_dashboard.test.test_data import utils +from tuskar_ui.test import helpers as test +from tuskar_ui.test.test_data import heat_data + + +TEST_DATA = utils.TestDataContainer() +heat_data.data(TEST_DATA) +INDEX_URL = urlresolvers.reverse( + 'horizon:infrastructure:history:index') + + +class HistoryTest(test.BaseAdminViewTests): + + def test_index(self): + events = TEST_DATA.heatclient_events.list() + + with patch('tuskar_ui.api.heat.Stack.events', + return_value=events): + res = self.client.get(INDEX_URL) + + self.assertTemplateUsed(res, 'infrastructure/history/index.html') diff --git a/tuskar_ui/infrastructure/history/urls.py b/tuskar_ui/infrastructure/history/urls.py new file mode 100644 index 000000000..15e57e1cd --- /dev/null +++ b/tuskar_ui/infrastructure/history/urls.py @@ -0,0 +1,23 @@ +# -*- coding: utf8 -*- +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.conf import urls + +from tuskar_ui.infrastructure.history import views + + +urlpatterns = urls.patterns( + '', + urls.url(r'^$', views.IndexView.as_view(), name='index'), +) diff --git a/tuskar_ui/infrastructure/history/views.py b/tuskar_ui/infrastructure/history/views.py new file mode 100644 index 000000000..59098915e --- /dev/null +++ b/tuskar_ui/infrastructure/history/views.py @@ -0,0 +1,30 @@ +# -*- coding: utf8 -*- +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from horizon import tables as horizon_tables + +from tuskar_ui import api +from tuskar_ui.infrastructure.history import tables + + +class IndexView(horizon_tables.DataTableView): + table_class = tables.HistoryTable + template_name = "infrastructure/history/index.html" + + def get_data(self): + plan = api.tuskar.OvercloudPlan.get_the_plan(self.request) + if plan: + stack = api.heat.Stack.get_by_plan(self.request, plan) + if stack: + return stack.events diff --git a/tuskar_ui/infrastructure/overcloud/tables.py b/tuskar_ui/infrastructure/overcloud/tables.py index dd7a0bf15..573a1d697 100644 --- a/tuskar_ui/infrastructure/overcloud/tables.py +++ b/tuskar_ui/infrastructure/overcloud/tables.py @@ -44,23 +44,3 @@ class ConfigurationTable(tables.DataTable): def get_object_id(self, datum): return datum[0] - - -class LogTable(tables.DataTable): - - timestamp = tables.Column('event_time', - verbose_name=_("Timestamp"), - attrs={'data-type': 'timestamp'}) - resource_name = tables.Column('resource_name', - verbose_name=_("Resource Name")) - resource_status = tables.Column('resource_status', - verbose_name=_("Status")) - resource_status_reason = tables.Column('resource_status_reason', - verbose_name=_("Reason")) - - class Meta: - name = "log" - verbose_name = _("Log") - multi_select = False - table_actions = () - row_actions = () diff --git a/tuskar_ui/infrastructure/overcloud/tabs.py b/tuskar_ui/infrastructure/overcloud/tabs.py index 3c2beedf9..a67a00345 100644 --- a/tuskar_ui/infrastructure/overcloud/tabs.py +++ b/tuskar_ui/infrastructure/overcloud/tabs.py @@ -149,25 +149,13 @@ class ConfigurationTab(tabs.TableTab): stack.parameters.items()] -class LogTab(tabs.TableTab): - table_classes = (tables.LogTable,) - name = _("Log") - slug = "log" - template_name = "horizon/common/_detail_table.html" - preload = False - - def get_log_data(self): - stack = self.tab_group.kwargs['stack'] - return stack.events - - class UndeployInProgressTabs(tabs.TabGroup): slug = "undeploy_in_progress" - tabs = (UndeployInProgressTab, LogTab) + tabs = (UndeployInProgressTab,) sticky = True class DetailTabs(tabs.TabGroup): slug = "detail" - tabs = (OverviewTab, ConfigurationTab, LogTab) + tabs = (OverviewTab, ConfigurationTab,) sticky = True diff --git a/tuskar_ui/infrastructure/overcloud/tests.py b/tuskar_ui/infrastructure/overcloud/tests.py index fc026963e..e0356caca 100644 --- a/tuskar_ui/infrastructure/overcloud/tests.py +++ b/tuskar_ui/infrastructure/overcloud/tests.py @@ -33,11 +33,8 @@ DETAIL_URL = urlresolvers.reverse( UNDEPLOY_IN_PROGRESS_URL = urlresolvers.reverse( 'horizon:infrastructure:overcloud:undeploy_in_progress', args=('overcloud',)) -UNDEPLOY_IN_PROGRESS_URL_LOG_TAB = ( - UNDEPLOY_IN_PROGRESS_URL + "?tab=undeploy_in_progress__log") DETAIL_URL_CONFIGURATION_TAB = (DETAIL_URL + "?tab=detail__configuration") -DETAIL_URL_LOG_TAB = (DETAIL_URL + "?tab=detail__log") DELETE_URL = urlresolvers.reverse( 'horizon:infrastructure:overcloud:undeploy_confirmation', args=('stack-id-1',)) @@ -145,21 +142,6 @@ class OvercloudTests(test.BaseAdminViewTests): self.assertTemplateUsed( res, 'horizon/common/_detail_table.html') - def test_detail_get_log_tab(self): - with contextlib.nested( - _mock_plan(), - patch('tuskar_ui.api.heat.Stack.events', - return_value=[]), - ): - res = self.client.get(DETAIL_URL_LOG_TAB) - - self.assertTemplateUsed( - res, 'infrastructure/overcloud/detail.html') - self.assertTemplateNotUsed( - res, 'infrastructure/overcloud/_detail_overview.html') - self.assertTemplateUsed( - res, 'horizon/common/_detail_table.html') - def test_delete_get(self): res = self.client.get(DELETE_URL) self.assertTemplateUsed( @@ -201,22 +183,3 @@ class OvercloudTests(test.BaseAdminViewTests): res = self.client.get(UNDEPLOY_IN_PROGRESS_URL) self.assertRedirectsNoFollow(res, DETAIL_URL) - - def test_undeploy_in_progress_log_tab(self): - with contextlib.nested( - _mock_plan(), - patch('tuskar_ui.api.heat.Stack.is_deleting', - return_value=True), - patch('tuskar_ui.api.heat.Stack.is_deployed', - return_value=False), - patch('tuskar_ui.api.heat.Stack.events', - return_value=[]), - ): - res = self.client.get(UNDEPLOY_IN_PROGRESS_URL_LOG_TAB) - - self.assertTemplateUsed( - res, 'infrastructure/overcloud/detail.html') - self.assertTemplateNotUsed( - res, 'infrastructure/overcloud/_undeploy_in_progress.html') - self.assertTemplateUsed( - res, 'horizon/common/_detail_table.html')