Merge "Add History panel"

This commit is contained in:
Jenkins 2014-08-01 16:45:17 +00:00 committed by Gerrit Code Review
commit 651158dfea
12 changed files with 190 additions and 71 deletions

View File

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

View File

@ -24,6 +24,7 @@ class BasePanels(horizon.PanelGroup):
'plans',
'nodes',
'flavors',
'history',
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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