Merge "Add basic get_live_state implementation"
This commit is contained in:
commit
d0935ef1fe
|
@ -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.
|
||||
|
||||
|
|
|
@ -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__')
|
||||
|
|
Loading…
Reference in New Issue