From 7867fe23510e170efdeb2f8644c81a09c53bf1e7 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Thu, 22 Nov 2012 11:06:57 +0100 Subject: [PATCH] Add a class to represent Events Change-Id: Ie360343a264f2348af518c8f8eb8cfa0763ad18c Signed-off-by: Zane Bitter --- heat/engine/resources/event.py | 96 ++++++++++++++++++++++++++++++++++ heat/tests/test_event.py | 96 ++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 heat/engine/resources/event.py create mode 100644 heat/tests/test_event.py diff --git a/heat/engine/resources/event.py b/heat/engine/resources/event.py new file mode 100644 index 00000000..d7a76cd3 --- /dev/null +++ b/heat/engine/resources/event.py @@ -0,0 +1,96 @@ +# 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 heat.db import api as db_api +from heat.common import exception +from heat.engine import identifier +from heat.openstack.common import log as logging + +logger = logging.getLogger('heat.engine.resources.event') + + +class Event(object): + '''Class representing a Resource state change.''' + + def __init__(self, context, stack, resource, + new_state, reason, + physical_resource_id, resource_properties, + 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. + ''' + self.context = context + self.resource = resource + self.stack = stack + self.new_state = new_state + self.reason = reason + self.physical_resource_id = physical_resource_id + self.resource_properties = dict(resource_properties) + self.timestamp = timestamp + self.id = id + + @classmethod + def load(cls, context, event_id): + '''Retrieve an Event from the database''' + from heat.engine import parser + + ev = db_api.event_get(context, event_id) + if ev is None: + message = 'No event exists with id "%s"' % str(event_id) + raise exception.NotFound(message) + + stack = parser.Stack.load(context, ev.stack_id) + resource = stack[ev.logical_resource_id] + + event = cls(context, stack, resource, + ev.name, ev.resource_status_reason, + ev.physical_resource_id, ev.resource_properties, + ev.created_at, ev.id) + + return event + + def store(self): + '''Store the Event in the database''' + ev = { + '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_status_reason': self.reason, + 'resource_type': self.resource.type(), + 'resource_properties': self.resource_properties, + } + + if self.timestamp is not None: + ev['created_at'] = self.timestamp + + if self.id is not None: + logger.warning('Duplicating event') + + new_ev = db_api.event_create(self.context, ev) + self.id = new_ev.id + return self.id + + def identifier(self): + '''Return a unique identifier for the event''' + if self.id is None: + return None + + return identifier.EventIdentifier(event_id=str(self.id), + **self.resource.identifier()) diff --git a/heat/tests/test_event.py b/heat/tests/test_event.py new file mode 100644 index 00000000..50853dd5 --- /dev/null +++ b/heat/tests/test_event.py @@ -0,0 +1,96 @@ +# 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 nose.plugins.attrib import attr +import mox +import unittest + +from heat.common import context +import heat.db as db_api +from heat.engine import parser +from heat.engine import template +from heat.engine.resources import event +from heat.engine.resources import resource + + +tmpl = { + 'Resources': { + 'EventTestResource': { + 'Type': 'GenericResourceType', + } + } +} + + +@attr(tag=['unit', 'event']) +@attr(speed='fast') +class EventTest(unittest.TestCase): + + def setUp(self): + self.username = 'event_test_user' + + self.m = mox.Mox() + + self.ctx = context.get_admin_context() + self.m.StubOutWithMock(self.ctx, 'username') + self.ctx.username = self.username + + self.m.ReplayAll() + + self.stack = parser.Stack(self.ctx, 'event_load_test_stack', + template.Template(tmpl)) + self.stack.store() + + self.resource = self.stack['EventTestResource'] + self.resource._store() + + def tearDown(self): + db_api.stack_delete(self.ctx, self.stack.id) + self.m.UnsetStubs() + + 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', {'foo': 'bar'}) + + e.store() + self.assertNotEqual(e.id, None) + + loaded_e = event.Event.load(self.ctx, e.id) + + self.assertEqual(loaded_e.stack.id, self.stack.id) + 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.reason, 'Testing') + self.assertNotEqual(loaded_e.timestamp, None) + self.assertEqual(loaded_e.resource_properties, {'foo': 'bar'}) + + def test_identifier(self): + e = event.Event(self.ctx, self.stack, self.resource, + 'TEST_IN_PROGRESS', 'Testing', + 'wibble', {'foo': 'bar'}) + + eid = e.store() + expected_identifier = { + 'stack_name': self.stack.name, + 'stack_id': self.stack.id, + 'tenant': self.ctx.tenant_id, + 'path': '/resources/EventTestResource/events/%s' % str(eid) + } + self.assertEqual(e.identifier(), expected_identifier) -- 2.45.2