Major refactoring of how OS clients are created and managed
* Single universal ClientManager class was dropped in favor of of individual in-context methods to create OS clients without ClientManager restrictions. * Environment class was renamed to ExecutionSession to avoid common confusion with io.murano.Environment * execution_session_local module was introduced to simplify keep of per-execution session (per-deployment) data. This is similar to thread-locals with the difference that there can be many threads in single session. * All OS-clients related code was migrated to keystone client sessions and API v3 (except for GLARE and Mistral that doesn't support sessions). This increases performance and solves authentication problems that could be caused by token expiration even with trusts enabled. * [DEFAULT]/home_region setting was introduced instead of [murano]/region_for_services to configure what region should be used by the clients by default (where Murano API resides). All client factories respect this setting. Change-Id: If02c7e5d7d39574d0621e0e8dc27d1f501a31984
This commit is contained in:
parent
12a8b87302
commit
fc76b3b1b4
|
@ -21,7 +21,7 @@ from oslo_config import cfg as config
|
|||
from oslo_log import log as logging
|
||||
|
||||
|
||||
import murano.dsl.helpers as helpers
|
||||
from murano.common import auth_utils
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
@ -30,9 +30,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class GlanceClient(object):
|
||||
def __init__(self, context):
|
||||
client_manager = helpers.get_environment(context).clients
|
||||
self.client = client_manager.get_client("glance", True,
|
||||
self.create_glance_client)
|
||||
self.client = self.create_glance_client()
|
||||
|
||||
def list(self):
|
||||
images = self.client.images.list()
|
||||
|
@ -67,14 +65,11 @@ class GlanceClient(object):
|
|||
def init_plugin(cls):
|
||||
cls.CONF = cfg.init_config(CONF)
|
||||
|
||||
def create_glance_client(self, keystone_client, auth_token):
|
||||
def create_glance_client(self):
|
||||
LOG.debug("Creating a glance client")
|
||||
glance_endpoint = keystone_client.service_catalog.url_for(
|
||||
service_type='image', endpoint_type=self.CONF.endpoint_type)
|
||||
client = glanceclient.Client(self.CONF.api_version,
|
||||
endpoint=glance_endpoint,
|
||||
token=auth_token)
|
||||
return client
|
||||
params = auth_utils.get_session_client_parameters(
|
||||
service_type='image', conf=self.CONF)
|
||||
return glanceclient.Client(self.CONF.api_version, **params)
|
||||
|
||||
|
||||
class AmbiguousNameException(Exception):
|
||||
|
|
|
@ -33,8 +33,7 @@ from murano.common import engine
|
|||
from murano.dsl import exceptions
|
||||
from murano.dsl import executor
|
||||
from murano.dsl import helpers
|
||||
from murano.engine import client_manager
|
||||
from murano.engine import environment
|
||||
from murano.engine import execution_session
|
||||
from murano.engine import mock_context_manager
|
||||
from murano.engine import package_loader
|
||||
|
||||
|
@ -202,19 +201,14 @@ class MuranoTestRunner(object):
|
|||
ks_opts = self._validate_keystone_opts(self.args)
|
||||
|
||||
client = ks_client.Client(**ks_opts)
|
||||
test_env = environment.Environment()
|
||||
test_env.token = client.auth_token
|
||||
test_env.tenant_id = client.auth_tenant_id
|
||||
test_env.clients = client_manager.ClientManager(test_env)
|
||||
|
||||
murano_client_factory = lambda: \
|
||||
test_env.clients.get_murano_client(test_env)
|
||||
test_session = execution_session.ExecutionSession()
|
||||
test_session.token = client.auth_token
|
||||
test_session.project_id = client.project_id
|
||||
|
||||
# Replace location of loading packages with provided from command line.
|
||||
if load_packages_from:
|
||||
cfg.CONF.packages_opts.load_packages_from = load_packages_from
|
||||
with package_loader.CombinedPackageLoader(
|
||||
murano_client_factory, client.tenant_id) as pkg_loader:
|
||||
with package_loader.CombinedPackageLoader(test_session) as pkg_loader:
|
||||
engine.get_plugin_loader().register_in_loader(pkg_loader)
|
||||
|
||||
package = self._load_package(pkg_loader, provided_pkg_name)
|
||||
|
@ -236,13 +230,13 @@ class MuranoTestRunner(object):
|
|||
dsl_executor = executor.MuranoDslExecutor(
|
||||
pkg_loader,
|
||||
mock_context_manager.MockContextManager(),
|
||||
test_env)
|
||||
test_session)
|
||||
obj = package.find_class(pkg_class, False).new(
|
||||
None, dsl_executor.object_store, dsl_executor)(None)
|
||||
self._call_service_method('setUp', dsl_executor, obj)
|
||||
obj.type.methods[m].usage = 'Action'
|
||||
|
||||
test_env.start()
|
||||
test_session.start()
|
||||
try:
|
||||
obj.type.invoke(m, dsl_executor, obj, (), {})
|
||||
LOG.debug('\n.....{0}.{1}.....OK'.format(obj.type.name,
|
||||
|
@ -254,7 +248,7 @@ class MuranoTestRunner(object):
|
|||
''.format(obj.type.name, m))
|
||||
exit_code = 1
|
||||
finally:
|
||||
test_env.finish()
|
||||
test_session.finish()
|
||||
return exit_code
|
||||
|
||||
def get_parser(self):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2014 Mirantis, Inc.
|
||||
# Copyright (c) 2016 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
|
||||
|
@ -13,92 +13,151 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
from keystoneclient.auth import identity
|
||||
from keystoneclient import session as ks_session
|
||||
from keystoneclient.v3 import client as ks_client
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
|
||||
|
||||
def get_client(token, project_id):
|
||||
settings = _get_keystone_settings()
|
||||
kwargs = {
|
||||
'token': token,
|
||||
'project_id': project_id,
|
||||
'auth_url': settings['auth_url']
|
||||
}
|
||||
kwargs.update(settings['ssl'])
|
||||
|
||||
kwargs['region_name'] = settings['region_name']
|
||||
keystone = ks_client.Client(**kwargs)
|
||||
keystone.management_url = settings['auth_url']
|
||||
|
||||
return keystone
|
||||
from murano.dsl import helpers
|
||||
|
||||
|
||||
def get_client_for_admin(project_name):
|
||||
return _admin_client(project_name=project_name)
|
||||
|
||||
|
||||
def _admin_client(trust_id=None, project_name=None):
|
||||
settings = _get_keystone_settings()
|
||||
|
||||
kwargs = {
|
||||
'project_name': project_name,
|
||||
'trust_id': trust_id
|
||||
}
|
||||
for key in ('username', 'password', 'auth_url'):
|
||||
kwargs[key] = settings[key]
|
||||
kwargs.update(settings['ssl'])
|
||||
kwargs['region_name'] = settings['region_name']
|
||||
|
||||
client = ks_client.Client(**kwargs)
|
||||
|
||||
# without resetting this attributes keystone client cannot re-authenticate
|
||||
client.project_id = None
|
||||
client.project_name = None
|
||||
|
||||
client.management_url = settings['auth_url']
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def get_client_for_trusts(trust_id):
|
||||
return _admin_client(trust_id)
|
||||
|
||||
|
||||
def create_trust(token, project_id):
|
||||
client = get_client(token, project_id)
|
||||
|
||||
settings = _get_keystone_settings()
|
||||
trustee_id = get_client_for_admin(
|
||||
settings['project_name']).user_id
|
||||
|
||||
roles = [t['name'] for t in client.auth_ref['roles']]
|
||||
trust = client.trusts.create(trustor_user=client.user_id,
|
||||
trustee_user=trustee_id,
|
||||
impersonation=True,
|
||||
role_names=roles,
|
||||
project=project_id)
|
||||
|
||||
return trust.id
|
||||
|
||||
|
||||
def delete_trust(trust_id):
|
||||
keystone_client = get_client_for_trusts(trust_id)
|
||||
keystone_client.trusts.delete(trust_id)
|
||||
|
||||
|
||||
def _get_keystone_settings():
|
||||
@helpers.memoize
|
||||
def _get_keystone_admin_parameters(scoped):
|
||||
importutils.import_module('keystonemiddleware.auth_token')
|
||||
return {
|
||||
settings = {
|
||||
'auth_url': cfg.CONF.keystone_authtoken.auth_uri.replace('v2.0', 'v3'),
|
||||
'username': cfg.CONF.keystone_authtoken.admin_user,
|
||||
'password': cfg.CONF.keystone_authtoken.admin_password,
|
||||
'project_name': cfg.CONF.keystone_authtoken.admin_tenant_name,
|
||||
'ssl': {
|
||||
'cacert': cfg.CONF.keystone.ca_file,
|
||||
'insecure': cfg.CONF.keystone.insecure,
|
||||
'cert': cfg.CONF.keystone.cert_file,
|
||||
'key': cfg.CONF.keystone.key_file,
|
||||
},
|
||||
'region_name': cfg.CONF.murano.region_name_for_services
|
||||
'user_domain_name': 'default'
|
||||
}
|
||||
if scoped:
|
||||
settings.update({
|
||||
'project_name': cfg.CONF.keystone_authtoken.admin_tenant_name,
|
||||
'project_domain_name': 'default'
|
||||
})
|
||||
return settings
|
||||
|
||||
|
||||
@helpers.memoize
|
||||
def create_keystone_admin_client(scoped):
|
||||
kwargs = _get_keystone_admin_parameters(scoped)
|
||||
password_auth = identity.Password(**kwargs)
|
||||
session = ks_session.Session(auth=password_auth)
|
||||
_set_ssl_parameters(cfg.CONF.keystone_authtoken, session)
|
||||
return ks_client.Client(session=session)
|
||||
|
||||
|
||||
def get_client_session(execution_session=None, conf=None):
|
||||
if not execution_session:
|
||||
execution_session = helpers.get_execution_session()
|
||||
trust_id = execution_session.trust_id
|
||||
if trust_id is None:
|
||||
return get_token_client_session(
|
||||
token=execution_session.token,
|
||||
project_id=execution_session.project_id)
|
||||
kwargs = _get_keystone_admin_parameters(False)
|
||||
kwargs['trust_id'] = trust_id
|
||||
password_auth = identity.Password(**kwargs)
|
||||
session = ks_session.Session(auth=password_auth)
|
||||
_set_ssl_parameters(conf, session)
|
||||
return session
|
||||
|
||||
|
||||
def get_token_client_session(token=None, project_id=None, conf=None):
|
||||
auth_url = _get_keystone_admin_parameters(False)['auth_url']
|
||||
if token is None or project_id is None:
|
||||
execution_session = helpers.get_execution_session()
|
||||
token = execution_session.token
|
||||
project_id = execution_session.project_id
|
||||
token_auth = identity.Token(auth_url, token=token, project_id=project_id)
|
||||
session = ks_session.Session(auth=token_auth)
|
||||
_set_ssl_parameters(conf, session)
|
||||
return session
|
||||
|
||||
|
||||
def create_keystone_client(token=None, project_id=None, conf=None):
|
||||
return ks_client.Client(session=get_token_client_session(
|
||||
token=token, project_id=project_id, conf=conf))
|
||||
|
||||
|
||||
def create_trust(trustee_token=None, trustee_project_id=None):
|
||||
admin_client = create_keystone_admin_client(True)
|
||||
user_client = create_keystone_client(
|
||||
token=trustee_token, project_id=trustee_project_id)
|
||||
trustee_user = admin_client.session.auth.get_user_id(admin_client.session)
|
||||
auth_ref = user_client.session.auth.get_access(user_client.session)
|
||||
trustor_user = auth_ref.user_id
|
||||
project = auth_ref.project_id
|
||||
roles = auth_ref.role_names
|
||||
trust = user_client.trusts.create(
|
||||
trustor_user=trustor_user,
|
||||
trustee_user=trustee_user,
|
||||
impersonation=True,
|
||||
role_names=roles,
|
||||
project=project)
|
||||
return trust.id
|
||||
|
||||
|
||||
def delete_trust(trust):
|
||||
user_client = create_keystone_admin_client(True)
|
||||
user_client.trusts.delete(trust)
|
||||
|
||||
|
||||
def _get_config_option(conf_section, option_names, default=None):
|
||||
if not isinstance(option_names, (list, tuple)):
|
||||
option_names = (option_names,)
|
||||
for name in option_names:
|
||||
if hasattr(conf_section, name):
|
||||
return getattr(conf_section, name)
|
||||
return default
|
||||
|
||||
|
||||
def _set_ssl_parameters(conf_section, session):
|
||||
if not conf_section:
|
||||
return
|
||||
insecure = _get_config_option(conf_section, 'insecure', False)
|
||||
if insecure:
|
||||
session.verify = False
|
||||
else:
|
||||
session.verify = _get_config_option(
|
||||
conf_section, ('ca_file', 'cafile', 'cacert')) or True
|
||||
|
||||
cert_file = _get_config_option(conf_section, ('cert_file', 'certfile'))
|
||||
key_file = _get_config_option(conf_section, ('key_file', 'keyfile'))
|
||||
|
||||
if cert_file and key_file:
|
||||
session.cert = (cert_file, key_file)
|
||||
elif cert_file:
|
||||
session.cert = cert_file
|
||||
else:
|
||||
session.cert = None
|
||||
|
||||
|
||||
def get_session_client_parameters(service_type=None,
|
||||
region='',
|
||||
interface=None,
|
||||
service_name=None,
|
||||
conf=None,
|
||||
session=None,
|
||||
execution_session=None):
|
||||
if region == '':
|
||||
region = cfg.CONF.home_region
|
||||
result = {
|
||||
'session': session or get_client_session(
|
||||
execution_session=execution_session, conf=conf)
|
||||
}
|
||||
|
||||
url = _get_config_option(conf, 'url')
|
||||
if url:
|
||||
result['endpoint_override'] = url
|
||||
else:
|
||||
if not interface:
|
||||
interface = _get_config_option(conf, 'endpoint_type')
|
||||
result.update({
|
||||
'service_type': service_type,
|
||||
'service_name': service_name,
|
||||
'interface': interface,
|
||||
'region_name': region
|
||||
})
|
||||
return result
|
||||
|
|
|
@ -58,6 +58,8 @@ rabbit_opts = [
|
|||
]
|
||||
|
||||
heat_opts = [
|
||||
cfg.StrOpt('url', help='Optional heat endpoint override'),
|
||||
|
||||
cfg.BoolOpt('insecure', default=False,
|
||||
help='This option explicitly allows Murano to perform '
|
||||
'"insecure" SSL connections and transfers with Heat API.'),
|
||||
|
@ -82,13 +84,26 @@ heat_opts = [
|
|||
]
|
||||
|
||||
mistral_opts = [
|
||||
cfg.StrOpt('url', help='Optional mistral endpoint override'),
|
||||
|
||||
cfg.StrOpt('endpoint_type', default='publicURL',
|
||||
help='Mistral endpoint type.'),
|
||||
|
||||
cfg.StrOpt('service_type', default='workflowv2',
|
||||
help='Mistral service type.')
|
||||
help='Mistral service type.'),
|
||||
|
||||
cfg.BoolOpt('insecure', default=False,
|
||||
help='This option explicitly allows Murano to perform '
|
||||
'"insecure" SSL connections and transfers with Mistral.'),
|
||||
|
||||
cfg.StrOpt('ca_cert',
|
||||
help='(SSL) Tells Murano to use the specified client '
|
||||
'certificate file when communicating with Mistral.')
|
||||
]
|
||||
|
||||
neutron_opts = [
|
||||
cfg.StrOpt('url', help='Optional neutron endpoint override'),
|
||||
|
||||
cfg.BoolOpt('insecure', default=False,
|
||||
help='This option explicitly allows Murano to perform '
|
||||
'"insecure" SSL connections and transfers with Neutron API.'),
|
||||
|
@ -101,24 +116,6 @@ neutron_opts = [
|
|||
help='Neutron endpoint type.')
|
||||
]
|
||||
|
||||
keystone_opts = [
|
||||
cfg.BoolOpt('insecure', default=False,
|
||||
help='This option explicitly allows Murano to perform '
|
||||
'"insecure" SSL connections and transfers with '
|
||||
'Keystone API running Kyestone API.'),
|
||||
|
||||
cfg.StrOpt('ca_file',
|
||||
help='(SSL) Tells Murano to use the specified certificate file '
|
||||
'to verify the peer when communicating with Keystone.'),
|
||||
|
||||
cfg.StrOpt('cert_file',
|
||||
help='(SSL) Tells Murano to use the specified client '
|
||||
'certificate file when communicating with Keystone.'),
|
||||
|
||||
cfg.StrOpt('key_file', help='(SSL/SSH) Private key file name to '
|
||||
'communicate with Keystone API')
|
||||
]
|
||||
|
||||
murano_opts = [
|
||||
cfg.StrOpt('url', help='Optional murano url in format '
|
||||
'like http://0.0.0.0:8082 used by Murano engine'),
|
||||
|
@ -148,10 +145,7 @@ murano_opts = [
|
|||
cfg.ListOpt('enabled_plugins',
|
||||
help="List of enabled Extension Plugins. "
|
||||
"Remove or leave commented to enable all installed "
|
||||
"plugins."),
|
||||
|
||||
cfg.StrOpt('region_name_for_services',
|
||||
help="Default region name used to get services endpoints.")
|
||||
"plugins.")
|
||||
]
|
||||
|
||||
networking_opts = [
|
||||
|
@ -271,6 +265,11 @@ file_server = [
|
|||
help='Set a file server.')
|
||||
]
|
||||
|
||||
home_region = cfg.StrOpt(
|
||||
'home_region', default=None,
|
||||
help="Default region name used to get services endpoints.")
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(paste_deploy_opts, group='paste_deploy')
|
||||
CONF.register_cli_opts(bind_opts)
|
||||
|
@ -278,10 +277,10 @@ CONF.register_opts(rabbit_opts, group='rabbitmq')
|
|||
CONF.register_opts(heat_opts, group='heat')
|
||||
CONF.register_opts(mistral_opts, group='mistral')
|
||||
CONF.register_opts(neutron_opts, group='neutron')
|
||||
CONF.register_opts(keystone_opts, group='keystone')
|
||||
CONF.register_opts(murano_opts, group='murano')
|
||||
CONF.register_opts(engine_opts, group='engine')
|
||||
CONF.register_opts(file_server)
|
||||
CONF.register_opt(home_region)
|
||||
CONF.register_cli_opts(metadata_dir)
|
||||
CONF.register_opts(packages_opts, group='packages_opts')
|
||||
CONF.register_opts(stats_opts, group='stats')
|
||||
|
|
|
@ -34,7 +34,7 @@ from murano.dsl import dsl_exception
|
|||
from murano.dsl import executor as dsl_executor
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import serializer
|
||||
from murano.engine import environment
|
||||
from murano.engine import execution_session
|
||||
from murano.engine import package_loader
|
||||
from murano.engine.system import status_reporter
|
||||
from murano.engine.system import yaql_functions
|
||||
|
@ -125,8 +125,8 @@ class TaskExecutor(object):
|
|||
return self._action
|
||||
|
||||
@property
|
||||
def environment(self):
|
||||
return self._environment
|
||||
def session(self):
|
||||
return self._session
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
|
@ -137,14 +137,14 @@ class TaskExecutor(object):
|
|||
reporter = status_reporter.StatusReporter(task['id'])
|
||||
self._action = task.get('action')
|
||||
self._model = task['model']
|
||||
self._environment = environment.Environment()
|
||||
self._environment.token = task['token']
|
||||
self._environment.tenant_id = task['tenant_id']
|
||||
self._environment.system_attributes = self._model.get('SystemData', {})
|
||||
self._session = execution_session.ExecutionSession()
|
||||
self._session.token = task['token']
|
||||
self._session.project_id = task['tenant_id']
|
||||
self._session.system_attributes = self._model.get('SystemData', {})
|
||||
self._reporter = reporter
|
||||
|
||||
self._model_policy_enforcer = enforcer.ModelPolicyEnforcer(
|
||||
self._environment)
|
||||
self._session)
|
||||
|
||||
def execute(self):
|
||||
try:
|
||||
|
@ -152,13 +152,9 @@ class TaskExecutor(object):
|
|||
except Exception as e:
|
||||
return self.exception_result(e, None, '<system>')
|
||||
|
||||
murano_client_factory = \
|
||||
lambda: self._environment.clients.get_murano_client()
|
||||
with package_loader.CombinedPackageLoader(
|
||||
murano_client_factory,
|
||||
self._environment.tenant_id) as pkg_loader:
|
||||
with package_loader.CombinedPackageLoader(self._session) as pkg_loader:
|
||||
result = self._execute(pkg_loader)
|
||||
self._model['SystemData'] = self._environment.system_attributes
|
||||
self._model['SystemData'] = self._session.system_attributes
|
||||
result['model'] = self._model
|
||||
|
||||
if (not self._model.get('Objects') and
|
||||
|
@ -174,7 +170,7 @@ class TaskExecutor(object):
|
|||
get_plugin_loader().register_in_loader(pkg_loader)
|
||||
|
||||
executor = dsl_executor.MuranoDslExecutor(
|
||||
pkg_loader, ContextManager(), self.environment)
|
||||
pkg_loader, ContextManager(), self.session)
|
||||
try:
|
||||
obj = executor.load(self.model)
|
||||
except Exception as e:
|
||||
|
@ -188,20 +184,20 @@ class TaskExecutor(object):
|
|||
|
||||
try:
|
||||
LOG.debug('Invoking pre-cleanup hooks')
|
||||
self.environment.start()
|
||||
self.session.start()
|
||||
executor.cleanup(self._model)
|
||||
except Exception as e:
|
||||
return self.exception_result(e, obj, '<GC>')
|
||||
finally:
|
||||
LOG.debug('Invoking post-cleanup hooks')
|
||||
self.environment.finish()
|
||||
self.session.finish()
|
||||
self._model['ObjectsCopy'] = copy.deepcopy(self._model.get('Objects'))
|
||||
|
||||
action_result = None
|
||||
if self.action:
|
||||
try:
|
||||
LOG.debug('Invoking pre-execution hooks')
|
||||
self.environment.start()
|
||||
self.session.start()
|
||||
try:
|
||||
action_result = self._invoke(executor)
|
||||
finally:
|
||||
|
@ -213,7 +209,7 @@ class TaskExecutor(object):
|
|||
return self.exception_result(e, obj, self.action['method'])
|
||||
finally:
|
||||
LOG.debug('Invoking post-execution hooks')
|
||||
self.environment.finish()
|
||||
self.session.finish()
|
||||
|
||||
try:
|
||||
action_result = serializer.serialize(action_result)
|
||||
|
@ -266,16 +262,16 @@ class TaskExecutor(object):
|
|||
def _create_trust(self):
|
||||
if not CONF.engine.use_trusts:
|
||||
return
|
||||
trust_id = self._environment.system_attributes.get('TrustId')
|
||||
trust_id = self._session.system_attributes.get('TrustId')
|
||||
if not trust_id:
|
||||
trust_id = auth_utils.create_trust(self._environment.token,
|
||||
self._environment.tenant_id)
|
||||
self._environment.system_attributes['TrustId'] = trust_id
|
||||
self._environment.trust_id = trust_id
|
||||
trust_id = auth_utils.create_trust(
|
||||
self._session.token, self._session.project_id)
|
||||
self._session.system_attributes['TrustId'] = trust_id
|
||||
self._session.trust_id = trust_id
|
||||
|
||||
def _delete_trust(self):
|
||||
trust_id = self._environment.trust_id
|
||||
trust_id = self._session.trust_id
|
||||
if trust_id:
|
||||
auth_utils.delete_trust(self._environment.trust_id)
|
||||
self._environment.system_attributes['TrustId'] = None
|
||||
self._environment.trust_id = None
|
||||
auth_utils.delete_trust(self._session.trust_id)
|
||||
self._session.system_attributes['TrustId'] = None
|
||||
self._session.trust_id = None
|
||||
|
|
|
@ -263,9 +263,10 @@ class EnvironmentServices(object):
|
|||
|
||||
@staticmethod
|
||||
def get_network_driver(context):
|
||||
ks = auth_utils.get_client(context.auth_token, context.tenant)
|
||||
session = auth_utils.get_token_client_session(
|
||||
context.auth_token, context.tenant)
|
||||
try:
|
||||
ks.service_catalog.url_for(service_type='network')
|
||||
session.get_endpoint(service_type='network')
|
||||
except ks_exceptions.EndpointNotFound:
|
||||
LOG.debug("Will use NovaNetwork as a network driver")
|
||||
return "nova"
|
||||
|
|
|
@ -25,15 +25,15 @@ CTX_CALLER_CONTEXT = '$?callerContext'
|
|||
CTX_CURRENT_INSTRUCTION = '$?currentInstruction'
|
||||
CTX_CURRENT_EXCEPTION = '$?currentException'
|
||||
CTX_CURRENT_METHOD = '$?currentMethod'
|
||||
CTX_ENVIRONMENT = '$?environment'
|
||||
CTX_EXECUTOR = '$?executor'
|
||||
CTX_EXECUTION_SESSION = '$?executionSession'
|
||||
CTX_ORIGINAL_CONTEXT = '$?originalContext'
|
||||
CTX_PACKAGE_LOADER = '$?packageLoader'
|
||||
CTX_SKIP_FRAME = '$?skipFrame'
|
||||
CTX_THIS = '$?this'
|
||||
CTX_TYPE = '$?type'
|
||||
CTX_VARIABLE_SCOPE = '$?variableScope'
|
||||
CTX_YAQL_ENGINE = '$?yaqlEngine'
|
||||
CTX_ORIGINAL_CONTEXT = '$?originalContext'
|
||||
|
||||
DM_OBJECTS = 'Objects'
|
||||
DM_OBJECTS_COPY = 'ObjectsCopy'
|
||||
|
@ -45,6 +45,10 @@ META_NO_TRACE = '?noTrace'
|
|||
CORE_LIBRARY = 'io.murano'
|
||||
CORE_LIBRARY_OBJECT = 'io.murano.Object'
|
||||
|
||||
TL_CONTEXT = '__murano_context'
|
||||
TL_ID = '__thread_id'
|
||||
TL_SESSION = '__murano_execution_session'
|
||||
|
||||
RUNTIME_VERSION_1_0 = semantic_version.Version('1.0.0')
|
||||
RUNTIME_VERSION_1_1 = semantic_version.Version('1.1.0')
|
||||
RUNTIME_VERSION_1_2 = semantic_version.Version('1.2.0')
|
||||
|
|
|
@ -247,8 +247,8 @@ class Interfaces(object):
|
|||
return MuranoObjectInterface(mpl_object)
|
||||
|
||||
@property
|
||||
def environment(self):
|
||||
return helpers.get_environment()
|
||||
def execution_session(self):
|
||||
return helpers.get_execution_session()
|
||||
|
||||
@property
|
||||
def caller(self):
|
||||
|
|
|
@ -39,10 +39,10 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class MuranoDslExecutor(object):
|
||||
def __init__(self, package_loader, context_manager, environment=None):
|
||||
def __init__(self, package_loader, context_manager, session=None):
|
||||
self._package_loader = package_loader
|
||||
self._context_manager = context_manager
|
||||
self._environment = environment
|
||||
self._session = session
|
||||
self._attribute_store = attribute_store.AttributeStore()
|
||||
self._object_store = object_store.ObjectStore(self)
|
||||
self._locks = {}
|
||||
|
@ -66,6 +66,12 @@ class MuranoDslExecutor(object):
|
|||
|
||||
def invoke_method(self, method, this, context, args, kwargs,
|
||||
skip_stub=False):
|
||||
with helpers.execution_session(self._session):
|
||||
return self._invoke_method(
|
||||
method, this, context, args, kwargs, skip_stub=skip_stub)
|
||||
|
||||
def _invoke_method(self, method, this, context, args, kwargs,
|
||||
skip_stub=False):
|
||||
if isinstance(this, dsl.MuranoObjectInterface):
|
||||
this = this.object
|
||||
kwargs = utils.filter_parameters_dict(kwargs)
|
||||
|
@ -190,6 +196,10 @@ class MuranoDslExecutor(object):
|
|||
return tuple(), parameter_values
|
||||
|
||||
def load(self, data):
|
||||
with helpers.execution_session(self._session):
|
||||
return self._load(data)
|
||||
|
||||
def _load(self, data):
|
||||
if not isinstance(data, dict):
|
||||
raise TypeError()
|
||||
self._attribute_store.load(data.get(constants.DM_ATTRIBUTES) or [])
|
||||
|
@ -199,6 +209,10 @@ class MuranoDslExecutor(object):
|
|||
return dsl.MuranoObjectInterface(result, executor=self)
|
||||
|
||||
def cleanup(self, data):
|
||||
with helpers.execution_session(self._session):
|
||||
return self._cleanup(data)
|
||||
|
||||
def _cleanup(self, data):
|
||||
objects_copy = data.get(constants.DM_OBJECTS_COPY)
|
||||
if not objects_copy:
|
||||
return
|
||||
|
@ -244,7 +258,7 @@ class MuranoDslExecutor(object):
|
|||
context[constants.CTX_EXECUTOR] = weakref.ref(self)
|
||||
context[constants.CTX_PACKAGE_LOADER] = weakref.ref(
|
||||
self._package_loader)
|
||||
context[constants.CTX_ENVIRONMENT] = self._environment
|
||||
context[constants.CTX_EXECUTION_SESSION] = self._session
|
||||
context[constants.CTX_ATTRIBUTE_STORE] = weakref.ref(
|
||||
self._attribute_store)
|
||||
self._root_context_cache[runtime_version] = context
|
||||
|
|
|
@ -109,10 +109,14 @@ def generate_id():
|
|||
def parallel_select(collection, func, limit=1000):
|
||||
# workaround for eventlet issue 232
|
||||
# https://github.com/eventlet/eventlet/issues/232
|
||||
context = get_context()
|
||||
session = get_execution_session()
|
||||
|
||||
def wrapper(element):
|
||||
try:
|
||||
with contextual(get_context()):
|
||||
return func(element), False, None
|
||||
with contextual(context):
|
||||
with execution_session(session):
|
||||
return func(element), False, None
|
||||
except Exception as e:
|
||||
return e, True, sys.exc_info()[2]
|
||||
|
||||
|
@ -132,7 +136,7 @@ def enum(**enums):
|
|||
|
||||
def get_context():
|
||||
current_thread = eventlet.greenthread.getcurrent()
|
||||
return getattr(current_thread, '__murano_context', None)
|
||||
return getattr(current_thread, constants.TL_CONTEXT, None)
|
||||
|
||||
|
||||
def get_executor(context=None):
|
||||
|
@ -146,9 +150,15 @@ def get_type(context=None):
|
|||
return context[constants.CTX_TYPE]
|
||||
|
||||
|
||||
def get_environment(context=None):
|
||||
def get_execution_session(context=None):
|
||||
context = context or get_context()
|
||||
return context[constants.CTX_ENVIRONMENT]
|
||||
session = None
|
||||
if context is not None:
|
||||
session = context[constants.CTX_EXECUTION_SESSION]
|
||||
if session is None:
|
||||
current_thread = eventlet.greenthread.getcurrent()
|
||||
session = getattr(current_thread, constants.TL_SESSION, None)
|
||||
return session
|
||||
|
||||
|
||||
def get_object_store(context=None):
|
||||
|
@ -215,27 +225,37 @@ def get_current_thread_id():
|
|||
global _threads_sequencer
|
||||
|
||||
current_thread = eventlet.greenthread.getcurrent()
|
||||
thread_id = getattr(current_thread, '__thread_id', None)
|
||||
thread_id = getattr(current_thread, constants.TL_ID, None)
|
||||
if thread_id is None:
|
||||
thread_id = 'T' + str(_threads_sequencer)
|
||||
_threads_sequencer += 1
|
||||
setattr(current_thread, '__thread_id', thread_id)
|
||||
setattr(current_thread, constants.TL_ID, thread_id)
|
||||
return thread_id
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def contextual(ctx):
|
||||
def thread_local_attribute(name, value):
|
||||
current_thread = eventlet.greenthread.getcurrent()
|
||||
current_context = getattr(current_thread, '__murano_context', None)
|
||||
if ctx:
|
||||
setattr(current_thread, '__murano_context', ctx)
|
||||
old_value = getattr(current_thread, name, None)
|
||||
if value is not None:
|
||||
setattr(current_thread, name, value)
|
||||
elif hasattr(current_thread, name):
|
||||
delattr(current_thread, name)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if current_context:
|
||||
setattr(current_thread, '__murano_context', current_context)
|
||||
elif hasattr(current_thread, '__murano_context'):
|
||||
delattr(current_thread, '__murano_context')
|
||||
if old_value is not None:
|
||||
setattr(current_thread, name, old_value)
|
||||
elif hasattr(current_thread, name):
|
||||
delattr(current_thread, name)
|
||||
|
||||
|
||||
def contextual(ctx):
|
||||
return thread_local_attribute(constants.TL_CONTEXT, ctx)
|
||||
|
||||
|
||||
def execution_session(session):
|
||||
return thread_local_attribute(constants.TL_SESSION, session)
|
||||
|
||||
|
||||
def parse_version_spec(version_spec):
|
||||
|
@ -332,7 +352,10 @@ def is_instance_of(obj, class_name, pov_or_version_spec=None):
|
|||
|
||||
def memoize(func):
|
||||
cache = {}
|
||||
return get_memoize_func(func, cache)
|
||||
|
||||
|
||||
def get_memoize_func(func, cache):
|
||||
@functools.wraps(func)
|
||||
def wrap(*args):
|
||||
if args not in cache:
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
# Copyright (c) 2016 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.
|
||||
|
||||
# This code is almost a complete copy of eventlet.corolocal with only
|
||||
# the concept of current thread replaced with current session
|
||||
|
||||
import weakref
|
||||
|
||||
import six
|
||||
|
||||
from murano.dsl import helpers
|
||||
|
||||
|
||||
# the entire purpose of this class is to store off the constructor
|
||||
# arguments in a local variable without calling __init__ directly
|
||||
class _localbase(object):
|
||||
__slots__ = '_local__args', '_local__sessions'
|
||||
|
||||
def __new__(cls, *args, **kw):
|
||||
self = object.__new__(cls)
|
||||
object.__setattr__(self, '_local__args', (args, kw))
|
||||
object.__setattr__(
|
||||
self, '_local__sessions', weakref.WeakKeyDictionary())
|
||||
if (args or kw) and (cls.__init__ is object.__init__):
|
||||
raise TypeError('Initialization arguments are not supported')
|
||||
return self
|
||||
|
||||
|
||||
def _patch(session_local):
|
||||
sessions_dict = object.__getattribute__(session_local, '_local__sessions')
|
||||
session = helpers.get_execution_session()
|
||||
localdict = sessions_dict.get(session)
|
||||
if localdict is None:
|
||||
# must be the first time we've seen this session, call __init__
|
||||
localdict = {}
|
||||
sessions_dict[session] = localdict
|
||||
cls = type(session_local)
|
||||
if cls.__init__ is not object.__init__:
|
||||
args, kw = object.__getattribute__(session_local, '_local__args')
|
||||
session_local.__init__(*args, **kw)
|
||||
object.__setattr__(session_local, '__dict__', localdict)
|
||||
|
||||
|
||||
class _local(_localbase):
|
||||
def __getattribute__(self, attr):
|
||||
_patch(self)
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
_patch(self)
|
||||
return object.__setattr__(self, attr, value)
|
||||
|
||||
def __delattr__(self, attr):
|
||||
_patch(self)
|
||||
return object.__delattr__(self, attr)
|
||||
|
||||
|
||||
def session_local(cls):
|
||||
return type(cls.__name__, (cls, _local), {})
|
||||
|
||||
|
||||
class SessionLocalDict(six.moves.UserDict, object):
|
||||
def __init__(self, **kwargs):
|
||||
self.__session_data = weakref.WeakKeyDictionary()
|
||||
self.__default = {}
|
||||
super(SessionLocalDict, self).__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
session = helpers.get_execution_session()
|
||||
if session is None:
|
||||
return self.__default
|
||||
return self.__session_data.setdefault(session, {})
|
||||
|
||||
@data.setter
|
||||
def data(self, value):
|
||||
session = helpers.get_execution_session()
|
||||
if session is None:
|
||||
self.__default = value
|
||||
else:
|
||||
self.__session_data[session] = value
|
||||
|
||||
|
||||
def execution_session_memoize(func):
|
||||
cache = SessionLocalDict()
|
||||
return helpers.get_memoize_func(func, cache)
|
|
@ -1,241 +0,0 @@
|
|||
# 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
|
||||
|
||||
from eventlet import semaphore
|
||||
import heatclient.client as hclient
|
||||
import keystoneclient
|
||||
import keystoneclient.auth.identity.access as access
|
||||
import muranoclient.v1.client as muranoclient
|
||||
import neutronclient.v2_0.client as nclient
|
||||
from oslo_config import cfg
|
||||
|
||||
from murano.common import auth_utils
|
||||
from muranoclient.glance import client as art_client
|
||||
|
||||
try:
|
||||
# integration with congress is optional
|
||||
import congressclient.v1.client as congress_client
|
||||
except ImportError as congress_client_import_error:
|
||||
congress_client = None
|
||||
try:
|
||||
import mistralclient.api.client as mistralclient
|
||||
except ImportError as mistral_import_error:
|
||||
mistralclient = None
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ClientManager(object):
|
||||
def __init__(self, environment):
|
||||
self._trusts_keystone_client = None
|
||||
self._token_keystone_client = None
|
||||
self._cache = {}
|
||||
self._semaphore = semaphore.BoundedSemaphore()
|
||||
self._environment = weakref.proxy(environment)
|
||||
|
||||
def get_client(self, name, use_trusts, client_factory):
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
keystone_client = None if name == 'keystone' else \
|
||||
self.get_keystone_client(use_trusts)
|
||||
|
||||
self._semaphore.acquire()
|
||||
try:
|
||||
client, used_token = self._cache.get(
|
||||
(name, use_trusts), (None, None))
|
||||
fresh_token = None if keystone_client is None \
|
||||
else keystone_client.auth_token
|
||||
if use_trusts and used_token != fresh_token:
|
||||
client = None
|
||||
if not client:
|
||||
token = fresh_token
|
||||
if not use_trusts:
|
||||
token = self._environment.token
|
||||
client = client_factory(keystone_client, token)
|
||||
self._cache[(name, use_trusts)] = (client, token)
|
||||
return client
|
||||
finally:
|
||||
self._semaphore.release()
|
||||
|
||||
def get_keystone_client(self, use_trusts=True):
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
factory = lambda _1, _2: \
|
||||
auth_utils.get_client_for_trusts(self._environment.trust_id) \
|
||||
if use_trusts else auth_utils.get_client(
|
||||
self._environment.token, self._environment.tenant_id)
|
||||
|
||||
return self.get_client('keystone', use_trusts, factory)
|
||||
|
||||
def get_congress_client(self, use_trusts=True):
|
||||
"""Client for congress services
|
||||
|
||||
:return: initialized congress client
|
||||
:raise ImportError: in case that python-congressclient
|
||||
is not present on python path
|
||||
"""
|
||||
|
||||
if not congress_client:
|
||||
# congress client was not imported
|
||||
raise congress_client_import_error
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
def factory(keystone_client, auth_token):
|
||||
auth = access.AccessInfoPlugin(keystone_client.auth_ref)
|
||||
session = keystoneclient.session.Session(auth=auth)
|
||||
return congress_client.Client(session=session,
|
||||
service_type='policy')
|
||||
|
||||
return self.get_client('congress', use_trusts, factory)
|
||||
|
||||
def get_heat_client(self, use_trusts=True):
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
def factory(keystone_client, auth_token):
|
||||
heat_settings = CONF.heat
|
||||
|
||||
heat_url = keystone_client.service_catalog.url_for(
|
||||
service_type='orchestration',
|
||||
endpoint_type=heat_settings.endpoint_type)
|
||||
|
||||
kwargs = {
|
||||
'token': auth_token,
|
||||
'ca_file': heat_settings.ca_file or None,
|
||||
'cert_file': heat_settings.cert_file or None,
|
||||
'key_file': heat_settings.key_file or None,
|
||||
'insecure': heat_settings.insecure
|
||||
}
|
||||
|
||||
if not CONF.engine.use_trusts:
|
||||
kwargs.update({
|
||||
'username': 'badusername',
|
||||
'password': 'badpassword'
|
||||
})
|
||||
return hclient.Client('1', heat_url, **kwargs)
|
||||
|
||||
return self.get_client('heat', use_trusts, factory)
|
||||
|
||||
def get_neutron_client(self, use_trusts=True):
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
def factory(keystone_client, auth_token):
|
||||
neutron_settings = CONF.neutron
|
||||
|
||||
neutron_url = keystone_client.service_catalog.url_for(
|
||||
service_type='network',
|
||||
endpoint_type=neutron_settings.endpoint_type)
|
||||
|
||||
return nclient.Client(
|
||||
endpoint_url=neutron_url,
|
||||
token=auth_token,
|
||||
ca_cert=neutron_settings.ca_cert or None,
|
||||
insecure=neutron_settings.insecure)
|
||||
|
||||
return self.get_client('neutron', use_trusts, factory)
|
||||
|
||||
def get_murano_client(self, use_trusts=True):
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
def factory(keystone_client, auth_token):
|
||||
murano_settings = CONF.murano
|
||||
|
||||
murano_url = \
|
||||
murano_settings.url or keystone_client.service_catalog.url_for(
|
||||
service_type='application-catalog',
|
||||
endpoint_type=murano_settings.endpoint_type)
|
||||
|
||||
if CONF.packages_opts.packages_service == 'glance':
|
||||
glance_settings = CONF.glance
|
||||
glance_url = (glance_settings.url or
|
||||
keystone_client.service_catalog.url_for(
|
||||
service_type='image',
|
||||
endpoint_type=glance_settings.endpoint_type))
|
||||
|
||||
arts = art_client.Client(
|
||||
endpoint=glance_url, token=auth_token,
|
||||
insecure=glance_settings.insecure,
|
||||
key_file=glance_settings.key_file or None,
|
||||
ca_file=glance_settings.ca_file or None,
|
||||
cert_file=glance_settings.cert_file or None,
|
||||
type_name='murano',
|
||||
type_version=1)
|
||||
else:
|
||||
arts = None
|
||||
|
||||
return muranoclient.Client(
|
||||
endpoint=murano_url,
|
||||
key_file=murano_settings.key_file or None,
|
||||
ca_file=murano_settings.cacert or None,
|
||||
cert_file=murano_settings.cert_file or None,
|
||||
insecure=murano_settings.insecure,
|
||||
auth_url=keystone_client.auth_url,
|
||||
token=auth_token,
|
||||
artifacts_client=arts)
|
||||
|
||||
return self.get_client('murano', use_trusts, factory)
|
||||
|
||||
def get_mistral_client(self, use_trusts=True):
|
||||
if not mistralclient:
|
||||
raise mistral_import_error
|
||||
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
def factory(keystone_client, auth_token):
|
||||
mistral_settings = CONF.mistral
|
||||
|
||||
endpoint_type = mistral_settings.endpoint_type
|
||||
service_type = mistral_settings.service_type
|
||||
|
||||
mistral_url = keystone_client.service_catalog.url_for(
|
||||
service_type=service_type,
|
||||
endpoint_type=endpoint_type)
|
||||
|
||||
return mistralclient.client(mistral_url=mistral_url,
|
||||
project_id=keystone_client.tenant_id,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
auth_token=auth_token,
|
||||
user_id=keystone_client.user_id)
|
||||
|
||||
return self.get_client('mistral', use_trusts, factory)
|
||||
|
||||
def get_artifacts_client(self, use_trusts=True):
|
||||
if not CONF.engine.use_trusts:
|
||||
use_trusts = False
|
||||
|
||||
def factory(keystone_client, auth_token):
|
||||
glance_settings = CONF.glance
|
||||
|
||||
glance_url = (glance_settings.url or
|
||||
keystone_client.service_catalog.url_for(
|
||||
service_type='image',
|
||||
endpoint_type=glance_settings.endpoint_type))
|
||||
|
||||
return art_client.Client(endpoint=glance_url, token=auth_token,
|
||||
insecure=glance_settings.insecure,
|
||||
key_file=glance_settings.key_file or None,
|
||||
cacert=glance_settings.cacert or None,
|
||||
cert_file=(glance_settings.cert_file or
|
||||
None),
|
||||
type_name='murano',
|
||||
type_version=1)
|
||||
return self.get_client('artifacts', use_trusts, factory)
|
|
@ -16,18 +16,16 @@
|
|||
from oslo_log import log as logging
|
||||
|
||||
from murano.common.i18n import _LE
|
||||
from murano.engine import client_manager
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Environment(object):
|
||||
class ExecutionSession(object):
|
||||
def __init__(self):
|
||||
self.token = None
|
||||
self.tenant_id = None
|
||||
self.project_id = None
|
||||
self.trust_id = None
|
||||
self.system_attributes = {}
|
||||
self.clients = client_manager.ClientManager(self)
|
||||
self._set_up_list = []
|
||||
self._tear_down_list = []
|
||||
|
|
@ -24,10 +24,13 @@ import uuid
|
|||
|
||||
import eventlet
|
||||
from muranoclient.common import exceptions as muranoclient_exc
|
||||
from muranoclient.glance import client as glare_client
|
||||
import muranoclient.v1.client as muranoclient
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from murano.common import auth_utils
|
||||
from murano.common.i18n import _LE, _LI, _LW
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import exceptions
|
||||
|
@ -48,18 +51,67 @@ usage_mem_locks = collections.defaultdict(m_utils.ReaderWriterLock)
|
|||
|
||||
|
||||
class ApiPackageLoader(package_loader.MuranoPackageLoader):
|
||||
def __init__(self, murano_client_factory, tenant_id, root_loader=None):
|
||||
def __init__(self, execution_session, root_loader=None):
|
||||
self._cache_directory = self._get_cache_directory()
|
||||
self._murano_client_factory = murano_client_factory
|
||||
self.tenant_id = tenant_id
|
||||
self._class_cache = {}
|
||||
self._package_cache = {}
|
||||
self._root_loader = root_loader or self
|
||||
self._execution_session = execution_session
|
||||
self._last_glare_token = None
|
||||
self._glare_client = None
|
||||
self._murano_client = None
|
||||
self._murano_client_session = None
|
||||
|
||||
self._mem_locks = []
|
||||
self._ipc_locks = []
|
||||
self._downloaded = []
|
||||
|
||||
def _get_glare_client(self):
|
||||
glance_settings = CONF.glance
|
||||
session = auth_utils.get_client_session(self._execution_session)
|
||||
token = session.auth.get_token(session)
|
||||
if self._last_glare_token != token:
|
||||
self._last_glare_token = token
|
||||
self._glare_client = None
|
||||
|
||||
if self._glare_client is None:
|
||||
url = glance_settings.url
|
||||
if not url:
|
||||
url = session.get_endpoint(
|
||||
service_type='image',
|
||||
interface=glance_settings.endpoint_type,
|
||||
region_name=CONF.home_region)
|
||||
|
||||
self._glare_client = glare_client.Client(
|
||||
endpoint=url, token=token,
|
||||
insecure=glance_settings.insecure,
|
||||
key_file=glance_settings.key_file or None,
|
||||
ca_file=glance_settings.ca_file or None,
|
||||
cert_file=glance_settings.cert_file or None,
|
||||
type_name='murano',
|
||||
type_version=1)
|
||||
return self._glare_client
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
murano_settings = CONF.murano
|
||||
last_glare_client = self._glare_client
|
||||
if CONF.packages_opts.packages_service == 'glance':
|
||||
artifacts_client = self._get_glare_client()
|
||||
else:
|
||||
artifacts_client = None
|
||||
if artifacts_client != last_glare_client:
|
||||
self._murano_client = None
|
||||
if not self._murano_client:
|
||||
parameters = auth_utils.get_session_client_parameters(
|
||||
service_type='application-catalog',
|
||||
execution_session=self._execution_session,
|
||||
conf=murano_settings
|
||||
)
|
||||
self._murano_client = muranoclient.Client(
|
||||
artifacts_client=artifacts_client, **parameters)
|
||||
return self._murano_client
|
||||
|
||||
def load_class_package(self, class_name, version_spec):
|
||||
packages = self._class_cache.get(class_name)
|
||||
if packages:
|
||||
|
@ -97,8 +149,9 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
|
|||
exc_info = sys.exc_info()
|
||||
six.reraise(exceptions.NoPackageFound(package_name),
|
||||
None, exc_info[2])
|
||||
return self._to_dsl_package(
|
||||
self._get_package_by_definition(package_definition))
|
||||
else:
|
||||
return self._to_dsl_package(
|
||||
self._get_package_by_definition(package_definition))
|
||||
|
||||
def register_package(self, package):
|
||||
for name in package.classes:
|
||||
|
@ -129,7 +182,7 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
|
|||
def _get_definition(self, filter_opts):
|
||||
filter_opts['catalog'] = True
|
||||
try:
|
||||
packages = list(self._murano_client_factory().packages.filter(
|
||||
packages = list(self.client.packages.filter(
|
||||
**filter_opts))
|
||||
if len(packages) > 1:
|
||||
LOG.debug('Ambiguous package resolution: more then 1 package '
|
||||
|
@ -180,8 +233,7 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
|
|||
download_ipc_lock = m_utils.ExclusiveInterProcessLock(
|
||||
path=download_lock_path, sleep_func=eventlet.sleep)
|
||||
|
||||
with download_mem_locks[package_id].write_lock(),\
|
||||
download_ipc_lock:
|
||||
with download_mem_locks[package_id].write_lock(), download_ipc_lock:
|
||||
|
||||
# NOTE(kzaitsev):
|
||||
# in case there were 2 concurrent threads/processes one might have
|
||||
|
@ -198,8 +250,7 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
|
|||
try:
|
||||
LOG.debug("Attempting to download package {} {}".format(
|
||||
package_def.fully_qualified_name, package_id))
|
||||
package_data = self._murano_client_factory().packages.download(
|
||||
package_id)
|
||||
package_data = self.client.packages.download(package_id)
|
||||
except muranoclient_exc.HTTPException as e:
|
||||
msg = 'Error loading package id {0}: {1}'.format(
|
||||
package_id, str(e)
|
||||
|
@ -304,7 +355,7 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
|
|||
public = None
|
||||
other = []
|
||||
for package in packages:
|
||||
if package.owner_id == self.tenant_id:
|
||||
if package.owner_id == self._execution_session.project_id:
|
||||
return package
|
||||
elif package.is_public:
|
||||
public = package
|
||||
|
@ -451,10 +502,9 @@ class DirectoryPackageLoader(package_loader.MuranoPackageLoader):
|
|||
|
||||
|
||||
class CombinedPackageLoader(package_loader.MuranoPackageLoader):
|
||||
def __init__(self, murano_client_factory, tenant_id, root_loader=None):
|
||||
def __init__(self, execution_session, root_loader=None):
|
||||
root_loader = root_loader or self
|
||||
self.api_loader = ApiPackageLoader(
|
||||
murano_client_factory, tenant_id, root_loader)
|
||||
self.api_loader = ApiPackageLoader(execution_session, root_loader)
|
||||
self.directory_loaders = []
|
||||
|
||||
for folder in CONF.packages_opts.load_packages_from:
|
||||
|
|
|
@ -66,7 +66,7 @@ class AgentListener(object):
|
|||
return
|
||||
|
||||
if self._receive_thread is None:
|
||||
helpers.get_environment().on_session_finish(
|
||||
helpers.get_execution_session().on_session_finish(
|
||||
lambda: self.stop())
|
||||
self._receive_thread = eventlet.spawn(self._receive)
|
||||
|
||||
|
|
|
@ -16,15 +16,18 @@
|
|||
import copy
|
||||
|
||||
import eventlet
|
||||
import heatclient.client as hclient
|
||||
import heatclient.exc as heat_exc
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from murano.common import auth_utils
|
||||
from murano.common.i18n import _LW
|
||||
from murano.common import utils
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import session_local_storage
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
@ -46,17 +49,35 @@ class HeatStack(object):
|
|||
self._hot_environment = ''
|
||||
self._applied = True
|
||||
self._description = description
|
||||
self._clients = helpers.get_environment().clients
|
||||
self._last_stack_timestamps = (None, None)
|
||||
self._tags = ''
|
||||
self._client = self._get_client(CONF.home_region)
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _create_client(session, region_name):
|
||||
parameters = auth_utils.get_session_client_parameters(
|
||||
service_type='orchestration', region=region_name,
|
||||
conf=CONF.heat, session=session)
|
||||
return hclient.Client('1', **parameters)
|
||||
|
||||
@staticmethod
|
||||
@session_local_storage.execution_session_memoize
|
||||
def _get_client(region_name):
|
||||
session = auth_utils.get_client_session(conf=CONF.heat)
|
||||
return HeatStack._create_client(session, region_name)
|
||||
|
||||
@classmethod
|
||||
def _get_token_client(cls):
|
||||
ks_session = auth_utils.get_token_client_session(conf=CONF.heat)
|
||||
return cls._create_client(ks_session, CONF.home_region)
|
||||
|
||||
def current(self):
|
||||
client = self._clients.get_heat_client()
|
||||
if self._template is not None:
|
||||
return self._template
|
||||
try:
|
||||
stack_info = client.stacks.get(stack_id=self._name)
|
||||
template = client.stacks.template(
|
||||
stack_info = self._client.stacks.get(stack_id=self._name)
|
||||
template = self._client.stacks.template(
|
||||
stack_id='{0}/{1}'.format(
|
||||
stack_info.stack_name,
|
||||
stack_info.id))
|
||||
|
@ -126,11 +147,11 @@ class HeatStack(object):
|
|||
def _wait_state(self, status_func, wait_progress=False):
|
||||
tries = 4
|
||||
delay = 1
|
||||
|
||||
while tries > 0:
|
||||
while True:
|
||||
client = self._clients.get_heat_client()
|
||||
try:
|
||||
stack_info = client.stacks.get(
|
||||
stack_info = self._client.stacks.get(
|
||||
stack_id=self._name)
|
||||
status = stack_info.stack_status
|
||||
tries = 4
|
||||
|
@ -194,7 +215,7 @@ class HeatStack(object):
|
|||
resources = template.get('Resources') or template.get('resources')
|
||||
if current_status == 'NOT_FOUND':
|
||||
if resources is not None:
|
||||
token_client = self._clients.get_heat_client(use_trusts=False)
|
||||
token_client = self._get_token_client()
|
||||
token_client.stacks.create(
|
||||
stack_name=self._name,
|
||||
parameters=self._parameters,
|
||||
|
@ -207,9 +228,7 @@ class HeatStack(object):
|
|||
self._wait_state(lambda status: status == 'CREATE_COMPLETE')
|
||||
else:
|
||||
if resources is not None:
|
||||
trust_client = self._clients.get_heat_client()
|
||||
|
||||
trust_client.stacks.update(
|
||||
self._client.stacks.update(
|
||||
stack_id=self._name,
|
||||
parameters=self._parameters,
|
||||
files=self._files,
|
||||
|
@ -225,11 +244,10 @@ class HeatStack(object):
|
|||
self._applied = not utils.is_different(self._template, template)
|
||||
|
||||
def delete(self):
|
||||
client = self._clients.get_heat_client()
|
||||
try:
|
||||
if not self.current():
|
||||
return
|
||||
client.stacks.delete(stack_id=self._name)
|
||||
self._client.stacks.delete(stack_id=self._name)
|
||||
self._wait_state(
|
||||
lambda status: status in ('DELETE_COMPLETE', 'NOT_FOUND'),
|
||||
wait_progress=True)
|
||||
|
|
|
@ -17,9 +17,17 @@
|
|||
import json
|
||||
|
||||
import eventlet
|
||||
try:
|
||||
import mistralclient.api.client as mistralclient
|
||||
except ImportError as mistral_import_error:
|
||||
mistralclient = None
|
||||
from oslo_config import cfg
|
||||
|
||||
from murano.common import auth_utils
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import session_local_storage
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class MistralError(Exception):
|
||||
|
@ -28,18 +36,44 @@ class MistralError(Exception):
|
|||
|
||||
@dsl.name('io.murano.system.MistralClient')
|
||||
class MistralClient(object):
|
||||
def __init__(self, context):
|
||||
self._clients = helpers.get_environment(context).clients
|
||||
def __init__(self):
|
||||
self._client = self._create_client(CONF.home_region)
|
||||
|
||||
@staticmethod
|
||||
@session_local_storage.execution_session_memoize
|
||||
def _create_client(region):
|
||||
if not mistralclient:
|
||||
raise mistral_import_error
|
||||
|
||||
mistral_settings = CONF.mistral
|
||||
|
||||
endpoint_type = mistral_settings.endpoint_type
|
||||
service_type = mistral_settings.service_type
|
||||
session = auth_utils.get_client_session()
|
||||
|
||||
mistral_url = mistral_settings.url or session.get_endpoint(
|
||||
service_type=service_type,
|
||||
endpoint_type=endpoint_type,
|
||||
region_name=region)
|
||||
auth_ref = session.auth.get_access(session)
|
||||
|
||||
return mistralclient.client(
|
||||
mistral_url=mistral_url,
|
||||
project_id=auth_ref.project_id,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
auth_token=auth_ref.auth_token,
|
||||
user_id=auth_ref.user_id,
|
||||
insecure=mistral_settings.insecure,
|
||||
cacert=mistral_settings.ca_cert
|
||||
)
|
||||
|
||||
def upload(self, definition):
|
||||
mistral_client = self._clients.get_mistral_client()
|
||||
mistral_client.workflows.create(definition)
|
||||
self._client.workflows.create(definition)
|
||||
|
||||
def run(self, name, timeout=600, inputs=None, params=None):
|
||||
mistral_client = self._clients.get_mistral_client()
|
||||
execution = mistral_client.executions.create(workflow_name=name,
|
||||
workflow_input=inputs,
|
||||
params=params)
|
||||
execution = self._client.executions.create(
|
||||
workflow_name=name, workflow_input=inputs, params=params)
|
||||
# For the fire and forget functionality - when we do not want to wait
|
||||
# for the result of the run.
|
||||
if timeout == 0:
|
||||
|
@ -51,7 +85,7 @@ class MistralClient(object):
|
|||
with eventlet.timeout.Timeout(timeout):
|
||||
while state not in ('ERROR', 'SUCCESS'):
|
||||
eventlet.sleep(2)
|
||||
execution = mistral_client.executions.get(execution.id)
|
||||
execution = self._client.executions.get(execution.id)
|
||||
state = execution.state
|
||||
except eventlet.timeout.Timeout:
|
||||
error_message = (
|
||||
|
|
|
@ -16,15 +16,18 @@ import math
|
|||
|
||||
import netaddr
|
||||
from netaddr.strategy import ipv4
|
||||
import neutronclient.v2_0.client as nclient
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import retrying
|
||||
|
||||
from murano.common import auth_utils
|
||||
from murano.common import exceptions as exc
|
||||
from murano.common.i18n import _LI
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import session_local_storage
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -33,11 +36,19 @@ LOG = logging.getLogger(__name__)
|
|||
@dsl.name('io.murano.system.NetworkExplorer')
|
||||
class NetworkExplorer(object):
|
||||
def __init__(self):
|
||||
environment = helpers.get_environment()
|
||||
self._clients = environment.clients
|
||||
self._tenant_id = environment.tenant_id
|
||||
session = helpers.get_execution_session()
|
||||
self._project_id = session.project_id
|
||||
self._settings = CONF.networking
|
||||
self._available_cidrs = self._generate_possible_cidrs()
|
||||
self._client = self._get_client(CONF.home_region)
|
||||
|
||||
@staticmethod
|
||||
@session_local_storage.execution_session_memoize
|
||||
def _get_client(region_name):
|
||||
neutron_settings = CONF.neutron
|
||||
return nclient.Client(**auth_utils.get_session_client_parameters(
|
||||
service_type='network', region=region_name, conf=neutron_settings
|
||||
))
|
||||
|
||||
# NOTE(starodubcevna): to avoid simultaneous router requests we use retry
|
||||
# decorator with random delay 1-10 seconds between attempts and maximum
|
||||
|
@ -47,11 +58,10 @@ class NetworkExplorer(object):
|
|||
wait_random_min=1000, wait_random_max=10000,
|
||||
stop_max_delay=30000)
|
||||
def get_default_router(self):
|
||||
client = self._clients.get_neutron_client()
|
||||
router_name = self._settings.router_name
|
||||
|
||||
routers = client.list_routers(
|
||||
tenant_id=self._tenant_id, name=router_name).get('routers')
|
||||
routers = self._client.list_routers(
|
||||
tenant_id=self._project_id, name=router_name).get('routers')
|
||||
if len(routers) == 0:
|
||||
LOG.debug('Router {name} not found'.format(name=router_name))
|
||||
if self._settings.create_router:
|
||||
|
@ -61,7 +71,7 @@ class NetworkExplorer(object):
|
|||
kwargs = {'id': external_network} \
|
||||
if uuidutils.is_uuid_like(external_network) \
|
||||
else {'name': external_network}
|
||||
networks = client.list_networks(**kwargs).get('networks')
|
||||
networks = self._client.list_networks(**kwargs).get('networks')
|
||||
ext_nets = filter(lambda n: n['router:external'], networks)
|
||||
if len(ext_nets) == 0:
|
||||
raise KeyError('Router %s could not be created, '
|
||||
|
@ -77,7 +87,8 @@ class NetworkExplorer(object):
|
|||
'admin_state_up': True,
|
||||
}
|
||||
}
|
||||
router = client.create_router(body=body_data).get('router')
|
||||
router = self._client.create_router(
|
||||
body=body_data).get('router')
|
||||
LOG.info(_LI('Created router: {id}').format(id=router['id']))
|
||||
return router['id']
|
||||
else:
|
||||
|
@ -112,20 +123,18 @@ class NetworkExplorer(object):
|
|||
return self._settings.default_dns
|
||||
|
||||
def get_external_network_id_for_router(self, router_id):
|
||||
client = self._clients.get_neutron_client()
|
||||
router = client.show_router(router_id).get('router')
|
||||
router = self._client.show_router(router_id).get('router')
|
||||
if not router or 'external_gateway_info' not in router:
|
||||
return None
|
||||
return router['external_gateway_info'].get('network_id')
|
||||
|
||||
def get_external_network_id_for_network(self, network_id):
|
||||
client = self._clients.get_neutron_client()
|
||||
network = client.show_network(network_id).get('network')
|
||||
network = self._client.show_network(network_id).get('network')
|
||||
if network.get('router:external', False):
|
||||
return network_id
|
||||
|
||||
# Get router interfaces of the network
|
||||
router_ports = client.list_ports(
|
||||
router_ports = self._client.list_ports(
|
||||
**{'device_owner': 'network:router_interface',
|
||||
'network_id': network_id}).get('ports')
|
||||
|
||||
|
@ -141,14 +150,13 @@ class NetworkExplorer(object):
|
|||
def _get_cidrs_taken_by_router(self, router_id):
|
||||
if not router_id:
|
||||
return []
|
||||
client = self._clients.get_neutron_client()
|
||||
ports = client.list_ports(device_id=router_id)['ports']
|
||||
ports = self._client.list_ports(device_id=router_id)['ports']
|
||||
subnet_ids = []
|
||||
for port in ports:
|
||||
for fixed_ip in port['fixed_ips']:
|
||||
subnet_ids.append(fixed_ip['subnet_id'])
|
||||
|
||||
all_subnets = client.list_subnets()['subnets']
|
||||
all_subnets = self._client.list_subnets()['subnets']
|
||||
filtered_cidrs = [netaddr.IPNetwork(subnet['cidr']) for subnet in
|
||||
all_subnets if subnet['id'] in subnet_ids]
|
||||
|
||||
|
@ -169,13 +177,10 @@ class NetworkExplorer(object):
|
|||
return list(net.subnet(width - bits_for_hosts))
|
||||
|
||||
def list_networks(self):
|
||||
client = self._clients.get_neutron_client()
|
||||
return client.list_networks()['networks']
|
||||
return self._client.list_networks()['networks']
|
||||
|
||||
def list_subnetworks(self):
|
||||
client = self._clients.get_neutron_client()
|
||||
return client.list_subnets()['subnets']
|
||||
return self._client.list_subnets()['subnets']
|
||||
|
||||
def list_ports(self):
|
||||
client = self._clients.get_neutron_client()
|
||||
return client.list_ports()['ports']
|
||||
return self._client.list_ports()['ports']
|
||||
|
|
|
@ -34,12 +34,12 @@ class TestFixture(object):
|
|||
return exc.load(model)
|
||||
|
||||
def finish_env(self):
|
||||
env = helpers.get_environment()
|
||||
env.finish()
|
||||
session = helpers.get_execution_session()
|
||||
session.finish()
|
||||
|
||||
def start_env(self):
|
||||
env = helpers.get_environment()
|
||||
env.start()
|
||||
session = helpers.get_execution_session()
|
||||
session.start()
|
||||
|
||||
def assert_equal(self, expected, observed, message=None):
|
||||
self._test_case.assertEqual(expected, observed, message)
|
||||
|
|
|
@ -34,8 +34,9 @@ _opt_lists = [
|
|||
('rabbitmq', murano.common.config.rabbit_opts),
|
||||
('heat', murano.common.config.heat_opts),
|
||||
('neutron', murano.common.config.neutron_opts),
|
||||
('keystone', murano.common.config.keystone_opts),
|
||||
('murano', murano.common.config.murano_opts),
|
||||
('glance', murano.common.config.glance_opts),
|
||||
('mistral', murano.common.config.mistral_opts),
|
||||
('networking', murano.common.config.networking_opts),
|
||||
('stats', murano.common.config.stats_opts),
|
||||
('packages_opts', murano.common.config.packages_opts),
|
||||
|
|
|
@ -15,14 +15,22 @@
|
|||
|
||||
import re
|
||||
|
||||
try:
|
||||
# integration with congress is optional
|
||||
import congressclient.v1.client as congress_client
|
||||
except ImportError as congress_client_import_error:
|
||||
congress_client = None
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from murano.common import auth_utils
|
||||
from murano.common.i18n import _, _LI
|
||||
from murano.policy import congress_rules
|
||||
from murano.policy.modify.actions import action_manager as am
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
|
@ -40,10 +48,25 @@ class ModelPolicyEnforcer(object):
|
|||
table along with congress data rules to return validation results.
|
||||
"""
|
||||
|
||||
def __init__(self, environment, action_manager=None):
|
||||
self._environment = environment
|
||||
self._client_manager = environment.clients
|
||||
def __init__(self, execution_session, action_manager=None):
|
||||
self._execution_session = execution_session
|
||||
self._action_manager = action_manager or am.ModifyActionManager()
|
||||
self._client = None
|
||||
|
||||
def _create_client(self):
|
||||
if not congress_client:
|
||||
# congress client was not imported
|
||||
raise congress_client_import_error
|
||||
return congress_client.Client(
|
||||
**auth_utils.get_session_client_parameters(
|
||||
service_type='policy',
|
||||
execution_session=self._execution_session))
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
if self._client is None:
|
||||
self._client = self._create_client()
|
||||
return self._client
|
||||
|
||||
def modify(self, obj, package_loader=None):
|
||||
"""Modifies model using Congress rule engine.
|
||||
|
@ -110,7 +133,7 @@ class ModelPolicyEnforcer(object):
|
|||
|
||||
def _execute_simulation(self, package_loader, env_id, model, query):
|
||||
rules = congress_rules.CongressRulesManager().convert(
|
||||
model, package_loader, self._environment.tenant_id)
|
||||
model, package_loader, self._execution_session.project_id)
|
||||
rules_str = list(map(str, rules))
|
||||
# cleanup of data populated by murano driver
|
||||
rules_str.insert(0, 'deleteEnv("{0}")'.format(env_id))
|
||||
|
@ -118,9 +141,7 @@ class ModelPolicyEnforcer(object):
|
|||
LOG.debug('Congress rules: \n {rules} '
|
||||
.format(rules='\n '.join(rules_str)))
|
||||
|
||||
client = self._check_client()
|
||||
|
||||
validation_result = client.execute_policy_action(
|
||||
validation_result = self.client.execute_policy_action(
|
||||
"murano_system",
|
||||
"simulate",
|
||||
False,
|
||||
|
@ -130,12 +151,6 @@ class ModelPolicyEnforcer(object):
|
|||
'sequence': rules_line})
|
||||
return validation_result
|
||||
|
||||
def _check_client(self):
|
||||
client = self._client_manager.get_congress_client(self._environment)
|
||||
if not client:
|
||||
raise ValueError(_('Congress client is not configured!'))
|
||||
return client
|
||||
|
||||
@staticmethod
|
||||
def _parse_simulation_result(query, env_id, results):
|
||||
"""Transforms list of strings in format
|
||||
|
|
|
@ -25,7 +25,7 @@ from murano.dsl import helpers
|
|||
from murano.dsl import murano_object
|
||||
from murano.dsl import serializer
|
||||
from murano.dsl import yaql_integration
|
||||
from murano.engine import environment
|
||||
from murano.engine import execution_session
|
||||
from murano.engine.system import yaql_functions
|
||||
from murano.tests.unit.dsl.foundation import object_model
|
||||
|
||||
|
@ -77,7 +77,7 @@ class Runner(object):
|
|||
|
||||
self.executor = executor.MuranoDslExecutor(
|
||||
package_loader, TestContextManager(functions),
|
||||
environment.Environment())
|
||||
execution_session.ExecutionSession())
|
||||
self._root = self.executor.load(model).object
|
||||
|
||||
def _execute(self, name, object_id, *args, **kwargs):
|
||||
|
|
|
@ -19,7 +19,7 @@ from murano.common import exceptions as exc
|
|||
from murano.dsl import constants
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import yaql_integration
|
||||
from murano.engine import environment
|
||||
from murano.engine import execution_session
|
||||
from murano.engine.system import agent
|
||||
from murano.engine.system import agent_listener
|
||||
from murano.tests.unit.dsl.foundation import object_model as om
|
||||
|
@ -37,7 +37,8 @@ class TestAgentListener(test_case.DslTestCase):
|
|||
'AgentListenerTests')
|
||||
self.runner = self.new_runner(model)
|
||||
self.context = yaql_integration.create_empty_context()
|
||||
self.context[constants.CTX_ENVIRONMENT] = environment.Environment()
|
||||
self.context[constants.CTX_EXECUTION_SESSION] = \
|
||||
execution_session.ExecutionSession()
|
||||
|
||||
def test_listener_enabled(self):
|
||||
self.override_config('disable_murano_agent', False, 'engine')
|
||||
|
|
|
@ -18,7 +18,7 @@ from yaql import specs
|
|||
from murano.dsl import constants
|
||||
from murano.dsl import executor
|
||||
from murano.dsl import murano_class
|
||||
from murano.engine import environment
|
||||
from murano.engine import execution_session
|
||||
from murano.engine import mock_context_manager
|
||||
from murano.engine.system import test_fixture
|
||||
from murano.tests.unit import base
|
||||
|
@ -58,7 +58,7 @@ class MockRunner(runner.Runner):
|
|||
model = {'Objects': model}
|
||||
self.executor = executor.MuranoDslExecutor(
|
||||
package_loader, TestMockContextManager(functions),
|
||||
environment.Environment())
|
||||
execution_session.ExecutionSession())
|
||||
self._root = self.executor.load(model).object
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import tempfile
|
|||
import mock
|
||||
from oslo_config import cfg
|
||||
import semantic_version
|
||||
import testtools
|
||||
|
||||
from murano.dsl import murano_package as dsl_package
|
||||
from murano.engine import package_loader
|
||||
|
@ -37,23 +38,22 @@ class TestPackageCache(base.MuranoTestCase):
|
|||
CONF.set_override('packages_cache', self.location, 'packages_opts')
|
||||
|
||||
self.murano_client = mock.MagicMock()
|
||||
self.murano_client_factory = mock.MagicMock(
|
||||
return_value=self.murano_client)
|
||||
self.loader = package_loader.ApiPackageLoader(
|
||||
self.murano_client_factory, 'test_tenant_id')
|
||||
package_loader.ApiPackageLoader.client = self.murano_client
|
||||
self.loader = package_loader.ApiPackageLoader(None)
|
||||
|
||||
def tearDown(self):
|
||||
CONF.set_override('packages_cache', self.old_location, 'packages_opts')
|
||||
shutil.rmtree(self.location, ignore_errors=True)
|
||||
super(TestPackageCache, self).tearDown()
|
||||
|
||||
@testtools.skipIf(os.name == 'nt', "Doesn't work on Windows")
|
||||
def test_load_package(self):
|
||||
fqn = 'io.murano.apps.test'
|
||||
path, name = utils.compose_package(
|
||||
'test',
|
||||
os.path.join(self.location, 'manifest.yaml'),
|
||||
self.location, archive_dir=self.location)
|
||||
with open(path) as f:
|
||||
with open(path, 'rb') as f:
|
||||
package_data = f.read()
|
||||
spec = semantic_version.Spec('*')
|
||||
|
||||
|
@ -154,9 +154,9 @@ class TestCombinedPackageLoader(base.MuranoTestCase):
|
|||
location = os.path.dirname(__file__)
|
||||
CONF.set_override('load_packages_from', [location], 'packages_opts',
|
||||
enforce_type=True)
|
||||
cls.murano_client_factory = mock.MagicMock()
|
||||
cls.execution_session = mock.MagicMock()
|
||||
cls.loader = package_loader.CombinedPackageLoader(
|
||||
cls.murano_client_factory, 'test_tenant_id')
|
||||
cls.execution_session)
|
||||
cls.api_loader = mock.MagicMock()
|
||||
cls.loader.api_loader = cls.api_loader
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import mock
|
|||
from oslo_config import cfg
|
||||
|
||||
from murano.common import engine
|
||||
from murano.engine import client_manager
|
||||
from murano.policy import model_policy_enforcer
|
||||
from murano.tests.unit import base
|
||||
|
||||
|
@ -45,14 +44,8 @@ class TestModelPolicyEnforcer(base.MuranoTestCase):
|
|||
|
||||
self.congress_client_mock = \
|
||||
mock.Mock(spec=congressclient.v1.client.Client)
|
||||
|
||||
self.client_manager_mock = mock.Mock(spec=client_manager.ClientManager)
|
||||
|
||||
self.client_manager_mock.get_congress_client.return_value = \
|
||||
self.congress_client_mock
|
||||
|
||||
self.environment = mock.Mock()
|
||||
self.environment.clients = self.client_manager_mock
|
||||
model_policy_enforcer.ModelPolicyEnforcer._create_client = mock.Mock(
|
||||
return_value=self.congress_client_mock)
|
||||
|
||||
def test_enforcer_disabled(self):
|
||||
executor = engine.TaskExecutor(self.task)
|
||||
|
@ -74,17 +67,11 @@ class TestModelPolicyEnforcer(base.MuranoTestCase):
|
|||
.validate.assert_called_once_with(self.model_dict,
|
||||
self.package_loader)
|
||||
|
||||
def test_enforcer_no_client(self):
|
||||
self.client_manager_mock.get_congress_client.return_value = None
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(self.environment)
|
||||
model = {'?': {'id': '123', 'type': 'class'}}
|
||||
self.assertRaises(ValueError, enforcer.validate, model)
|
||||
|
||||
def test_validation_pass(self):
|
||||
self.congress_client_mock.execute_policy_action.return_value = \
|
||||
{"result": []}
|
||||
model = {'?': {'id': '123', 'type': 'class'}}
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(self.environment)
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(mock.Mock())
|
||||
enforcer.validate(model)
|
||||
|
||||
def test_validation_failure(self):
|
||||
|
@ -92,7 +79,7 @@ class TestModelPolicyEnforcer(base.MuranoTestCase):
|
|||
{"result": ['predeploy_errors("123","instance1","failure")']}
|
||||
|
||||
model = {'?': {'id': '123', 'type': 'class'}}
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(self.environment)
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(mock.Mock())
|
||||
self.assertRaises(model_policy_enforcer.ValidationError,
|
||||
enforcer.validate, model)
|
||||
|
||||
|
@ -107,7 +94,7 @@ class TestModelPolicyEnforcer(base.MuranoTestCase):
|
|||
|
||||
action_manager = mock.MagicMock()
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(
|
||||
self.environment, action_manager)
|
||||
mock.Mock(), action_manager)
|
||||
|
||||
enforcer.modify(obj)
|
||||
self.assertTrue(action_manager.apply_action.called)
|
||||
|
@ -120,7 +107,7 @@ class TestModelPolicyEnforcer(base.MuranoTestCase):
|
|||
'predeploy_errors("env2","instance1","Instance 3 has problem")'
|
||||
]
|
||||
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(self.environment)
|
||||
enforcer = model_policy_enforcer.ModelPolicyEnforcer(None)
|
||||
result = enforcer._parse_simulation_result(
|
||||
'predeploy_errors', 'env1', congress_response)
|
||||
|
||||
|
|
|
@ -17,50 +17,34 @@ from heatclient.v1 import stacks
|
|||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import murano_class
|
||||
from murano.dsl import object_store
|
||||
from murano.engine import client_manager
|
||||
from murano.engine import environment
|
||||
from murano.engine.system import heat_stack
|
||||
from murano.tests.unit import base
|
||||
|
||||
MOD_NAME = 'murano.engine.system.heat_stack'
|
||||
CLS_NAME = 'murano.engine.system.heat_stack.HeatStack'
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestHeatStack(base.MuranoTestCase):
|
||||
def setUp(self):
|
||||
super(TestHeatStack, self).setUp()
|
||||
self.mock_murano_class = mock.Mock(spec=murano_class.MuranoClass)
|
||||
self.mock_murano_class.name = 'io.murano.system.HeatStack'
|
||||
self.mock_murano_class.declared_parents = []
|
||||
self.heat_client_mock = mock.MagicMock()
|
||||
self.heat_client_mock = mock.Mock()
|
||||
self.heat_client_mock.stacks = mock.MagicMock(spec=stacks.StackManager)
|
||||
self.mock_object_store = mock.Mock(spec=object_store.ObjectStore)
|
||||
self.environment_mock = mock.Mock(
|
||||
spec=environment.Environment)
|
||||
client_manager_mock = mock.Mock(spec=client_manager.ClientManager)
|
||||
client_manager_mock.get_heat_client.return_value = \
|
||||
self.heat_client_mock
|
||||
self.environment_mock.clients = client_manager_mock
|
||||
CONF.set_override('stack_tags', ['test-murano'], 'heat',
|
||||
enforce_type=True)
|
||||
self.mock_tag = ','.join(CONF.heat.stack_tags)
|
||||
heat_stack.HeatStack._get_token_client = mock.Mock(
|
||||
return_value=self.heat_client_mock)
|
||||
heat_stack.HeatStack._get_client = mock.Mock(
|
||||
return_value=self.heat_client_mock)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
@mock.patch(CLS_NAME + '._wait_state')
|
||||
@mock.patch(CLS_NAME + '._get_status')
|
||||
def test_push_adds_version(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack(
|
||||
'test-stack', 'Generated by TestHeatStack')
|
||||
hs = heat_stack.HeatStack('test-stack', 'Generated by TestHeatStack')
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._hot_environment = ''
|
||||
|
@ -68,9 +52,7 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
hs._applied = False
|
||||
hs.push()
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack(
|
||||
'test-stack', 'Generated by TestHeatStack')
|
||||
hs = heat_stack.HeatStack('test-stack', 'Generated by TestHeatStack')
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._parameters = {}
|
||||
|
@ -93,17 +75,14 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
@mock.patch(CLS_NAME + '._wait_state')
|
||||
@mock.patch(CLS_NAME + '._get_status')
|
||||
def test_description_is_optional(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
hs._hot_environment = ''
|
||||
|
@ -126,17 +105,14 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
@mock.patch(CLS_NAME + '._wait_state')
|
||||
@mock.patch(CLS_NAME + '._get_status')
|
||||
def test_heat_files_are_sent(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._description = None
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {"heatFile": "file"}
|
||||
|
@ -160,17 +136,14 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
@mock.patch(CLS_NAME + '._wait_state')
|
||||
@mock.patch(CLS_NAME + '._get_status')
|
||||
def test_heat_environments_are_sent(self, status_get, wait_st):
|
||||
"""Assert that if heat_template_version is omitted, it's added."""
|
||||
|
||||
status_get.return_value = 'NOT_FOUND'
|
||||
wait_st.return_value = {}
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._description = None
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {"heatFile": "file"}
|
||||
|
@ -194,14 +167,11 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
)
|
||||
self.assertTrue(hs._applied)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack.current')
|
||||
@mock.patch(CLS_NAME + '.current')
|
||||
def test_update_wrong_template_version(self, current):
|
||||
"""Template version other than expected should cause error."""
|
||||
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack(
|
||||
'test-stack', 'Generated by TestHeatStack')
|
||||
hs = heat_stack.HeatStack('test-stack', 'Generated by TestHeatStack')
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
|
||||
invalid_template = {
|
||||
|
@ -227,8 +197,8 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
expected['heat_template_version'] = '2013-05-23'
|
||||
self.assertEqual(expected, hs._template)
|
||||
|
||||
@mock.patch(MOD_NAME + '.HeatStack._wait_state')
|
||||
@mock.patch(MOD_NAME + '.HeatStack._get_status')
|
||||
@mock.patch(CLS_NAME + '._wait_state')
|
||||
@mock.patch(CLS_NAME + '._get_status')
|
||||
def test_heat_stack_tags_are_sent(self, status_get, wait_st):
|
||||
"""Assert that heat_stack `tags` parameter get push & with
|
||||
value from config parameter `stack_tags`.
|
||||
|
@ -238,10 +208,7 @@ class TestHeatStack(base.MuranoTestCase):
|
|||
wait_st.return_value = {}
|
||||
CONF.set_override('stack_tags', ['test-murano', 'murano-tag'], 'heat',
|
||||
enforce_type=True)
|
||||
context = {constants.CTX_ENVIRONMENT: self.environment_mock}
|
||||
|
||||
with helpers.contextual(context):
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs = heat_stack.HeatStack('test-stack', None)
|
||||
hs._description = None
|
||||
hs._template = {'resources': {'test': 1}}
|
||||
hs._files = {}
|
||||
|
|
Loading…
Reference in New Issue