Add cache-manage utility using v2 API

In Rocky, the v1 dependent glance-cache-manage command was removed while
removing Images API v1 entry points.

As a part of edge-computing image caching will play a big role. To
provide support of image caching, added cache-manage utility using
V2 API. This utility will have the same interface as Queens
glance-cache-manage utility [0] as far as possible.

[0] https://docs.openstack.org/glance/queens/cli/glancecachemanage.html

In addition to above command line options added below new options for doamin
information to use in v3 authentication:
--os-domain-id
--os-user-domain-id
--os-project-domain-id

Implements: blueprint cache-manage
Change-Id: I127c84f8ea610cc20eb3ac2f4f6a9a47a7233f5f
This commit is contained in:
Abhishek Kekane 2018-11-20 05:00:46 +00:00
parent 88a8ad7823
commit d03e80a735
6 changed files with 872 additions and 24 deletions

View File

@ -20,7 +20,7 @@ Image Cache Management API
from oslo_log import log as logging
import routes
from glance.api import cached_images
from glance.api.v2 import cached_images
from glance.common import wsgi
from glance.i18n import _LI
@ -32,37 +32,37 @@ class CacheManageFilter(wsgi.Middleware):
mapper = routes.Mapper()
resource = cached_images.create_resource()
mapper.connect("/v1/cached_images",
mapper.connect("/v2/cached_images",
controller=resource,
action="get_cached_images",
conditions=dict(method=["GET"]))
mapper.connect("/v1/cached_images/{image_id}",
mapper.connect("/v2/cached_images/{image_id}",
controller=resource,
action="delete_cached_image",
conditions=dict(method=["DELETE"]))
mapper.connect("/v1/cached_images",
mapper.connect("/v2/cached_images",
controller=resource,
action="delete_cached_images",
conditions=dict(method=["DELETE"]))
mapper.connect("/v1/queued_images/{image_id}",
mapper.connect("/v2/queued_images/{image_id}",
controller=resource,
action="queue_image",
conditions=dict(method=["PUT"]))
mapper.connect("/v1/queued_images",
mapper.connect("/v2/queued_images",
controller=resource,
action="get_queued_images",
conditions=dict(method=["GET"]))
mapper.connect("/v1/queued_images/{image_id}",
mapper.connect("/v2/queued_images/{image_id}",
controller=resource,
action="delete_queued_image",
conditions=dict(method=["DELETE"]))
mapper.connect("/v1/queued_images",
mapper.connect("/v2/queued_images",
controller=resource,
action="delete_queued_images",
conditions=dict(method=["DELETE"]))

View File

@ -0,0 +1,128 @@
# Copyright 2018 RedHat Inc.
# All Rights Reserved.
#
# 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.
"""
Controller for Image Cache Management API
"""
from oslo_log import log as logging
import webob.exc
from glance.api import policy
from glance.common import exception
from glance.common import wsgi
from glance import image_cache
LOG = logging.getLogger(__name__)
class CacheController(object):
"""
A controller for managing cached images.
"""
def __init__(self):
self.cache = image_cache.ImageCache()
self.policy = policy.Enforcer()
def _enforce(self, req):
"""Authorize request against 'manage_image_cache' policy"""
try:
self.policy.enforce(req.context, 'manage_image_cache', {})
except exception.Forbidden:
LOG.debug("User not permitted to manage the image cache")
raise webob.exc.HTTPForbidden()
def get_cached_images(self, req):
"""
GET /cached_images
Returns a mapping of records about cached images.
"""
self._enforce(req)
images = self.cache.get_cached_images()
return dict(cached_images=images)
def delete_cached_image(self, req, image_id):
"""
DELETE /cached_images/<IMAGE_ID>
Removes an image from the cache.
"""
self._enforce(req)
self.cache.delete_cached_image(image_id)
def delete_cached_images(self, req):
"""
DELETE /cached_images - Clear all active cached images
Removes all images from the cache.
"""
self._enforce(req)
return dict(num_deleted=self.cache.delete_all_cached_images())
def get_queued_images(self, req):
"""
GET /queued_images
Returns a mapping of records about queued images.
"""
self._enforce(req)
images = self.cache.get_queued_images()
return dict(queued_images=images)
def queue_image(self, req, image_id):
"""
PUT /queued_images/<IMAGE_ID>
Queues an image for caching. We do not check to see if
the image is in the registry here. That is done by the
prefetcher...
"""
self._enforce(req)
self.cache.queue_image(image_id)
def delete_queued_image(self, req, image_id):
"""
DELETE /queued_images/<IMAGE_ID>
Removes an image from the cache.
"""
self._enforce(req)
self.cache.delete_queued_image(image_id)
def delete_queued_images(self, req):
"""
DELETE /queued_images - Clear all active queued images
Removes all images from the cache.
"""
self._enforce(req)
return dict(num_deleted=self.cache.delete_all_queued_images())
class CachedImageDeserializer(wsgi.JSONRequestDeserializer):
pass
class CachedImageSerializer(wsgi.JSONResponseSerializer):
pass
def create_resource():
"""Cached Images resource factory method"""
deserializer = CachedImageDeserializer()
serializer = CachedImageSerializer()
return wsgi.Resource(CacheController(), deserializer, serializer)

528
glance/cmd/cache_manage.py Normal file
View File

@ -0,0 +1,528 @@
#!/usr/bin/env python
# Copyright 2018 RedHat Inc.
# All Rights Reserved.
#
# 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.
"""
A simple cache management utility for Glance.
"""
from __future__ import print_function
import argparse
import collections
import datetime
import functools
import os
import sys
import time
import uuid
from oslo_utils import encodeutils
import prettytable
from six.moves import input
# If ../glance/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,
os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'glance', '__init__.py')):
sys.path.insert(0, possible_topdir)
from glance.common import exception
import glance.image_cache.client
from glance.version import version_info as version
SUCCESS = 0
FAILURE = 1
def validate_input(func):
"""Decorator to enforce validation on input"""
@functools.wraps(func)
def wrapped(*args, **kwargs):
if len(args[0].command) > 2:
print("Please specify the ID of the image you wish for command "
"'%s' from the cache as the first and only "
"argument." % args[0].command[0])
return FAILURE
if len(args[0].command) == 2:
image_id = args[0].command[1]
try:
image_id = uuid.UUID(image_id)
except ValueError:
print("Image ID '%s' is not a valid UUID." % image_id)
return FAILURE
return func(args[0], **kwargs)
return wrapped
def catch_error(action):
"""Decorator to provide sensible default error handling for actions."""
def wrap(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
ret = func(*args, **kwargs)
return SUCCESS if ret is None else ret
except exception.NotFound:
options = args[0]
print("Cache management middleware not enabled on host %s" %
options.host)
return FAILURE
except exception.Forbidden:
print("Not authorized to make this request.")
return FAILURE
except Exception as e:
options = args[0]
if options.debug:
raise
print("Failed to %s. Got error:" % action)
pieces = encodeutils.exception_to_unicode(e).split('\n')
for piece in pieces:
print(piece)
return FAILURE
return wrapper
return wrap
@catch_error('show cached images')
def list_cached(args):
"""%(prog)s list-cached [options]
List all images currently cached.
"""
client = get_client(args)
images = client.get_cached_images()
if not images:
print("No cached images.")
return SUCCESS
print("Found %d cached images..." % len(images))
pretty_table = prettytable.PrettyTable(("ID",
"Last Accessed (UTC)",
"Last Modified (UTC)",
"Size",
"Hits"))
pretty_table.align['Size'] = "r"
pretty_table.align['Hits'] = "r"
for image in images:
last_accessed = image['last_accessed']
if last_accessed == 0:
last_accessed = "N/A"
else:
last_accessed = datetime.datetime.utcfromtimestamp(
last_accessed).isoformat()
pretty_table.add_row((
image['image_id'],
last_accessed,
datetime.datetime.utcfromtimestamp(
image['last_modified']).isoformat(),
image['size'],
image['hits']))
print(pretty_table.get_string())
return SUCCESS
@catch_error('show queued images')
def list_queued(args):
"""%(prog)s list-queued [options]
List all images currently queued for caching.
"""
client = get_client(args)
images = client.get_queued_images()
if not images:
print("No queued images.")
return SUCCESS
print("Found %d queued images..." % len(images))
pretty_table = prettytable.PrettyTable(("ID",))
for image in images:
pretty_table.add_row((image,))
print(pretty_table.get_string())
@catch_error('queue the specified image for caching')
@validate_input
def queue_image(args):
"""%(prog)s queue-image <IMAGE_ID> [options]
Queues an image for caching.
"""
image_id = args.command[1]
if (not args.force and
not user_confirm("Queue image %(image_id)s for caching?" %
{'image_id': image_id}, default=False)):
return SUCCESS
client = get_client(args)
client.queue_image_for_caching(image_id)
if args.verbose:
print("Queued image %(image_id)s for caching" %
{'image_id': image_id})
return SUCCESS
@catch_error('delete the specified cached image')
@validate_input
def delete_cached_image(args):
"""%(prog)s delete-cached-image <IMAGE_ID> [options]
Deletes an image from the cache.
"""
image_id = args.command[1]
if (not args.force and
not user_confirm("Delete cached image %(image_id)s?" %
{'image_id': image_id}, default=False)):
return SUCCESS
client = get_client(args)
client.delete_cached_image(image_id)
if args.verbose:
print("Deleted cached image %(image_id)s" % {'image_id': image_id})
return SUCCESS
@catch_error('Delete all cached images')
def delete_all_cached_images(args):
"""%(prog)s delete-all-cached-images [options]
Remove all images from the cache.
"""
if (not args.force and
not user_confirm("Delete all cached images?", default=False)):
return SUCCESS
client = get_client(args)
num_deleted = client.delete_all_cached_images()
if args.verbose:
print("Deleted %(num_deleted)s cached images" %
{'num_deleted': num_deleted})
return SUCCESS
@catch_error('delete the specified queued image')
@validate_input
def delete_queued_image(args):
"""%(prog)s delete-queued-image <IMAGE_ID> [options]
Deletes an image from the cache.
"""
image_id = args.command[1]
if (not args.force and
not user_confirm("Delete queued image %(image_id)s?" %
{'image_id': image_id}, default=False)):
return SUCCESS
client = get_client(args)
client.delete_queued_image(image_id)
if args.verbose:
print("Deleted queued image %(image_id)s" % {'image_id': image_id})
return SUCCESS
@catch_error('Delete all queued images')
def delete_all_queued_images(args):
"""%(prog)s delete-all-queued-images [options]
Remove all images from the cache queue.
"""
if (not args.force and
not user_confirm("Delete all queued images?", default=False)):
return SUCCESS
client = get_client(args)
num_deleted = client.delete_all_queued_images()
if args.verbose:
print("Deleted %(num_deleted)s queued images" %
{'num_deleted': num_deleted})
return SUCCESS
def get_client(options):
"""Return a new client object to a Glance server.
specified by the --host and --port options
supplied to the CLI
"""
# Generate auth_url based on identity_api_version
identity_version = env('OS_IDENTITY_API_VERSION', default='3')
auth_url = options.os_auth_url
if identity_version == '3' and "/v3" not in auth_url:
auth_url = auth_url + "/v3"
elif identity_version == '2' and "/v2" not in auth_url:
auth_url = auth_url + "/v2.0"
user_domain_id = options.os_user_domain_id
if not user_domain_id:
user_domain_id = options.os_domain_id
project_domain_id = options.os_project_domain_id
if not user_domain_id:
project_domain_id = options.os_domain_id
return glance.image_cache.client.get_client(
host=options.host,
port=options.port,
username=options.os_username,
password=options.os_password,
project=options.os_project_name,
user_domain_id=user_domain_id,
project_domain_id=project_domain_id,
auth_url=auth_url,
auth_strategy=options.os_auth_strategy,
auth_token=options.os_auth_token,
region=options.os_region_name,
insecure=options.insecure)
def env(*vars, **kwargs):
"""Search for the first defined of possibly many env vars.
Returns the first environment variable defined in vars, or
returns the default defined in kwargs.
"""
for v in vars:
value = os.environ.get(v)
if value:
return value
return kwargs.get('default', '')
def print_help(args):
"""
Print help specific to a command
"""
command = lookup_command(args.command[1])
print(command.__doc__ % {'prog': os.path.basename(sys.argv[0])})
def parse_args(parser):
"""Set up the CLI and config-file options that may be
parsed and program commands.
:param parser: The option parser
"""
parser.add_argument('command', default='help', nargs='+',
help='The command to execute')
parser.add_argument('-v', '--verbose', default=False, action="store_true",
help="Print more verbose output.")
parser.add_argument('-d', '--debug', default=False, action="store_true",
help="Print debugging output.")
parser.add_argument('-H', '--host', metavar="ADDRESS", default="0.0.0.0",
help="Address of Glance API host.")
parser.add_argument('-p', '--port', dest="port", metavar="PORT",
type=int, default=9292,
help="Port the Glance API host listens on.")
parser.add_argument('-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_argument('-f', '--force', dest="force",
default=False, action="store_true",
help="Prevent select actions from requesting "
"user confirmation.")
parser.add_argument('--os-auth-token',
dest='os_auth_token',
default=env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN].')
parser.add_argument('-A', '--os_auth_token', '--auth_token',
dest='os_auth_token',
help=argparse.SUPPRESS)
parser.add_argument('--os-username',
dest='os_username',
default=env('OS_USERNAME'),
help='Defaults to env[OS_USERNAME].')
parser.add_argument('-I', '--os_username',
dest='os_username',
help=argparse.SUPPRESS)
parser.add_argument('--os-password',
dest='os_password',
default=env('OS_PASSWORD'),
help='Defaults to env[OS_PASSWORD].')
parser.add_argument('-K', '--os_password',
dest='os_password',
help=argparse.SUPPRESS)
parser.add_argument('--os-region-name',
dest='os_region_name',
default=env('OS_REGION_NAME'),
help='Defaults to env[OS_REGION_NAME].')
parser.add_argument('-R', '--os_region_name',
dest='os_region_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-id',
dest='os_project_id',
default=env('OS_PROJECT_ID'),
help='Defaults to env[OS_PROJECT_ID].')
parser.add_argument('--os_project_id',
dest='os_project_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-name',
dest='os_project_name',
default=env('OS_PROJECT_NAME'),
help='Defaults to env[OS_PROJECT_NAME].')
parser.add_argument('-T', '--os_project_name',
dest='os_project_name',
help=argparse.SUPPRESS)
# arguments related user, project domain
parser.add_argument('--os-user-domain-id',
dest='os_user_domain_id',
default=env('OS_USER_DOMAIN_ID'),
help='Defaults to env[OS_USER_DOMAIN_ID].')
parser.add_argument('--os-project-domain-id',
dest='os_project_domain_id',
default=env('OS_PROJECT_DOMAIN_ID'),
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
parser.add_argument('--os-domain-id',
dest='os_domain_id',
default=env('OS_DOMAIN_ID', default='default'),
help='Defaults to env[OS_DOMAIN_ID].')
parser.add_argument('--os-auth-url',
default=env('OS_AUTH_URL'),
help='Defaults to env[OS_AUTH_URL].')
parser.add_argument('-N', '--os_auth_url',
dest='os_auth_url',
help=argparse.SUPPRESS)
parser.add_argument('-S', '--os_auth_strategy', dest="os_auth_strategy",
metavar="STRATEGY",
help="Authentication strategy (keystone or noauth).")
version_string = version.cached_version_string()
parser.add_argument('--version', action='version',
version=version_string)
return parser.parse_args()
CACHE_COMMANDS = collections.OrderedDict()
CACHE_COMMANDS['help'] = (
print_help, 'Output help for one of the commands below')
CACHE_COMMANDS['list-cached'] = (
list_cached, 'List all images currently cached')
CACHE_COMMANDS['list-queued'] = (
list_queued, 'List all images currently queued for caching')
CACHE_COMMANDS['queue-image'] = (
queue_image, 'Queue an image for caching')
CACHE_COMMANDS['delete-cached-image'] = (
delete_cached_image, 'Purges an image from the cache')
CACHE_COMMANDS['delete-all-cached-images'] = (
delete_all_cached_images, 'Removes all images from the cache')
CACHE_COMMANDS['delete-queued-image'] = (
delete_queued_image, 'Deletes an image from the cache queue')
CACHE_COMMANDS['delete-all-queued-images'] = (
delete_all_queued_images, 'Deletes all images from the cache queue')
def _format_command_help():
"""Formats the help string for subcommands."""
help_msg = "Commands:\n\n"
for command, info in CACHE_COMMANDS.items():
if command == 'help':
command = 'help <command>'
help_msg += " %-28s%s\n\n" % (command, info[1])
return help_msg
def lookup_command(command_name):
try:
command = CACHE_COMMANDS[command_name]
return command[0]
except KeyError:
print('\nError: "%s" is not a valid command.\n' % command_name)
print(_format_command_help())
sys.exit("Unknown command: %(cmd_name)s" % {'cmd_name': command_name})
def user_confirm(prompt, default=False):
"""Yes/No question dialog with user.
:param prompt: question/statement to present to user (string)
:param default: boolean value to return if empty string
is received as response to prompt
"""
if default:
prompt_default = "[Y/n]"
else:
prompt_default = "[y/N]"
answer = input("%s %s " % (prompt, prompt_default))
if answer == "":
return default
else:
return answer.lower() in ("yes", "y")
def main():
parser = argparse.ArgumentParser(
description=_format_command_help(),
formatter_class=argparse.RawDescriptionHelpFormatter)
args = parse_args(parser)
if args.command[0] == 'help' and len(args.command) == 1:
parser.print_help()
return
# Look up the command to run
command = lookup_command(args.command[0])
try:
start_time = time.time()
result = command(args)
end_time = time.time()
if args.verbose:
print("Completed in %-0.4f sec." % (end_time - start_time))
sys.exit(result)
except (RuntimeError, NotImplementedError) as e:
sys.exit("ERROR: %s" % e)
if __name__ == '__main__':
main()

View File

@ -94,6 +94,11 @@ class KeystoneStrategy(BaseStrategy):
if self.creds.get("tenant") is None:
raise exception.MissingCredentialError(required='tenant')
# For v3 also check project is present
if self.creds['auth_url'].rstrip('/').endswith('v3'):
if self.creds.get("project") is None:
raise exception.MissingCredentialError(required='project')
def authenticate(self):
"""Authenticate with the Keystone service.
@ -113,10 +118,15 @@ class KeystoneStrategy(BaseStrategy):
# If OS_AUTH_URL is missing a trailing slash add one
if not auth_url.endswith('/'):
auth_url += '/'
token_url = urlparse.urljoin(auth_url, "tokens")
# 1. Check Keystone version
is_v2 = auth_url.rstrip('/').endswith('v2.0')
if is_v2:
is_v3 = auth_url.rstrip('/').endswith('v3')
if is_v3:
token_url = urlparse.urljoin(auth_url, "auth/tokens")
self._v3_auth(token_url)
elif is_v2:
self._v2_auth(token_url)
else:
self._v1_auth(token_url)
@ -186,6 +196,52 @@ class KeystoneStrategy(BaseStrategy):
else:
raise Exception(_('Unexpected response: %s') % resp.status)
def _v3_auth(self, token_url):
creds = {
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": self.creds['username'],
"domain": {"id": self.creds['user_domain_id']},
"password": self.creds['password']
}
}
},
"scope": {
"project": {
"name": self.creds['project'],
"domain": {
"id": self.creds['project_domain_id']
}
}
}
}
}
headers = {'Content-Type': 'application/json'}
req_body = jsonutils.dumps(creds)
resp, resp_body = self._do_request(
token_url, 'POST', headers=headers, body=req_body)
resp_body = jsonutils.loads(resp_body)
if resp.status == 201:
resp_auth = resp['x-subject-token']
creds_region = self.creds.get('region')
if self.configure_via_auth:
endpoint = get_endpoint(resp_body['token']['catalog'],
endpoint_region=creds_region)
self.management_url = endpoint
self.auth_token = resp_auth
elif resp.status == 305:
raise exception.RedirectException(resp['location'])
elif resp.status == 400:
raise exception.AuthBadRequest(url=token_url)
elif resp.status == 401:
raise Exception(_('Unexpected response: %s') % resp.status)
def _v2_auth(self, token_url):
creds = self.creds

View File

@ -0,0 +1,136 @@
# Copyright 2018 RedHat Inc.
# All Rights Reserved.
#
# 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 os
from oslo_serialization import jsonutils as json
from glance.common import client as base_client
from glance.common import exception
from glance.i18n import _
class CacheClient(base_client.BaseClient):
DEFAULT_PORT = 9292
DEFAULT_DOC_ROOT = '/v2'
def delete_cached_image(self, image_id):
"""
Delete a specified image from the cache
"""
self.do_request("DELETE", "/cached_images/%s" % image_id)
return True
def get_cached_images(self, **kwargs):
"""
Returns a list of images stored in the image cache.
"""
res = self.do_request("GET", "/cached_images")
data = json.loads(res.read())['cached_images']
return data
def get_queued_images(self, **kwargs):
"""
Returns a list of images queued for caching
"""
res = self.do_request("GET", "/queued_images")
data = json.loads(res.read())['queued_images']
return data
def delete_all_cached_images(self):
"""
Delete all cached images
"""
res = self.do_request("DELETE", "/cached_images")
data = json.loads(res.read())
num_deleted = data['num_deleted']
return num_deleted
def queue_image_for_caching(self, image_id):
"""
Queue an image for prefetching into cache
"""
self.do_request("PUT", "/queued_images/%s" % image_id)
return True
def delete_queued_image(self, image_id):
"""
Delete a specified image from the cache queue
"""
self.do_request("DELETE", "/queued_images/%s" % image_id)
return True
def delete_all_queued_images(self):
"""
Delete all queued images
"""
res = self.do_request("DELETE", "/queued_images")
data = json.loads(res.read())
num_deleted = data['num_deleted']
return num_deleted
def get_client(host, port=None, timeout=None, use_ssl=False, username=None,
password=None, project=None,
user_domain_id=None, project_domain_id=None,
auth_url=None, auth_strategy=None,
auth_token=None, region=None, 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 = {
'username': username or
os.getenv('OS_AUTH_USER', os.getenv('OS_USERNAME')),
'password': password or
os.getenv('OS_AUTH_KEY', os.getenv('OS_PASSWORD')),
'project': project or
os.getenv('OS_AUTH_PROJECT', os.getenv('OS_PROJECT_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'),
'user_domain_id': user_domain_id or os.getenv(
'OS_USER_DOMAIN_ID', 'default'),
'project_domain_id': project_domain_id or os.getenv(
'OS_PROJECT_DOMAIN_ID', 'default')
}
if creds['strategy'] == 'keystone' and not creds['auth_url']:
msg = _("--os_auth_url option or OS_AUTH_URL environment variable "
"required when keystone authentication strategy is enabled\n")
raise exception.ClientConfigurationError(msg)
return CacheClient(
host=host,
port=port,
timeout=timeout,
use_ssl=use_ssl,
auth_token=auth_token or
os.getenv('OS_TOKEN'),
creds=creds,
insecure=insecure,
configure_via_auth=False)

View File

@ -10,8 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from glance.api import cached_images
from glance.api.middleware import cache_manage
from glance.api.v2 import cached_images
import glance.common.config
import glance.common.wsgi
import glance.image_cache
@ -44,14 +44,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
# check
self.assertIsNone(resource)
@mock.patch.object(cached_images.Controller, "get_cached_images")
@mock.patch.object(cached_images.CacheController, "get_cached_images")
def test_get_cached_images(self,
mock_get_cached_images):
# setup
mock_get_cached_images.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/cached_images")
request = webob.Request.blank("/v2/cached_images")
# call
resource = self.cache_manage_filter.process_request(request)
@ -61,14 +61,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
self.assertEqual('"' + self.stub_value + '"',
resource.body.decode('utf-8'))
@mock.patch.object(cached_images.Controller, "delete_cached_image")
@mock.patch.object(cached_images.CacheController, "delete_cached_image")
def test_delete_cached_image(self,
mock_delete_cached_image):
# setup
mock_delete_cached_image.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/cached_images/" + self.image_id,
request = webob.Request.blank("/v2/cached_images/" + self.image_id,
environ={'REQUEST_METHOD': "DELETE"})
# call
@ -80,14 +80,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
self.assertEqual('"' + self.stub_value + '"',
resource.body.decode('utf-8'))
@mock.patch.object(cached_images.Controller, "delete_cached_images")
@mock.patch.object(cached_images.CacheController, "delete_cached_images")
def test_delete_cached_images(self,
mock_delete_cached_images):
# setup
mock_delete_cached_images.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/cached_images",
request = webob.Request.blank("/v2/cached_images",
environ={'REQUEST_METHOD': "DELETE"})
# call
@ -98,14 +98,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
self.assertEqual('"' + self.stub_value + '"',
resource.body.decode('utf-8'))
@mock.patch.object(cached_images.Controller, "queue_image")
@mock.patch.object(cached_images.CacheController, "queue_image")
def test_put_queued_image(self,
mock_queue_image):
# setup
mock_queue_image.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/queued_images/" + self.image_id,
request = webob.Request.blank("/v2/queued_images/" + self.image_id,
environ={'REQUEST_METHOD': "PUT"})
# call
@ -116,14 +116,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
self.assertEqual('"' + self.stub_value + '"',
resource.body.decode('utf-8'))
@mock.patch.object(cached_images.Controller, "get_queued_images")
@mock.patch.object(cached_images.CacheController, "get_queued_images")
def test_get_queued_images(self,
mock_get_queued_images):
# setup
mock_get_queued_images.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/queued_images")
request = webob.Request.blank("/v2/queued_images")
# call
resource = self.cache_manage_filter.process_request(request)
@ -133,14 +133,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
self.assertEqual('"' + self.stub_value + '"',
resource.body.decode('utf-8'))
@mock.patch.object(cached_images.Controller, "delete_queued_image")
@mock.patch.object(cached_images.CacheController, "delete_queued_image")
def test_delete_queued_image(self,
mock_delete_queued_image):
# setup
mock_delete_queued_image.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/queued_images/" + self.image_id,
request = webob.Request.blank("/v2/queued_images/" + self.image_id,
environ={'REQUEST_METHOD': 'DELETE'})
# call
@ -152,14 +152,14 @@ class TestCacheManageFilter(test_utils.BaseTestCase):
self.assertEqual('"' + self.stub_value + '"',
resource.body.decode('utf-8'))
@mock.patch.object(cached_images.Controller, "delete_queued_images")
@mock.patch.object(cached_images.CacheController, "delete_queued_images")
def test_delete_queued_images(self,
mock_delete_queued_images):
# setup
mock_delete_queued_images.return_value = self.stub_value
# prepare
request = webob.Request.blank("/v1/queued_images",
request = webob.Request.blank("/v2/queued_images",
environ={'REQUEST_METHOD': 'DELETE'})
# call