diff --git a/heat/engine/api.py b/heat/engine/api.py index 5972bc788..125f062ae 100644 --- a/heat/engine/api.py +++ b/heat/engine/api.py @@ -379,20 +379,17 @@ def format_stack_preview(stack): return fmt_stack -def format_event(event): - stack_identifier = event.stack.identifier() - event_timestamp = event.timestamp or timeutils.utcnow() - +def format_event(event, stack_identifier): result = { - rpc_api.EVENT_ID: dict(event.identifier()), + rpc_api.EVENT_ID: dict(event.identifier(stack_identifier)), rpc_api.EVENT_STACK_ID: dict(stack_identifier), rpc_api.EVENT_STACK_NAME: stack_identifier.stack_name, - rpc_api.EVENT_TIMESTAMP: event_timestamp.isoformat(), + rpc_api.EVENT_TIMESTAMP: event.created_at.isoformat(), rpc_api.EVENT_RES_NAME: event.resource_name, rpc_api.EVENT_RES_PHYSICAL_ID: event.physical_resource_id, - rpc_api.EVENT_RES_ACTION: event.action, - rpc_api.EVENT_RES_STATUS: event.status, - rpc_api.EVENT_RES_STATUS_DATA: event.reason, + rpc_api.EVENT_RES_ACTION: event.resource_action, + rpc_api.EVENT_RES_STATUS: event.resource_status, + rpc_api.EVENT_RES_STATUS_DATA: event.resource_status_reason, rpc_api.EVENT_RES_TYPE: event.resource_type, rpc_api.EVENT_RES_PROPERTIES: event.resource_properties, } diff --git a/heat/engine/event.py b/heat/engine/event.py index cbe57222a..1aedfa511 100644 --- a/heat/engine/event.py +++ b/heat/engine/event.py @@ -17,8 +17,6 @@ import six import oslo_db.exception from oslo_log import log as logging -from heat.common import exception -from heat.common.i18n import _ from heat.common import identifier from heat.objects import event as event_object @@ -54,25 +52,6 @@ class Event(object): self.timestamp = timestamp self.id = id - @classmethod - def load(cls, context, event_id, event=None, stack=None): - """Retrieve an Event from the database.""" - from heat.engine import stack as parser - - ev = (event if event is not None else - event_object.Event.get_by_id(context, event_id)) - if ev is None: - message = _('No event exists with id "%s"') % str(event_id) - raise exception.NotFound(message) - - st = (stack if stack is not None else - parser.Stack.load(context, ev.stack_id)) - - return cls(context, st, ev.resource_action, ev.resource_status, - ev.resource_status_reason, ev.physical_resource_id, - ev.resource_properties, ev.resource_name, - ev.resource_type, ev.uuid, ev.created_at, ev.id) - def store(self): """Store the Event in the database.""" ev = { diff --git a/heat/engine/service.py b/heat/engine/service.py index 50a596ed7..e8eb10478 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -47,7 +47,6 @@ from heat.engine import attributes from heat.engine.cfn import template as cfntemplate from heat.engine import clients from heat.engine import environment -from heat.engine import event as evt from heat.engine.hot import functions as hot_functions from heat.engine import parameter_groups from heat.engine import properties @@ -1592,8 +1591,10 @@ class EngineService(service.Service): :param sort_dir: the direction of the sort ('asc' or 'desc'). """ + stacks = {} if stack_identity is not None: st = self._get_stack(cnxt, stack_identity, show_deleted=True) + stacks[st.id] = st events = event_object.Event.get_all_by_stack( cnxt, @@ -1611,16 +1612,18 @@ class EngineService(service.Service): sort_dir=sort_dir, filters=filters) - stacks = {} - - def get_stack(stack_id): + def get_stack_identifier(stack_id): if stack_id not in stacks: - stacks[stack_id] = parser.Stack.load(cnxt, stack_id) - return stacks[stack_id] + s = stack_object.Stack.get_by_id( + cnxt, + stack_id, + show_deleted=True) + if not s: + return + stacks[stack_id] = s + return stacks[stack_id].identifier() - return [api.format_event(evt.Event.load(cnxt, - e.id, e, - get_stack(e.stack_id))) + return [api.format_event(e, get_stack_identifier(e.stack_id)) for e in events] def _authorize_stack_user(self, cnxt, stack, resource_name): diff --git a/heat/objects/event.py b/heat/objects/event.py index a94168f15..10f8cd5cd 100644 --- a/heat/objects/event.py +++ b/heat/objects/event.py @@ -17,6 +17,7 @@ from oslo_versionedobjects import base from oslo_versionedobjects import fields +from heat.common import identifier from heat.db import api as db_api from heat.objects import base as heat_base from heat.objects import fields as heat_fields @@ -80,3 +81,11 @@ class Event( def create(cls, context, values): return cls._from_db_object(context, cls(), db_api.event_create(context, values)) + + def identifier(self, stack_identifier): + """Return a unique identifier for the event.""" + + res_id = identifier.ResourceIdentifier( + resource_name=self.resource_name, **stack_identifier) + + return identifier.EventIdentifier(event_id=str(self.uuid), **res_id) diff --git a/heat/tests/test_engine_api_utils.py b/heat/tests/test_engine_api_utils.py index fbad8b78d..addfc3d6a 100644 --- a/heat/tests/test_engine_api_utils.py +++ b/heat/tests/test_engine_api_utils.py @@ -20,13 +20,13 @@ from oslo_utils import timeutils import six from heat.common import exception -from heat.common import identifier from heat.common import template_format from heat.engine import api from heat.engine import event from heat.engine import parameters from heat.engine import stack as parser from heat.engine import template +from heat.objects import event as event_object from heat.rpc import api as rpc_api from heat.tests import common from heat.tests import utils @@ -49,17 +49,19 @@ class FormatTest(common.HeatTestCase): 'generic4': {'Type': 'StackResourceType'} } }) - self.stack = parser.Stack(utils.dummy_context(), 'test_stack', + self.context = utils.dummy_context() + self.stack = parser.Stack(self.context, 'test_stack', tmpl, stack_id=str(uuid.uuid4())) - def _dummy_event(self, event_id): + def _dummy_event(self): resource = self.stack['generic1'] - return event.Event(utils.dummy_context(), self.stack, 'CREATE', - 'COMPLETE', 'state changed', - 'z3455xyc-9f88-404d-a85b-5315293e67de', - resource.properties, resource.name, resource.type(), - uuid='abc123yc-9f88-404d-a85b-531529456xyz', - id=event_id) + ev = event.Event(self.context, self.stack, 'CREATE', + 'COMPLETE', 'state changed', + 'z3455xyc-9f88-404d-a85b-5315293e67de', + resource.properties, resource.name, resource.type(), + uuid='abc123yc-9f88-404d-a85b-531529456xyz') + event_id = ev.store() + return event_object.Event.get_by_id(self.context, event_id) def test_format_stack_resource(self): self.stack.created_time = datetime(2015, 8, 3, 17, 5, 1) @@ -281,10 +283,7 @@ class FormatTest(common.HeatTestCase): self.assertEqual('foobar', formatted[rpc_api.RES_PARENT_RESOURCE]) def test_format_event_identifier_uuid(self): - self._test_format_event('abc123yc-9f88-404d-a85b-531529456xyz') - - def _test_format_event(self, event_id): - event = self._dummy_event(event_id) + event = self._dummy_event() event_keys = set(( rpc_api.EVENT_ID, @@ -299,16 +298,16 @@ class FormatTest(common.HeatTestCase): rpc_api.EVENT_RES_TYPE, rpc_api.EVENT_RES_PROPERTIES)) - formatted = api.format_event(event) + formatted = api.format_event(event, self.stack.identifier()) self.assertEqual(event_keys, set(formatted.keys())) event_id_formatted = formatted[rpc_api.EVENT_ID] - event_identifier = identifier.EventIdentifier( - event_id_formatted['tenant'], - event_id_formatted['stack_name'], - event_id_formatted['stack_id'], - event_id_formatted['path']) - self.assertEqual(event_id, event_identifier.event_id) + self.assertEqual({ + 'path': '/resources/generic1/events/%s' % event.uuid, + 'stack_id': self.stack.id, + 'stack_name': 'test_stack', + 'tenant': 'test_tenant_id' + }, event_id_formatted) @mock.patch.object(api, 'format_stack_resource') def test_format_stack_preview(self, mock_fmt_resource): diff --git a/heat/tests/test_event.py b/heat/tests/test_event.py index af9bef231..1f58aac4d 100644 --- a/heat/tests/test_event.py +++ b/heat/tests/test_event.py @@ -14,9 +14,7 @@ import mock from oslo_config import cfg import oslo_db.exception -from oslo_utils import timeutils -from heat.common import exception from heat.engine import event from heat.engine import rsrc_defn from heat.engine import stack @@ -104,73 +102,6 @@ class EventTest(EventCommon): super(EventTest, self).setUp() self._setup_stack(tmpl) - def test_load(self): - self.resource.resource_id_set('resource_physical_id') - - e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', self.resource.properties, - self.resource.name, self.resource.type()) - - e.store() - self.assertIsNotNone(e.id) - - 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('wibble', loaded_e.physical_resource_id) - self.assertEqual('TEST', loaded_e.action) - self.assertEqual('IN_PROGRESS', loaded_e.status) - self.assertEqual('Testing', loaded_e.reason) - self.assertIsNotNone(loaded_e.timestamp) - self.assertEqual({'Foo': 'goo'}, loaded_e.resource_properties) - - def test_load_with_timestamp(self): - self.resource.resource_id_set('resource_physical_id') - timestamp = timeutils.utcnow() - - e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', self.resource.properties, - self.resource.name, self.resource.type(), - timestamp=timestamp) - - e.store() - self.assertIsNotNone(e.id) - - loaded_e = event.Event.load(self.ctx, e.id) - - self.assertEqual(timestamp, loaded_e.timestamp) - - def test_load_no_event(self): - with mock.patch("heat.objects.event.Event") as event_mock: - event_mock.get_by_id.return_value = None - self.assertRaisesRegex(exception.NotFound, - "^No event exists with id", - event.Event.load, self.ctx, 1) - - def test_load_given_stack_event(self): - self.resource.resource_id_set('resource_physical_id') - - e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', - 'wibble', self.resource.properties, - self.resource.name, self.resource.type()) - - e.store() - self.assertIsNotNone(e.id) - - ev = event_object.Event.get_by_id(self.ctx, e.id) - - 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('wibble', loaded_e.physical_resource_id) - self.assertEqual('TEST', loaded_e.action) - self.assertEqual('IN_PROGRESS', loaded_e.status) - self.assertEqual('Testing', loaded_e.reason) - self.assertIsNotNone(loaded_e.timestamp) - self.assertEqual({'Foo': 'goo'}, loaded_e.resource_properties) - def test_store_caps_events(self): cfg.CONF.set_override('event_purge_batch_size', 1, enforce_type=True) cfg.CONF.set_override('max_events_per_stack', 1, enforce_type=True)