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
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,
}

View File

@ -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 = {

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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)