From d233cf67032af0a4484ebf4bbf668c060f61c792 Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Tue, 13 Aug 2013 15:47:58 +0800 Subject: [PATCH] Make Event object independent of resource object The event table is pretty much self contained. So we actually don't need to referece the corresponding resources to retrieve information. Fixes bug #1204506 Change-Id: If0b88e0f9e243cf3cc6d747ffa04b9a429ea83a2 --- heat/engine/api.py | 4 +-- heat/engine/event.py | 36 +++++++++---------- heat/engine/resource.py | 6 ++-- heat/tests/test_engine_service.py | 58 +++++++++++++++++++++++++++++++ heat/tests/test_event.py | 29 +++++++--------- 5 files changed, 94 insertions(+), 39 deletions(-) diff --git a/heat/engine/api.py b/heat/engine/api.py index 33aeeef7..33862de0 100644 --- a/heat/engine/api.py +++ b/heat/engine/api.py @@ -127,12 +127,12 @@ def format_event(event): api.EVENT_STACK_ID: dict(stack_identifier), api.EVENT_STACK_NAME: stack_identifier.stack_name, api.EVENT_TIMESTAMP: timeutils.isotime(event.timestamp), - api.EVENT_RES_NAME: event.resource.name, + api.EVENT_RES_NAME: event.logical_resource_id, api.EVENT_RES_PHYSICAL_ID: event.physical_resource_id, api.EVENT_RES_ACTION: event.action, api.EVENT_RES_STATUS: event.status, api.EVENT_RES_STATUS_DATA: event.reason, - api.EVENT_RES_TYPE: event.resource.type(), + api.EVENT_RES_TYPE: event.resource_type, api.EVENT_RES_PROPERTIES: event.resource_properties, } diff --git a/heat/engine/event.py b/heat/engine/event.py index 8fe17614..6c095cc9 100644 --- a/heat/engine/event.py +++ b/heat/engine/event.py @@ -24,22 +24,22 @@ logger = logging.getLogger(__name__) class Event(object): '''Class representing a Resource state change.''' - def __init__(self, context, stack, resource, - action, status, reason, - physical_resource_id, resource_properties, - timestamp=None, id=None): + def __init__(self, context, stack, action, status, reason, + physical_resource_id, resource_properties, resource_name, + resource_type, timestamp=None, id=None): ''' - Initialise from a context, stack, resource, event information and - current resource data. The timestamp and database ID may also be - initialised if the event is already in the database. + Initialise from a context, stack, and event information. The timestamp + and database ID may also be initialised if the event is already in the + database. ''' self.context = context - self.resource = resource self.stack = stack self.action = action self.status = status self.reason = reason self.physical_resource_id = physical_resource_id + self.logical_resource_id = resource_name + self.resource_type = resource_type try: self.resource_properties = dict(resource_properties) except ValueError as ex: @@ -60,24 +60,22 @@ class Event(object): st = stack if stack is not None else\ parser.Stack.load(context, ev.stack_id) - resource = st[ev.logical_resource_id] - return cls(context, st, resource, - ev.resource_action, ev.resource_status, - ev.resource_status_reason, - ev.physical_resource_id, ev.resource_properties, - ev.created_at, ev.id) + return cls(context, st, ev.resource_action, ev.resource_status, + ev.resource_status_reason, ev.physical_resource_id, + ev.resource_properties, ev.logical_resource_id, + ev.resource_type, ev.created_at, ev.id) def store(self): '''Store the Event in the database.''' ev = { - 'logical_resource_id': self.resource.name, + 'logical_resource_id': self.logical_resource_id, 'physical_resource_id': self.physical_resource_id, 'stack_id': self.stack.id, 'resource_action': self.action, 'resource_status': self.status, 'resource_status_reason': self.reason, - 'resource_type': self.resource.type(), + 'resource_type': self.resource_type, 'resource_properties': self.resource_properties, } @@ -96,5 +94,7 @@ class Event(object): if self.id is None: return None - return identifier.EventIdentifier(event_id=str(self.id), - **self.resource.identifier()) + res_id = identifier.ResourceIdentifier( + resource_name=self.logical_resource_id, **self.stack.identifier()) + + return identifier.EventIdentifier(event_id=str(self.id), **res_id) diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 656a71cb..02ed1098 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -595,9 +595,9 @@ class Resource(object): def _add_event(self, action, status, reason): '''Add a state change event to the database.''' - ev = event.Event(self.context, self.stack, self, - action, status, reason, - self.resource_id, self.properties) + ev = event.Event(self.context, self.stack, action, status, reason, + self.resource_id, self.properties, + self.name, self.type()) try: ev.store() diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index 0b4a64df..b69a17e2 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -744,6 +744,64 @@ class StackServiceTest(HeatTestCase): self.m.VerifyAll() + @stack_context('service_event_list_deleted_resource_test_stack') + def test_stack_event_list_deleted_resource(self): + rsrs._register_class('GenericResourceType', + generic_rsrc.GenericResource) + + def run(stack_id, func, *args): + func(*args) + self.eng._start_in_thread = run + + new_tmpl = {'Resources': {'AResource': {'Type': + 'GenericResourceType'}}} + + self.m.StubOutWithMock(instances.Instance, 'handle_delete') + instances.Instance.handle_delete() + + self.m.ReplayAll() + + result = self.eng.update_stack(self.ctx, self.stack.identifier(), + new_tmpl, None, None, {}) + + # The self.stack reference needs to be updated. Since the underlying + # stack is updated in update_stack, the original reference is now + # pointing to an orphaned stack object. + self.stack = parser.Stack.load(self.ctx, stack_id=result['stack_id']) + + self.assertEqual(self.stack.identifier(), result) + self.assertTrue(isinstance(result, dict)) + self.assertTrue(result['stack_id']) + events = self.eng.list_events(self.ctx, self.stack.identifier()) + + self.assertEqual(6, len(events)) + + for ev in events: + self.assertIn('event_identity', ev) + self.assertEqual(dict, type(ev['event_identity'])) + self.assertTrue(ev['event_identity']['path'].rsplit('/', 1)[1]) + + self.assertIn('logical_resource_id', ev) + self.assertIn('physical_resource_id', ev) + self.assertIn('resource_properties', ev) + self.assertIn('resource_status_reason', ev) + + self.assertIn(ev['resource_action'], ('CREATE', 'DELETE')) + self.assertIn(ev['resource_status'], ('IN_PROGRESS', 'COMPLETE')) + + self.assertIn('resource_type', ev) + self.assertIn(ev['resource_type'], ('AWS::EC2::Instance', + 'GenericResourceType')) + + self.assertIn('stack_identity', ev) + + self.assertIn('stack_name', ev) + self.assertEqual(self.stack.name, ev['stack_name']) + + self.assertIn('event_time', ev) + + self.m.VerifyAll() + @stack_context('service_event_list_test_stack') def test_stack_event_list_by_tenant(self): events = self.eng.list_events(self.ctx, None) diff --git a/heat/tests/test_event.py b/heat/tests/test_event.py index fd326528..75c6edf2 100644 --- a/heat/tests/test_event.py +++ b/heat/tests/test_event.py @@ -59,9 +59,9 @@ class EventTest(HeatTestCase): def test_load(self): self.resource.resource_id_set('resource_physical_id') - e = event.Event(self.ctx, self.stack, self.resource, - 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', self.resource.properties) + e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', + 'wibble', self.resource.properties, + self.resource.name, self.resource.type()) e.store() self.assertNotEqual(e.id, None) @@ -69,8 +69,7 @@ class EventTest(HeatTestCase): loaded_e = event.Event.load(self.ctx, e.id) self.assertEqual(self.stack.id, loaded_e.stack.id) - self.assertEqual(self.resource.name, loaded_e.resource.name) - self.assertEqual(self.resource.id, loaded_e.resource.id) + self.assertEqual(self.resource.name, loaded_e.logical_resource_id) self.assertEqual('wibble', loaded_e.physical_resource_id) self.assertEqual('TEST', loaded_e.action) self.assertEqual('IN_PROGRESS', loaded_e.status) @@ -81,9 +80,9 @@ class EventTest(HeatTestCase): def test_load_given_stack_event(self): self.resource.resource_id_set('resource_physical_id') - e = event.Event(self.ctx, self.stack, self.resource, - 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', self.resource.properties) + e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', + 'wibble', self.resource.properties, + self.resource.name, self.resource.type()) e.store() self.assertNotEqual(e.id, None) @@ -93,8 +92,7 @@ class EventTest(HeatTestCase): loaded_e = event.Event.load(self.ctx, e.id, stack=self.stack, event=ev) self.assertEqual(self.stack.id, loaded_e.stack.id) - self.assertEqual(self.resource.name, loaded_e.resource.name) - self.assertEqual(self.resource.id, loaded_e.resource.id) + self.assertEqual(self.resource.name, loaded_e.logical_resource_id) self.assertEqual('wibble', loaded_e.physical_resource_id) self.assertEqual('TEST', loaded_e.action) self.assertEqual('IN_PROGRESS', loaded_e.status) @@ -103,9 +101,9 @@ class EventTest(HeatTestCase): self.assertEqual({'Foo': 'goo'}, loaded_e.resource_properties) def test_identifier(self): - e = event.Event(self.ctx, self.stack, self.resource, - 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', self.resource.properties) + e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', + 'wibble', self.resource.properties, + self.resource.name, self.resource.type()) eid = e.store() expected_identifier = { @@ -121,7 +119,6 @@ class EventTest(HeatTestCase): 'Properties': {'Foo': False}} rname = 'bad_resource' res = generic_rsrc.ResourceWithRequiredProps(rname, tmpl, self.stack) - e = event.Event(self.ctx, self.stack, res, - 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', res.properties) + e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', + 'wibble', res.properties, res.name, res.type()) self.assertTrue('Error' in e.resource_properties) -- 2.45.2