Support packages in dsl and engine

* Added package property to MuranoClass
   * Added new class loader: PackageClassLoader
   * Support loading packages from API and file-system
   * Extended ApplicationPackage to support getting resources from packages
   * Rewritten ResourceLoader to support loading resources from packages

Co-Authored-By: Serg Melikyan <smelikyan@mirantis.com>
Change-Id: I47e70f960104f78433c285411328f315638186da
This commit is contained in:
Ekaterina Fedorova 2014-04-04 16:58:06 +04:00
parent 246a35b6f9
commit ad6b8ece18
14 changed files with 439 additions and 53 deletions

View File

@ -11,6 +11,9 @@ bind_host = 0.0.0.0
# Port the bind the server to
bind_port = 8082
# Directory to store application package cache
# packages_cache =
# Set up logging. Make sure the user has permissions to write to this file! To use syslog just set use_syslog parameter value to 'True'.
log_file = /tmp/murano-api.log
@ -142,3 +145,14 @@ auth_url = http://localhost:5000/v2.0
#key_file =
# If set then the server's certificate will not be verified
insecure = False
[murano_opts]
url = http:/localhost:8082
# Optional CA cert file to use in SSL connections
#cacert =
# Optional PEM-formatted certificate chain file
#cert_file =
# Optional PEM-formatted file that contains the private key
#key_file =
# If set then the server's certificate will not be verified
insecure = False

View File

