From 53c59e53eb332cb63017759d15601501ffb20b4d Mon Sep 17 00:00:00 2001 From: Tzu-Mainn Chen Date: Thu, 23 Jan 2014 15:52:22 -0500 Subject: [PATCH] Adds basic deployment log API and tab Change-Id: Iff44fd96f5e4d3f5f12f3dcbf58aa735f2d14d1b Implements: blueprint tripleo-deployment-log --- tuskar_ui/api.py | 20 ++++- tuskar_ui/infrastructure/overcloud/tables.py | 17 ++++ tuskar_ui/infrastructure/overcloud/tabs.py | 14 +++- tuskar_ui/test/api_tests/tuskar_tests.py | 16 ++++ tuskar_ui/test/test_data/tuskar_data.py | 82 ++++++++++++++++++-- 5 files changed, 140 insertions(+), 9 deletions(-) diff --git a/tuskar_ui/api.py b/tuskar_ui/api.py index 98e3e2554..f81971ceb 100644 --- a/tuskar_ui/api.py +++ b/tuskar_ui/api.py @@ -136,6 +136,22 @@ class Overcloud(base.APIDictWrapper): return stack return None + @cached_property + def stack_events(self): + """Return the Heat Events associated with this Overcloud + + :return: list of Heat Events associated with this Overcloud; + or an empty list if there is no Stack associated with + this Overcloud, or there are no Events + :rtype: list of heatclient.v1.events.Event + """ + if self.stack_id: + # TODO(Tzu-Mainn Chen): remove test data when possible + # events = heatclient(request).events.get(self.stack_id) + events = test_data().heatclient_events.list() + return events + return [] + @cached_property def is_deployed(self): """Check if this Overcloud is successfully deployed. @@ -165,9 +181,9 @@ class Overcloud(base.APIDictWrapper): # TODO(Tzu-Mainn Chen): uncomment when possible #resources = tuskarclient(request).overclouds.get_resources( # self.id, resource_category.id) - resources = [r for r in test_data().heatclient_resources.list() - if r.logical_resource_id == resource_category.name] + if r.logical_resource_id.startswith( + resource_category.name)] if not with_joins: return [Resource(r) for r in resources] diff --git a/tuskar_ui/infrastructure/overcloud/tables.py b/tuskar_ui/infrastructure/overcloud/tables.py index fa123570f..927366740 100644 --- a/tuskar_ui/infrastructure/overcloud/tables.py +++ b/tuskar_ui/infrastructure/overcloud/tables.py @@ -44,3 +44,20 @@ class ResourceCategoryInstanceTable(tables.DataTable): verbose_name = _("Instances") table_actions = () row_actions = () + + +class LogTable(tables.DataTable): + + timestamp = tables.Column('event_time', + verbose_name=_("Timestamp")) + resource_name = tables.Column('resource_name', + verbose_name=_("Resource Name")) + resource_status = tables.Column('resource_status', + verbose_name=_("Status")) + + 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 497070820..5d4f09b1f 100644 --- a/tuskar_ui/infrastructure/overcloud/tabs.py +++ b/tuskar_ui/infrastructure/overcloud/tabs.py @@ -20,6 +20,7 @@ from horizon import exceptions from horizon import tabs from tuskar_ui import api +from tuskar_ui.infrastructure.overcloud import tables class OverviewTab(tabs.Tab): @@ -72,7 +73,18 @@ class ConfigurationTab(tabs.Tab): return {} +class LogTab(tabs.TableTab): + table_classes = (tables.LogTable,) + name = _("Log") + slug = "log" + template_name = "horizon/common/_detail_table.html" + + def get_log_data(self): + overcloud = self.tab_group.kwargs['overcloud'] + return overcloud.stack_events + + class DetailTabs(tabs.TabGroup): slug = "detail" - tabs = (OverviewTab, ConfigurationTab) + tabs = (OverviewTab, ConfigurationTab, LogTab) sticky = True diff --git a/tuskar_ui/test/api_tests/tuskar_tests.py b/tuskar_ui/test/api_tests/tuskar_tests.py index 3aa82c5ef..be178b5ff 100644 --- a/tuskar_ui/test/api_tests/tuskar_tests.py +++ b/tuskar_ui/test/api_tests/tuskar_tests.py @@ -15,6 +15,7 @@ from __future__ import absolute_import from glanceclient.v1 import images +from heatclient.v1 import events from heatclient.v1 import stacks from tuskar_ui import api @@ -52,6 +53,21 @@ class TuskarAPITests(test.APITestCase): ret_val = api.Overcloud(overcloud).stack self.assertIsInstance(ret_val, stacks.Stack) + def test_overcloud_stack_events(self): + overcloud = self.tuskarclient_overclouds.first() + + ret_val = api.Overcloud(overcloud).stack_events + for e in ret_val: + self.assertIsInstance(e, events.Event) + self.assertEqual(8, len(ret_val)) + + def test_overcloud_stack_events_empty(self): + overcloud = self.tuskarclient_overclouds.first() + overcloud['stack_id'] = None + + ret_val = api.Overcloud(overcloud).stack_events + self.assertListEqual([], ret_val) + def test_overcloud_is_deployed(self): overcloud = self.tuskarclient_overclouds.first() diff --git a/tuskar_ui/test/test_data/tuskar_data.py b/tuskar_ui/test/test_data/tuskar_data.py index 2e2cba37c..e49a25501 100644 --- a/tuskar_ui/test/test_data/tuskar_data.py +++ b/tuskar_ui/test/test_data/tuskar_data.py @@ -13,6 +13,7 @@ from openstack_dashboard.test.test_data import utils as test_data_utils from glanceclient.v1 import images +from heatclient.v1 import events from heatclient.v1 import resources from heatclient.v1 import stacks from ironicclient.v1 import node @@ -31,6 +32,75 @@ def data(TEST): 'stack_status': 'RUNNING'}) TEST.heatclient_stacks.add(stack_1) + # Events + TEST.heatclient_events = test_data_utils.TestDataContainer() + event_1 = events.Event( + events.EventManager(None), + {'id': 1, + 'stack_id': 'stack-id-1', + 'resource_name': 'Controller', + 'resource_status': 'CREATE_IN_PROGRESS', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:26:15Z'}) + event_2 = events.Event( + events.EventManager(None), + {'id': 2, + 'stack_id': 'stack-id-1', + 'resource_name': 'Compute0', + 'resource_status': 'CREATE_IN_PROGRESS', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:26:27Z'}) + event_3 = events.Event( + events.EventManager(None), + {'id': 3, + 'stack_id': 'stack-id-1', + 'resource_name': 'Compute1', + 'resource_status': 'CREATE_IN_PROGRESS', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:26:44Z'}) + event_4 = events.Event( + events.EventManager(None), + {'id': 4, + 'stack_id': 'stack-id-1', + 'resource_name': 'Compute0', + 'resource_status': 'CREATE_COMPLETE', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:27:14Z'}) + event_5 = events.Event( + events.EventManager(None), + {'id': 5, + 'stack_id': 'stack-id-1', + 'resource_name': 'Compute2', + 'resource_status': 'CREATE_IN_PROGRESS', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:27:31Z'}) + event_6 = events.Event( + events.EventManager(None), + {'id': 6, + 'stack_id': 'stack-id-1', + 'resource_name': 'Compute1', + 'resource_status': 'CREATE_COMPLETE', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:28:01Z'}) + event_7 = events.Event( + events.EventManager(None), + {'id': 7, + 'stack_id': 'stack-id-1', + 'resource_name': 'Controller', + 'resource_status': 'CREATE_COMPLETE', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:28:59Z'}) + event_8 = events.Event( + events.EventManager(None), + {'id': 8, + 'stack_id': 'stack-id-1', + 'resource_name': 'Compute2', + 'resource_status': 'CREATE_COMPLETE', + 'resource_status_reason': 'state changed', + 'event_time': '2014-01-01T07:29:11Z'}) + TEST.heatclient_events.add(event_1, event_2, event_3, event_4, + event_5, event_6, event_7, event_8) + # Node TEST.ironicclient_nodes = test_data_utils.TestDataContainer() node_1 = node.Node( @@ -145,8 +215,8 @@ def data(TEST): resources.ResourceManager(None), {'id': '1-resource-id', 'stack_id': 'stack-id-1', - 'resource_name': 'Compute', - 'logical_resource_id': 'Compute', + 'resource_name': 'Compute0', + 'logical_resource_id': 'Compute0', 'physical_resource_id': 'aa', 'resource_status': 'CREATE_COMPLETE', 'resource_type': 'AWS::EC2::Instance'}) @@ -163,8 +233,8 @@ def data(TEST): resources.ResourceManager(None), {'id': '3-resource-id', 'stack_id': 'stack-id-1', - 'resource_name': 'Compute', - 'logical_resource_id': 'Compute', + 'resource_name': 'Compute1', + 'logical_resource_id': 'Compute1', 'physical_resource_id': 'cc', 'resource_status': 'CREATE_COMPLETE', 'resource_type': 'AWS::EC2::Instance'}) @@ -172,8 +242,8 @@ def data(TEST): resources.ResourceManager(None), {'id': '4-resource-id', 'stack_id': 'stack-id-4', - 'resource_name': 'Compute', - 'logical_resource_id': 'Compute', + 'resource_name': 'Compute2', + 'logical_resource_id': 'Compute2', 'physical_resource_id': 'dd', 'resource_status': 'CREATE_COMPLETE', 'resource_type': 'AWS::EC2::Instance'})