Merge "Add basic get_live_state implementation"

This commit is contained in:
Jenkins 2016-01-06 14:47:49 +00:00 committed by Gerrit Code Review
commit d0935ef1fe
2 changed files with 167 additions and 0 deletions

View File

@ -48,6 +48,7 @@ from heat.objects import stack as stack_objects
from heat.rpc import client as rpc_client
cfg.CONF.import_opt('action_retry_limit', 'heat.common.config')
cfg.CONF.import_opt('observe_on_update', 'heat.common.config')
LOG = logging.getLogger(__name__)
@ -924,6 +925,19 @@ class Resource(object):
before_props = before.properties(self.properties_schema,
self.context)
if cfg.CONF.observe_on_update:
try:
resource_reality = self.get_live_state(before_props)
self._update_properties_with_live_state(before_props,
resource_reality)
except ValueError as ex:
LOG.warning(_LW("Resource cannot be updated with it's live "
"state in case of "
"error: %s"), six.text_type(ex))
except exception.EntityNotFound:
raise exception.UpdateReplace(self)
# Regenerate the schema, else validation would fail
self.regenerate_info_schema(after)
after_props = after.properties(self.properties_schema,
@ -1478,6 +1492,31 @@ class Resource(object):
ex)
return None
def get_live_state(self, resource_properties=None):
"""Default implementation; should be overridden by resources.
:returns: dict of resource's real state of properties.
"""
if not self.resource_id:
raise exception.EntityNotFound(entity='Resource', name=self.name)
return {}
def _update_properties_with_live_state(self, resource_properties,
live_properties):
"""Update resource properties data with live state properties.
Note, that live_properties can contains None values, so there's next
situation: property equals to some value, but live state has no such
property, i.e. property equals to None, so during update property
should be updated with None.
"""
for key in resource_properties:
if key in live_properties:
if resource_properties.get(key) != live_properties.get(key):
resource_properties.data.update(
{key: live_properties.get(key)})
def _resolve_attribute(self, name):
"""Default implementation of resolving resource's attributes.

View File

@ -1876,6 +1876,134 @@ class ResourceTest(common.HeatTestCase):
self.assertItemsEqual([2], res.requires)
self._assert_resource_lock(res.id, None, 2)
def _prepare_resource_live_state(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'abc',
'FooInt': 2})
res = generic_rsrc.ResourceWithProps('test_resource',
tmpl, self.stack)
res.update_allowed_properties = ('Foo', 'FooInt',)
scheduler.TaskRunner(res.create)()
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
return res
def test_update_resource_live_state_all_args(self):
res = self._prepare_resource_live_state()
self.patchobject(res, 'get_live_state', return_value={'Foo': 'abb',
'FooInt': 2})
cfg.CONF.set_override('observe_on_update', True)
utmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'bca',
'FooInt': 3})
scheduler.TaskRunner(res.update, utmpl)()
self.assertEqual((res.UPDATE, res.COMPLETE), res.state)
self.assertEqual({'Foo': 'bca', 'FooInt': 3}, res.properties.data)
def test_update_resource_live_state_some_args(self):
res = self._prepare_resource_live_state()
self.patchobject(res, 'get_live_state', return_value={'Foo': 'bca'})
cfg.CONF.set_override('observe_on_update', True)
utmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'bca',
'FooInt': 3})
scheduler.TaskRunner(res.update, utmpl)()
self.assertEqual((res.UPDATE, res.COMPLETE), res.state)
self.assertEqual({'Foo': 'bca', 'FooInt': 3}, res.properties.data)
def test_update_resource_live_state_not_found(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithRequiredProps',
{'Foo': 'abc'})
res = generic_rsrc.ResourceWithRequiredProps('test_resource',
tmpl, self.stack)
res.update_allowed_properties = ('Foo',)
self.patchobject(res, 'get_live_state',
side_effect=[exception.EntityNotFound(
entity='resource', name='test')])
cfg.CONF.set_override('observe_on_update', True)
scheduler.TaskRunner(res.create)()
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
utmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'bca'})
self.assertRaises(exception.UpdateReplace,
scheduler.TaskRunner(res.update, utmpl))
def test_update_resource_live_state_other_error(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithRequiredProps',
{'Foo': 'abc'})
res = generic_rsrc.ResourceWithRequiredProps('test_resource',
tmpl, self.stack)
res.update_allowed_properties = ('Foo',)
self.patchobject(res, 'get_live_state',
side_effect=[exception.Error('boom')])
cfg.CONF.set_override('observe_on_update', True)
scheduler.TaskRunner(res.create)()
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
utmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'bca'})
self.assertRaises(exception.Error,
scheduler.TaskRunner(res.update, utmpl))
def test_update_resource_live_state_not_found_id(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithRequiredProps',
{'Foo': 'abc'})
res = generic_rsrc.ResourceWithRequiredProps('test_resource',
tmpl, self.stack)
res.update_allowed_properties = ('Foo',)
cfg.CONF.set_override('observe_on_update', True)
scheduler.TaskRunner(res.create)()
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
utmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'bca'})
res.resource_id = None
self.assertRaises(exception.UpdateReplace,
scheduler.TaskRunner(res.update, utmpl))
def test_update_resource_live_state_no_stored_properties(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithRequiredProps',
{'Foo': 'abc'})
res = generic_rsrc.ResourceWithRequiredProps('test_resource',
tmpl, self.stack)
res.update_allowed_properties = ('Foo',)
cfg.CONF.set_override('observe_on_update', True)
scheduler.TaskRunner(res.create)()
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
utmpl = rsrc_defn.ResourceDefinition('test_resource',
'ResourceWithPropsType',
{'Foo': 'bca'})
self.patchobject(res, 'get_live_state', side_effect=[ValueError])
scheduler.TaskRunner(res.update, utmpl)()
self.assertEqual((res.UPDATE, res.COMPLETE), res.state)
@mock.patch.object(resource.scheduler.TaskRunner, '__init__',
return_value=None)
@mock.patch.object(resource.scheduler.TaskRunner, '__call__')