Destroy orphan objects

Murano has a garbage collector that destroys objects
which were deleted in API after last deployment.
However if objects were removed from object model
during deployment (action execution) or were created
and not saved there they left unnoticed by the engine
causing resource leak.

With this change all objects that are presented in object store
but were not serialized to result object model are get destroyed.

Change-Id: I107b34e3ae4e1b5835645e116f9445535dfb7636
Closes-Bug: #1562804
This commit is contained in:
Stan Lagun 2016-03-31 02:06:59 +03:00
parent 14f08ecc85
commit 855e5f0d34
5 changed files with 45 additions and 12 deletions

View File

@ -210,7 +210,12 @@ class TaskExecutor(object):
action_result = self._invoke(executor) action_result = self._invoke(executor)
finally: finally:
try: try:
self._model = serializer.serialize_model(obj, executor) self._model, alive_object_ids = \
serializer.serialize_model(obj, executor)
LOG.debug('Cleaning up orphan objects')
n = executor.cleanup_orphans(alive_object_ids)
LOG.debug('{} orphan objects were destroyed'.format(n))
except Exception as e: except Exception as e:
return self.exception_result(e, None, '<model>') return self.exception_result(e, None, '<model>')
except Exception as e: except Exception as e:

View File

@ -242,14 +242,35 @@ class MuranoDslExecutor(object):
objects_to_clean.append(obj) objects_to_clean.append(obj)
if objects_to_clean: if objects_to_clean:
for obj in objects_to_clean: for obj in objects_to_clean:
methods = obj.type.find_methods(lambda m: m.name == '.destroy') self._destroy_object(obj)
for method in methods:
try: def cleanup_orphans(self, alive_object_ids):
method.invoke(self, obj, (), {}, None) with helpers.execution_session(self._session):
except Exception as e: orphan_ids = self._collect_orphans(alive_object_ids)
LOG.warning(_LW( self._destroy_orphans(orphan_ids)
'Muted exception during execution of .destroy ' return len(orphan_ids)
'on {0}: {1}').format(obj, e), exc_info=True)
def _collect_orphans(self, alive_object_ids):
orphan_ids = []
for obj_id in self._object_store.iterate():
if obj_id not in alive_object_ids:
orphan_ids.append(obj_id)
return orphan_ids
def _destroy_orphans(self, orphan_ids):
for obj_id in orphan_ids:
self._destroy_object(self._object_store.get(obj_id))
self._object_store.remove(obj_id)
def _destroy_object(self, obj):
methods = obj.type.find_methods(lambda m: m.name == '.destroy')
for method in methods:
try:
method.invoke(self, obj, (), {}, None)
except Exception as e:
LOG.warning(_LW(
'Muted exception during execution of .destroy '
'on {0}: {1}').format(obj, e), exc_info=True)
def _list_potential_object_ids(self, data): def _list_potential_object_ids(self, data):
if isinstance(data, dict): if isinstance(data, dict):

View File

@ -52,6 +52,12 @@ class ObjectStore(object):
def put(self, murano_object): def put(self, murano_object):
self._store[murano_object.object_id] = murano_object self._store[murano_object.object_id] = murano_object
def iterate(self):
return six.iterkeys(self._store)
def remove(self, object_id):
self._store.pop(object_id)
def load(self, value, owner, context=None): def load(self, value, owner, context=None):
if value is None: if value is None:
return None return None

View File

@ -26,7 +26,7 @@ class ObjRef(object):
def serialize(obj): def serialize(obj):
return serialize_model(obj, None, True)['Objects'] return serialize_model(obj, None, True)[0]['Objects']
def _serialize_object(root_object, designer_attributes, allow_refs): def _serialize_object(root_object, designer_attributes, allow_refs):
@ -53,6 +53,7 @@ def serialize_model(root_object, executor, allow_refs=False):
tree = None tree = None
tree_copy = None tree_copy = None
attributes = [] attributes = []
serialized_objects = set()
else: else:
tree, serialized_objects = _serialize_object( tree, serialized_objects = _serialize_object(
root_object, designer_attributes, allow_refs) root_object, designer_attributes, allow_refs)
@ -66,7 +67,7 @@ def serialize_model(root_object, executor, allow_refs=False):
'Objects': tree, 'Objects': tree,
'ObjectsCopy': tree_copy, 'ObjectsCopy': tree_copy,
'Attributes': attributes 'Attributes': attributes
} }, serialized_objects
def _serialize_available_action(obj, current_actions): def _serialize_available_action(obj, current_actions):

View File

@ -124,7 +124,7 @@ class Runner(object):
@property @property
def serialized_model(self): def serialized_model(self):
return serializer.serialize_model(self._root, self.executor) return serializer.serialize_model(self._root, self.executor)[0]
@property @property
def preserve_exception(self): def preserve_exception(self):