Use keystoneauth for Ironic and Swift clients
This patch does not change the options in config file yet to showcase backward compatibility with old config options. Change-Id: I1da93b59b2f4813c42008277bd6479dc6673e7f1
This commit is contained in:
parent
a12d1af680
commit
35f332539d
258
example.conf
258
example.conf
@ -387,59 +387,149 @@
|
||||
# From ironic_inspector.common.ironic
|
||||
#
|
||||
|
||||
# Keystone authentication endpoint for accessing Ironic API. Use
|
||||
# [keystone_authtoken]/auth_uri for keystone authentication. (string
|
||||
# value)
|
||||
# Deprecated group/name - [discoverd]/os_auth_url
|
||||
#os_auth_url =
|
||||
# Authentication URL (unknown value)
|
||||
#auth_url = <None>
|
||||
|
||||
# User name for accessing Ironic API. Use
|
||||
# [keystone_authtoken]/admin_user for keystone authentication. (string
|
||||
# value)
|
||||
# Deprecated group/name - [discoverd]/os_username
|
||||
#os_username =
|
||||
# Method to use for authentication: noauth or keystone. (string value)
|
||||
# Allowed values: keystone, noauth
|
||||
#auth_strategy = keystone
|
||||
|
||||
# Password for accessing Ironic API. Use
|
||||
# [keystone_authtoken]/admin_password for keystone authentication.
|
||||
# (string value)
|
||||
# Deprecated group/name - [discoverd]/os_password
|
||||
#os_password =
|
||||
# Authentication type to load (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/auth_plugin
|
||||
#auth_type = <None>
|
||||
|
||||
# Tenant name for accessing Ironic API. Use
|
||||
# [keystone_authtoken]/admin_tenant_name for keystone authentication.
|
||||
# (string value)
|
||||
# Deprecated group/name - [discoverd]/os_tenant_name
|
||||
#os_tenant_name =
|
||||
# PEM encoded Certificate Authority to use when verifying HTTPs
|
||||
# connections. (string value)
|
||||
#cafile = <None>
|
||||
|
||||
# Keystone admin endpoint. DEPRECATED: use
|
||||
# [keystone_authtoken]/identity_uri. (string value)
|
||||
# PEM encoded client certificate cert file (string value)
|
||||
#certfile = <None>
|
||||
|
||||
# Optional domain ID to use with v3 and v2 parameters. It will be used
|
||||
# for both the user and project domain in v3 and ignored in v2
|
||||
# authentication. (unknown value)
|
||||
#default_domain_id = <None>
|
||||
|
||||
# Optional domain name to use with v3 API and v2 parameters. It will
|
||||
# be used for both the user and project domain in v3 and ignored in v2
|
||||
# authentication. (unknown value)
|
||||
#default_domain_name = <None>
|
||||
|
||||
# Domain ID to scope to (unknown value)
|
||||
#domain_id = <None>
|
||||
|
||||
# Domain name to scope to (unknown value)
|
||||
#domain_name = <None>
|
||||
|
||||
# Keystone admin endpoint. DEPRECATED: Use [keystone_authtoken]
|
||||
# section for keystone token validation. (string value)
|
||||
# Deprecated group/name - [discoverd]/identity_uri
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
#identity_uri =
|
||||
|
||||
# Method to use for authentication: noauth or keystone. (string value)
|
||||
# Allowed values: keystone, noauth
|
||||
#auth_strategy = keystone
|
||||
# Verify HTTPS connections. (boolean value)
|
||||
#insecure = false
|
||||
|
||||
# Ironic API URL, used to set Ironic API URL when auth_strategy option
|
||||
# is noauth to work with standalone Ironic without keystone. (string
|
||||
# value)
|
||||
#ironic_url = http://localhost:6385/
|
||||
|
||||
# Ironic service type. (string value)
|
||||
#os_service_type = baremetal
|
||||
# PEM encoded client certificate key file (string value)
|
||||
#keyfile = <None>
|
||||
|
||||
# Maximum number of retries in case of conflict error (HTTP 409).
|
||||
# (integer value)
|
||||
#max_retries = 30
|
||||
|
||||
# Keystone authentication endpoint for accessing Ironic API. Use
|
||||
# [keystone_authtoken] section for keystone token validation. (string
|
||||
# value)
|
||||
# Deprecated group/name - [discoverd]/os_auth_url
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
# Reason: Use options presented by configured keystone auth plugin.
|
||||
#os_auth_url =
|
||||
|
||||
# Ironic endpoint type. (string value)
|
||||
#os_endpoint_type = internalURL
|
||||
|
||||
# Password for accessing Ironic API. Use [keystone_authtoken] section
|
||||
# for keystone token validation. (string value)
|
||||
# Deprecated group/name - [discoverd]/os_password
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
# Reason: Use options presented by configured keystone auth plugin.
|
||||
#os_password =
|
||||
|
||||
# Keystone region used to get Ironic endpoints. (string value)
|
||||
#os_region = <None>
|
||||
|
||||
# Ironic service type. (string value)
|
||||
#os_service_type = baremetal
|
||||
|
||||
# Tenant name for accessing Ironic API. Use [keystone_authtoken]
|
||||
# section for keystone token validation. (string value)
|
||||
# Deprecated group/name - [discoverd]/os_tenant_name
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
# Reason: Use options presented by configured keystone auth plugin.
|
||||
#os_tenant_name =
|
||||
|
||||
# User name for accessing Ironic API. Use [keystone_authtoken] section
|
||||
# for keystone token validation. (string value)
|
||||
# Deprecated group/name - [discoverd]/os_username
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
# Reason: Use options presented by configured keystone auth plugin.
|
||||
#os_username =
|
||||
|
||||
# User's password (unknown value)
|
||||
#password = <None>
|
||||
|
||||
# Domain ID containing project (unknown value)
|
||||
#project_domain_id = <None>
|
||||
|
||||
# Domain name containing project (unknown value)
|
||||
#project_domain_name = <None>
|
||||
|
||||
# Project ID to scope to (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/tenant-id
|
||||
#project_id = <None>
|
||||
|
||||
# Project name to scope to (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/tenant-name
|
||||
#project_name = <None>
|
||||
|
||||
# Interval between retries in case of conflict error (HTTP 409).
|
||||
# (integer value)
|
||||
#retry_interval = 2
|
||||
|
||||
# Maximum number of retries in case of conflict error (HTTP 409).
|
||||
# (integer value)
|
||||
#max_retries = 30
|
||||
# Tenant ID (unknown value)
|
||||
#tenant_id = <None>
|
||||
|
||||
# Tenant Name (unknown value)
|
||||
#tenant_name = <None>
|
||||
|
||||
# Timeout value for http requests (integer value)
|
||||
#timeout = <None>
|
||||
|
||||
# Trust ID (unknown value)
|
||||
#trust_id = <None>
|
||||
|
||||
# User's domain id (unknown value)
|
||||
#user_domain_id = <None>
|
||||
|
||||
# User's domain name (unknown value)
|
||||
#user_domain_name = <None>
|
||||
|
||||
# User id (unknown value)
|
||||
#user_id = <None>
|
||||
|
||||
# Username (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/username
|
||||
#username = <None>
|
||||
|
||||
|
||||
[keystone_authtoken]
|
||||
@ -676,34 +766,112 @@
|
||||
# From ironic_inspector.common.swift
|
||||
#
|
||||
|
||||
# Maximum number of times to retry a Swift request, before failing.
|
||||
# (integer value)
|
||||
#max_retries = 2
|
||||
# Authentication URL (unknown value)
|
||||
#auth_url = <None>
|
||||
|
||||
# Authentication type to load (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/auth_plugin
|
||||
#auth_type = <None>
|
||||
|
||||
# PEM encoded Certificate Authority to use when verifying HTTPs
|
||||
# connections. (string value)
|
||||
#cafile = <None>
|
||||
|
||||
# PEM encoded client certificate cert file (string value)
|
||||
#certfile = <None>
|
||||
|
||||
# Default Swift container to use when creating objects. (string value)
|
||||
#container = ironic-inspector
|
||||
|
||||
# Optional domain ID to use with v3 and v2 parameters. It will be used
|
||||
# for both the user and project domain in v3 and ignored in v2
|
||||
# authentication. (unknown value)
|
||||
#default_domain_id = <None>
|
||||
|
||||
# Optional domain name to use with v3 API and v2 parameters. It will
|
||||
# be used for both the user and project domain in v3 and ignored in v2
|
||||
# authentication. (unknown value)
|
||||
#default_domain_name = <None>
|
||||
|
||||
# Number of seconds that the Swift object will last before being
|
||||
# deleted. (set to 0 to never delete the object). (integer value)
|
||||
#delete_after = 0
|
||||
|
||||
# Default Swift container to use when creating objects. (string value)
|
||||
#container = ironic-inspector
|
||||
# Domain ID to scope to (unknown value)
|
||||
#domain_id = <None>
|
||||
|
||||
# User name for accessing Swift API. (string value)
|
||||
#username =
|
||||
# Domain name to scope to (unknown value)
|
||||
#domain_name = <None>
|
||||
|
||||
# Password for accessing Swift API. (string value)
|
||||
#password =
|
||||
# Verify HTTPS connections. (boolean value)
|
||||
#insecure = false
|
||||
|
||||
# Tenant name for accessing Swift API. (string value)
|
||||
#tenant_name =
|
||||
# PEM encoded client certificate key file (string value)
|
||||
#keyfile = <None>
|
||||
|
||||
# Keystone authentication API version (string value)
|
||||
#os_auth_version = 2
|
||||
# Maximum number of times to retry a Swift request, before failing.
|
||||
# (integer value)
|
||||
#max_retries = 2
|
||||
|
||||
# Keystone authentication URL (string value)
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
# Reason: Use options presented by configured keystone auth plugin.
|
||||
#os_auth_url =
|
||||
|
||||
# Keystone authentication API version (string value)
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
# Reason: Use options presented by configured keystone auth plugin.
|
||||
#os_auth_version = 2
|
||||
|
||||
# Swift endpoint type. (string value)
|
||||
#os_endpoint_type = internalURL
|
||||
|
||||
# Keystone region to get endpoint for. (string value)
|
||||
#os_region = <None>
|
||||
|
||||
# Swift service type. (string value)
|
||||
#os_service_type = object-store
|
||||
|
||||
# Swift endpoint type. (string value)
|
||||
#os_endpoint_type = internalURL
|
||||
# User's password (unknown value)
|
||||
#password = <None>
|
||||
|
||||
# Domain ID containing project (unknown value)
|
||||
#project_domain_id = <None>
|
||||
|
||||
# Domain name containing project (unknown value)
|
||||
#project_domain_name = <None>
|
||||
|
||||
# Project ID to scope to (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/tenant-id
|
||||
#project_id = <None>
|
||||
|
||||
# Project name to scope to (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/tenant-name
|
||||
#project_name = <None>
|
||||
|
||||
# Tenant ID (unknown value)
|
||||
#tenant_id = <None>
|
||||
|
||||
# Tenant Name (unknown value)
|
||||
#tenant_name = <None>
|
||||
|
||||
# Timeout value for http requests (integer value)
|
||||
#timeout = <None>
|
||||
|
||||
# Trust ID (unknown value)
|
||||
#trust_id = <None>
|
||||
|
||||
# User's domain id (unknown value)
|
||||
#user_domain_id = <None>
|
||||
|
||||
# User's domain name (unknown value)
|
||||
#user_domain_name = <None>
|
||||
|
||||
# User id (unknown value)
|
||||
#user_id = <None>
|
||||
|
||||
# Username (unknown value)
|
||||
# Deprecated group/name - [DEFAULT]/username
|
||||
#username = <None>
|
||||
|
@ -14,10 +14,10 @@
|
||||
import socket
|
||||
|
||||
from ironicclient import client
|
||||
from keystoneclient import client as keystone_client
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic_inspector.common.i18n import _
|
||||
from ironic_inspector.common import keystone
|
||||
from ironic_inspector import utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -32,35 +32,50 @@ DEFAULT_IRONIC_API_VERSION = '1.11'
|
||||
IRONIC_GROUP = 'ironic'
|
||||
|
||||
IRONIC_OPTS = [
|
||||
cfg.StrOpt('os_region',
|
||||
help='Keystone region used to get Ironic endpoints.'),
|
||||
cfg.StrOpt('os_auth_url',
|
||||
default='',
|
||||
help='Keystone authentication endpoint for accessing Ironic '
|
||||
'API. Use [keystone_authtoken]/auth_uri for keystone '
|
||||
'authentication.',
|
||||
deprecated_group='discoverd'),
|
||||
'API. Use [keystone_authtoken] section for keystone '
|
||||
'token validation.',
|
||||
deprecated_group='discoverd',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use options presented by configured '
|
||||
'keystone auth plugin.'),
|
||||
cfg.StrOpt('os_username',
|
||||
default='',
|
||||
help='User name for accessing Ironic API. '
|
||||
'Use [keystone_authtoken]/admin_user for keystone '
|
||||
'authentication.',
|
||||
deprecated_group='discoverd'),
|
||||
'Use [keystone_authtoken] section for keystone '
|
||||
'token validation.',
|
||||
deprecated_group='discoverd',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use options presented by configured '
|
||||
'keystone auth plugin.'),
|
||||
cfg.StrOpt('os_password',
|
||||
default='',
|
||||
help='Password for accessing Ironic API. '
|
||||
'Use [keystone_authtoken]/admin_password for keystone '
|
||||
'authentication.',
|
||||
'Use [keystone_authtoken] section for keystone '
|
||||
'token validation.',
|
||||
secret=True,
|
||||
deprecated_group='discoverd'),
|
||||
deprecated_group='discoverd',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use options presented by configured '
|
||||
'keystone auth plugin.'),
|
||||
cfg.StrOpt('os_tenant_name',
|
||||
default='',
|
||||
help='Tenant name for accessing Ironic API. '
|
||||
'Use [keystone_authtoken]/admin_tenant_name for keystone '
|
||||
'authentication.',
|
||||
deprecated_group='discoverd'),
|
||||
'Use [keystone_authtoken] section for keystone '
|
||||
'token validation.',
|
||||
deprecated_group='discoverd',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use options presented by configured '
|
||||
'keystone auth plugin.'),
|
||||
cfg.StrOpt('identity_uri',
|
||||
default='',
|
||||
help='Keystone admin endpoint. '
|
||||
'DEPRECATED: use [keystone_authtoken]/identity_uri.',
|
||||
'DEPRECATED: Use [keystone_authtoken] section for '
|
||||
'keystone token validation.',
|
||||
deprecated_group='discoverd',
|
||||
deprecated_for_removal=True),
|
||||
cfg.StrOpt('auth_strategy',
|
||||
@ -90,6 +105,24 @@ IRONIC_OPTS = [
|
||||
|
||||
|
||||
CONF.register_opts(IRONIC_OPTS, group=IRONIC_GROUP)
|
||||
keystone.register_auth_opts(IRONIC_GROUP)
|
||||
|
||||
IRONIC_SESSION = None
|
||||
LEGACY_MAP = {
|
||||
'auth_url': 'os_auth_url',
|
||||
'username': 'os_username',
|
||||
'password': 'os_password',
|
||||
'tenant_name': 'os_tenant_name'
|
||||
}
|
||||
|
||||
|
||||
def reset_ironic_session():
|
||||
"""Reset the global session variable.
|
||||
|
||||
Mostly useful for unit tests.
|
||||
"""
|
||||
global IRONIC_SESSION
|
||||
IRONIC_SESSION = None
|
||||
|
||||
|
||||
def get_ipmi_address(node):
|
||||
@ -114,33 +147,28 @@ def get_client(token=None,
|
||||
"""Get Ironic client instance."""
|
||||
# NOTE: To support standalone ironic without keystone
|
||||
if CONF.ironic.auth_strategy == 'noauth':
|
||||
args = {'os_auth_token': 'noauth',
|
||||
'ironic_url': CONF.ironic.ironic_url}
|
||||
elif token is None:
|
||||
args = {'os_password': CONF.ironic.os_password,
|
||||
'os_username': CONF.ironic.os_username,
|
||||
'os_auth_url': CONF.ironic.os_auth_url,
|
||||
'os_tenant_name': CONF.ironic.os_tenant_name,
|
||||
'os_service_type': CONF.ironic.os_service_type,
|
||||
'os_endpoint_type': CONF.ironic.os_endpoint_type}
|
||||
args = {'token': 'noauth',
|
||||
'endpoint': CONF.ironic.ironic_url}
|
||||
else:
|
||||
keystone_creds = {'password': CONF.ironic.os_password,
|
||||
'username': CONF.ironic.os_username,
|
||||
'auth_url': CONF.ironic.os_auth_url,
|
||||
'tenant_name': CONF.ironic.os_tenant_name}
|
||||
keystone = keystone_client.Client(**keystone_creds)
|
||||
# FIXME(sambetts): Work around for Bug 1539839 as client.authenticate
|
||||
# is not called.
|
||||
keystone.authenticate()
|
||||
ironic_url = keystone.service_catalog.url_for(
|
||||
service_type=CONF.ironic.os_service_type,
|
||||
endpoint_type=CONF.ironic.os_endpoint_type)
|
||||
args = {'os_auth_token': token,
|
||||
'ironic_url': ironic_url}
|
||||
global IRONIC_SESSION
|
||||
if not IRONIC_SESSION:
|
||||
IRONIC_SESSION = keystone.get_session(
|
||||
IRONIC_GROUP, legacy_mapping=LEGACY_MAP)
|
||||
if token is None:
|
||||
args = {'session': IRONIC_SESSION,
|
||||
'region_name': CONF.ironic.os_region}
|
||||
else:
|
||||
ironic_url = IRONIC_SESSION.get_endpoint(
|
||||
service_type=CONF.ironic.os_service_type,
|
||||
endpoint_type=CONF.ironic.os_endpoint_type,
|
||||
region_name=CONF.ironic.os_region
|
||||
)
|
||||
args = {'token': token,
|
||||
'endpoint': ironic_url}
|
||||
args['os_ironic_api_version'] = api_version
|
||||
args['max_retries'] = CONF.ironic.max_retries
|
||||
args['retry_interval'] = CONF.ironic.retry_interval
|
||||
return client.get_client(1, **args)
|
||||
return client.Client(1, **args)
|
||||
|
||||
|
||||
def check_provision_state(node, with_credentials=False):
|
||||
@ -173,4 +201,4 @@ def dict_to_capabilities(caps_dict):
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [(IRONIC_GROUP, IRONIC_OPTS)]
|
||||
return keystone.add_auth_options(IRONIC_OPTS, IRONIC_GROUP)
|
||||
|
129
ironic_inspector/common/keystone.py
Normal file
129
ironic_inspector/common/keystone.py
Normal file
@ -0,0 +1,129 @@
|
||||
# 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 copy
|
||||
|
||||
from keystoneauth1 import exceptions
|
||||
from keystoneauth1 import loading
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from six.moves.urllib import parse # for legacy options loading only
|
||||
|
||||
from ironic_inspector.common.i18n import _LW
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def register_auth_opts(group):
|
||||
loading.register_session_conf_options(CONF, group)
|
||||
loading.register_auth_conf_options(CONF, group)
|
||||
CONF.set_default('auth_type', default='password', group=group)
|
||||
|
||||
|
||||
def get_session(group, legacy_mapping=None, legacy_auth_opts=None):
|
||||
auth = _get_auth(group, legacy_mapping, legacy_auth_opts)
|
||||
session = loading.load_session_from_conf_options(
|
||||
CONF, group, auth=auth)
|
||||
return session
|
||||
|
||||
|
||||
def _get_auth(group, legacy_mapping=None, legacy_opts=None):
|
||||
try:
|
||||
auth = loading.load_auth_from_conf_options(CONF, group)
|
||||
except exceptions.MissingRequiredOptions:
|
||||
auth = _get_legacy_auth(group, legacy_mapping, legacy_opts)
|
||||
else:
|
||||
if auth is None:
|
||||
auth = _get_legacy_auth(group, legacy_mapping, legacy_opts)
|
||||
return auth
|
||||
|
||||
|
||||
def _get_legacy_auth(group, legacy_mapping, legacy_opts):
|
||||
"""Load auth plugin from legacy options.
|
||||
|
||||
If legacy_opts is not empty, these options will be registered first.
|
||||
|
||||
legacy_mapping is a dict that maps the following keys to legacy option
|
||||
names:
|
||||
auth_url
|
||||
username
|
||||
password
|
||||
tenant_name
|
||||
"""
|
||||
LOG.warning(_LW("Group [%s]: Using legacy auth loader is deprecated. "
|
||||
"Consider specifying appropriate keystone auth plugin as "
|
||||
"'auth_type' and corresponding plugin options."), group)
|
||||
if legacy_opts:
|
||||
for opt in legacy_opts:
|
||||
try:
|
||||
CONF.register_opt(opt, group=group)
|
||||
except cfg.DuplicateOptError:
|
||||
pass
|
||||
|
||||
conf = getattr(CONF, group)
|
||||
auth_params = {a: getattr(conf, legacy_mapping[a])
|
||||
for a in legacy_mapping}
|
||||
legacy_loader = loading.get_plugin_loader('password')
|
||||
# NOTE(pas-ha) only Swift had this option, take it into account
|
||||
try:
|
||||
auth_version = conf.get('os_auth_version')
|
||||
except cfg.NoSuchOptError:
|
||||
auth_version = None
|
||||
# NOTE(pas-ha) mimic defaults of keystoneclient
|
||||
if _is_apiv3(auth_params['auth_url'], auth_version):
|
||||
auth_params.update({
|
||||
'project_domain_id': 'default',
|
||||
'user_domain_id': 'default'})
|
||||
return legacy_loader.load_from_options(**auth_params)
|
||||
|
||||
|
||||
# NOTE(pas-ha): for backward compat with legacy options loading only
|
||||
def _is_apiv3(auth_url, auth_version):
|
||||
"""Check if V3 version of API is being used or not.
|
||||
|
||||
This method inspects auth_url and auth_version, and checks whether V3
|
||||
version of the API is being used or not.
|
||||
When no auth_version is specified and auth_url is not a versioned
|
||||
endpoint, v2.0 is assumed.
|
||||
:param auth_url: a http or https url to be inspected (like
|
||||
'http://127.0.0.1:9898/').
|
||||
:param auth_version: a string containing the version (like 'v2', 'v3.0')
|
||||
or None
|
||||
:returns: True if V3 of the API is being used.
|
||||
"""
|
||||
return (auth_version in ('v3.0', '3') or
|
||||
'/v3' in parse.urlparse(auth_url).path)
|
||||
|
||||
|
||||
def add_auth_options(options, group):
|
||||
|
||||
def add_options(opts, opts_to_add):
|
||||
for new_opt in opts_to_add:
|
||||
for opt in opts:
|
||||
if opt.name == new_opt.name:
|
||||
break
|
||||
else:
|
||||
opts.append(new_opt)
|
||||
|
||||
opts = copy.deepcopy(options)
|
||||
opts.insert(0, loading.get_auth_common_conf_options()[0])
|
||||
# NOTE(dims): There are a lot of auth plugins, we just generate
|
||||
# the config options for a few common ones
|
||||
plugins = ['password', 'v2password', 'v3password']
|
||||
for name in plugins:
|
||||
plugin = loading.get_plugin_loader(name)
|
||||
add_options(opts, loading.get_auth_plugin_conf_options(plugin))
|
||||
add_options(opts, loading.get_session_conf_options())
|
||||
opts.sort(key=lambda x: x.name)
|
||||
return [(group, opts)]
|
@ -17,10 +17,12 @@ import json
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
import six
|
||||
from swiftclient import client as swift_client
|
||||
from swiftclient import exceptions as swift_exceptions
|
||||
|
||||
from ironic_inspector.common.i18n import _
|
||||
from ironic_inspector.common import keystone
|
||||
from ironic_inspector import utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -28,7 +30,7 @@ CONF = cfg.CONF
|
||||
|
||||
LOG = log.getLogger('ironic_inspector.common.swift')
|
||||
|
||||
|
||||
SWIFT_GROUP = 'swift'
|
||||
SWIFT_OPTS = [
|
||||
cfg.IntOpt('max_retries',
|
||||
default=2,
|
||||
@ -41,6 +43,32 @@ SWIFT_OPTS = [
|
||||
cfg.StrOpt('container',
|
||||
default='ironic-inspector',
|
||||
help='Default Swift container to use when creating objects.'),
|
||||
cfg.StrOpt('os_auth_version',
|
||||
default='2',
|
||||
help='Keystone authentication API version',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use options presented by configured '
|
||||
'keystone auth plugin.'),
|
||||
cfg.StrOpt('os_auth_url',
|
||||
default='',
|
||||
help='Keystone authentication URL',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Use options presented by configured '
|
||||
'keystone auth plugin.'),
|
||||
cfg.StrOpt('os_service_type',
|
||||
default='object-store',
|
||||
help='Swift service type.'),
|
||||
cfg.StrOpt('os_endpoint_type',
|
||||
default='internalURL',
|
||||
help='Swift endpoint type.'),
|
||||
cfg.StrOpt('os_region',
|
||||
help='Keystone region to get endpoint for.'),
|
||||
]
|
||||
|
||||
# NOTE(pas-ha) these old options conflict with options exported by
|
||||
# most used keystone auth plugins. Need to register them manually
|
||||
# for the backward-compat case.
|
||||
LEGACY_OPTS = [
|
||||
cfg.StrOpt('username',
|
||||
default='',
|
||||
help='User name for accessing Swift API.'),
|
||||
@ -51,59 +79,67 @@ SWIFT_OPTS = [
|
||||
cfg.StrOpt('tenant_name',
|
||||
default='',
|
||||
help='Tenant name for accessing Swift API.'),
|
||||
cfg.StrOpt('os_auth_version',
|
||||
default='2',
|
||||
help='Keystone authentication API version'),
|
||||
cfg.StrOpt('os_auth_url',
|
||||
default='',
|
||||
help='Keystone authentication URL'),
|
||||
cfg.StrOpt('os_service_type',
|
||||
default='object-store',
|
||||
help='Swift service type.'),
|
||||
cfg.StrOpt('os_endpoint_type',
|
||||
default='internalURL',
|
||||
help='Swift endpoint type.'),
|
||||
]
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [
|
||||
('swift', SWIFT_OPTS)
|
||||
]
|
||||
|
||||
CONF.register_opts(SWIFT_OPTS, group='swift')
|
||||
CONF.register_opts(SWIFT_OPTS, group=SWIFT_GROUP)
|
||||
keystone.register_auth_opts(SWIFT_GROUP)
|
||||
|
||||
OBJECT_NAME_PREFIX = 'inspector_data'
|
||||
SWIFT_SESSION = None
|
||||
LEGACY_MAP = {
|
||||
'auth_url': 'os_auth_url',
|
||||
'username': 'username',
|
||||
'password': 'password',
|
||||
'tenant_name': 'tenant_name',
|
||||
}
|
||||
|
||||
|
||||
def reset_swift_session():
|
||||
"""Reset the global session variable.
|
||||
|
||||
Mostly useful for unit tests.
|
||||
"""
|
||||
global SWIFT_SESSION
|
||||
SWIFT_SESSION = None
|
||||
|
||||
|
||||
class SwiftAPI(object):
|
||||
"""API for communicating with Swift."""
|
||||
|
||||
def __init__(self, user=None, tenant_name=None, key=None,
|
||||
auth_url=None, auth_version=None,
|
||||
service_type=None, endpoint_type=None):
|
||||
def __init__(self):
|
||||
"""Constructor for creating a SwiftAPI object.
|
||||
|
||||
:param user: the name of the user for Swift account
|
||||
:param tenant_name: the name of the tenant for Swift account
|
||||
:param key: the 'password' or key to authenticate with
|
||||
:param auth_url: the url for authentication
|
||||
:param auth_version: the version of api to use for authentication
|
||||
:param service_type: service type in the service catalog
|
||||
:param endpoint_type: service endpoint type
|
||||
Authentification is loaded from config file.
|
||||
"""
|
||||
self.connection = swift_client.Connection(
|
||||
retries=CONF.swift.max_retries,
|
||||
user=user or CONF.swift.username,
|
||||
tenant_name=tenant_name or CONF.swift.tenant_name,
|
||||
key=key or CONF.swift.password,
|
||||
authurl=auth_url or CONF.swift.os_auth_url,
|
||||
auth_version=auth_version or CONF.swift.os_auth_version,
|
||||
os_options={
|
||||
'service_type': service_type or CONF.swift.os_service_type,
|
||||
'endpoint_type': endpoint_type or CONF.swift.os_endpoint_type
|
||||
}
|
||||
global SWIFT_SESSION
|
||||
if not SWIFT_SESSION:
|
||||
SWIFT_SESSION = keystone.get_session(
|
||||
SWIFT_GROUP, legacy_mapping=LEGACY_MAP,
|
||||
legacy_auth_opts=LEGACY_OPTS)
|
||||
# TODO(pas-ha): swiftclient does not support keystone sessions ATM.
|
||||
# Must be reworked when LP bug #1518938 is fixed.
|
||||
swift_url = SWIFT_SESSION.get_endpoint(
|
||||
service_type=CONF.swift.os_service_type,
|
||||
endpoint_type=CONF.swift.os_endpoint_type,
|
||||
region_name=CONF.swift.os_region
|
||||
)
|
||||
token = SWIFT_SESSION.get_token()
|
||||
params = dict(retries=CONF.swift.max_retries,
|
||||
preauthurl=swift_url,
|
||||
preauthtoken=token)
|
||||
# NOTE(pas-ha):session.verify is for HTTPS urls and can be
|
||||
# - False (do not verify)
|
||||
# - True (verify but try to locate system CA certificates)
|
||||
# - Path (verify using specific CA certificate)
|
||||
# This is normally handled inside the Session instance,
|
||||
# but swiftclient still does not support sessions,
|
||||
# so we need to reconstruct these options from Session here.
|
||||
verify = SWIFT_SESSION.verify
|
||||
params['insecure'] = not verify
|
||||
if verify and isinstance(verify, six.string_types):
|
||||
params['cacert'] = verify
|
||||
|
||||
self.connection = swift_client.Connection(**params)
|
||||
|
||||
def create_object(self, object, data, container=CONF.swift.container,
|
||||
headers=None):
|
||||
@ -182,3 +218,7 @@ def get_introspection_data(uuid):
|
||||
swift_api = SwiftAPI()
|
||||
swift_object_name = '%s-%s' % (OBJECT_NAME_PREFIX, uuid)
|
||||
return swift_api.get_object(swift_object_name)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return keystone.add_auth_options(SWIFT_OPTS, SWIFT_GROUP)
|
||||
|
@ -351,7 +351,6 @@ class Service(object):
|
||||
|
||||
log.set_defaults(default_log_levels=[
|
||||
'sqlalchemy=WARNING',
|
||||
'keystoneclient=INFO',
|
||||
'iso8601=WARNING',
|
||||
'requests=WARNING',
|
||||
'urllib3.connectionpool=WARNING',
|
||||
|
@ -16,10 +16,10 @@ import socket
|
||||
import unittest
|
||||
|
||||
from ironicclient import client
|
||||
from keystoneclient import client as keystone_client
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic_inspector.common import ironic as ir_utils
|
||||
from ironic_inspector.common import keystone
|
||||
from ironic_inspector.test import base
|
||||
from ironic_inspector import utils
|
||||
|
||||
@ -27,37 +27,44 @@ from ironic_inspector import utils
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@mock.patch.object(keystone, 'register_auth_opts')
|
||||
@mock.patch.object(keystone, 'get_session')
|
||||
@mock.patch.object(client, 'Client')
|
||||
class TestGetClient(base.BaseTest):
|
||||
def setUp(self):
|
||||
super(TestGetClient, self).setUp()
|
||||
CONF.set_override('auth_strategy', 'keystone')
|
||||
ir_utils.reset_ironic_session()
|
||||
self.cfg.config(auth_strategy='keystone')
|
||||
self.cfg.config(os_region='somewhere', group='ironic')
|
||||
self.addCleanup(ir_utils.reset_ironic_session)
|
||||
|
||||
@mock.patch.object(client, 'get_client')
|
||||
@mock.patch.object(keystone_client, 'Client')
|
||||
def test_get_client_with_auth_token(self, mock_keystone_client,
|
||||
mock_client):
|
||||
def test_get_client_with_auth_token(self, mock_client, mock_load,
|
||||
mock_opts):
|
||||
fake_token = 'token'
|
||||
fake_ironic_url = 'http://127.0.0.1:6385'
|
||||
mock_keystone_client().service_catalog.url_for.return_value = (
|
||||
fake_ironic_url)
|
||||
mock_sess = mock.Mock()
|
||||
mock_sess.get_endpoint.return_value = fake_ironic_url
|
||||
mock_load.return_value = mock_sess
|
||||
ir_utils.get_client(fake_token)
|
||||
args = {'os_auth_token': fake_token,
|
||||
'ironic_url': fake_ironic_url,
|
||||
'os_ironic_api_version': '1.11',
|
||||
mock_sess.get_endpoint.assert_called_once_with(
|
||||
endpoint_type=CONF.ironic.os_endpoint_type,
|
||||
service_type=CONF.ironic.os_service_type,
|
||||
region_name=CONF.ironic.os_region)
|
||||
args = {'token': fake_token,
|
||||
'endpoint': fake_ironic_url,
|
||||
'os_ironic_api_version': ir_utils.DEFAULT_IRONIC_API_VERSION,
|
||||
'max_retries': CONF.ironic.max_retries,
|
||||
'retry_interval': CONF.ironic.retry_interval}
|
||||
mock_client.assert_called_once_with(1, **args)
|
||||
|
||||
@mock.patch.object(client, 'get_client')
|
||||
def test_get_client_without_auth_token(self, mock_client):
|
||||
def test_get_client_without_auth_token(self, mock_client, mock_load,
|
||||
mock_opts):
|
||||
mock_sess = mock.Mock()
|
||||
mock_load.return_value = mock_sess
|
||||
ir_utils.get_client(None)
|
||||
args = {'os_password': CONF.ironic.os_password,
|
||||
'os_username': CONF.ironic.os_username,
|
||||
'os_auth_url': CONF.ironic.os_auth_url,
|
||||
'os_tenant_name': CONF.ironic.os_tenant_name,
|
||||
'os_endpoint_type': CONF.ironic.os_endpoint_type,
|
||||
'os_service_type': CONF.ironic.os_service_type,
|
||||
'os_ironic_api_version': '1.11',
|
||||
args = {'session': mock_sess,
|
||||
'region_name': 'somewhere',
|
||||
'os_ironic_api_version': ir_utils.DEFAULT_IRONIC_API_VERSION,
|
||||
'max_retries': CONF.ironic.max_retries,
|
||||
'retry_interval': CONF.ironic.retry_interval}
|
||||
mock_client.assert_called_once_with(1, **args)
|
||||
@ -92,7 +99,7 @@ class TestGetIpmiAddress(base.BaseTest):
|
||||
driver_info={'foo': '192.168.1.1'})
|
||||
self.assertIsNone(ir_utils.get_ipmi_address(node))
|
||||
|
||||
CONF.set_override('ipmi_address_fields', ['foo', 'bar', 'baz'])
|
||||
self.cfg.config(ipmi_address_fields=['foo', 'bar', 'baz'])
|
||||
ip = ir_utils.get_ipmi_address(node)
|
||||
self.assertEqual(ip, '192.168.1.1')
|
||||
|
||||
|
115
ironic_inspector/test/test_keystone.py
Normal file
115
ironic_inspector/test/test_keystone.py
Normal file
@ -0,0 +1,115 @@
|
||||
# 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 mock
|
||||
|
||||
from keystoneauth1 import exceptions as kaexc
|
||||
from keystoneauth1 import loading as kaloading
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic_inspector.common import keystone
|
||||
from ironic_inspector.test import base
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
TESTGROUP = 'keystone_test'
|
||||
|
||||
|
||||
class KeystoneTest(base.BaseTest):
|
||||
|
||||
def setUp(self):
|
||||
super(KeystoneTest, self).setUp()
|
||||
self.cfg.conf.register_group(cfg.OptGroup(TESTGROUP))
|
||||
|
||||
def test_register_auth_opts(self):
|
||||
keystone.register_auth_opts(TESTGROUP)
|
||||
auth_opts = ['auth_type', 'auth_section']
|
||||
sess_opts = ['certfile', 'keyfile', 'insecure', 'timeout', 'cafile']
|
||||
for o in auth_opts + sess_opts:
|
||||
self.assertIn(o, self.cfg.conf[TESTGROUP])
|
||||
self.assertEqual('password', self.cfg.conf[TESTGROUP]['auth_type'])
|
||||
|
||||
@mock.patch.object(keystone, '_get_auth')
|
||||
def test_get_session(self, auth_mock):
|
||||
keystone.register_auth_opts(TESTGROUP)
|
||||
self.cfg.config(group=TESTGROUP,
|
||||
cafile='/path/to/ca/file')
|
||||
auth1 = mock.Mock()
|
||||
auth_mock.return_value = auth1
|
||||
sess = keystone.get_session(TESTGROUP)
|
||||
self.assertEqual('/path/to/ca/file', sess.verify)
|
||||
self.assertEqual(auth1, sess.auth)
|
||||
|
||||
@mock.patch('keystoneauth1.loading.load_auth_from_conf_options')
|
||||
@mock.patch.object(keystone, '_get_legacy_auth')
|
||||
def test__get_auth(self, legacy_mock, load_mock):
|
||||
auth1 = mock.Mock()
|
||||
load_mock.side_effect = [
|
||||
auth1,
|
||||
None,
|
||||
kaexc.MissingRequiredOptions([kaloading.Opt('spam')])]
|
||||
auth2 = mock.Mock()
|
||||
legacy_mock.return_value = auth2
|
||||
self.assertEqual(auth1, keystone._get_auth(TESTGROUP))
|
||||
self.assertEqual(auth2, keystone._get_auth(TESTGROUP))
|
||||
self.assertEqual(auth2, keystone._get_auth(TESTGROUP))
|
||||
|
||||
@mock.patch('keystoneauth1.loading._plugins.identity.generic.Password.'
|
||||
'load_from_options')
|
||||
def test__get_legacy_auth(self, load_mock):
|
||||
self.cfg.register_opts(
|
||||
[cfg.StrOpt('identity_url'),
|
||||
cfg.StrOpt('old_user'),
|
||||
cfg.StrOpt('old_password')],
|
||||
group=TESTGROUP)
|
||||
self.cfg.config(group=TESTGROUP,
|
||||
identity_url='http://fake:5000/v3',
|
||||
old_password='ham',
|
||||
old_user='spam')
|
||||
options = [cfg.StrOpt('old_tenant_name', default='fake'),
|
||||
cfg.StrOpt('old_user')]
|
||||
mapping = {'username': 'old_user',
|
||||
'password': 'old_password',
|
||||
'auth_url': 'identity_url',
|
||||
'tenant_name': 'old_tenant_name'}
|
||||
|
||||
keystone._get_legacy_auth(TESTGROUP, mapping, options)
|
||||
load_mock.assert_called_once_with(username='spam',
|
||||
password='ham',
|
||||
tenant_name='fake',
|
||||
user_domain_id='default',
|
||||
project_domain_id='default',
|
||||
auth_url='http://fake:5000/v3')
|
||||
|
||||
def test__is_api_v3(self):
|
||||
cases = ((False, 'http://fake:5000', None),
|
||||
(False, 'http://fake:5000/v2.0', None),
|
||||
(True, 'http://fake:5000/v3', None),
|
||||
(True, 'http://fake:5000', '3'),
|
||||
(True, 'http://fake:5000', 'v3.0'))
|
||||
for case in cases:
|
||||
result, url, version = case
|
||||
self.assertEqual(result, keystone._is_apiv3(url, version))
|
||||
|
||||
def test_add_auth_options(self):
|
||||
group, opts = keystone.add_auth_options([], TESTGROUP)[0]
|
||||
self.assertEqual(TESTGROUP, group)
|
||||
# check that there is no duplicates
|
||||
names = {o.dest for o in opts}
|
||||
self.assertEqual(len(names), len(opts))
|
||||
# NOTE(pas-ha) checking for most standard auth and session ones only
|
||||
expected = {'timeout', 'insecure', 'cafile', 'certfile', 'keyfile',
|
||||
'auth_type', 'auth_url', 'username', 'password',
|
||||
'tenant_name', 'project_name', 'trust_id',
|
||||
'domain_id', 'user_domain_id', 'project_domain_id'}
|
||||
self.assertTrue(expected.issubset(names))
|
@ -14,23 +14,18 @@
|
||||
|
||||
# Mostly copied from ironic/tests/test_swift.py
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
from unittest import mock
|
||||
except ImportError:
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from six.moves import reload_module
|
||||
from swiftclient import client as swift_client
|
||||
from swiftclient import exceptions as swift_exception
|
||||
|
||||
from ironic_inspector.common import keystone
|
||||
from ironic_inspector.common import swift
|
||||
from ironic_inspector.test import base as test_base
|
||||
from ironic_inspector import utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class BaseTest(test_base.NodeTest):
|
||||
def setUp(self):
|
||||
@ -52,61 +47,43 @@ class BaseTest(test_base.NodeTest):
|
||||
}
|
||||
|
||||
|
||||
@mock.patch.object(keystone, 'register_auth_opts')
|
||||
@mock.patch.object(keystone, 'get_session')
|
||||
@mock.patch.object(swift_client, 'Connection', autospec=True)
|
||||
class SwiftTestCase(BaseTest):
|
||||
|
||||
def setUp(self):
|
||||
super(SwiftTestCase, self).setUp()
|
||||
swift.reset_swift_session()
|
||||
self.swift_exception = swift_exception.ClientException('', '')
|
||||
self.cfg.config(group='swift',
|
||||
os_service_type='object-store',
|
||||
os_endpoint_type='internalURL',
|
||||
os_region='somewhere',
|
||||
max_retries=2)
|
||||
self.addCleanup(swift.reset_swift_session)
|
||||
|
||||
CONF.set_override('username', 'swift', 'swift')
|
||||
CONF.set_override('tenant_name', 'tenant', 'swift')
|
||||
CONF.set_override('password', 'password', 'swift')
|
||||
CONF.set_override('os_auth_url', 'http://authurl/v2.0', 'swift')
|
||||
CONF.set_override('os_auth_version', '2', 'swift')
|
||||
CONF.set_override('max_retries', 2, 'swift')
|
||||
CONF.set_override('os_service_type', 'object-store', 'swift')
|
||||
CONF.set_override('os_endpoint_type', 'internalURL', 'swift')
|
||||
|
||||
# The constructor of SwiftAPI accepts arguments whose
|
||||
# default values are values of some config options above. So reload
|
||||
# the module to make sure the required values are set.
|
||||
reload_module(sys.modules['ironic_inspector.common.swift'])
|
||||
|
||||
def test___init__(self, connection_mock):
|
||||
swift.SwiftAPI(user=CONF.swift.username,
|
||||
tenant_name=CONF.swift.tenant_name,
|
||||
key=CONF.swift.password,
|
||||
auth_url=CONF.swift.os_auth_url,
|
||||
auth_version=CONF.swift.os_auth_version)
|
||||
params = {'retries': 2,
|
||||
'user': 'swift',
|
||||
'tenant_name': 'tenant',
|
||||
'key': 'password',
|
||||
'authurl': 'http://authurl/v2.0',
|
||||
'auth_version': '2',
|
||||
'os_options': {'service_type': 'object-store',
|
||||
'endpoint_type': 'internalURL'}}
|
||||
connection_mock.assert_called_once_with(**params)
|
||||
|
||||
def test___init__defaults(self, connection_mock):
|
||||
def test___init__(self, connection_mock, load_mock, opts_mock):
|
||||
swift_url = 'http://swiftapi'
|
||||
token = 'secret_token'
|
||||
mock_sess = mock.Mock()
|
||||
mock_sess.get_token.return_value = token
|
||||
mock_sess.get_endpoint.return_value = swift_url
|
||||
mock_sess.verify = False
|
||||
load_mock.return_value = mock_sess
|
||||
swift.SwiftAPI()
|
||||
params = {'retries': 2,
|
||||
'user': 'swift',
|
||||
'tenant_name': 'tenant',
|
||||
'key': 'password',
|
||||
'authurl': 'http://authurl/v2.0',
|
||||
'auth_version': '2',
|
||||
'os_options': {'service_type': 'object-store',
|
||||
'endpoint_type': 'internalURL'}}
|
||||
'preauthurl': swift_url,
|
||||
'preauthtoken': token,
|
||||
'insecure': True}
|
||||
connection_mock.assert_called_once_with(**params)
|
||||
mock_sess.get_endpoint.assert_called_once_with(
|
||||
service_type='object-store',
|
||||
endpoint_type='internalURL',
|
||||
region_name='somewhere')
|
||||
|
||||
def test_create_object(self, connection_mock):
|
||||
swiftapi = swift.SwiftAPI(user=CONF.swift.username,
|
||||
tenant_name=CONF.swift.tenant_name,
|
||||
key=CONF.swift.password,
|
||||
auth_url=CONF.swift.os_auth_url,
|
||||
auth_version=CONF.swift.os_auth_version)
|
||||
def test_create_object(self, connection_mock, load_mock, opts_mock):
|
||||
swiftapi = swift.SwiftAPI()
|
||||
connection_obj_mock = connection_mock.return_value
|
||||
|
||||
connection_obj_mock.put_object.return_value = 'object-uuid'
|
||||
@ -119,12 +96,9 @@ class SwiftTestCase(BaseTest):
|
||||
'ironic-inspector', 'object', 'some-string-data', headers=None)
|
||||
self.assertEqual('object-uuid', object_uuid)
|
||||
|
||||
def test_create_object_create_container_fails(self, connection_mock):
|
||||
swiftapi = swift.SwiftAPI(user=CONF.swift.username,
|
||||
tenant_name=CONF.swift.tenant_name,
|
||||
key=CONF.swift.password,
|
||||
auth_url=CONF.swift.os_auth_url,
|
||||
auth_version=CONF.swift.os_auth_version)
|
||||
def test_create_object_create_container_fails(self, connection_mock,
|
||||
load_mock, opts_mock):
|
||||
swiftapi = swift.SwiftAPI()
|
||||
connection_obj_mock = connection_mock.return_value
|
||||
connection_obj_mock.put_container.side_effect = self.swift_exception
|
||||
self.assertRaises(utils.Error, swiftapi.create_object, 'object',
|
||||
@ -133,12 +107,9 @@ class SwiftTestCase(BaseTest):
|
||||
'inspector')
|
||||
self.assertFalse(connection_obj_mock.put_object.called)
|
||||
|
||||
def test_create_object_put_object_fails(self, connection_mock):
|
||||
swiftapi = swift.SwiftAPI(user=CONF.swift.username,
|
||||
tenant_name=CONF.swift.tenant_name,
|
||||
key=CONF.swift.password,
|
||||
auth_url=CONF.swift.os_auth_url,
|
||||
auth_version=CONF.swift.os_auth_version)
|
||||
def test_create_object_put_object_fails(self, connection_mock, load_mock,
|
||||
opts_mock):
|
||||
swiftapi = swift.SwiftAPI()
|
||||
connection_obj_mock = connection_mock.return_value
|
||||
connection_obj_mock.put_object.side_effect = self.swift_exception
|
||||
self.assertRaises(utils.Error, swiftapi.create_object, 'object',
|
||||
@ -148,12 +119,8 @@ class SwiftTestCase(BaseTest):
|
||||
connection_obj_mock.put_object.assert_called_once_with(
|
||||
'ironic-inspector', 'object', 'some-string-data', headers=None)
|
||||
|
||||
def test_get_object(self, connection_mock):
|
||||
swiftapi = swift.SwiftAPI(user=CONF.swift.username,
|
||||
tenant_name=CONF.swift.tenant_name,
|
||||
key=CONF.swift.password,
|
||||
auth_url=CONF.swift.os_auth_url,
|
||||
auth_version=CONF.swift.os_auth_version)
|
||||
def test_get_object(self, connection_mock, load_mock, opts_mock):
|
||||
swiftapi = swift.SwiftAPI()
|
||||
connection_obj_mock = connection_mock.return_value
|
||||
|
||||
expected_obj = self.data
|
||||
@ -165,12 +132,8 @@ class SwiftTestCase(BaseTest):
|
||||
'ironic-inspector', 'object')
|
||||
self.assertEqual(expected_obj, swift_obj)
|
||||
|
||||
def test_get_object_fails(self, connection_mock):
|
||||
swiftapi = swift.SwiftAPI(user=CONF.swift.username,
|
||||
tenant_name=CONF.swift.tenant_name,
|
||||
key=CONF.swift.password,
|
||||
auth_url=CONF.swift.os_auth_url,
|
||||
auth_version=CONF.swift.os_auth_version)
|
||||
def test_get_object_fails(self, connection_mock, load_mock, opts_mock):
|
||||
swiftapi = swift.SwiftAPI()
|
||||
connection_obj_mock = connection_mock.return_value
|
||||
connection_obj_mock.get_object.side_effect = self.swift_exception
|
||||
self.assertRaises(utils.Error, swiftapi.get_object,
|
||||
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
features:
|
||||
- Ironic-Inspector is now using keystoneauth and proper auth_plugins
|
||||
instead of keystoneclient for communicating with Ironic and Swift.
|
||||
It allows to finely tune authentification for each service independently.
|
||||
For each service, the keystone session is created and reused, minimizing
|
||||
the number of authentification requests to Keystone.
|
||||
upgrade:
|
||||
- Operators are advised to specify a proper keystoneauth plugin
|
||||
and its appropriate settings in [ironic] and [swift] config sections.
|
||||
Backward compatibility with previous authentification options is included.
|
||||
Using authentification informaiton for Ironic and Swift from
|
||||
[keystone_authtoken] config section is no longer supported.
|
||||
deprecations:
|
||||
- Most of current authentification options for either Ironic or Swift are
|
||||
deprecated and will be removed in a future release. Please configure
|
||||
the keystoneauth auth plugin authentification instead.
|
@ -8,11 +8,11 @@ Flask<1.0,>=0.10 # BSD
|
||||
futurist>=0.11.0 # Apache-2.0
|
||||
jsonpath-rw<2.0,>=1.2.0 # Apache-2.0
|
||||
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
|
||||
keystoneauth1>=2.1.0 # Apache-2.0
|
||||
keystonemiddleware!=4.1.0,>=4.0.0 # Apache-2.0
|
||||
netaddr!=0.7.16,>=0.7.12 # BSD
|
||||
pbr>=1.6 # Apache-2.0
|
||||
python-ironicclient>=1.1.0 # Apache-2.0
|
||||
python-keystoneclient!=1.8.0,!=2.1.0,>=1.6.0 # Apache-2.0
|
||||
python-swiftclient>=2.2.0 # Apache-2.0
|
||||
oslo.concurrency>=3.5.0 # Apache-2.0
|
||||
oslo.config>=3.7.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user