167 lines
5.7 KiB
Python
167 lines
5.7 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 inspect
|
|
import weakref
|
|
|
|
import semantic_version
|
|
import six
|
|
|
|
from murano.dsl import constants
|
|
from murano.dsl import dsl_types
|
|
from murano.dsl import exceptions
|
|
from murano.dsl import helpers
|
|
from murano.dsl import murano_class
|
|
from murano.dsl import murano_object
|
|
from murano.dsl import principal_objects
|
|
from murano.dsl import yaql_integration
|
|
|
|
|
|
class MuranoPackage(dsl_types.MuranoPackage):
|
|
|
|
def __init__(self, package_loader, name, version=None,
|
|
runtime_version=None, requirements=None):
|
|
super(MuranoPackage, self).__init__()
|
|
self._package_loader = weakref.proxy(package_loader)
|
|
self._name = name
|
|
self._version = helpers.parse_version(version)
|
|
self._runtime_version = helpers.parse_version(runtime_version)
|
|
self._requirements = {
|
|
name: semantic_version.Spec('==' + str(self._version.major))
|
|
}
|
|
if name != constants.CORE_LIBRARY:
|
|
self._requirements[constants.CORE_LIBRARY] = \
|
|
semantic_version.Spec('==0')
|
|
self._classes = {}
|
|
self._imported_types = {object, murano_object.MuranoObject}
|
|
for key, value in six.iteritems(requirements or {}):
|
|
self._requirements[key] = helpers.parse_version_spec(value)
|
|
|
|
self._load_queue = {}
|
|
self._native_load_queue = {}
|
|
if self.name == constants.CORE_LIBRARY:
|
|
principal_objects.register(self)
|
|
|
|
@property
|
|
def package_loader(self):
|
|
return self._package_loader
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
@property
|
|
def version(self):
|
|
return self._version
|
|
|
|
@property
|
|
def runtime_version(self):
|
|
return self._runtime_version
|
|
|
|
@property
|
|
def requirements(self):
|
|
return self._requirements
|
|
|
|
@property
|
|
def classes(self):
|
|
return set(self._classes.keys()).union(
|
|
self._load_queue.keys()).union(self._native_load_queue.keys())
|
|
|
|
def get_resource(self, name):
|
|
raise NotImplementedError('resource API is not implemented')
|
|
|
|
# noinspection PyMethodMayBeStatic
|
|
def get_class_config(self, name):
|
|
return {}
|
|
|
|
def _register_mpl_class(self, data, name=None):
|
|
type_obj = self._classes.get(name)
|
|
if not type_obj:
|
|
type_obj = murano_class.MuranoClass.create(data, self, name)
|
|
self._classes[name] = type_obj
|
|
return type_obj
|
|
|
|
def _register_native_class(self, cls, name):
|
|
if cls in self._imported_types:
|
|
return self._classes[name]
|
|
|
|
try:
|
|
m_class = self.find_class(name, False)
|
|
except exceptions.NoClassFound:
|
|
m_class = self._register_mpl_class({'Name': name}, name)
|
|
|
|
m_class.extend_with_class(cls)
|
|
|
|
for method_name in dir(cls):
|
|
if method_name.startswith('_'):
|
|
continue
|
|
method = getattr(cls, method_name)
|
|
if not inspect.ismethod(method):
|
|
continue
|
|
# TODO(slagun): update the code below to use yaql native
|
|
# method for this when https://review.openstack.org/#/c/220748/
|
|
# will get merged and Murano requirements bump to corresponding
|
|
# yaql version
|
|
method_name_alias = (getattr(
|
|
method, '__murano_name', None) or
|
|
yaql_integration.CONVENTION.convert_function_name(
|
|
method_name.rstrip('_')))
|
|
m_class.add_method(method_name_alias, method)
|
|
self._imported_types.add(cls)
|
|
return m_class
|
|
|
|
def register_class(self, cls, name=None):
|
|
if inspect.isclass(cls):
|
|
name = name or getattr(cls, '__murano_name', None) or cls.__name__
|
|
self._native_load_queue[name] = cls
|
|
elif isinstance(cls, murano_class.MuranoClass):
|
|
self._classes[cls.name] = cls
|
|
else:
|
|
self._load_queue[name] = cls
|
|
|
|
def find_class(self, name, search_requirements=True):
|
|
payload = self._native_load_queue.pop(name, None)
|
|
if payload is not None:
|
|
return self._register_native_class(payload, name)
|
|
|
|
payload = self._load_queue.pop(name, None)
|
|
if payload is not None:
|
|
if callable(payload):
|
|
payload = payload()
|
|
return self._register_mpl_class(payload, name)
|
|
|
|
result = self._classes.get(name)
|
|
if result:
|
|
return result
|
|
if search_requirements:
|
|
pkgs_for_search = []
|
|
for package_name, version_spec in six.iteritems(
|
|
self._requirements):
|
|
if package_name == self.name:
|
|
continue
|
|
referenced_package = self._package_loader.load_package(
|
|
package_name, version_spec)
|
|
try:
|
|
return referenced_package.find_class(name, False)
|
|
except exceptions.NoClassFound:
|
|
pkgs_for_search.append(referenced_package)
|
|
continue
|
|
raise exceptions.NoClassFound(name, packages=pkgs_for_search)
|
|
|
|
raise exceptions.NoClassFound(name, packages=[self])
|
|
|
|
@property
|
|
def context(self):
|
|
return None
|