
Adds a support for Nova Network if Neutron is not present in the current OpenStack deployment. Supporting the Nova Network requires modifications in three different parts of generated Heat Stack: 1) Generated Security Groups and their rules should be of type 'AWS::EC2::SecurityGroup', not 'OS::Neutron::SecurityGroup' 2) Security Group assignments should go to security_groups property of Instance resource, not the network port (as port concept is not present when using NovaNetwork) 3) FloatingIP should be of type OS::Nova::FloatingIP and should be associated with an Instance by OS::Nova::FloatingIPAssociation resource. To achieve p1 a SecurityGroupManager class of Core Library is made abstract and is inherited by two concrete implementations: NeutronSecurityGroupManager (containing the old MuranoPL code which generated templates based on OS::Neutron::SecurityGroup) and a new AwsSecurityGroupManager, which generates AWS-compliant firewall rules which are consumed by NovaNetwork. The particular concreate instance of this class is generated by the default network of environment: Network class has got a new method called generateSecurityGroupManager which returns an appropriate implementation. For pp 2-3 a new inheritor of Network class has been added to the Core Library: an io.murano.resources.NovaNetwork. It generates FloatingIP association resources if needed and returns a securityGroupName object as one of the outputs of its joinInstance methods. The Instance class has been modified to properly handle these types of outputs. The instance of the NovaNetwork class is generated at the API side when a new Environment is created and a is assigned to the defaultNetworks.environment property of the environment if the neutron is not defined in keystone. Also this change moves the auth_utils module from engine to common, as Keystone Client it contains is now used by the API process as well. This changed is based on some of the code from the outdated changeset I6f4b7908bd4bbcd375f64705c7dd06e3954f1ec7 Co-Authored-By: Alexander Tivelkov <ativelkov@mirantis.com> Co-Authored-By: Stan Lagun <slagun@mirantis.com> DocImpact Change-Id: I4c48f33de100a5730ba1d086540d0d99e8fbf9b1 Implements-Blueprint: nova-network-support
208 lines
7.7 KiB
Python
208 lines
7.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.
|
|
|
|
from eventlet import semaphore
|
|
import heatclient.client as hclient
|
|
import keystoneclient
|
|
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 murano.common import config
|
|
from murano.dsl import helpers
|
|
from murano.engine import environment
|
|
|
|
|
|
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
|
|
|
|
|
|
class ClientManager(object):
|
|
def __init__(self):
|
|
self._trusts_keystone_client = None
|
|
self._token_keystone_client = None
|
|
self._cache = {}
|
|
self._semaphore = semaphore.BoundedSemaphore()
|
|
|
|
def _get_environment(self, context):
|
|
if isinstance(context, environment.Environment):
|
|
return context
|
|
return helpers.get_environment(context)
|
|
|
|
def get_client(self, context, name, use_trusts, client_factory):
|
|
if not config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
|
|
keystone_client = None if name == 'keystone' else \
|
|
self.get_keystone_client(context, 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:
|
|
env = self._get_environment(context)
|
|
token = env.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, context, use_trusts=True):
|
|
if not config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
env = self._get_environment(context)
|
|
factory = lambda _1, _2: \
|
|
auth_utils.get_client_for_trusts(env.trust_id) \
|
|
if use_trusts else auth_utils.get_client(env.token, env.tenant_id)
|
|
|
|
return self.get_client(context, 'keystone', use_trusts, factory)
|
|
|
|
def get_congress_client(self, context, 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 config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
|
|
def factory(keystone_client, auth_token):
|
|
auth = keystoneclient.auth.identity.v2.Token(
|
|
auth_url=cfg.CONF.keystone_authtoken.auth_uri,
|
|
tenant_name=keystone_client.tenant_name,
|
|
token=auth_token)
|
|
session = keystoneclient.session.Session(auth=auth)
|
|
return congress_client.Client(session=session,
|
|
service_type='policy')
|
|
|
|
return self.get_client(context, 'congress', use_trusts, factory)
|
|
|
|
def get_heat_client(self, context, use_trusts=True):
|
|
if not config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
|
|
def factory(keystone_client, auth_token):
|
|
heat_settings = config.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 config.CONF.engine.use_trusts:
|
|
kwargs.update({
|
|
'username': 'badusername',
|
|
'password': 'badpassword'
|
|
})
|
|
return hclient.Client('1', heat_url, **kwargs)
|
|
|
|
return self.get_client(context, 'heat', use_trusts, factory)
|
|
|
|
def get_neutron_client(self, context, use_trusts=True):
|
|
if not config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
|
|
def factory(keystone_client, auth_token):
|
|
neutron_settings = config.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(context, 'neutron', use_trusts, factory)
|
|
|
|
def get_murano_client(self, context, use_trusts=True):
|
|
if not config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
|
|
def factory(keystone_client, auth_token):
|
|
murano_settings = config.CONF.murano
|
|
|
|
murano_url = \
|
|
murano_settings.url or keystone_client.service_catalog.url_for(
|
|
service_type='application_catalog',
|
|
endpoint_type=murano_settings.endpoint_type)
|
|
|
|
return muranoclient.Client(
|
|
endpoint=murano_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,
|
|
auth_url=keystone_client.auth_url,
|
|
token=auth_token)
|
|
|
|
return self.get_client(context, 'murano', use_trusts, factory)
|
|
|
|
def get_mistral_client(self, context, use_trusts=True):
|
|
if not mistralclient:
|
|
raise mistral_import_error
|
|
|
|
if not config.CONF.engine.use_trusts:
|
|
use_trusts = False
|
|
|
|
def factory(keystone_client, auth_token):
|
|
mistral_settings = config.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,
|
|
auth_url=keystone_client.auth_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(context, 'mistral', use_trusts, factory)
|