flame/flameclient/session.py
Yves-Gwenael Bourhis e798119841 Addapted flame to openstacksdk and shade.
Flame needed pemanent adjustments to mathe the changes in the
python-openstackclients. We now use openstacksdk or shade which will
handle themselves the compatibility.

We also made flame modular so that any-one can add features by
implementing there own flame managers and adding their modules to
the `openstack_flame` entry point.

This new flame version is also fully compatible with python 3.

Change-Id: I586a165b5022031963f504874bd50e1b11fe0d27
2018-11-22 11:50:41 +01:00

279 lines
9.9 KiB
Python

# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import argparse
import logging
import sys
from keystoneauth1 import loading
from keystoneauth1 import session as keystone_session
from openstack import connection as os_connection
# We get a subclass of openstack.config.loader.OpenStackConfig which is more
# complete:
from os_client_config.config import OpenStackConfig # noqa
from shade.openstackcloud import OpenStackCloud # noqa
from flameclient import utils
LOG = logging.getLogger(__name__)
def list_auth_types():
return [loader for loader in loading.get_available_plugin_loaders()]
def get_loader(auth_type):
if auth_type not in list_auth_types():
raise ValueError(
"'auth_type' has to be one of %s (received %s)" % (
list_auth_types(), auth_type
)
)
return loading.get_plugin_loader(auth_type)
def extract_loader_kwargs(loader, **kwargs):
"""Get keystoneauth1.loading.get_plugin_loader's auth kwargs
Return the specific auth kwargs and remaining kwargs
"""
loader_keys = [option.dest for option in loader.get_options()]
loader_kwargs = {
key: kwargs[key] for key in loader_keys if key in kwargs
}
remaining_kwargs = {
key: kwargs[key] for key in kwargs if key not in loader_keys
}
return loader_kwargs, remaining_kwargs
def get_openstack_config_with_envvars(
parser, config=None, load_envvars=False, load_yaml_config=False
):
"""Add openstack_options to argparse.ArgumentParser
:param argparse.ArgumentParser parser: argparse.ArgumentParser instance
or None.
:param bool load_envvars:
Whether or not to load config settings from environment variables.
Defaults to True.
:returns: OpenStackConfig instance
"""
if config is None:
# If we use `openstack.config.loader.OpenStackConfig` instead of
# `os_client_config.config.OpenStackConfig` and then instantiate a
# `shade.openstackcloud.OpenStackCloud` instance as `cloud`, when
# trying to access the `cloud.keystone_client` attribute (or any other
# `*_client attribute`), we get a
# "TypeError: 'NoneType' object is not callable" exception
# This is why we use `os_client_config.config.OpenStackConfig`: it's to
# have all `shade.openstackcloud.OpenStackCloud` attributes working
# properly. Whether we use them or not, whether these attributes are
# deprecated or not, we do not want a 'broken' shade instance in case
# third party managers would use them.
config = OpenStackConfig(
load_envvars=load_envvars, load_yaml_config=load_yaml_config
)
if parser:
if load_envvars or load_yaml_config:
config.register_argparse_arguments(parser, sys.argv)
else:
config.register_argparse_arguments(parser, [])
return config
def get_openstack_cli_arguments(
parser=None, load_envvars=True, load_yaml_config=True,
renamed_args=False
):
if parser is None:
parser = argparse.ArgumentParser()
get_openstack_config_with_envvars(
parser, load_envvars=load_envvars, load_yaml_config=load_yaml_config
)
known_args, unknown_args = parser.parse_known_args()
if renamed_args:
known_args = utils.rename_os_options(known_args, clean=False)
return known_args, unknown_args
def get_openstack_envvars_as_kwargs(
with_args=False, parser=None, load_envvars=True, load_yaml_config=True
):
"""Get openstack environment variables"""
known_args, unknown_args = get_openstack_cli_arguments(
parser=parser, load_envvars=load_envvars,
load_yaml_config=load_yaml_config
)
kwargs = utils.rename_os_kwargs(vars(known_args), clean=True)
if with_args:
return known_args, unknown_args, kwargs
return kwargs
def get_keystoneauth1_session(
load_envvars=False, load_yaml_config=False, **kwargs
):
if load_envvars:
kwargs.update(
get_openstack_envvars_as_kwargs(
load_envvars=load_envvars, load_yaml_config=load_yaml_config
)
)
auth_type = kwargs.get('auth_type', 'password')
loader = get_loader(auth_type)
loader_kwargs, _ = extract_loader_kwargs(loader, **kwargs)
auth = loader.load_from_options(**loader_kwargs)
return keystone_session.Session(auth=auth)
get_keystone_session = get_keystoneauth1_session
def get_openstack_config(
parser_or_options=None, load_envvars=False, load_yaml_config=False,
**kwargs
):
"""Same as os_client_config.get_config with less errors.
Indeed, if we source OS_* variable environments, and one calls:
os_client_config.get_config(
load_envvars=False, load_yaml_config=False, **kwargs
)
we get this kind of error:
ConfigException: Region fr1 is not a valid region name for cloud
envvars. Valid choices are fr0. Please note that region names are case
sensitive.
It seems like os_client_config.get_config fails to NOT handle envvars.
Also, os_client_config.get_config saves the config in a global variable...
This is absolutely not thread safe...
"""
parsed_options = None
parser = None
if isinstance(parser_or_options, argparse.ArgumentParser):
parser = parser_or_options
config = get_openstack_config_with_envvars(
parser=parser, load_envvars=load_envvars,
load_yaml_config=load_yaml_config
)
if parser_or_options is not None:
if isinstance(parser_or_options, argparse.Namespace):
parsed_options = parser_or_options
elif isinstance(parser_or_options, dict):
parsed_options = utils.dict_to_options(parser_or_options)
elif isinstance(parser_or_options, argparse.ArgumentParser):
if load_envvars or load_yaml_config:
parsed_options, _ = parser_or_options.parse_known_args(
sys.argv)
else:
parsed_options, _ = parser_or_options.parse_known_args([])
else:
raise AttributeError(
"'parser_options' has to be an 'argparse.ArgumentParser' or "
"'argparse.Namespace' instance or dict or None. "
"Received '%s'" % type(parser_or_options)
)
return config.get_one(
options=parsed_options,
load_yaml_config=load_yaml_config,
load_envvars=load_envvars,
**kwargs
)
def get_openstack_sdk_connection(
parser_or_options=None, load_envvars=False, load_yaml_config=False,
cloud_config=None, session=None,
**kwargs
):
if session is not None:
return os_connection.Connection(session=session, **kwargs)
if cloud_config is None:
# we could return
# `openstack.connect(load_envvars=load_envvars, load_yaml_config=load_yaml_config, **kwargs)` # noqa
# but by doing so magic things are lacking in the config and we have
# random failing methods on the instance. See comments in
# get_openstack_config_with_envvars for more information.
cloud_config = get_openstack_config(
parser_or_options=parser_or_options, load_envvars=load_envvars,
load_yaml_config=load_yaml_config,
**kwargs
)
if isinstance(parser_or_options, dict):
parser_or_options = utils.dict_to_options(parser_or_options)
return os_connection.from_config(
cloud_config=cloud_config, options=parser_or_options
)
def get_shade(
parser_or_options=None, cloud_config=None, connection=None,
load_envvars=False, load_yaml_config=False,
**kwargs
):
"""Get shade instance
You can use an `argparse.ArgumentParser` or `argparse.Namespace` instance
with `load_envvars` and/or `load_yaml_config` set to True,
Or you kan use kwargs to authenticate with `load_envvars` AND
`load_yaml_config` set to False:
cloud = get_shade(
auth_type='password',
auth_url='https://identity.fr1.cloudwatt.com/v2.0',
interface='public',
password='YourPassword',
project_id='Your ProjectID,
project_name='YourProjectName,
region_name='YourRegionName',
username='YourUserName'
)
You can also use a token instead of password with kwargs, If so, use
`auth_type='token'`.
"""
if cloud_config is not None:
return OpenStackCloud(cloud_config=cloud_config, **kwargs)
elif connection is not None:
return OpenStackCloud(cloud_config=connection.config, **kwargs)
else:
return OpenStackCloud(
cloud_config=get_openstack_config(
parser_or_options=parser_or_options, load_envvars=load_envvars,
load_yaml_config=load_yaml_config,
**kwargs
)
)