Use Event versioned object for event_list formatting

This change removes the event.Event.load method, and switches
api.format_event to using the Event versioned object instead.

Since the only purpose of loading the stack is to get its identifier,
this is now also coming from a Stack versioned object.

Related-Bug: #1588561
Change-Id: I7b1fc99894818b44cde08cf08b010b1d1fb94e9f
This commit is contained in:
Steve Baker 2016-06-03 14:56:43 +12:00
parent d9855dfe0b
commit 7bfbace79f
6 changed files with 46 additions and 128 deletions

View File

@ -379,20 +379,17 @@ def format_stack_preview(stack):
return fmt_stack return fmt_stack
def format_event(event): def format_event(event, stack_identifier):
stack_identifier = event.stack.identifier()
event_timestamp = event.timestamp or timeutils.utcnow()
result = { 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_ID: dict(stack_identifier),
rpc_api.EVENT_STACK_NAME: stack_identifier.stack_name, 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_NAME: event.resource_name,
rpc_api.EVENT_RES_PHYSICAL_ID: event.physical_resource_id, rpc_api.EVENT_RES_PHYSICAL_ID: event.physical_resource_id,
rpc_api.EVENT_RES_ACTION: event.action, rpc_api.EVENT_RES_ACTION: event.resource_action,
rpc_api.EVENT_RES_STATUS: event.status, rpc_api.EVENT_RES_STATUS: event.resource_status,
rpc_api.EVENT_RES_STATUS_DATA: event.reason, rpc_api.EVENT_RES_STATUS_DATA: event.resource_status_reason,
rpc_api.EVENT_RES_TYPE: event.resource_type, rpc_api.EVENT_RES_TYPE: event.resource_type,
rpc_api.EVENT_RES_PROPERTIES: event.resource_properties, rpc_api.EVENT_RES_PROPERTIES: event.resource_properties,
} }

View File

