summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.zuul.yaml10
-rw-r--r--doc/source/configuration/configuring.rst11
-rw-r--r--doc/source/index.rst26
-rw-r--r--glance/api/middleware/cache_manage.py16
-rw-r--r--glance/api/v2/cached_images.py128
-rw-r--r--glance/cmd/cache_manage.py528
-rw-r--r--glance/common/auth.py58
-rw-r--r--glance/image_cache/client.py136
-rw-r--r--glance/locale/de/LC_MESSAGES/glance.po15
-rw-r--r--glance/locale/en_GB/LC_MESSAGES/glance.po123
-rw-r--r--glance/locale/es/LC_MESSAGES/glance.po13
-rw-r--r--glance/locale/fr/LC_MESSAGES/glance.po15
-rw-r--r--glance/locale/it/LC_MESSAGES/glance.po15
-rw-r--r--glance/locale/ja/LC_MESSAGES/glance.po14
-rw-r--r--glance/locale/ko_KR/LC_MESSAGES/glance.po14
-rw-r--r--glance/locale/pt_BR/LC_MESSAGES/glance.po15
-rw-r--r--glance/locale/ru/LC_MESSAGES/glance.po14
-rw-r--r--glance/locale/tr_TR/LC_MESSAGES/glance.po13
-rw-r--r--glance/locale/zh_CN/LC_MESSAGES/glance.po13
-rw-r--r--glance/locale/zh_TW/LC_MESSAGES/glance.po12
-rw-r--r--glance/tests/unit/api/middleware/test_cache_manage.py30
-rw-r--r--releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po7
22 files changed, 1013 insertions, 213 deletions
diff --git a/.zuul.yaml b/.zuul.yaml
index f65fe4ef5..4be1e86fe 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -28,8 +28,13 @@
parent: tox
abstract: true
description: Abstract job for Glance vs. oslo libraries
- # NOTE(rosmaita): only need functional test jobs, oslo is
- # already running periodic jobs using our unit tests
+ # NOTE(rosmaita): we only need functional test jobs, oslo is
+ # already running periodic jobs using our unit tests. Those
+ # jobs are configured for glance in openstack-infra/project-config/
+ # zuul.d/projects.yaml using the template 'periodic-jobs-with-oslo-master'
+ # which is defined in openstack-infra/openstack-zuul-jobs/zuul.d/
+ # project-templates.yaml; the jobs the template refers to are
+ # defined in openstack-infra/openstack-zuul-jobs/zuul.d/jobs.yaml
required-projects:
- name: openstack/debtcollector
- name: openstack/futurist
@@ -196,6 +201,7 @@
- openstack-python-jobs
- openstack-python35-jobs
- openstack-python36-jobs
+ - openstack-python37-jobs
- periodic-stable-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3
diff --git a/doc/source/configuration/configuring.rst b/doc/source/configuration/configuring.rst
index 6a664b0fb..cb2dcb251 100644
--- a/doc/source/configuration/configuring.rst
+++ b/doc/source/configuration/configuring.rst
@@ -444,6 +444,17 @@ Configuring the Filesystem Storage Backend
permissions, a file will still be saved, but a warning message
will appear in the Glance log.
+``filesystem_store_chunk_size=SIZE_IN_BYTES``
+ Optional. Default: ``65536``
+
+ Can only be specified in configuration files.
+
+ `This option is specific to the filesystem storage backend.`
+
+ The chunk size used when reading or writing image files. Raising this value
+ may improve the throughput but it may also slightly increase the memory
+ usage when handling a large number of requests.
+
Configuring the Filesystem Storage Backend with multiple stores
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 12593e5af..17fbaf6c0 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -23,7 +23,10 @@ About Glance
The Image service (glance) project provides a service where users can upload
and discover data assets that are meant to be used with other services.
-This currently includes images and metadata definitions.
+This currently includes *images* and *metadata definitions*.
+
+Images
+------
Glance image services include discovering, registering, and
retrieving virtual machine (VM) images. Glance has a RESTful API that allows
@@ -35,6 +38,27 @@ VM images made available through Glance can be stored in a variety of
locations from simple filesystems to object-storage systems like the
OpenStack Swift project.
+Metadata Definitions
+--------------------
+
+Glance hosts a *metadefs* catalog. This provides the OpenStack community
+with a way to programmatically determine various metadata key names and
+valid values that can be applied to OpenStack resources.
+
+Note that what we're talking about here is simply a *catalog*; the keys and
+values don't actually do anything unless they are applied to individual
+OpenStack resources using the APIs or client tools provided by the services
+responsible for those resources.
+
+It's also worth noting that there is no special relationship between the
+Image Service and the Metadefs Service. If you want to apply the keys and
+values defined in the Metadefs Service to images, you must use the Image
+Service API or client tools just as you would for any other OpenStack
+service.
+
+Design Principles
+-----------------
+
Glance, as with all OpenStack projects, is written with the following design
guidelines in mind:
diff --git a/glance/api/middleware/cache_manage.py b/glance/api/middleware/cache_manage.py
index 77c695419..44af6742e 100644
--- a/glance/api/middleware/cache_manage.py
+++ b/glance/api/middleware/cache_manage.py
@@ -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"]))
diff --git a/glance/api/v2/cached_images.py b/glance/api/v2/cached_images.py
new file mode 100644
index 000000000..72f405b3f
--- /dev/null
+++ b/glance/api/v2/cached_images.py
@@ -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)
diff --git a/glance/cmd/cache_manage.py b/glance/cmd/cache_manage.py
new file mode 100644
index 000000000..26021a481
--- /dev/null
+++ b/glance/cmd/cache_manage.py
@@ -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()
diff --git a/glance/common/auth.py b/glance/common/auth.py
index 2f9734754..ea76932b3 100644
--- a/glance/common/auth.py
+++ b/glance/common/auth.py
@@ -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
diff --git a/glance/image_cache/client.py b/glance/image_cache/client.py
new file mode 100644
index 000000000..fdc815527
--- /dev/null
+++ b/glance/image_cache/client.py
@@ -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)
diff --git a/glance/locale/de/LC_MESSAGES/glance.po b/glance/locale/de/LC_MESSAGES/glance.po
index 93201b3de..b81e69115 100644
--- a/glance/locale/de/LC_MESSAGES/glance.po
+++ b/glance/locale/de/LC_MESSAGES/glance.po
@@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -99,6 +99,13 @@ msgstr "%s ist bereits gestoppt"
msgid "%s is stopped"
msgstr "%s ist gestoppt"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"Option --os_auth_url oder Umgebungsvariable OS_AUTH_URL erforderlich, wenn "
+"die Keystone-Authentifizierungsstrategie aktiviert ist\n"
+
msgid "A body is not expected with this request."
msgstr "Es wird kein Body bei dieser Anforderung erwartet. "
@@ -1737,12 +1744,6 @@ msgstr "Schema kann nicht geladen werden: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "Konfigurationsdatei zum Einfügen für %s konnte nicht gefunden werden."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"Hochladen von doppelten Abbilddaten für Abbild %(image_id)s nicht möglich: "
-"%(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Unerwarteter Hauptteiltyp. Erwartet wurde list/dict."
diff --git a/glance/locale/en_GB/LC_MESSAGES/glance.po b/glance/locale/en_GB/LC_MESSAGES/glance.po
index 8dc94a45d..0f2b4dd16 100644
--- a/glance/locale/en_GB/LC_MESSAGES/glance.po
+++ b/glance/locale/en_GB/LC_MESSAGES/glance.po
@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-11-20 06:34+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -738,43 +738,6 @@ msgstr ""
msgid ""
"\n"
-"Desired output format for image conversion plugin.\n"
-"\n"
-"Provide a valid image format to which the conversion plugin\n"
-"will convert the image before storing it to the back-end.\n"
-"\n"
-"Note, if the Image Conversion plugin for image import is defined, users\n"
-"should only upload disk formats that are supported by `quemu-img` otherwise\n"
-"the conversion and import will fail.\n"
-"\n"
-"Possible values:\n"
-" * qcow2\n"
-" * raw\n"
-" * vdmk\n"
-"\n"
-"Related Options:\n"
-" * disk_formats\n"
-msgstr ""
-"\n"
-"Desired output format for image conversion plugin.\n"
-"\n"
-"Provide a valid image format to which the conversion plugin\n"
-"will convert the image before storing it to the back-end.\n"
-"\n"
-"Note, if the Image Conversion plugin for image import is defined, users\n"
-"should only upload disk formats that are supported by `quemu-img` otherwise\n"
-"the conversion and import will fail.\n"
-"\n"
-"Possible values:\n"
-" * qcow2\n"
-" * raw\n"
-" * vdmk\n"
-"\n"
-"Related Options:\n"
-" * disk_formats\n"
-
-msgid ""
-"\n"
"Dictionary contains metadata properties to be injected in image.\n"
"\n"
"Possible values:\n"
@@ -2417,63 +2380,6 @@ msgstr ""
msgid ""
"\n"
-"Show all image locations when returning an image.\n"
-"\n"
-"This configuration option indicates whether to show all the image\n"
-"locations when returning image details to the user. When multiple\n"
-"image locations exist for an image, the locations are ordered based\n"
-"on the location strategy indicated by the configuration opt\n"
-"``location_strategy``. The image locations are shown under the\n"
-"image property ``locations``.\n"
-"\n"
-"NOTES:\n"
-" * Revealing image locations can present a GRAVE SECURITY RISK as\n"
-" image locations can sometimes include credentials. Hence, this\n"
-" is set to ``False`` by default. Set this to ``True`` with\n"
-" EXTREME CAUTION and ONLY IF you know what you are doing!\n"
-" * If an operator wishes to avoid showing any image location(s)\n"
-" to the user, then both this option and\n"
-" ``show_image_direct_url`` MUST be set to ``False``.\n"
-"\n"
-"Possible values:\n"
-" * True\n"
-" * False\n"
-"\n"
-"Related options:\n"
-" * show_image_direct_url\n"
-" * location_strategy\n"
-"\n"
-msgstr ""
-"\n"
-"Show all image locations when returning an image.\n"
-"\n"
-"This configuration option indicates whether to show all the image\n"
-"locations when returning image details to the user. When multiple\n"
-"image locations exist for an image, the locations are ordered based\n"
-"on the location strategy indicated by the configuration opt\n"
-"``location_strategy``. The image locations are shown under the\n"
-"image property ``locations``.\n"
-"\n"
-"NOTES:\n"
-" * Revealing image locations can present a GRAVE SECURITY RISK as\n"
-" image locations can sometimes include credentials. Hence, this\n"
-" is set to ``False`` by default. Set this to ``True`` with\n"
-" EXTREME CAUTION and ONLY IF you know what you are doing!\n"
-" * If an operator wishes to avoid showing any image location(s)\n"
-" to the user, then both this option and\n"
-" ``show_image_direct_url`` MUST be set to ``False``.\n"
-"\n"
-"Possible values:\n"
-" * True\n"
-" * False\n"
-"\n"
-"Related options:\n"
-" * show_image_direct_url\n"
-" * location_strategy\n"
-"\n"
-
-msgid ""
-"\n"
"Show direct image location when returning an image.\n"
"\n"
"This configuration option indicates whether to show the direct image\n"
@@ -3455,6 +3361,13 @@ msgstr "'glance-direct' method is not available at this site."
msgid "'node_staging_uri' is not set correctly. Could not load staging store."
msgstr "'node_staging_uri' is not set correctly. Could not load staging store."
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"Keystone authentication strategy is enabled\n"
+
msgid "A body is not expected with this request."
msgstr "A body is not expected with this request."
@@ -4365,13 +4278,6 @@ msgstr "It's not allowed to replace locations if image status is %s."
msgid "It's not allowed to update locations if locations are invisible."
msgstr "It's not allowed to update locations if locations are invisible."
-msgid ""
-"Key:Value pair of store identifier and store type. In case of multiple "
-"backends should be separatedusing comma."
-msgstr ""
-"Key:Value pair of store identifier and store type. In case of multiple "
-"backends should be separated using a comma."
-
msgid "List of strings related to the image"
msgstr "List of strings related to the image"
@@ -5270,15 +5176,6 @@ msgstr ""
"deleted after reaching the time based on their expires_at property."
msgid ""
-"This option will be removed in the Pike release or later because the same "
-"functionality can be achieved with greater granularity by using policies. "
-"Please see the Newton release notes for more information."
-msgstr ""
-"This option will be removed in the Pike release or later because the same "
-"functionality can be achieved with greater granularity by using policies. "
-"Please see the Newton release notes for more information."
-
-msgid ""
"Time in hours for which a task lives after, either succeeding or failing"
msgstr ""
"Time in hours for which a task lives after, either succeeding or failing"
@@ -5374,10 +5271,6 @@ msgstr ""
"Unable to place database under Alembic's migration control. Unknown database "
"state, can't proceed further."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Unexpected body type. Expected list/dict."
diff --git a/glance/locale/es/LC_MESSAGES/glance.po b/glance/locale/es/LC_MESSAGES/glance.po
index 9c18c72c1..0595d833f 100644
--- a/glance/locale/es/LC_MESSAGES/glance.po
+++ b/glance/locale/es/LC_MESSAGES/glance.po
@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -99,6 +99,13 @@ msgstr "%s ya se detuvo"
msgid "%s is stopped"
msgstr "%s se ha detenido"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"Se necesita la opción --os_auth_url ovariable de ambiente OS_AUTH_URL cuando "
+"la estrategia de autenticación keystone está habilitada\n"
+
msgid "A body is not expected with this request."
msgstr "No se espera un cuerpo en esta solicitud."
@@ -1709,10 +1716,6 @@ msgstr "No se ha podido cargar el esquema: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "No se puede ubicar el fichero de configuración de pegado para %s."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr "No se puede cargar datos de imagen duplicada %(image_id)s: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Tipo de cuerpo inesperado. Se esperaba list/dict."
diff --git a/glance/locale/fr/LC_MESSAGES/glance.po b/glance/locale/fr/LC_MESSAGES/glance.po
index 8ce1cd86f..b5e2ac7ee 100644
--- a/glance/locale/fr/LC_MESSAGES/glance.po
+++ b/glance/locale/fr/LC_MESSAGES/glance.po
@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -100,6 +100,13 @@ msgstr "%s est déjà stoppé"
msgid "%s is stopped"
msgstr "%s est arrêté"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"Option --os_auth_url ou variable d'environnement OS_AUTH_URL requise lorsque "
+"la stratégie d'authentification keystone est activée\n"
+
msgid "A body is not expected with this request."
msgstr "Un corps n'est pas attendu avec cette demande."
@@ -1737,12 +1744,6 @@ msgid "Unable to locate paste config file for %s."
msgstr ""
"Impossible de localiser le fichier de configuration du collage pour %s."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"Impossible de télécharger des données image en double pour l'image "
-"%(image_id)s : %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Type de corps inattendu. Type attendu : list/dict."
diff --git a/glance/locale/it/LC_MESSAGES/glance.po b/glance/locale/it/LC_MESSAGES/glance.po
index f7b4389a8..cd5981c46 100644
--- a/glance/locale/it/LC_MESSAGES/glance.po
+++ b/glance/locale/it/LC_MESSAGES/glance.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -95,6 +95,13 @@ msgstr "%s è già stato arrestato"
msgid "%s is stopped"
msgstr "%s è stato arrestato"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"l'opzione --os_auth_url o la variabile d'ambiente OS_AUTH_URL sono "
+"obbligatori quando è abilitato il modo di autenticazione keystone\n"
+
msgid "A body is not expected with this request."
msgstr "Un corpo non è previsto con questa richiesta."
@@ -1722,12 +1729,6 @@ msgstr "Impossibile caricare lo schema: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "Impossibile individuare il file di configurazione paste per %s."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"Impossibile caricare i dati dell'immagine duplicata per l'immagine "
-"%(image_id)s: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Tipo di corpo imprevisto. Elenco/dizionario previsto."
diff --git a/glance/locale/ja/LC_MESSAGES/glance.po b/glance/locale/ja/LC_MESSAGES/glance.po
index c585db278..d3b35eae0 100644
--- a/glance/locale/ja/LC_MESSAGES/glance.po
+++ b/glance/locale/ja/LC_MESSAGES/glance.po
@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -100,6 +100,13 @@ msgstr ""
"'node_staging_uri' が正しく設定されていません。ステージングストアをロードでき"
"ませんでした。"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"keystone 認証戦略が有効な場合は、--os_auth_url オプションまたはOS_AUTH_URL 環"
+"境変数が必要です\n"
+
msgid "A body is not expected with this request."
msgstr "この要求では本文は予期されません。"
@@ -1849,11 +1856,6 @@ msgstr "スキーマをロードできません: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "%s の paste 設定ファイルが見つかりません。"
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"イメージ %(image_id)s の重複イメージデータはアップロードできません: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "予期しない本文タイプ。予期されたのはリストまたは辞書です。"
diff --git a/glance/locale/ko_KR/LC_MESSAGES/glance.po b/glance/locale/ko_KR/LC_MESSAGES/glance.po
index 60a660294..d4d378461 100644
--- a/glance/locale/ko_KR/LC_MESSAGES/glance.po
+++ b/glance/locale/ko_KR/LC_MESSAGES/glance.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -94,6 +94,13 @@ msgstr "%s이(가) 이미 중지되었습니다."
msgid "%s is stopped"
msgstr "%s이(가) 중지됨"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"키스톤 인증 전략이 사용될 경우 --os_auth_url 옵션 또는 OS_AUTH_URL 환경 변수"
+"가 필요합니다.\n"
+
msgid "A body is not expected with this request."
msgstr "이 요청에는 본문이 없어야 합니다."
@@ -1636,11 +1643,6 @@ msgstr "스키마를 로드할 수 없음: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "%s에 대한 붙여넣기 구성 파일을 찾을 수 없습니다."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"이미지 %(image_id)s에 대한 중복 이미지 데이터를 업로드할 수 없음: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "예기치않은 본문 타입. list/dict를 예상합니다."
diff --git a/glance/locale/pt_BR/LC_MESSAGES/glance.po b/glance/locale/pt_BR/LC_MESSAGES/glance.po
index bff556759..754ee685c 100644
--- a/glance/locale/pt_BR/LC_MESSAGES/glance.po
+++ b/glance/locale/pt_BR/LC_MESSAGES/glance.po
@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -97,6 +97,13 @@ msgstr "%s já está parado"
msgid "%s is stopped"
msgstr "%s está parado"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"opção --os_auth_url ou variável de ambiente OS_AUTH_URL requerida quando "
+"estratégia de autenticação keystone está ativada\n"
+
msgid "A body is not expected with this request."
msgstr "Um corpo não é esperado com essa solicitação."
@@ -1697,12 +1704,6 @@ msgstr "Não é possível carregar o esquema: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "Impossível localizar o arquivo de configuração de colagem para %s."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"Não é possível fazer upload de dados de imagem duplicados para a imagem "
-"%(image_id)s: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Tipo de corpo inesperado. Lista/dicionário esperados."
diff --git a/glance/locale/ru/LC_MESSAGES/glance.po b/glance/locale/ru/LC_MESSAGES/glance.po
index 47be06368..9553a90d0 100644
--- a/glance/locale/ru/LC_MESSAGES/glance.po
+++ b/glance/locale/ru/LC_MESSAGES/glance.po
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -89,6 +89,13 @@ msgstr "%s уже остановлен"
msgid "%s is stopped"
msgstr "%s остановлен"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"Опция --os_auth_url или переменная среды OS_AUTH_URL требуется, если "
+"включена стратегия идентификации Keystone\n"
+
msgid "A body is not expected with this request."
msgstr "В этом запросе не должно быть тела."
@@ -1664,11 +1671,6 @@ msgstr "Не удалось загрузить схему: %(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "Не удается найти/вставить файл конфигурации для %s."
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr ""
-"Не удается загрузить данные для дубликата образа %(image_id)s: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "Непредвиденный тип тела. Ожидался список или словарь."
diff --git a/glance/locale/tr_TR/LC_MESSAGES/glance.po b/glance/locale/tr_TR/LC_MESSAGES/glance.po
index 48b13221c..f8ad086ee 100644
--- a/glance/locale/tr_TR/LC_MESSAGES/glance.po
+++ b/glance/locale/tr_TR/LC_MESSAGES/glance.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -89,6 +89,13 @@ msgstr "%s zaten durdurulmuş"
msgid "%s is stopped"
msgstr "%s durduruldu"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"--os_auth_url seçeneği ya da OS_AUTH_URL ortam değişkeni, keystone kimlik "
+"doğrulama stratejisi etkinken gereklidir\n"
+
#, python-format
msgid ""
"A metadata definition object with name=%(object_name)s already exists in "
@@ -1490,10 +1497,6 @@ msgid "Unable to locate paste config file for %s."
msgstr "%s için yapıştırma yapılandırma dosyası yerleştirilemedi."
#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr "%(image_id)s imajı için çift imaj verisi yüklenemedi: %(error)s"
-
-#, python-format
msgid "Unexpected response: %s"
msgstr "Beklenmeyen yanıt: %s"
diff --git a/glance/locale/zh_CN/LC_MESSAGES/glance.po b/glance/locale/zh_CN/LC_MESSAGES/glance.po
index 92243b270..a8a62880e 100644
--- a/glance/locale/zh_CN/LC_MESSAGES/glance.po
+++ b/glance/locale/zh_CN/LC_MESSAGES/glance.po
@@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -95,6 +95,13 @@ msgstr "%s 已停止"
msgid "%s is stopped"
msgstr "%s 已停止"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"当启用了 keystone 认证策略时,需要 --os_auth_url 选项或 OS_AUTH_URL 环境变"
+"量\n"
+
msgid "A body is not expected with this request."
msgstr "此请求不应有主体。"
@@ -1618,10 +1625,6 @@ msgstr "无法装入模式:%(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "对于 %s,找不到粘贴配置文件。"
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr "无法为镜像%(image_id)s上传重复的数据: %(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "意外主体类型。应该为 list/dict。"
diff --git a/glance/locale/zh_TW/LC_MESSAGES/glance.po b/glance/locale/zh_TW/LC_MESSAGES/glance.po
index da6dc386c..f32fb1953 100644
--- a/glance/locale/zh_TW/LC_MESSAGES/glance.po
+++ b/glance/locale/zh_TW/LC_MESSAGES/glance.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: glance VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2018-08-09 04:23+0000\n"
+"POT-Creation-Date: 2019-03-08 07:05+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -89,6 +89,12 @@ msgstr "已停止 %s"
msgid "%s is stopped"
msgstr "%s 已停止"
+msgid ""
+"--os_auth_url option or OS_AUTH_URL environment variable required when "
+"keystone authentication strategy is enabled\n"
+msgstr ""
+"--os_auth_url 選項或 OS_AUTH_URL 環境變數(啟用 Keystone 鑑別策略時需要)\n"
+
msgid "A body is not expected with this request."
msgstr "此要求預期不含內文。"
@@ -1562,10 +1568,6 @@ msgstr "無法載入綱目:%(reason)s"
msgid "Unable to locate paste config file for %s."
msgstr "找不到 %s 的 paste 配置檔。"
-#, python-format
-msgid "Unable to upload duplicate image data for image%(image_id)s: %(error)s"
-msgstr "無法上傳映像檔 %(image_id)s 的重複映像檔資料:%(error)s"
-
msgid "Unexpected body type. Expected list/dict."
msgstr "非預期的內文類型。預期為清單/字典。"
diff --git a/glance/tests/unit/api/middleware/test_cache_manage.py b/glance/tests/unit/api/middleware/test_cache_manage.py
index d66aacb87..04751f578 100644
--- a/glance/tests/unit/api/middleware/test_cache_manage.py
+++ b/glance/tests/unit/api/middleware/test_cache_manage.py
@@ -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
diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
index c68324623..382a06902 100644
--- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
+++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
@@ -4,11 +4,11 @@ msgid ""
msgstr ""
"Project-Id-Version: Glance Release Notes\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-11-20 06:33+0000\n"
+"POT-Creation-Date: 2019-03-08 07:04+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2018-12-04 05:26+0000\n"
+"PO-Revision-Date: 2018-11-07 06:12+0000\n"
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
"Language-Team: English (United Kingdom)\n"
"Language: en_GB\n"
@@ -117,9 +117,6 @@ msgstr "17.0.0"
msgid "17.0.0.0rc1"
msgstr "17.0.0.0rc1"
-msgid "17.0.0.0rc1-40"
-msgstr "17.0.0.0rc1-40"
-
msgid ""
"A new interoperable image import method, ``web-download`` is introduced."
msgstr ""