Convert Events to separate action/status

First step in decoupling action/status in Resource

This adjusts the Event DB columns, model and class to split
action/status, but in order to be backwards compatible we set the action
to None, which means we can do the right thing in the CFN API, and
resource_status still maps correctly to what is expected in
python-heatclient (which will need to change after the Resource
action/status split happens)

Change-Id: I261d47d2a3fdb3cdc2a8aaf6c159763f4636f633
This commit is contained in:
Steven Hardy 2013-06-13 11:19:32 +01:00
parent a234b3a3d0
commit 5206990f9a
11 changed files with 130 additions and 20 deletions

View File

@ -469,6 +469,12 @@ class StackController(object):
}
result = api_utils.reformat_dict_keys(keymap, e)
action = e[engine_api.EVENT_RES_ACTION]
status = e[engine_api.EVENT_RES_STATUS]
if action and status:
result['ResourceStatus'] = '_'.join((action, status))
else:
result['ResourceStatus'] = status
result['ResourceProperties'] = json.dumps(result[
'ResourceProperties'])

View File

@ -29,6 +29,7 @@ summary_keys = [
engine_api.EVENT_ID,
engine_api.EVENT_TIMESTAMP,
engine_api.EVENT_RES_NAME,
engine_api.EVENT_RES_ACTION,
engine_api.EVENT_RES_STATUS,
engine_api.EVENT_RES_STATUS_DATA,
engine_api.EVENT_RES_PHYSICAL_ID,

View File

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from sqlalchemy import *
from migrate import *
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
event = Table('event', meta, autoload=True)
# Currently there is a 'name' column which really holds the
# resource status, so rename it and add a separate action column
# action is e.g "CREATE" and status is e.g "IN_PROGRESS"
event.c.name.alter(name='resource_status')
Column('resource_action', String(255)).create(event)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
event = Table('event', meta, autoload=True)
event.c.resource_status.alter(name='name')
event.c.resource_action.drop()

View File

@ -197,7 +197,8 @@ class Event(BASE, HeatBase):
stack_id = Column(String, ForeignKey('stack.id'), nullable=False)
stack = relationship(Stack, backref=backref('events'))
name = Column(String)
resource_action = Column(String)
resource_status = Column(String)
logical_resource_id = Column(String)
physical_resource_id = Column(String)
resource_status_reason = Column(String)

View File

@ -126,7 +126,8 @@ def format_event(event):
EVENT_TIMESTAMP: timeutils.isotime(event.timestamp),
EVENT_RES_NAME: event.resource.name,
EVENT_RES_PHYSICAL_ID: event.physical_resource_id,
EVENT_RES_STATUS: event.new_state,
EVENT_RES_ACTION: event.action,
EVENT_RES_STATUS: event.status,
EVENT_RES_STATUS_DATA: event.reason,
EVENT_RES_TYPE: event.resource.type(),
EVENT_RES_PROPERTIES: event.resource_properties,

View File

@ -25,7 +25,7 @@ class Event(object):
'''Class representing a Resource state change.'''
def __init__(self, context, stack, resource,
new_state, reason,
action, status, reason,
physical_resource_id, resource_properties,
timestamp=None, id=None):
'''
@ -36,7 +36,8 @@ class Event(object):
self.context = context
self.resource = resource
self.stack = stack
self.new_state = new_state
self.action = action
self.status = status
self.reason = reason
self.physical_resource_id = physical_resource_id
try:
@ -60,7 +61,8 @@ class Event(object):
resource = stack[ev.logical_resource_id]
event = cls(context, stack, resource,
ev.name, ev.resource_status_reason,
ev.resource_action, ev.resource_status,
ev.resource_status_reason,
ev.physical_resource_id, ev.resource_properties,
ev.created_at, ev.id)
@ -72,9 +74,8 @@ class Event(object):
'logical_resource_id': self.resource.name,
'physical_resource_id': self.physical_resource_id,
'stack_id': self.stack.id,
'stack_name': self.stack.name,
'resource_status': self.new_state,
'name': self.new_state,
'resource_action': self.action,
'resource_status': self.status,
'resource_status_reason': self.reason,
'resource_type': self.resource.type(),
'resource_properties': self.resource_properties,

View File

@ -505,7 +505,7 @@ class Resource(object):
def _add_event(self, new_state, reason):
'''Add a state change event to the database.'''
ev = event.Event(self.context, self.stack, self,
new_state, reason,
None, new_state, reason,
self.resource_id, self.properties)
try:

View File

@ -48,28 +48,28 @@ STACK_OUTPUT_KEYS = (
RES_KEYS = (
RES_DESCRIPTION, RES_UPDATED_TIME,
RES_NAME, RES_PHYSICAL_ID, RES_METADATA,
RES_NAME, RES_PHYSICAL_ID, RES_METADATA, RES_ACTION,
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
RES_ID, RES_STACK_ID, RES_STACK_NAME,
) = (
'description', 'updated_time',
'logical_resource_id', 'physical_resource_id', 'metadata',
'resource_status', 'resource_status_reason', 'resource_type',
'resource_identity', STACK_ID, STACK_NAME,
'resource_action', 'resource_status', 'resource_status_reason',
'resource_type', 'resource_identity', STACK_ID, STACK_NAME,
)
EVENT_KEYS = (
EVENT_ID,
EVENT_STACK_ID, EVENT_STACK_NAME,
EVENT_TIMESTAMP,
EVENT_RES_NAME, EVENT_RES_PHYSICAL_ID,
EVENT_RES_NAME, EVENT_RES_PHYSICAL_ID, EVENT_RES_ACTION,
EVENT_RES_STATUS, EVENT_RES_STATUS_DATA, EVENT_RES_TYPE,
EVENT_RES_PROPERTIES,
) = (
'event_identity',
STACK_ID, STACK_NAME,
"event_time",
RES_NAME, RES_PHYSICAL_ID,
RES_NAME, RES_PHYSICAL_ID, RES_ACTION,
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
'resource_properties',
)

View File

@ -957,6 +957,7 @@ class CfnStackControllerTest(HeatTestCase):
u'stack_name': u'wordpress',
u'stack_id': u'6',
u'path': u'/resources/WikiDatabase/events/42'},
u'resource_action': u'TEST',
u'resource_status': u'IN_PROGRESS',
u'physical_resource_id': None,
u'resource_properties': {u'UserData': u'blah'},
@ -983,7 +984,68 @@ class CfnStackControllerTest(HeatTestCase):
{'StackEvents':
[{'EventId': u'42',
'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
'ResourceStatus': u'IN_PROGRESS',
'ResourceStatus': u'TEST_IN_PROGRESS',
'ResourceType': u'AWS::EC2::Instance',
'Timestamp': u'2012-07-23T13:05:39Z',
'StackName': u'wordpress',
'ResourceProperties':
json.dumps({u'UserData': u'blah'}),
'PhysicalResourceId': None,
'ResourceStatusReason': u'state changed',
'LogicalResourceId': u'WikiDatabase'}]}}}
self.assertEqual(response, expected)
self.m.VerifyAll()
def test_events_list_onlystatus(self):
# Format a dummy request
stack_name = "wordpress"
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
params = {'Action': 'DescribeStackEvents', 'StackName': stack_name}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = [{u'stack_name': u'wordpress',
u'event_time': u'2012-07-23T13:05:39Z',
u'stack_identity': {u'tenant': u't',
u'stack_name': u'wordpress',
u'stack_id': u'6',
u'path': u''},
u'logical_resource_id': u'WikiDatabase',
u'resource_status_reason': u'state changed',
u'event_identity':
{u'tenant': u't',
u'stack_name': u'wordpress',
u'stack_id': u'6',
u'path': u'/resources/WikiDatabase/events/42'},
u'resource_action': None,
u'resource_status': u'TEST_IN_PROGRESS',
u'physical_resource_id': None,
u'resource_properties': {u'UserData': u'blah'},
u'resource_type': u'AWS::EC2::Instance'}]
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, self.topic,
{'namespace': None,
'method': 'identify_stack',
'args': {'stack_name': stack_name},
'version': self.api_version}, None).AndReturn(identity)
rpc.call(dummy_req.context, self.topic,
{'namespace': None,
'method': 'list_events',
'args': {'stack_identity': identity},
'version': self.api_version}, None).AndReturn(engine_resp)
self.m.ReplayAll()
response = self.controller.events_list(dummy_req)
expected = {'DescribeStackEventsResponse':
{'DescribeStackEventsResult':
{'StackEvents':
[{'EventId': u'42',
'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
'ResourceStatus': u'TEST_IN_PROGRESS',
'ResourceType': u'AWS::EC2::Instance',
'Timestamp': u'2012-07-23T13:05:39Z',
'StackName': u'wordpress',

View File

@ -557,7 +557,7 @@ class stackServiceTest(HeatTestCase):
self.assertEqual(ev['resource_properties']['InstanceType'],
'm1.large')
self.assertTrue('resource_status' in ev)
self.assertTrue('resource_action' in ev)
self.assertTrue(ev['resource_status'] in ('IN_PROGRESS',
'CREATE_COMPLETE'))

View File

@ -67,7 +67,7 @@ class EventTest(HeatTestCase):
self.resource.resource_id_set('resource_physical_id')
e = event.Event(self.ctx, self.stack, self.resource,
'TEST_IN_PROGRESS', 'Testing',
'TEST', 'IN_PROGRESS', 'Testing',
'wibble', self.resource.properties)
e.store()
@ -79,14 +79,15 @@ class EventTest(HeatTestCase):
self.assertEqual(loaded_e.resource.name, self.resource.name)
self.assertEqual(loaded_e.resource.id, self.resource.id)
self.assertEqual(loaded_e.physical_resource_id, 'wibble')
self.assertEqual(loaded_e.new_state, 'TEST_IN_PROGRESS')
self.assertEqual(loaded_e.action, 'TEST')
self.assertEqual(loaded_e.status, 'IN_PROGRESS')
self.assertEqual(loaded_e.reason, 'Testing')
self.assertNotEqual(loaded_e.timestamp, None)
self.assertEqual(loaded_e.resource_properties, {'foo': True})
def test_identifier(self):
e = event.Event(self.ctx, self.stack, self.resource,
'TEST_IN_PROGRESS', 'Testing',
'TEST', 'IN_PROGRESS', 'Testing',
'wibble', self.resource.properties)
eid = e.store()
@ -103,6 +104,6 @@ class EventTest(HeatTestCase):
rname = 'bad_resource'
res = generic_rsrc.GenericResource(rname, tmpl, self.stack)
e = event.Event(self.ctx, self.stack, res,
'TEST_IN_PROGRESS', 'Testing',
'TEST', 'IN_PROGRESS', 'Testing',
'wibble', res.properties)
self.assertTrue('Error' in e.resource_properties)