@ -17,8 +17,6 @@ import six
import oslo_db.exception import oslo_db.exception
from oslo_log import log as logging from oslo_log import log as logging
from heat.common import exception
from heat.common.i18n import _
from heat.common import identifier from heat.common import identifier
from heat.objects import event as event_object from heat.objects import event as event_object
@ -54,25 +52,6 @@ class Event(object):
self.timestamp = timestamp self.timestamp = timestamp
self.id = id 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): def store(self):
"""Store the Event in the database.""" """Store the Event in the database."""
ev = { ev = {

View File

@ -47,7 +47,6 @@ from heat.engine import attributes
from heat.engine.cfn import template as cfntemplate from heat.engine.cfn import template as cfntemplate
from heat.engine import clients from heat.engine import clients
from heat.engine import environment from heat.engine import environment
from heat.engine import event as evt
from heat.engine.hot import functions as hot_functions from heat.engine.hot import functions as hot_functions
from heat.engine import parameter_groups from heat.engine import parameter_groups
from heat.engine import properties from heat.engine import properties
@ -1592,8 +1591,10 @@ class EngineService(service.Service):
:param sort_dir: the direction of the sort ('asc' or 'desc'). :param sort_dir: the direction of the sort ('asc' or 'desc').
""" """
stacks = {}
if stack_identity is not None: if stack_identity is not None:
st = self._get_stack(cnxt, stack_identity, show_deleted=True) st = self._get_stack(cnxt, stack_identity, show_deleted=True)
stacks[st.id] = st
events = event_object.Event.get_all_by_stack( events = event_object.Event.get_all_by_stack(
cnxt, cnxt,
@ -1611,16 +1612,18 @@ class EngineService(service.Service):
sort_dir=sort_dir, sort_dir=sort_dir,
filters=filters) filters=filters)
stacks = {} def get_stack_identifier(stack_id):
def get_stack(stack_id):
if stack_id not in stacks: if stack_id not in stacks:
stacks[stack_id] = parser.Stack.load(cnxt, stack_id) s = stack_object.Stack.get_by_id(
return stacks[stack_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, return [api.format_event(e, get_stack_identifier(e.stack_id))
e.id, e,
get_stack(e.stack_id)))
for e in events] for e in events]
def _authorize_stack_user(self, cnxt, stack, resource_name): def _authorize_stack_user(self, cnxt, stack, resource_name):

View File

@ -17,6 +17,7 @@
from oslo_versionedobjects import base from oslo_versionedobjects import base
from oslo_versionedobjects import fields from oslo_versionedobjects import fields
from heat.common import identifier
from heat.db import api as db_api from heat.db import api as db_api
from heat.objects import base as heat_base from heat.objects import base as heat_base
from heat.objects import fields as heat_fields from heat.objects import fields as heat_fields
@ -80,3 +81,11 @@ class Event(
def create(cls, context, values): def create(cls, context, values):
return cls._from_db_object(context, cls(), return cls._from_db_object(context, cls(),
db_api.event_create(context, values)) 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)

View File

@ -20,13 +20,13 @@ from oslo_utils import timeutils
import six import six
from heat.common import exception from heat.common import exception
from heat.common import identifier
from heat.common import template_format from heat.common import template_format
from heat.engine import api from heat.engine import api
from heat.engine import event from heat.engine import event
from heat.engine import parameters from heat.engine import parameters
from heat.engine import stack as parser from heat.engine import stack as parser
from heat.engine import template from heat.engine import template
from heat.objects import event as event_object
from heat.rpc import api as rpc_api from heat.rpc import api as rpc_api
from heat.tests import common from heat.tests import common
from heat.tests import utils from heat.tests import utils
@ -49,17 +49,19 @@ class FormatTest(common.HeatTestCase):
'generic4': {'Type': 'StackResourceType'} '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())) tmpl, stack_id=str(uuid.uuid4()))
def _dummy_event(self, event_id): def _dummy_event(self):
resource = self.stack['generic1'] resource = self.stack['generic1']
return event.Event(utils.dummy_context(), self.stack, 'CREATE', ev = event.Event(self.context, self.stack, 'CREATE',
'COMPLETE', 'state changed', 'COMPLETE', 'state changed',
'z3455xyc-9f88-404d-a85b-5315293e67de', 'z3455xyc-9f88-404d-a85b-5315293e67de',
resource.properties, resource.name, resource.type(), resource.properties, resource.name, resource.type(),
uuid='abc123yc-9f88-404d-a85b-531529456xyz', uuid='abc123yc-9f88-404d-a85b-531529456xyz')
id=event_id) event_id = ev.store()
return event_object.Event.get_by_id(self.context, event_id)
def test_format_stack_resource(self): def test_format_stack_resource(self):
self.stack.created_time = datetime(2015, 8, 3, 17, 5, 1) 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]) self.assertEqual('foobar', formatted[rpc_api.RES_PARENT_RESOURCE])
def test_format_event_identifier_uuid(self): def test_format_event_identifier_uuid(self):
self._test_format_event('abc123yc-9f88-404d-a85b-531529456xyz') event = self._dummy_event()
def _test_format_event(self, event_id):
event = self._dummy_event(event_id)
event_keys = set(( event_keys = set((
rpc_api.EVENT_ID, rpc_api.EVENT_ID,
@ -299,16 +298,16 @@ class FormatTest(common.HeatTestCase):
rpc_api.EVENT_RES_TYPE, rpc_api.EVENT_RES_TYPE,
rpc_api.EVENT_RES_PROPERTIES)) 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())) self.assertEqual(event_keys, set(formatted.keys()))
event_id_formatted = formatted[rpc_api.EVENT_ID] event_id_formatted = formatted[rpc_api.EVENT_ID]
event_identifier = identifier.EventIdentifier( self.assertEqual({
event_id_formatted['tenant'], 'path': '/resources/generic1/events/%s' % event.uuid,
event_id_formatted['stack_name'], 'stack_id': self.stack.id,
event_id_formatted['stack_id'], 'stack_name': 'test_stack',
event_id_formatted['path']) 'tenant': 'test_tenant_id'
self.assertEqual(event_id, event_identifier.event_id) }, event_id_formatted)
@mock.patch.object(api, 'format_stack_resource') @mock.patch.object(api, 'format_stack_resource')
def test_format_stack_preview(self, mock_fmt_resource): def test_format_stack_preview(self, mock_fmt_resource):

View File

@ -14,9 +14,7 @@
import mock import mock
from oslo_config import cfg from oslo_config import cfg
import oslo_db.exception import oslo_db.exception
from oslo_utils import timeutils
from heat.common import exception
from heat.engine import event from heat.engine import event
from heat.engine import rsrc_defn from heat.engine import rsrc_defn
from heat.engine import stack from heat.engine import stack
@ -104,73 +102,6 @@ class EventTest(EventCommon):
super(EventTest, self).setUp() super(EventTest, self).setUp()
self._setup_stack(tmpl) 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): def test_store_caps_events(self):
cfg.CONF.set_override('event_purge_batch_size', 1, enforce_type=True) cfg.CONF.set_override('event_purge_batch_size', 1, enforce_type=True)
cfg.CONF.set_override('max_events_per_stack', 1, enforce_type=True) cfg.CONF.set_override('max_events_per_stack', 1, enforce_type=True)