deb-murano/murano/dsl/object_store.py
Stan Lagun 77e3c6e2e9 Ability to instantiate object graph
Adds ability to load object sub-graph in a single call.
The graph may either be defined in the serializable
object-model format

?:
  id: ...
  type: ...
  classVersion: ...
  package: ...
 prop1: value
 prop2: value2

 or in the MuranoPL format

 ns:Type:
   prop1: value1
   prop2: value2
 id: ...
 name: ...

During the load IDs of the loaded objects are re-generated so
that explicit specification of IDs is only necessary for specification
of references within the graph and are not retained.

The load is done in the temporary ObjectStore. Loaded objects are
copied to the main ObjectStore then.

3rd overload of the new() function was added to deserialize the model:
new($model, $owner)

Change-Id: I849a5d6c9a28843398a4c4c884a1c3138ac7ede3
2016-07-28 14:13:22 +00:00

143 lines
5.2 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)
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
# 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
@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')
try:
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)
obj.initialize(context, parsed['properties'])
if owner is self._root_owner:
self._initializing = False
obj.initialize(context, parsed['properties'])
finally:
if owner is self._root_owner:
self._initializing = False
return obj