@ -21,6 +21,7 @@ import logging.config
import logging.handlers
import os
import sys
import tempfile
from oslo.config import cfg
from paste import deploy
@ -76,6 +77,16 @@ keystone_opts = [
cfg.StrOpt('key_file')
]
murano_opts = [
cfg.StrOpt('url', help=_('Optional murano url in format '
'like http://0.0.0.0:8082')),
cfg.BoolOpt('insecure', default=False),
cfg.StrOpt('cacert'),
cfg.StrOpt('cert_file'),
cfg.StrOpt('key_file'),
cfg.StrOpt('endpoint_type', default='publicURL')
]
stats_opt = [
cfg.IntOpt('period', default=5,
help=_('Statistics collection interval in minutes.'
@ -84,6 +95,9 @@ stats_opt = [
metadata_dir = cfg.StrOpt('metadata-dir', default='./meta')
temp_pkg_cache = os.path.join(tempfile.gettempdir(), 'murano-packages-cache')
packages_cache = cfg.StrOpt('packages-cache', default=temp_pkg_cache)
CONF = cfg.CONF
CONF.register_opts(paste_deploy_opts, group='paste_deploy')
CONF.register_cli_opts(bind_opts)
@ -92,9 +106,11 @@ CONF.register_opts(rabbit_opts, group='rabbitmq')
CONF.register_opts(heat_opts, group='heat')
CONF.register_opts(neutron_opts, group='neutron')
CONF.register_opts(keystone_opts, group='keystone')
CONF.register_opts(murano_opts, group='murano')
CONF.register_opt(cfg.StrOpt('file_server'))
CONF.register_cli_opt(cfg.StrOpt('murano_metadata_url'))
CONF.register_cli_opt(metadata_dir)
CONF.register_cli_opt(packages_cache)
CONF.register_opts(stats_opt, group='stats')
CONF.import_opt('connection',

View File

@ -1,4 +1,4 @@
# Copyright (c) 2013 Mirantis Inc.
# 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.
@ -25,38 +25,40 @@ from muranoapi.common import rpc
from muranoapi.dsl import executor
from muranoapi.dsl import results_serializer
from muranoapi.engine import environment
from muranoapi.engine import simple_cloader
from muranoapi.engine import package_class_loader
from muranoapi.engine import package_loader
import muranoapi.engine.system.system_objects as system_objects
from muranoapi.openstack.common.gettextutils import _ # noqa
from muranoapi.openstack.common import log as logging
RPC_SERVICE = None
log = logging.getLogger(__name__)
LOG = logging.getLogger(__name__)
class TaskProcessingEndpoint(object):
@staticmethod
def handle_task(context, task):
s_task = token_sanitizer.TokenSanitizer().sanitize(task)
log.info(_('Starting processing task: {0}').format(
anyjson.dumps(s_task)))
LOG.info(_('Starting processing task: {task_desc}').format(
task_desc=anyjson.dumps(s_task)))
env = environment.Environment()
env.token = task['token']
env.tenant_id = task['tenant_id']
cl = simple_cloader.SimpleClassLoader(config.CONF.metadata_dir)
system_objects.register(cl, config.CONF.metadata_dir)
with package_loader.ApiPackageLoader(task['token']) as pkg_loader:
class_loader = package_class_loader.PackageClassLoader(pkg_loader)
system_objects.register(class_loader, pkg_loader)
exc = executor.MuranoDslExecutor(cl, env)
obj = exc.load(task['model'])
exc = executor.MuranoDslExecutor(class_loader, env)
obj = exc.load(task['model'])
if obj is not None:
obj.type.invoke('deploy', exc, obj, {})
if obj is not None:
obj.type.invoke('deploy', exc, obj, {})
s_res = results_serializer.serialize(obj, exc)
rpc.api().process_result(s_res)
s_res = results_serializer.serialize(obj, exc)
rpc.api().process_result(s_res)
def _prepare_rpc_service(server_id):

View File

@ -30,18 +30,31 @@ import muranoapi.dsl.typespec as typespec
class MuranoClassLoader(object):
def __init__(self):
self._loaded_types = {}
self._packages_cache = {}
principal_objects.register(self)
def _get_package(self, class_name):
package_name = self.find_package_name(class_name)
if package_name is None:
raise exceptions.NoPackageForClassFound(class_name)
if package_name not in self._packages_cache:
package = self.load_package(package_name)
self._packages_cache[package_name] = package
return self._packages_cache[package_name]
def get_class(self, name, create_missing=False):
if name in self._loaded_types:
return self._loaded_types[name]
data = self.load_definition(name)
if data is None:
try:
data = self.load_definition(name)
package = self._get_package(name)
except (exceptions.NoPackageForClassFound, exceptions.NoClassFound):
if create_missing:
data = {'Name': name}
package = None
else:
raise exceptions.NoClassFound(name)
raise
namespaces = data.get('Namespaces', {})
ns_resolver = namespace_resolver.NamespaceResolver(namespaces)
@ -55,7 +68,7 @@ class MuranoClassLoader(object):
class_parents[i] = self.get_class(full_name)
type_obj = murano_class.MuranoClass(self, ns_resolver, name,
class_parents)
package, class_parents)
properties = data.get('Properties', {})
for property_name, property_spec in properties.iteritems():
@ -71,6 +84,12 @@ class MuranoClassLoader(object):
def load_definition(self, name):
raise NotImplementedError()
def find_package_name(self, class_name):
raise NotImplementedError()
def load_package(self, class_name):
raise NotImplementedError()
def create_root_context(self):
return yaql.create_context(True)

View File

@ -36,6 +36,17 @@ class NoClassFound(Exception):
super(NoClassFound, self).__init__('Class %s is not found' % name)
class NoPackageFound(Exception):
def __init__(self, name):
super(NoPackageFound, self).__init__('Package %s is not found' % name)
class NoPackageForClassFound(Exception):
def __init__(self, name):
super(NoPackageForClassFound, self).__init__('Package for class %s '
'is not found' % name)
class AmbiguousMethodName(Exception):
def __init__(self, name):
super(AmbiguousMethodName, self).__init__(

View File

@ -29,7 +29,9 @@ def classname(name):
class MuranoClass(object):
def __init__(self, class_loader, namespace_resolver, name, parents=None):
def __init__(self, class_loader, namespace_resolver, name, package,
parents=None):
self._package = package
self._class_loader = class_loader
self._methods = {}
self._namespace_resolver = namespace_resolver
@ -51,6 +53,10 @@ class MuranoClass(object):
def name(self):
return self._name
@property
def package(self):
return self._package
@property
def namespace_resolver(self):
return self._namespace_resolver

View File

@ -0,0 +1,27 @@
# 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.
class MuranoPackage(object):
def __init__(self):
super(MuranoPackage, self).__init__()
self._name = None
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value

View File

@ -0,0 +1,53 @@
# 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.
from oslo.config import cfg
from muranoapi.dsl import class_loader
from muranoapi.dsl import exceptions
from muranoapi.dsl import murano_package
from muranoapi.engine.system import yaql_functions
from muranoapi.openstack.common import log as logging
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class PackageClassLoader(class_loader.MuranoClassLoader):
def __init__(self, package_loader):
self.package_loader = package_loader
self._packages_cache = {}
super(PackageClassLoader, self).__init__()
def load_definition(self, name):
try:
package = self.package_loader.get_package_by_class(name)
return package.get_class(name)
except Exception:
raise exceptions.NoClassFound(name)
def load_package(self, name):
package = murano_package.MuranoPackage()
package.name = name
return package
def find_package_name(self, class_name):
app_pkg = self.package_loader.get_package_by_class(class_name)
return None if app_pkg is None else app_pkg.full_name
def create_root_context(self):
context = super(PackageClassLoader, self).create_root_context()
yaql_functions.register(context)
return context

View File

@ -0,0 +1,218 @@
# 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 abc
import os
import shutil
import tempfile
import uuid
from keystoneclient.v2_0 import client as keystoneclient
from muranoclient.common import exceptions as muranoclient_exc
from muranoclient.v1 import client as muranoclient
import six
import yaml
from muranoapi.common import config
from muranoapi.dsl import exceptions
from muranoapi.dsl import yaql_expression
from muranoapi.openstack.common import log as logging
from muranoapi.packages import application_package as app_pkg
from muranoapi.packages import exceptions as pkg_exc
LOG = logging.getLogger(__name__)
class YaqlYamlLoader(yaml.Loader):
pass
def yaql_constructor(loader, node):
value = loader.construct_scalar(node)
return yaql_expression.YaqlExpression(value)
yaml.add_constructor(u'!yaql', yaql_constructor, YaqlYamlLoader)
yaml.add_implicit_resolver(u'!yaql', yaql_expression.YaqlExpression,
Loader=YaqlYamlLoader)
class PackageLoader(six.with_metaclass(abc.ABCMeta)):
@abc.abstractmethod
def get_package(self, name):
pass
@abc.abstractmethod
def get_package_by_class(self, name):
pass
class ApiPackageLoader(PackageLoader):
def __init__(self, token_id):
self._cache_directory = self._get_cache_directory()
self._client = self._get_murano_client(token_id)
def get_package_by_class(self, name):
filter_opts = {'class_name': name, 'limit': 1}
try:
package_definition = self._get_definition(filter_opts)
return self._get_package_by_definition(package_definition)
except(LookupError, pkg_exc.PackageLoadError):
raise exceptions.NoPackageForClassFound(name)
def get_package(self, name):
filter_opts = {'fqn': name, 'limit': 1}
try:
package_definition = self._get_definition(filter_opts)
return self._get_package_by_definition(package_definition)
except(LookupError, pkg_exc.PackageLoadError):
raise exceptions.NoPackageFound(name)
@staticmethod
def _get_cache_directory():
directory = os.path.join(config.CONF.packages_cache, str(uuid.uuid4()))
directory = os.path.abspath(directory)
os.makedirs(directory)
LOG.debug('Cache for package loader is located at: '
'{0}'.format(directory))
return directory
@staticmethod
def _get_murano_client(token_id):
murano_settings = config.CONF.murano
endpoint_url = murano_settings.url
if endpoint_url is None:
keystone_settings = config.CONF.keystone
keystone_client = keystoneclient.Client(
endpoint=keystone_settings.auth_url,
cacert=keystone_settings.ca_file or None,
cert=keystone_settings.cert_file or None,
key=keystone_settings.key_file or None,
insecure=keystone_settings.insecure
)
endpoint_url = keystone_client.url_for(
service_type='murano',
endpoint_type=murano_settings.endpoint_type
)
return muranoclient.Client(
endpoint=endpoint_url,
key_file=murano_settings.key_file or None,
cacert=murano_settings.cacert or None,
cert_file=murano_settings.cert_file or None,
insecure=murano_settings.insecure,
token=token_id
)
def _get_definition(self, filter_opts):
try:
packages = self._client.packages.filter(**filter_opts)
if not packages:
LOG.debug('There are no packages matching filter '
'{0}'.format(filter_opts))
# TODO(smelikyan): This exception should be replaced with one
# defined in python-muranoclient
raise LookupError()
return packages[0]
except muranoclient_exc.HTTPException:
LOG.debug('Failed to get package definition from repository')
raise LookupError()
def _get_package_by_definition(self, package_def):
package_id = package_def.id
package_name = package_def.fully_qualified_name
package_directory = os.path.join(self._cache_directory, package_name)
if os.path.exists(package_directory):
try:
return app_pkg.load_from_dir(package_directory, preload=True,
loader=YaqlYamlLoader)
except pkg_exc.PackageLoadError:
LOG.exception('Unable to load package from cache. Clean-up...')
shutil.rmtree(package_directory, ignore_errors=True)
try:
package_data = self._client.packages.download(package_id)
except muranoclient_exc.HTTPException:
LOG.exception('Unable to download '
'package with id {0}'.format(package_id))
raise pkg_exc.PackageLoadError()
try:
with tempfile.NamedTemporaryFile(delete=False) as package_file:
package_file.write(package_data)
return app_pkg.load_from_file(
package_file.name,
target_dir=package_directory,
drop_dir=False,
loader=YaqlYamlLoader
)
except IOError:
LOG.exception('Unable to write package file')
raise pkg_exc.PackageLoadError()
finally:
try:
os.remove(package_file.name)
except OSError:
pass
def cleanup(self):
shutil.rmtree(self._cache_directory, ignore_errors=True)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.cleanup()
return False
class DirectoryPackageLoader(PackageLoader):
def __init__(self, base_path):
self._base_path = base_path
self._processed_entries = set()
self._packages_by_class = {}
self._packages_by_name = {}
self._build_index()
def get_package(self, name):
return self._packages_by_name.get(name)
def get_package_by_class(self, name):
return self._packages_by_class.get(name)
def _build_index(self):
for entry in os.listdir(self._base_path):
if not os.path.isdir(entry) or entry in self._processed_entries:
continue
try:
package = app_pkg.load_from_dir(entry, preload=True,
loader=YaqlYamlLoader)
except pkg_exc.PackageLoadError:
LOG.exception('Unable to load package from path: '
'{0}'.format(entry))
continue
for c in package.classes:
self._packages_by_class[c] = package
self._packages_by_name[package.full_name] = package
self._processed_entries.add(entry)

View File

@ -14,21 +14,19 @@
# limitations under the License.
import json as jsonlib
import os.path
import yaml as yamllib
import muranoapi.dsl.murano_object as murano_object
class ResourceManager(murano_object.MuranoObject):
def initialize(self, base_path, _context, _class):
def initialize(self, package_loader, _context, _class):
if _class is None:
_class = _context.get_data('$')
class_name = _class.type.name
self._base_path = os.path.join(base_path, class_name, 'resources')
self._package = package_loader.get_package(_class.type.package.name)
def string(self, name):
path = os.path.join(self._base_path, name)
path = self._package.get_resource(name)
with open(path) as file:
return file.read()

View File

@ -34,14 +34,14 @@ def _auto_register(class_loader):
class_loader.import_class(class_def)
def register(class_loader, path):
def register(class_loader, package_loader):
_auto_register(class_loader)
@murano_class.classname('io.murano.system.Resources')
class ResourceManagerWrapper(resource_manager.ResourceManager):
def initialize(self, _context, _class=None):
super(ResourceManagerWrapper, self).initialize(
path, _context, _class)
package_loader, _context, _class)
class_loader.import_class(agent.Agent)
class_loader.import_class(agent_listener.AgentListener)

View File

@ -12,6 +12,7 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import imghdr
import os
import shutil
@ -24,6 +25,17 @@ import muranoapi.packages.exceptions as e
import muranoapi.packages.versions.v1
class DummyLoader(yaml.Loader):
pass
def yaql_constructor(loader, node):
value = loader.construct_scalar(node)
return value
yaml.add_constructor(u'!yaql', yaql_constructor, DummyLoader)
class PackageTypes(object):
Library = 'Library'
Application = 'Application'
@ -31,7 +43,8 @@ class PackageTypes(object):
class ApplicationPackage(object):
def __init__(self, source_directory, yaml_content):
def __init__(self, source_directory, manifest, loader=DummyLoader):
self.yaml_loader = loader
self._source_directory = source_directory
self._full_name = None
self._package_type = None
@ -42,7 +55,7 @@ class ApplicationPackage(object):
self._classes = None
self._ui = None
self._logo = None
self._format = yaml_content.get('Format')
self._format = manifest.get('Format')
self._ui_cache = None
self._raw_ui_cache = None
self._logo_cache = None
@ -99,6 +112,9 @@ class ApplicationPackage(object):
self._load_class(name)
return self._classes_cache[name]
def get_resource(self, name):
return os.path.join(self._source_directory, 'Resources', name)
def validate(self):
self._classes_cache.clear()
for class_name in self._classes:
@ -109,7 +125,7 @@ class ApplicationPackage(object):
# Private methods
def _load_ui(self, load_yaml=False):
if self._raw_ui_cache and load_yaml:
self._ui_cache = yaml.load(self._raw_ui_cache)
self._ui_cache = yaml.load(self._raw_ui_cache, self.yaml_loader)
else:
ui_file = self._ui
full_path = os.path.join(self._source_directory, 'UI', ui_file)
@ -121,7 +137,8 @@ class ApplicationPackage(object):
with open(full_path) as stream:
self._raw_ui_cache = stream.read()
if load_yaml:
self._ui_cache = yaml.load(self._raw_ui_cache)
self._ui_cache = yaml.load(self._raw_ui_cache,
self.yaml_loader)
except Exception as ex:
trace = sys.exc_info()[2]
raise e.PackageUILoadError(str(ex)), None, trace
@ -154,7 +171,7 @@ class ApplicationPackage(object):
'definition not found')
try:
with open(full_path) as stream:
self._classes_cache[name] = yaml.load(stream)
self._classes_cache[name] = yaml.load(stream, self.yaml_loader)
except Exception as ex:
trace = sys.exc_info()[2]
msg = 'Unable to load class definition due to "{0}"'.format(
@ -162,7 +179,8 @@ class ApplicationPackage(object):
raise e.PackageClassLoadError(name, msg), None, trace
def load_from_dir(source_directory, filename='manifest.yaml', preload=False):
def load_from_dir(source_directory, filename='manifest.yaml', preload=False,
loader=DummyLoader):
formats = {'1.0': muranoapi.packages.versions.v1}
if not os.path.isdir(source_directory) or not os.path.exists(
@ -174,7 +192,7 @@ def load_from_dir(source_directory, filename='manifest.yaml', preload=False):
try:
with open(full_path) as stream:
content = yaml.load(stream)
content = yaml.load(stream, DummyLoader)
except Exception as ex:
trace = sys.exc_info()[2]
raise e.PackageLoadError(
@ -184,14 +202,15 @@ def load_from_dir(source_directory, filename='manifest.yaml', preload=False):
if not p_format or p_format not in formats:
raise e.PackageFormatError(
'Unknown or missing format version')
package = ApplicationPackage(source_directory, content)
package = ApplicationPackage(source_directory, content, loader)
formats[p_format].load(package, content)
if preload:
package.validate()
return package
def load_from_file(archive_path, target_dir=None, drop_dir=False):
def load_from_file(archive_path, target_dir=None, drop_dir=False,
loader=DummyLoader):
if not os.path.isfile(archive_path):
raise e.PackageLoadError('Unable to find package file')
created = False
@ -211,7 +230,7 @@ def load_from_file(archive_path, target_dir=None, drop_dir=False):
"zip' archive")
package = zipfile.ZipFile(archive_path)
package.extractall(path=target_dir)
return load_from_dir(target_dir, preload=True)
return load_from_dir(target_dir, preload=True, loader=loader)
finally:
if drop_dir:
if created:

View File

@ -114,13 +114,13 @@ class TestClassesManipulation(unittest.TestCase):
resolver = mock.Mock(resolve_name=lambda name: name)
def test_class_name(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
self.assertEqual(ROOT_CLASS, cls.name)
def test_class_namespace_resolver(self):
resolver = ns_resolver.NamespaceResolver({})
cls = murano_class.MuranoClass(None, resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, resolver, ROOT_CLASS, None)
self.assertEqual(resolver, cls.namespace_resolver)
@ -131,21 +131,23 @@ class TestClassesManipulation(unittest.TestCase):
self.assertEqual([], root_class.parents)
def test_non_root_class_resolves_parents(self):
root_cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
root_cls = murano_class.MuranoClass(None, self.resolver,
ROOT_CLASS, None)
class_loader = mock.Mock(get_class=lambda name: root_cls)
desc_cl1 = murano_class.MuranoClass(class_loader, self.resolver, 'Obj')
desc_cl1 = murano_class.MuranoClass(class_loader, self.resolver,
'Obj', None)
desc_cl2 = murano_class.MuranoClass(
class_loader, self.resolver, 'Obj', [root_cls])
class_loader, self.resolver, 'Obj', None, [root_cls])
self.assertEqual([root_cls], desc_cl1.parents)
self.assertEqual([root_cls], desc_cl2.parents)
def test_class_initial_properties(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
self.assertEqual([], cls.properties)
def test_fails_add_incompatible_property_to_class(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
kwargs = {'name': 'sampleProperty', 'property_typespec': {}}
self.assertRaises(TypeError, cls.add_property, **kwargs)
@ -153,7 +155,7 @@ class TestClassesManipulation(unittest.TestCase):
@unittest.skip
def test_add_property_to_class(self):
prop = typespec.PropertySpec({'Default': 1}, self.resolver)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls.add_property('firstPrime', prop)
class_properties = cls.properties
@ -190,9 +192,9 @@ class TestClassesManipulation(unittest.TestCase):
self.assertEqual(void_prop, child.find_property('Void'))
def test_class_is_compatible(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
descendant_cls = murano_class.MuranoClass(
None, self.resolver, 'DescendantCls', [cls])
None, self.resolver, 'DescendantCls', None, [cls])
obj = mock.Mock(spec=murano_object.MuranoObject)
descendant_obj = mock.Mock(spec=murano_object.MuranoObject)
obj.type = cls
@ -204,7 +206,7 @@ class TestClassesManipulation(unittest.TestCase):
self.assertFalse(descendant_cls.is_compatible(obj))
def test_new_method_calls_initialize(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls.object_class = mock.Mock()
with mock.patch('inspect.getargspec') as spec_mock:
@ -214,7 +216,7 @@ class TestClassesManipulation(unittest.TestCase):
self.assertTrue(obj.initialize.called)
def test_new_method_not_calls_initialize(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls.object_class = mock.Mock()
obj = cls.new(None, None, None)
@ -241,9 +243,9 @@ class TestObjectsManipulation(unittest.TestCase):
pass
def test_object_parent_properties_initialization(self):
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls = murano_class.MuranoClass(None, self.resolver,
'SomeClass', [root])
'SomeClass', None, [root])
root.new = mock.Mock()
init_kwargs = {'theArg': 0}
obj = murano_object.MuranoObject(cls, None, None, None)
@ -326,13 +328,13 @@ class TestObjectsManipulation(unittest.TestCase):
self.assertRaises(LookupError, lambda: obj.conflictProp)
def test_fails_setting_undeclared_property(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
obj = cls.new(None, None, None, {})
self.assertRaises(AttributeError, obj.set_property, 'newOne', 10)
def test_set_undeclared_property_as_internal(self):
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
cls = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
obj = cls.new(None, None, None, {})
obj.cast = mock.Mock(return_value=obj)
prop_value = 10
@ -403,9 +405,9 @@ class TestObjectsManipulation(unittest.TestCase):
self.assertEqual(root_alt, cls_obj_casted2root_alt.type)
def test_fails_object_down_cast(self):
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS)
root = murano_class.MuranoClass(None, self.resolver, ROOT_CLASS, None)
cls = murano_class.MuranoClass(
None, self.resolver, 'SomeClass', [root])
None, self.resolver, 'SomeClass', None, [root])
root_obj = root.new(None, None, None)
self.assertRaises(TypeError, root_obj.cast, cls)

View File

@ -38,3 +38,4 @@ oslo.messaging>=1.3.0a9
# not listed in global requirements
yaql>=0.2.2,<0.3
python-muranoclient==0.4.dev127.g108313a