deb-murano/murano/dsl/object_store.py
Stan Lagun 91d52895d1 Don't put non-initialized objects into ObjectStore
Because object model deserialization happens in
several phases nested objects without fixed ID
will be created again upon each pass and only
the last one will be the property value. Previously
created objects remain in the ObjectStore but were
not initialized. This is going to cause issues once garbage
collection is implemented because it may attempt to
collect uninitialized objects.

With this commit objects that are not fully initialized are
not copied into the permanent object store anymore.

Change-Id: I6924b93d79c69181fe2cc8baa329a3822892af3c
2016-08-12 04:55:32 +00:00

157 lines
5.4 KiB
Python

# Copyright (c) 2014 Mirantis, Inc.
#
# 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.
import weakref
import six
from murano.dsl import dsl_types
from murano.dsl import helpers
from murano.dsl import murano_object
class ObjectStore(object):
def __init__(self, executor, parent_store=None):
self._parent_store = parent_store
self._store = {}
self._designer_attributes_store = {}
self._executor = weakref.ref(executor)
@property
def executor(self):
return self._executor()
def get(self, object_id):
if object_id in self._store:
result = self._store[object_id]
if not isinstance(result, dsl_types.MuranoObject):
result = result.object
return result
if self._parent_store:
return self._parent_store.get(object_id)
return None
def has(self, object_id):
return object_id in self._store
def put(self, murano_object, object_id=None):
self._store[object_id or 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, default_type=None,
scope_type=None, context=None, keep_ids=False):
# do the object model load in a temporary object store and copy
# loaded objects here after that
model_store = InitializationObjectStore(
owner, self, keep_ids)
with helpers.with_object_store(model_store):
result = model_store.load(
value, owner, scope_type=scope_type,
default_type=default_type, context=context)
for obj_id in model_store.iterate():
obj = model_store.get(obj_id)
if obj.initialized:
self.put(obj)
return result
@staticmethod
def _get_designer_attributes(header):
return dict((k, v) for k, v in six.iteritems(header)
if str(k).startswith('_'))
def designer_attributes(self, object_id):
return self._designer_attributes_store.get(object_id, {})
@property
def initializing(self):
return False
@property
def parent_store(self):
return self._parent_store
# Temporary ObjectStore to load object graphs. Does 2-phase load
# and maintains internal state on what phase is currently running
# as well as objects that are in the middle of initialization.
# Required in order to isolate semi-initialized objects from regular
# objects in main ObjectStore and internal state between graph loads
# in different threads. Once the load is done all objects are copied
# to the parent ObjectStore
class InitializationObjectStore(ObjectStore):
def __init__(self, root_owner, parent_store, keep_ids):
super(InitializationObjectStore, self).__init__(
parent_store.executor, parent_store)
self._initializing = False
self._root_owner = root_owner
self._keep_ids = keep_ids
self._initializers = []
@property
def initializing(self):
return self._initializing
def load(self, value, owner, default_type=None,
scope_type=None, context=None, **kwargs):
parsed = helpers.parse_object_definition(value, scope_type, context)
if not parsed:
raise ValueError('Invalid object representation format')
if owner is self._root_owner:
self._initializing = True
class_obj = parsed['type'] or default_type
if not class_obj:
raise ValueError(
'Invalid object representation: '
'no type information was provided')
if isinstance(class_obj, dsl_types.MuranoTypeReference):
class_obj = class_obj.type
object_id = parsed['id']
obj = None if object_id is None else self._store.get(object_id)
if not obj:
obj = murano_object.MuranoObject(
class_obj, helpers.weak_proxy(owner),
name=parsed['name'],
object_id=object_id if self._keep_ids else None)
self.put(obj, object_id or obj.object_id)
system_value = ObjectStore._get_designer_attributes(
parsed['extra'])
self._designer_attributes_store[object_id] = system_value
if context is None:
context = self.executor.create_object_context(obj)
def run_initialize():
self._initializers.extend(
obj.initialize(context, parsed['properties']))
run_initialize()
if owner is self._root_owner:
self._initializing = False
run_initialize()
if owner is self._root_owner:
with helpers.with_object_store(self.parent_store):
for fn in self._initializers:
fn()
return obj