deb-murano/murano/dsl/serializer.py
Stan Lagun 068831ccd8 Package versioning
With this change MuranoPackage becomes first-class DSL citizen.
Packages have version, runtime_version (that is specified
in Format attribute of the manifest file) and a list of classes.
Previously engine used to have package loader which had most
of "load" functionality and class loader that mostly acted as an
adapter from package loader to interface that DSL used to
get classes. Now class loader is gone and is replaced with
package loader at the DSL level. Package loader is responsible
for loading packages by either package or class name (as it was
before) plus semantic_version spec (for example ">=1.2,<2.0").
Package loader can now keep track of several versions of the same
package.

Also packages now have requirements with version specs.
All class names that are encountered in application code are
looked up within requirements only. As a consequence
packages that use other packages without referencing
them explicitly will become broken. An exception from this rule
is core library which is referenced automatically.

Partially implements: blueprint murano-versioning

Change-Id: I8789ba45b6210e71bf4977a766f82b66d2a2d270
2015-09-03 12:06:42 +00:00

172 lines
5.8 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 types
from yaql import utils
from murano.dsl import dsl
from murano.dsl import dsl_types
from murano.dsl import murano_method
class ObjRef(object):
def __init__(self, obj):
self.ref_obj = obj
def serialize(obj):
return serialize_model(obj, None, True)['Objects']
def _serialize_object(root_object, designer_attributes, allow_refs):
serialized_objects = set()
obj = root_object
while True:
obj, need_another_pass = _pass12_serialize(
obj, None, serialized_objects, designer_attributes)
if not need_another_pass:
break
tree = [obj]
_pass3_serialize(tree, serialized_objects, allow_refs)
return tree[0], serialized_objects
def serialize_model(root_object, executor, allow_refs=False):
if executor is not None:
designer_attributes = executor.object_store.designer_attributes
else:
designer_attributes = None
if root_object is None:
tree = None
tree_copy = None
attributes = []
else:
tree, serialized_objects = _serialize_object(
root_object, designer_attributes, allow_refs)
tree_copy, _ = _serialize_object(root_object, None, allow_refs)
if executor is not None:
attributes = executor.attribute_store.serialize(serialized_objects)
else:
attributes = []
return {
'Objects': tree,
'ObjectsCopy': tree_copy,
'Attributes': attributes
}
def _serialize_available_action(obj, current_actions):
result = {}
actions = obj.type.find_methods(
lambda m: m.usage == murano_method.MethodUsages.Action)
for action in actions:
action_id = '{0}_{1}'.format(obj.object_id, action.name)
entry = current_actions.get(action_id, {'enabled': True})
entry['name'] = action.name
result[action_id] = entry
return result
def _pass12_serialize(value, parent, serialized_objects,
designer_attributes_getter):
if isinstance(value, dsl.MuranoObjectInterface):
value = value.object
if isinstance(value, (types.StringTypes, types.IntType, types.FloatType,
types.BooleanType, types.NoneType)):
return value, False
if isinstance(value, dsl_types.MuranoObject):
if value.owner is not parent or value.object_id in serialized_objects:
return ObjRef(value), True
elif isinstance(value, ObjRef):
if (value.ref_obj.object_id not in serialized_objects
and is_nested_in(value.ref_obj.owner, parent)):
value = value.ref_obj
else:
return value, False
if isinstance(value, dsl_types.MuranoObject):
result = value.to_dictionary()
if designer_attributes_getter is not None:
result['?'].update(designer_attributes_getter(value.object_id))
# deserialize and merge list of actions
result['?']['_actions'] = _serialize_available_action(
value, result['?'].get('_actions', {}))
serialized_objects.add(value.object_id)
return _pass12_serialize(
result, value, serialized_objects, designer_attributes_getter)
elif isinstance(value, utils.MappingType):
result = {}
need_another_pass = False
for d_key, d_value in value.iteritems():
result_key = str(d_key)
result_value = _pass12_serialize(
d_value, parent, serialized_objects,
designer_attributes_getter)
result[result_key] = result_value[0]
if result_value[1]:
need_another_pass = True
return result, need_another_pass
elif utils.is_sequence(value) or isinstance(value, utils.SetType):
need_another_pass = False
result = []
for t in value:
v, nmp = _pass12_serialize(
t, parent, serialized_objects, designer_attributes_getter)
if nmp:
need_another_pass = True
result.append(v)
return result, need_another_pass
else:
raise ValueError()
def _pass3_serialize(value, serialized_objects, allow_refs=False):
if isinstance(value, dict):
for d_key, d_value in value.items():
if isinstance(d_value, ObjRef):
if (d_value.ref_obj.object_id in serialized_objects
or allow_refs):
value[d_key] = d_value.ref_obj.object_id
else:
del value[d_key]
else:
_pass3_serialize(d_value, serialized_objects, allow_refs)
elif isinstance(value, list):
index = 0
while index < len(value):
item = value[index]
if isinstance(item, ObjRef):
if item.ref_obj.object_id in serialized_objects or allow_refs:
value[index] = item.ref_obj.object_id
else:
value.pop(index)
index -= 1
else:
_pass3_serialize(item, serialized_objects, allow_refs)
index += 1
return value
def is_nested_in(obj, ancestor):
while True:
if obj is ancestor:
return True
if obj is None:
return False
obj = obj.owner