Glance cache updates to support Keystone Essex.

Glance caching updates to support keystone service tenants:

 * Update glance-cache-manage so that it supports the same style OS_
  environment variables and CLI options for auth that bin/glance does.

 * Updates registry/client so that it supports Keystone KSL style
  service tenants. As services no longer use admin_tokens this is
  a requirement to be able to use caching w/ KSL.

Fixes LP Bug #949486.

Change-Id: I8d0e56a9ec0d20ef56ed2ce8b860d987ae159e01
This commit is contained in:
Dan Prince 2012-03-09 02:12:35 -05:00
parent b43b676e58
commit 4d41027d21
9 changed files with 150 additions and 67 deletions

View File

@ -41,10 +41,10 @@ if os.path.exists(os.path.join(possible_topdir, 'glance', '__init__.py')):
gettext.install('glance', unicode=1)
from glance import version
from glance import client as glance_client
from glance.common import exception
from glance.common import utils
from glance import version
SUCCESS = 0
@ -784,43 +784,16 @@ def get_client(options):
specified by the --host and --port options
supplied to the CLI
"""
if options.auth_url or os.getenv('OS_AUTH_URL'):
force_strategy = 'keystone'
else:
force_strategy = None
creds = dict(username=options.username or \
os.getenv('OS_AUTH_USER', os.getenv('OS_USERNAME')),
password=options.password or \
os.getenv('OS_AUTH_KEY', os.getenv('OS_PASSWORD')),
tenant=options.tenant or \
os.getenv('OS_AUTH_TENANT',
os.getenv('OS_TENANT_NAME')),
auth_url=options.auth_url or os.getenv('OS_AUTH_URL'),
strategy=force_strategy or options.auth_strategy or \
os.getenv('OS_AUTH_STRATEGY', 'noauth'),
region=options.region or os.getenv('OS_REGION_NAME'),
)
if creds['strategy'] == 'keystone' and not creds['auth_url']:
msg = ("--auth_url option or OS_AUTH_URL environment variable "
"required when keystone authentication strategy is enabled\n")
raise exception.ClientConfigurationError(msg)
use_ssl = (options.use_ssl or (
creds['auth_url'] is not None and
creds['auth_url'].find('https') != -1))
client = (glance_client.ProgressClient if not options.is_silent_upload
else glance_client.Client)
return client(host=options.host,
return glance_client.get_client(host=options.host,
port=options.port,
use_ssl=use_ssl,
auth_tok=options.auth_token or
os.getenv('OS_TOKEN'),
creds=creds,
username=options.username,
password=options.password,
tenant=options.tenant,
auth_url=options.auth_url,
auth_strategy=options.auth_strategy,
auth_token=options.auth_token,
region=options.region,
is_silent_upload=options.is_silent_upload,
insecure=options.insecure)

View File

@ -38,9 +38,9 @@ if os.path.exists(os.path.join(possible_topdir, 'glance', '__init__.py')):
gettext.install('glance', unicode=1)
from glance import client as glance_client
from glance import version
from glance.common import exception
from glance.common import utils
from glance import version
SUCCESS = 0
@ -259,19 +259,16 @@ def get_client(options):
specified by the --host and --port options
supplied to the CLI
"""
creds = dict(username=os.getenv('OS_AUTH_USER'),
password=os.getenv('OS_AUTH_KEY'),
tenant=os.getenv('OS_AUTH_TENANT'),
auth_url=os.getenv('OS_AUTH_URL'),
strategy=os.getenv('OS_AUTH_STRATEGY', 'noauth'))
use_ssl = (options.host.find('https') != -1 or (
creds['auth_url'] is not None and
creds['auth_url'].find('https') != -1))
return glance_client.Client(host=options.host, port=options.port,
use_ssl=use_ssl, auth_tok=options.auth_token,
creds=creds)
return glance_client.get_client(host=options.host,
port=options.port,
username=options.username,
password=options.password,
tenant=options.tenant,
auth_url=options.auth_url,
auth_strategy=options.auth_strategy,
auth_token=options.auth_token,
region=options.region,
insecure=options.insecure)
def create_options(parser):
@ -296,6 +293,34 @@ def create_options(parser):
metavar="TOKEN", default=None,
help="Authentication token to use to identify the "
"client to the glance server")
parser.add_option('-I', '--username', dest="username",
metavar="USER", default=None,
help="User name used to acquire an authentication token")
parser.add_option('-K', '--password', dest="password",
metavar="PASSWORD", default=None,
help="Password used to acquire an authentication token")
parser.add_option('-R', '--region', dest="region",
metavar="REGION", default=None,
help="Region name. When using keystone authentication "
"version 2.0 or later this identifies the region "
"name to use when selecting the service endpoint. A "
"region name must be provided if more than one "
"region endpoint is available")
parser.add_option('-T', '--tenant', dest="tenant",
metavar="TENANT", default=None,
help="Tenant name")
parser.add_option('-N', '--auth_url', dest="auth_url",
metavar="AUTH_URL", default=None,
help="Authentication URL")
parser.add_option('-k', '--insecure', dest="insecure",
default=False, action="store_true",
help="Explicitly allow glance to perform \"insecure\" "
"SSL (https) requests. The server's certificate will "
"not be verified against any certificate authorities. "
"This option should be used with caution.")
parser.add_option('-S', '--auth_strategy', dest="auth_strategy",
metavar="STRATEGY", default=None,
help="Authentication strategy (keystone or noauth)")
parser.add_option('-f', '--force', dest="force", metavar="FORCE",
default=False, action="store_true",
help="Prevent select actions from requesting "

View File

@ -36,5 +36,8 @@ registry_host = 0.0.0.0
# Port the registry server is listening on
registry_port = 9191
# Admin token to use if using Keystone
# admin_token = 123
# Auth settings if using Keystone
# auth_url = http://127.0.0.1:5000/v2.0/
# admin_tenant_name = %SERVICE_TENANT_NAME%
# admin_user = %SERVICE_USER%
# admin_password = %SERVICE_PASSWORD%

View File

@ -399,3 +399,51 @@ class ProgressClient(V1Client):
return transfer_info
Client = V1Client
def get_client(host, port=None, username=None,
password=None, tenant=None,
auth_url=None, auth_strategy=None,
auth_token=None, region=None,
is_silent_upload=False, insecure=False):
"""
Returns a new client Glance client object based on common kwargs.
If an option isn't specified falls back to common environment variable
defaults.
"""
if auth_url or os.getenv('OS_AUTH_URL'):
force_strategy = 'keystone'
else:
force_strategy = None
creds = dict(username=username or
os.getenv('OS_AUTH_USER', os.getenv('OS_USERNAME')),
password=password or
os.getenv('OS_AUTH_KEY', os.getenv('OS_PASSWORD')),
tenant=tenant or
os.getenv('OS_AUTH_TENANT',
os.getenv('OS_TENANT_NAME')),
auth_url=auth_url or os.getenv('OS_AUTH_URL'),
strategy=force_strategy or auth_strategy or
os.getenv('OS_AUTH_STRATEGY', 'noauth'),
region=region or os.getenv('OS_REGION_NAME'),
)
if creds['strategy'] == 'keystone' and not creds['auth_url']:
msg = ("--auth_url option or OS_AUTH_URL environment variable "
"required when keystone authentication strategy is enabled\n")
raise exception.ClientConfigurationError(msg)
use_ssl = (creds['auth_url'] is not None and
creds['auth_url'].find('https') != -1)
client = (ProgressClient if not is_silent_upload else Client)
return client(host=host,
port=port,
use_ssl=use_ssl,
auth_tok=auth_token or
os.getenv('OS_TOKEN'),
creds=creds,
insecure=insecure)

View File

@ -211,7 +211,8 @@ class BaseClient(object):
def __init__(self, host, port=None, use_ssl=False, auth_tok=None,
creds=None, doc_root=None, key_file=None,
cert_file=None, ca_file=None, insecure=False):
cert_file=None, ca_file=None, insecure=False,
configure_via_auth=True):
"""
Creates a new client to some service.
@ -247,6 +248,7 @@ class BaseClient(object):
self.auth_tok = auth_tok
self.creds = creds or {}
self.connection = None
self.configure_via_auth = configure_via_auth
# doc_root can be a nullstring, which is valid, and why we
# cannot simply do doc_root or self.DEFAULT_DOC_ROOT below.
self.doc_root = (doc_root if doc_root is not None
@ -365,7 +367,7 @@ class BaseClient(object):
self.auth_tok = auth_plugin.auth_token
management_url = auth_plugin.management_url
if management_url:
if management_url and self.configure_via_auth:
self.configure_from_url(management_url)
@handle_unauthorized
@ -385,7 +387,6 @@ class BaseClient(object):
self._authenticate()
url = self._construct_url(action, params)
return self._do_request(method=method, url=url, body=body,
headers=headers)

View File

@ -26,6 +26,7 @@ import eventlet
from glance.common import exception
from glance.image_cache import ImageCache
from glance import registry
from glance.registry import context
import glance.store
import glance.store.filesystem
import glance.store.http
@ -45,10 +46,11 @@ class Prefetcher(object):
glance.store.create_stores(conf)
self.cache = ImageCache(conf)
registry.configure_registry_client(conf)
registry.configure_registry_admin_creds(conf)
def fetch_image_into_cache(self, image_id):
ctx = registry.get_client_context(self.conf,
is_admin=True, show_deleted=True)
ctx = context.RequestContext(is_admin=True, show_deleted=True)
try:
image_meta = registry.get_image_metadata(ctx, image_id)
if image_meta['status'] != 'active':

View File

@ -26,6 +26,7 @@ import eventlet
from glance.common import exception
from glance.image_cache import ImageCache
from glance import registry
from glance.registry import context
logger = logging.getLogger(__name__)
@ -37,10 +38,10 @@ class Queuer(object):
self.conf = conf
self.cache = ImageCache(conf)
registry.configure_registry_client(conf)
registry.configure_registry_admin_creds(conf)
def queue_image(self, image_id):
ctx = \
registry.get_client_context(conf, is_admin=True, show_deleted=True)
ctx = context.RequestContext(is_admin=True, show_deleted=True)
try:
image_meta = registry.get_image_metadata(ctx, image_id)
if image_meta['status'] != 'active':

View File

@ -20,6 +20,7 @@ Registry API
"""
import logging
import os
from glance.common import cfg
from glance.common import exception
@ -27,6 +28,7 @@ from glance.registry import client
logger = logging.getLogger('glance.registry')
_CLIENT_CREDS = None
_CLIENT_HOST = None
_CLIENT_PORT = None
_CLIENT_KWARGS = {}
@ -45,7 +47,14 @@ registry_client_opts = [
cfg.StrOpt('registry_client_ca_file'),
cfg.StrOpt('metadata_encryption_key'),
]
admin_token_opt = cfg.StrOpt('admin_token')
registry_client_ctx_opts = [
cfg.StrOpt('admin_user'),
cfg.StrOpt('admin_password'),
cfg.StrOpt('admin_tenant_name'),
cfg.StrOpt('auth_url'),
cfg.StrOpt('auth_strategy', default='noauth'),
cfg.StrOpt('auth_region'),
]
def get_registry_addr(conf):
@ -84,16 +93,33 @@ def configure_registry_client(conf):
}
def get_client_context(conf, **kwargs):
conf.register_opt(admin_token_opt)
from glance.common import context
return context.RequestContext(auth_tok=conf.admin_token, **kwargs)
def configure_registry_admin_creds(conf):
global _CLIENT_CREDS
conf.register_opts(registry_client_ctx_opts)
if conf.auth_url or os.getenv('OS_AUTH_URL'):
strategy = 'keystone'
else:
strategy = conf.auth_strategy
_CLIENT_CREDS = {
'user': conf.admin_user,
'password': conf.admin_password,
'username': conf.admin_user,
'tenant': conf.admin_tenant_name,
'auth_url': conf.auth_url,
'strategy': strategy,
'region': conf.auth_region,
}
def get_registry_client(cxt):
global _CLIENT_KWARGS, _CLIENT_HOST, _CLIENT_PORT, _METADATA_ENCRYPTION_KEY
global _CLIENT_CREDS, _CLIENT_KWARGS, _CLIENT_HOST, _CLIENT_PORT
global _METADATA_ENCRYPTION_KEY
kwargs = _CLIENT_KWARGS.copy()
kwargs['auth_tok'] = cxt.auth_tok
if _CLIENT_CREDS:
kwargs['creds'] = _CLIENT_CREDS
return client.RegistryClient(_CLIENT_HOST, _CLIENT_PORT,
_METADATA_ENCRYPTION_KEY, **kwargs)

View File

@ -40,7 +40,11 @@ class RegistryClient(BaseClient):
:param metadata_encryption_key: Key used to encrypt 'location' metadata
"""
self.metadata_encryption_key = metadata_encryption_key
BaseClient.__init__(self, host, port, **kwargs)
# NOTE (dprince): by default base client overwrites host and port
# settings when using keystone. configure_via_auth=False disables
# this behaviour to ensure we still send requests to the Registry API
BaseClient.__init__(self, host, port, configure_via_auth=False,
**kwargs)
def decrypt_metadata(self, image_metadata):
if (self.metadata_encryption_key is not None