rework creating admin context - use keystoneauth1 lib

rather than own creator of session and auth_plugin from
keystoneclient library.

Change-Id: Id7f71e08de13e77586832f6d12cba1caff3772fe
This commit is contained in:
Andrey Pavlov 2016-09-14 10:23:21 +03:00
parent 590ff04f07
commit d67adb7223
9 changed files with 139 additions and 27 deletions

View File

@ -28,6 +28,7 @@ EC2API_CONF_DIR=${EC2API_CONF_DIR:-/etc/ec2api}
EC2API_CONF_FILE=${EC2API_CONF_DIR}/ec2api.conf
EC2API_DEBUG=${EC2API_DEBUG:-True}
EC2API_STATE_PATH=${EC2API_STATE_PATH:=$DATA_DIR/ec2api}
EC2API_AUTH_CACHE_DIR=${EC2API_AUTH_CACHE_DIR:-/var/cache/ec2api}
EC2API_SERVICE_PORT=${EC2API_SERVICE_PORT:-8788}
EC2API_S3_SERVICE_PORT=${EC2API_S3_SERVICE_PORT:-3334}
@ -178,12 +179,9 @@ function configure_ec2api {
# ec2api Api Configuration
#-------------------------
iniset $EC2API_CONF_FILE DEFAULT admin_tenant_name $SERVICE_TENANT_NAME
iniset $EC2API_CONF_FILE DEFAULT admin_user $EC2API_ADMIN_USER
iniset $EC2API_CONF_FILE DEFAULT admin_password $SERVICE_PASSWORD
configure_auth_token_middleware $EC2API_CONF_FILE $EC2API_ADMIN_USER $EC2API_AUTH_CACHE_DIR
iniset $EC2API_CONF_FILE DEFAULT ec2api_workers "$API_WORKERS"
iniset $EC2API_CONF_FILE DEFAULT keystone_url "$KEYSTONE_SERVICE_URI"
iniset $EC2API_CONF_FILE DEFAULT keystone_ec2_tokens_url "$KEYSTONE_SERVICE_URI_V3/ec2tokens"
iniset $EC2API_CONF_FILE DEFAULT region_list "$REGION_NAME"

View File

@ -47,6 +47,10 @@ LOG = logging.getLogger(__name__)
ec2_opts = [
cfg.StrOpt('keystone_url',
default='http://localhost:5000/',
deprecated_for_removal=True,
deprecated_reason='code was switched to common section '
'"keystone_authtoken"',
deprecated_since='Newton',
help='URL for getting admin session.'),
cfg.StrOpt('keystone_ec2_tokens_url',
default='http://localhost:5000/v3/ec2tokens',

View File

@ -14,6 +14,7 @@
from cinderclient import client as cinderclient
from glanceclient import client as glanceclient
from keystoneauth1 import loading as ks_loading
from keystoneclient.auth.identity.generic import password as keystone_auth
from keystoneclient import client as keystoneclient
from keystoneclient import session as keystone_session
@ -31,8 +32,16 @@ logger = logging.getLogger(__name__)
ec2_opts = [
cfg.BoolOpt('ssl_insecure',
default=False,
deprecated_for_removal=True,
deprecated_reason='code was switched to common section '
'"keystone_authtoken"',
deprecated_since='Newton',
help="Verify HTTPS connections."),
cfg.StrOpt('ssl_ca_file',
deprecated_for_removal=True,
deprecated_reason='code was switched to common section '
'"keystone_authtoken"',
deprecated_since='Newton',
help="CA certificate file to use to verify "
"connecting clients"),
cfg.StrOpt('nova_service_type',
@ -45,20 +54,34 @@ ec2_opts = [
default='volumev2',
help='Service type of Volume API, registered in Keystone '
'catalog.'),
# TODO(andrey-mp): keystone v3 allows to pass domain_name
# or domain_id to auth. This code should support this feature.
cfg.StrOpt('admin_user',
deprecated_for_removal=True,
deprecated_reason='code was switched to common section '
'"keystone_authtoken"',
deprecated_since='Newton',
help=_("Admin user to access specific cloud resourses")),
cfg.StrOpt('admin_password',
deprecated_for_removal=True,
deprecated_reason='code was switched to common section '
'"keystone_authtoken"',
deprecated_since='Newton',
help=_("Admin password"),
secret=True),
cfg.StrOpt('admin_tenant_name',
deprecated_for_removal=True,
deprecated_reason='code was switched to common section '
'"keystone_authtoken"',
deprecated_since='Newton',
help=_("Admin tenant name")),
]
CONF = cfg.CONF
CONF.register_opts(ec2_opts)
GROUP_AUTHTOKEN = 'keystone_authtoken'
ks_loading.register_session_conf_options(CONF, GROUP_AUTHTOKEN)
ks_loading.register_auth_conf_options(CONF, GROUP_AUTHTOKEN)
# Nova API version with microversions support
REQUIRED_NOVA_API_VERSION = '2.1'
@ -106,8 +129,8 @@ def cinder(context):
def keystone(context):
auth_url = context.session.get_endpoint(service_type='identity')
return keystoneclient.Client(auth_url=auth_url,
url = context.session.get_endpoint(service_type='identity')
return keystoneclient.Client(auth_url=url,
session=context.session)
@ -202,27 +225,41 @@ class _rpc_RequestContextSerializer(messaging.NoOpSerializer):
_admin_session = None
def get_session_from_deprecated():
auth = keystone_auth.Password(
username=CONF.admin_user,
password=CONF.admin_password,
project_name=CONF.admin_tenant_name,
tenant_name=CONF.admin_tenant_name,
auth_url=CONF.keystone_url,
)
params = {'auth': auth}
update_request_params_with_ssl(params)
return keystone_session.Session(**params)
def get_os_admin_session():
"""Create a context to interact with OpenStack as an administrator."""
# NOTE(ft): this is a singletone because keystone's session looks thread
# safe for both regular and token renewal requests
global _admin_session
if not _admin_session:
auth = keystone_auth.Password(
username=CONF.admin_user,
password=CONF.admin_password,
project_name=CONF.admin_tenant_name,
tenant_name=CONF.admin_tenant_name,
auth_url=CONF.keystone_url,
)
params = {'auth': auth}
update_request_params_with_ssl(params)
_admin_session = keystone_session.Session(**params)
if not CONF[GROUP_AUTHTOKEN].auth_type:
_admin_session = get_session_from_deprecated()
else:
auth_plugin = ks_loading.load_auth_from_conf_options(
CONF, GROUP_AUTHTOKEN)
_admin_session = ks_loading.load_session_from_conf_options(
CONF, GROUP_AUTHTOKEN, auth=auth_plugin)
return _admin_session
def update_request_params_with_ssl(params):
verify = CONF.ssl_ca_file or not CONF.ssl_insecure
if not CONF[GROUP_AUTHTOKEN].auth_type:
verify = CONF.ssl_ca_file or not CONF.ssl_insecure
else:
verify = (CONF[GROUP_AUTHTOKEN].cafile or
not CONF[GROUP_AUTHTOKEN].insecure)
if verify is not True:
params['verify'] = verify

View File

@ -11,6 +11,10 @@
# limitations under the License.
import itertools
import operator
from keystoneauth1 import loading as ks_loading
from oslo_config import cfg
import ec2api.clients
import ec2api.db.api
@ -21,6 +25,9 @@ import ec2api.utils
import ec2api.wsgi
CONF = cfg.CONF
def list_opts():
return [
('DEFAULT',
@ -34,3 +41,20 @@ def list_opts():
ec2api.wsgi.wsgi_opts,
)),
]
GROUP_AUTHTOKEN = 'keystone_authtoken'
def list_auth_opts():
opt_list = ks_loading.register_session_conf_options(CONF, GROUP_AUTHTOKEN)
opt_list.insert(0, ks_loading.get_auth_common_conf_options()[0])
# NOTE(mhickey): 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:
for plugin_option in ks_loading.get_auth_plugin_conf_options(name):
if all(option.name != plugin_option.name for option in opt_list):
opt_list.append(plugin_option)
opt_list.sort(key=operator.attrgetter('name'))
return [(GROUP_AUTHTOKEN, opt_list)]

View File

@ -20,22 +20,59 @@ from oslo_config import fixture as config_fixture
from oslo_context import context
from oslotest import base as test_base
from ec2api import clients
from ec2api import context as ec2_context
cfg.CONF.import_opt('keystone_url', 'ec2api.api')
GROUP_AUTHTOKEN = 'keystone_authtoken'
class ContextTestCase(test_base.BaseTestCase):
def setUp(self):
super(ContextTestCase, self).setUp()
@mock.patch('keystoneauth1.loading.load_auth_from_conf_options')
@mock.patch('keystoneauth1.loading.load_session_from_conf_options')
def test_get_os_admin_context(self, session, auth):
conf = config_fixture.Config()
clients._admin_session = None
conf.config(auth_type='fake', group=GROUP_AUTHTOKEN)
imp.reload(ec2_context)
# NOTE(ft): initialize a regular context to populate oslo_context's
# local storage to prevent admin context to populate it.
# Used to implicitly validate overwrite=False argument of the call
# RequestContext constructor from inside get_os_admin_context
if not context.get_current():
ec2_context.RequestContext(None, None)
ctx = ec2_context.get_os_admin_context()
conf = cfg.CONF
auth.assert_called_once_with(conf, GROUP_AUTHTOKEN)
auth_plugin = auth.return_value
session.assert_called_once_with(conf, GROUP_AUTHTOKEN,
auth=auth_plugin)
self.assertIsNone(ctx.user_id)
self.assertIsNone(ctx.project_id)
self.assertIsNone(ctx.auth_token)
self.assertEqual([], ctx.service_catalog)
self.assertTrue(ctx.is_os_admin)
self.assertIsNotNone(ctx.session)
self.assertIsNotNone(ctx.session.auth)
self.assertNotEqual(context.get_current(), ctx)
session.reset_mock()
ec2_context.get_os_admin_context()
self.assertFalse(session.called)
@mock.patch('keystoneclient.auth.identity.generic.password.Password')
def test_get_os_admin_context_deprecated(self, password_plugin):
conf = config_fixture.Config()
clients._admin_session = None
conf.config(auth_type=None, group=GROUP_AUTHTOKEN)
conf.config(admin_user='admin',
admin_password='password',
admin_tenant_name='service')
@mock.patch('keystoneclient.auth.identity.generic.password.Password')
def test_get_os_admin_context(self, password_plugin):
imp.reload(ec2_context)
# NOTE(ft): initialize a regular context to populate oslo_context's
# local storage to prevent admin context to populate it.

View File

@ -2,6 +2,7 @@
output_file = etc/ec2api/ec2api.conf.sample
wrap_width = 79
namespace = ec2api
namespace = keystoneauth1
namespace = ec2api.api
namespace = ec2api.metadata
namespace = ec2api.s3

View File

@ -4,6 +4,8 @@
SERVICE_USERNAME=ec2api
SERVICE_PASSWORD=ec2api
SERVICE_TENANT=service
# this domain name will be used for project and user
SERVICE_DOMAIN_NAME=Default
EC2API_PORT=8788
CONNECTION="mysql://ec2api:ec2api@127.0.0.1/ec2api?charset=utf8"
LOG_DIR=/var/log/ec2api
@ -267,15 +269,22 @@ iniset $CONF_FILE DEFAULT api_paste_config $APIPASTE_FILE
iniset $CONF_FILE DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s] %(instance)s%(message)s"
iniset $CONF_FILE DEFAULT log_dir "$LOG_DIR"
iniset $CONF_FILE DEFAULT verbose True
iniset $CONF_FILE DEFAULT keystone_url "$OS_AUTH_URL"
iniset $CONF_FILE DEFAULT keystone_ec2_tokens_url "$OS_AUTH_URL/v3/ec2tokens"
iniset $CONF_FILE database connection "$CONNECTION"
iniset $CONF_FILE DEFAULT full_vpc_support "$VPC_SUPPORT"
iniset $CONF_FILE DEFAULT external_network "$EXTERNAL_NETWORK"
iniset $CONF_FILE DEFAULT admin_user $SERVICE_USERNAME
iniset $CONF_FILE DEFAULT admin_password $SERVICE_PASSWORD
iniset $CONF_FILE DEFAULT admin_tenant_name $SERVICE_TENANT
GROUP_AUTHTOKEN="keystone_authtoken"
iniset $CONF_FILE $GROUP_AUTHTOKEN signing_dir "$AUTH_CACHE_DIR"
iniset $CONF_FILE $GROUP_AUTHTOKEN auth_uri "$OS_AUTH_URL"
iniset $CONF_FILE $GROUP_AUTHTOKEN auth_url "$OS_AUTH_URL"
iniset $CONF_FILE $GROUP_AUTHTOKEN username $SERVICE_USERNAME
iniset $CONF_FILE $GROUP_AUTHTOKEN password $SERVICE_PASSWORD
iniset $CONF_FILE $GROUP_AUTHTOKEN project_name $SERVICE_TENANT
iniset $CONF_FILE $GROUP_AUTHTOKEN project_domain_name $SERVICE_DOMAIN_NAME
iniset $CONF_FILE $GROUP_AUTHTOKEN user_domain_name $SERVICE_DOMAIN_NAME
iniset $CONF_FILE $GROUP_AUTHTOKEN auth_type password
if [[ -f "$NOVA_CONF" ]]; then
# NOTE(ft): use swift instead internal s3 server if enabled

View File

@ -20,6 +20,7 @@ oslo.utils>=3.16.0 # Apache-2.0
Paste # MIT
PasteDeploy>=1.5.0 # MIT
pbr>=1.6 # Apache-2.0
keystoneauth1>=2.10.0 # Apache-2.0
python-cinderclient!=1.7.0,!=1.7.1,>=1.6.0 # Apache-2.0
python-glanceclient!=2.4.0,>=2.3.0 # Apache-2.0
python-keystoneclient!=2.1.0,>=2.0.0 # Apache-2.0

View File

@ -28,6 +28,7 @@ setup-hooks =
oslo.config.opts =
ec2api = ec2api.opts:list_opts
ec2api.api = ec2api.api.opts:list_opts
keystoneauth1 = ec2api.opts:list_auth_opts
ec2api.metadata = ec2api.metadata.opts:list_opts
ec2api.s3 = ec2api.s3.opts:list_opts