Merge "Add History panel"
This commit is contained in:
commit
651158dfea
@ -112,6 +112,20 @@ class Stack(base.APIResourceWrapper):
|
|||||||
if stack.id == stack_id:
|
if stack.id == stack_id:
|
||||||
return stack
|
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
|
@memoized.memoized
|
||||||
def resources(self, with_joins=True):
|
def resources(self, with_joins=True):
|
||||||
"""Return a list of all Resources associated with the Stack
|
"""Return a list of all Resources associated with the Stack
|
||||||
|
@ -24,6 +24,7 @@ class BasePanels(horizon.PanelGroup):
|
|||||||
'plans',
|
'plans',
|
||||||
'nodes',
|
'nodes',
|
||||||
'flavors',
|
'flavors',
|
||||||
|
'history',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
0
tuskar_ui/infrastructure/history/__init__.py
Normal file
0
tuskar_ui/infrastructure/history/__init__.py
Normal file
27
tuskar_ui/infrastructure/history/panel.py
Normal file
27
tuskar_ui/infrastructure/history/panel.py
Normal 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)
|
37
tuskar_ui/infrastructure/history/tables.py
Normal file
37
tuskar_ui/infrastructure/history/tables.py
Normal 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 = ()
|
@ -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 %}
|
39
tuskar_ui/infrastructure/history/tests.py
Normal file
39
tuskar_ui/infrastructure/history/tests.py
Normal 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')
|
23
tuskar_ui/infrastructure/history/urls.py
Normal file
23
tuskar_ui/infrastructure/history/urls.py
Normal 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'),
|
||||||
|
)
|
30
tuskar_ui/infrastructure/history/views.py
Normal file
30
tuskar_ui/infrastructure/history/views.py
Normal 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
|
@ -44,23 +44,3 @@ class ConfigurationTable(tables.DataTable):
|
|||||||
|
|
||||||
def get_object_id(self, datum):
|
def get_object_id(self, datum):
|
||||||
return datum[0]
|
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 = ()
|
|
||||||
|
@ -149,25 +149,13 @@ class ConfigurationTab(tabs.TableTab):
|
|||||||
stack.parameters.items()]
|
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):
|
class UndeployInProgressTabs(tabs.TabGroup):
|
||||||
slug = "undeploy_in_progress"
|
slug = "undeploy_in_progress"
|
||||||
tabs = (UndeployInProgressTab, LogTab)
|
tabs = (UndeployInProgressTab,)
|
||||||
sticky = True
|
sticky = True
|
||||||
|
|
||||||
|
|
||||||
class DetailTabs(tabs.TabGroup):
|
class DetailTabs(tabs.TabGroup):
|
||||||
slug = "detail"
|
slug = "detail"
|
||||||
tabs = (OverviewTab, ConfigurationTab, LogTab)
|
tabs = (OverviewTab, ConfigurationTab,)
|
||||||
sticky = True
|
sticky = True
|
||||||
|
@ -33,11 +33,8 @@ DETAIL_URL = urlresolvers.reverse(
|
|||||||
UNDEPLOY_IN_PROGRESS_URL = urlresolvers.reverse(
|
UNDEPLOY_IN_PROGRESS_URL = urlresolvers.reverse(
|
||||||
'horizon:infrastructure:overcloud:undeploy_in_progress',
|
'horizon:infrastructure:overcloud:undeploy_in_progress',
|
||||||
args=('overcloud',))
|
args=('overcloud',))
|
||||||
UNDEPLOY_IN_PROGRESS_URL_LOG_TAB = (
|
|
||||||
UNDEPLOY_IN_PROGRESS_URL + "?tab=undeploy_in_progress__log")
|
|
||||||
DETAIL_URL_CONFIGURATION_TAB = (DETAIL_URL +
|
DETAIL_URL_CONFIGURATION_TAB = (DETAIL_URL +
|
||||||
"?tab=detail__configuration")
|
"?tab=detail__configuration")
|
||||||
DETAIL_URL_LOG_TAB = (DETAIL_URL + "?tab=detail__log")
|
|
||||||
DELETE_URL = urlresolvers.reverse(
|
DELETE_URL = urlresolvers.reverse(
|
||||||
'horizon:infrastructure:overcloud:undeploy_confirmation',
|
'horizon:infrastructure:overcloud:undeploy_confirmation',
|
||||||
args=('stack-id-1',))
|
args=('stack-id-1',))
|
||||||
@ -145,21 +142,6 @@ class OvercloudTests(test.BaseAdminViewTests):
|
|||||||
self.assertTemplateUsed(
|
self.assertTemplateUsed(
|
||||||
res, 'horizon/common/_detail_table.html')
|
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):
|
def test_delete_get(self):
|
||||||
res = self.client.get(DELETE_URL)
|
res = self.client.get(DELETE_URL)
|
||||||
self.assertTemplateUsed(
|
self.assertTemplateUsed(
|
||||||
@ -201,22 +183,3 @@ class OvercloudTests(test.BaseAdminViewTests):
|
|||||||
res = self.client.get(UNDEPLOY_IN_PROGRESS_URL)
|
res = self.client.get(UNDEPLOY_IN_PROGRESS_URL)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, DETAIL_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')
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user