Merge "Completely remove support for deprecated Glance V1"

This commit is contained in:
Zuul 2018-10-16 02:48:58 +00:00 committed by Gerrit Code Review
commit 596c03e9eb
18 changed files with 123 additions and 250 deletions

View File

@ -110,7 +110,7 @@ def check_image_service(func):
if self.context.auth_token: if self.context.auth_token:
user_auth = keystone.get_service_auth(self.context, self.endpoint, user_auth = keystone.get_service_auth(self.context, self.endpoint,
service_auth) service_auth)
self.client = client.Client(self.version, session=session, self.client = client.Client(2, session=session,
auth=user_auth or service_auth, auth=user_auth or service_auth,
endpoint_override=self.endpoint, endpoint_override=self.endpoint,
global_request_id=self.context.global_id) global_request_id=self.context.global_id)
@ -121,9 +121,8 @@ def check_image_service(func):
class BaseImageService(object): class BaseImageService(object):
def __init__(self, client=None, version=1, context=None): def __init__(self, client=None, context=None):
self.client = client self.client = client
self.version = version
self.context = context self.context = context
self.endpoint = None self.endpoint = None
@ -134,7 +133,6 @@ class BaseImageService(object):
retry the request according to CONF.glance_num_retries. retry the request according to CONF.glance_num_retries.
:param context: The request context, for access checks. :param context: The request context, for access checks.
:param version: The requested API version.v
:param method: The method requested to be called. :param method: The method requested to be called.
:param args: A list of positional arguments for the method called :param args: A list of positional arguments for the method called
:param kwargs: A dict of keyword arguments for the method called :param kwargs: A dict of keyword arguments for the method called
@ -208,9 +206,7 @@ class BaseImageService(object):
""" """
image_id = service_utils.parse_image_id(image_href) image_id = service_utils.parse_image_id(image_href)
if (self.version == 2 if 'file' in CONF.glance.allowed_direct_url_schemes:
and 'file' in CONF.glance.allowed_direct_url_schemes):
location = self._get_location(image_id) location = self._get_location(image_id)
url = urlparse.urlparse(location) url = urlparse.urlparse(location)
if url.scheme == "file": if url.scheme == "file":
@ -222,10 +218,7 @@ class BaseImageService(object):
image_chunks = self.call(method, image_id) image_chunks = self.call(method, image_id)
# NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper # NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper
# around real data, so we have to check the wrapped data for None. # around real data, so we have to check the wrapped data for None.
# Glance V1 returns HTTP 404 in this case, so no need to fix it. if image_chunks.wrapped is None:
# TODO(dtantsur): remove the hasattr check when we no longer support
# Glance V1.
if hasattr(image_chunks, 'wrapped') and image_chunks.wrapped is None:
raise exception.ImageDownloadFailed( raise exception.ImageDownloadFailed(
image_href=image_href, reason=_('image contains no data.')) image_href=image_href, reason=_('image contains no data.'))

View File

@ -1,45 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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 abc
import six
@six.add_metaclass(abc.ABCMeta)
class ImageService(object):
"""Provides storage and retrieval of disk image objects within Glance."""
@abc.abstractmethod
def __init__(self):
"""Constructor."""
@abc.abstractmethod
def show(self, image_id):
"""Returns a dict with image data for the given opaque image id.
:param image_id: The opaque image identifier.
:returns: A dict containing image metadata.
:raises: ImageNotFound
"""
@abc.abstractmethod
def download(self, image_id, data=None):
"""Calls out to Glance for data and writes data.
:param image_id: The opaque image identifier.
:param data: (Optional) File object to write data to.
"""

View File

@ -23,10 +23,8 @@ from oslo_serialization import jsonutils
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six import six
import six.moves.urllib.parse as urlparse
from ironic.common import exception from ironic.common import exception
from ironic.common import image_service
from ironic.conf import CONF from ironic.conf import CONF
@ -36,31 +34,24 @@ _GLANCE_API_SERVER = None
""" iterator that cycles (indefinitely) over glance API servers. """ """ iterator that cycles (indefinitely) over glance API servers. """
_IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner',
'container_format', 'checksum', 'id',
'name', 'created_at', 'updated_at',
'deleted_at', 'deleted', 'status',
'min_disk', 'min_ram', 'tags', 'visibility',
'protected', 'file', 'schema']
def _extract_attributes(image): def _extract_attributes(image):
# TODO(pas-ha) in Queens unify these once GlanceV1 is no longer supported
IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner',
'container_format', 'checksum', 'id',
'name', 'created_at', 'updated_at',
'deleted_at', 'deleted', 'status',
'min_disk', 'min_ram', 'is_public']
IMAGE_ATTRIBUTES_V2 = ['tags', 'visibility', 'protected',
'file', 'schema']
output = {} output = {}
for attr in IMAGE_ATTRIBUTES: for attr in _IMAGE_ATTRIBUTES:
output[attr] = getattr(image, attr, None) output[attr] = getattr(image, attr, None)
output['properties'] = getattr(image, 'properties', {}) output['properties'] = {}
output['schema'] = image.schema
if hasattr(image, 'schema') and 'v2' in image['schema']: for image_property in set(image) - set(_IMAGE_ATTRIBUTES):
IMAGE_ATTRIBUTES = IMAGE_ATTRIBUTES + IMAGE_ATTRIBUTES_V2 output['properties'][image_property] = image[image_property]
for attr in IMAGE_ATTRIBUTES_V2:
output[attr] = getattr(image, attr, None)
output['schema'] = image['schema']
for image_property in set(image) - set(IMAGE_ATTRIBUTES):
output['properties'][image_property] = image[image_property]
return output return output
@ -154,23 +145,11 @@ def is_image_available(context, image):
if hasattr(context, 'auth_token') and context.auth_token: if hasattr(context, 'auth_token') and context.auth_token:
return True return True
if ((getattr(image, 'is_public', None) if getattr(image, 'visibility', None) == 'public' or context.is_admin:
or getattr(image, 'visibility', None) == 'public')
or context.is_admin):
return True return True
properties = image.properties
if context.project_id and ('owner_id' in properties):
return str(properties['owner_id']) == str(context.project_id)
if context.project_id and ('project_id' in properties): return (context.project_id and
return str(properties['project_id']) == str(context.project_id) getattr(image, 'owner', None) == context.project_id)
try:
user_id = properties['user_id']
except KeyError:
return False
return str(user_id) == str(context.user_id)
def is_image_active(image): def is_image_active(image):
@ -187,18 +166,3 @@ def is_glance_image(image_href):
return False return False
return (image_href.startswith('glance://') return (image_href.startswith('glance://')
or uuidutils.is_uuid_like(image_href)) or uuidutils.is_uuid_like(image_href))
def is_image_href_ordinary_file_name(image_href):
"""Check if image_href is a ordinary file name.
This method judges if image_href is an ordinary file name or not,
which is a file supposed to be stored in share file system.
The ordinary file name is neither glance image href
nor image service href.
:returns: True if image_href is ordinary file name, False otherwise.
"""
return not (is_glance_image(image_href)
or urlparse.urlparse(image_href).scheme.lower() in
image_service.protocol_mapping)

View File

@ -1,28 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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.
from ironic.common.glance_service import base_image_service
from ironic.common.glance_service import service
class GlanceImageService(base_image_service.BaseImageService,
service.ImageService):
def show(self, image_id):
return self._show(image_id, method='get')
def download(self, image_id, data=None):
return self._download(image_id, method='data', data=data)

View File

@ -23,7 +23,6 @@ from swiftclient import utils as swift_utils
from ironic.common import exception as exc from ironic.common import exception as exc
from ironic.common.glance_service import base_image_service from ironic.common.glance_service import base_image_service
from ironic.common.glance_service import service
from ironic.common.glance_service import service_utils from ironic.common.glance_service import service_utils
from ironic.common.i18n import _ from ironic.common.i18n import _
from ironic.common import keystone from ironic.common import keystone
@ -34,8 +33,7 @@ TempUrlCacheElement = collections.namedtuple('TempUrlCacheElement',
['url', 'url_expires_at']) ['url', 'url_expires_at'])
class GlanceImageService(base_image_service.BaseImageService, class GlanceImageService(base_image_service.BaseImageService):
service.ImageService):
# A dictionary containing cached temp URLs in namedtuples # A dictionary containing cached temp URLs in namedtuples
# in format: # in format:

View File

@ -21,7 +21,6 @@ import os
import shutil import shutil
from oslo_log import log from oslo_log import log
from oslo_utils import importutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import requests import requests
import sendfile import sendfile
@ -30,25 +29,16 @@ from six.moves import http_client
import six.moves.urllib.parse as urlparse import six.moves.urllib.parse as urlparse
from ironic.common import exception from ironic.common import exception
from ironic.common.glance_service.v2 import image_service
from ironic.common.i18n import _ from ironic.common.i18n import _
from ironic.common import utils from ironic.common import utils
from ironic.conf import CONF
IMAGE_CHUNK_SIZE = 1024 * 1024 # 1mb IMAGE_CHUNK_SIZE = 1024 * 1024 # 1mb
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
# TODO(pas-ha) in Queens change default to '2', # TODO(dtantsur): temporary re-import, refactor the code and remove it.
# but keep the versioned import in place (less work for possible Glance v3) GlanceImageService = image_service.GlanceImageService
def GlanceImageService(client=None, version=None, context=None):
module_str = 'ironic.common.glance_service'
if version is None:
version = CONF.glance.glance_api_version
module = importutils.import_versioned_module(module_str, version,
'image_service')
service_class = getattr(module, 'GlanceImageService')
return service_class(client, version, context)
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
@ -255,14 +245,12 @@ protocol_mapping = {
} }
def get_image_service(image_href, client=None, version=None, context=None): def get_image_service(image_href, client=None, context=None):
"""Get image service instance to download the image. """Get image service instance to download the image.
:param image_href: String containing href to get image service for. :param image_href: String containing href to get image service for.
:param client: Glance client to be used for download, used only if :param client: Glance client to be used for download, used only if
image_href is Glance href. image_href is Glance href.
:param version: Version of Glance API to use, used only if image_href is
Glance href.
:param context: request context, used only if image_href is Glance href. :param context: request context, used only if image_href is Glance href.
:raises: exception.ImageRefValidationFailed if no image service can :raises: exception.ImageRefValidationFailed if no image service can
handle specified href. handle specified href.
@ -287,5 +275,5 @@ def get_image_service(image_href, client=None, version=None, context=None):
) % scheme) ) % scheme)
if cls == GlanceImageService: if cls == GlanceImageService:
return cls(client, version, context) return cls(client, context)
return cls() return cls()

View File

@ -406,8 +406,7 @@ def get_temp_url_for_glance_image(context, image_uuid):
:param image_uuid: the UUID of the image in glance :param image_uuid: the UUID of the image in glance
:returns: the tmp url for the glance image. :returns: the tmp url for the glance image.
""" """
# Glance API version 2 is required for getting direct_url of the image. glance_service = service.GlanceImageService(context=context)
glance_service = service.GlanceImageService(version=2, context=context)
image_properties = glance_service.show(image_uuid) image_properties = glance_service.show(image_uuid)
LOG.debug('Got image info: %(info)s for image %(image_uuid)s.', LOG.debug('Got image info: %(info)s for image %(image_uuid)s.',
{'info': image_properties, 'image_uuid': image_uuid}) {'info': image_properties, 'image_uuid': image_uuid})

View File

@ -505,8 +505,7 @@ def get_instance_image_info(node, ctx):
labels = ('kernel', 'ramdisk') labels = ('kernel', 'ramdisk')
d_info = deploy_utils.get_image_instance_info(node) d_info = deploy_utils.get_image_instance_info(node)
if not (i_info.get('kernel') and i_info.get('ramdisk')): if not (i_info.get('kernel') and i_info.get('ramdisk')):
glance_service = service.GlanceImageService( glance_service = service.GlanceImageService(context=ctx)
version=CONF.glance.glance_api_version, context=ctx)
iproperties = glance_service.show(d_info['image_source'])['properties'] iproperties = glance_service.show(d_info['image_source'])['properties']
for label in labels: for label in labels:
i_info[label] = str(iproperties[label + '_id']) i_info[label] = str(iproperties[label + '_id'])

View File

@ -143,12 +143,6 @@ opts = [
help=_('Optional path to a CA certificate bundle to be used to ' help=_('Optional path to a CA certificate bundle to be used to '
'validate the SSL certificate served by glance. It is ' 'validate the SSL certificate served by glance. It is '
'used when glance_api_insecure is set to False.')), 'used when glance_api_insecure is set to False.')),
cfg.IntOpt('glance_api_version',
help=_('Glance API version (1 or 2) to use.'),
min=1, max=2, default=2,
deprecated_for_removal=True,
deprecated_reason=_('Ironic will only support using Glance API '
'version 2 in the Queens release.')),
] ]

View File

@ -1196,8 +1196,7 @@ def build_instance_info_for_deploy(task):
image_source = instance_info['image_source'] image_source = instance_info['image_source']
if service_utils.is_glance_image(image_source): if service_utils.is_glance_image(image_source):
glance = image_service.GlanceImageService(version=2, glance = image_service.GlanceImageService(context=task.context)
context=task.context)
image_info = glance.show(image_source) image_info = glance.show(image_source)
LOG.debug('Got image info: %(info)s for node %(node)s.', LOG.debug('Got image info: %(info)s for node %(node)s.',
{'info': image_info, 'node': node.uuid}) {'info': image_info, 'node': node.uuid})

View File

@ -24,11 +24,13 @@ from ironic_lib import metrics_utils
from ironic_lib import utils as ironic_utils from ironic_lib import utils as ironic_utils
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import importutils from oslo_utils import importutils
import six.moves.urllib.parse as urlparse
from ironic.common import boot_devices from ironic.common import boot_devices
from ironic.common import exception from ironic.common import exception
from ironic.common.glance_service import service_utils from ironic.common.glance_service import service_utils
from ironic.common.i18n import _ from ironic.common.i18n import _
from ironic.common import image_service
from ironic.common import images from ironic.common import images
from ironic.common import states from ironic.common import states
from ironic.conductor import utils as manager_utils from ironic.conductor import utils as manager_utils
@ -84,6 +86,21 @@ COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
def _is_image_href_ordinary_file_name(image_href):
"""Check if image_href is a ordinary file name.
This method judges if image_href is an ordinary file name or not,
which is a file supposed to be stored in share file system.
The ordinary file name is neither glance image href
nor image service href.
:returns: True if image_href is ordinary file name, False otherwise.
"""
return not (service_utils.is_glance_image(image_href)
or urlparse.urlparse(image_href).scheme.lower() in
image_service.protocol_mapping)
def _parse_config_option(): def _parse_config_option():
"""Parse config file options. """Parse config file options.
@ -134,7 +151,7 @@ def _parse_driver_info(node, mode='deploy'):
"parameters were missing in node's driver_info") % mode) "parameters were missing in node's driver_info") % mode)
deploy_utils.check_for_missing_params(deploy_info, error_msg) deploy_utils.check_for_missing_params(deploy_info, error_msg)
if service_utils.is_image_href_ordinary_file_name(image_iso): if _is_image_href_ordinary_file_name(image_iso):
image_iso_file = os.path.join(CONF.irmc.remote_image_share_root, image_iso_file = os.path.join(CONF.irmc.remote_image_share_root,
image_iso) image_iso)
if not os.path.isfile(image_iso_file): if not os.path.isfile(image_iso_file):
@ -166,7 +183,7 @@ def _parse_instance_info(node):
if i_info.get('irmc_boot_iso'): if i_info.get('irmc_boot_iso'):
deploy_info['irmc_boot_iso'] = i_info['irmc_boot_iso'] deploy_info['irmc_boot_iso'] = i_info['irmc_boot_iso']
if service_utils.is_image_href_ordinary_file_name( if _is_image_href_ordinary_file_name(
deploy_info['irmc_boot_iso']): deploy_info['irmc_boot_iso']):
boot_iso = os.path.join(CONF.irmc.remote_image_share_root, boot_iso = os.path.join(CONF.irmc.remote_image_share_root,
deploy_info['irmc_boot_iso']) deploy_info['irmc_boot_iso'])
@ -227,7 +244,7 @@ def _setup_vmedia(task, mode, ramdisk_options):
else: else:
iso = task.node.driver_info['irmc_deploy_iso'] iso = task.node.driver_info['irmc_deploy_iso']
if service_utils.is_image_href_ordinary_file_name(iso): if _is_image_href_ordinary_file_name(iso):
iso_file = iso iso_file = iso
else: else:
iso_file = _get_iso_name(task.node, label=mode) iso_file = _get_iso_name(task.node, label=mode)
@ -266,7 +283,7 @@ def _prepare_boot_iso(task, root_uuid):
# fetch boot iso # fetch boot iso
if deploy_info.get('irmc_boot_iso'): if deploy_info.get('irmc_boot_iso'):
boot_iso_href = deploy_info['irmc_boot_iso'] boot_iso_href = deploy_info['irmc_boot_iso']
if service_utils.is_image_href_ordinary_file_name(boot_iso_href): if _is_image_href_ordinary_file_name(boot_iso_href):
driver_internal_info['irmc_boot_iso'] = boot_iso_href driver_internal_info['irmc_boot_iso'] = boot_iso_href
else: else:
boot_iso_filename = _get_iso_name(task.node, label='boot') boot_iso_filename = _get_iso_name(task.node, label='boot')

View File

@ -48,7 +48,6 @@ class NullWriter(object):
class TestGlanceSerializer(testtools.TestCase): class TestGlanceSerializer(testtools.TestCase):
def test_serialize(self): def test_serialize(self):
metadata = {'name': 'image1', metadata = {'name': 'image1',
'is_public': True,
'foo': 'bar', 'foo': 'bar',
'properties': { 'properties': {
'prop1': 'propvalue1', 'prop1': 'propvalue1',
@ -62,7 +61,6 @@ class TestGlanceSerializer(testtools.TestCase):
expected = { expected = {
'name': 'image1', 'name': 'image1',
'is_public': True,
'foo': 'bar', 'foo': 'bar',
'properties': {'prop1': 'propvalue1', 'properties': {'prop1': 'propvalue1',
'mappings': [ 'mappings': [
@ -95,7 +93,7 @@ class TestGlanceImageService(base.TestCase):
self.context = context.RequestContext(auth_token=True) self.context = context.RequestContext(auth_token=True)
self.context.user_id = 'fake' self.context.user_id = 'fake'
self.context.project_id = 'fake' self.context.project_id = 'fake'
self.service = service.GlanceImageService(self.client, 2, self.context) self.service = service.GlanceImageService(self.client, self.context)
self.config(glance_api_servers=['http://localhost'], group='glance') self.config(glance_api_servers=['http://localhost'], group='glance')
self.config(auth_strategy='keystone', group='glance') self.config(auth_strategy='keystone', group='glance')
@ -103,9 +101,7 @@ class TestGlanceImageService(base.TestCase):
@staticmethod @staticmethod
def _make_fixture(**kwargs): def _make_fixture(**kwargs):
fixture = {'name': None, fixture = {'name': None,
'properties': {}, 'status': "active"}
'status': "active",
'is_public': None}
fixture.update(kwargs) fixture.update(kwargs)
return stubs.FakeImage(fixture) return stubs.FakeImage(fixture)
@ -128,25 +124,28 @@ class TestGlanceImageService(base.TestCase):
def test_show_passes_through_to_client(self): def test_show_passes_through_to_client(self):
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
image = self._make_fixture(name='image1', is_public=True, image = self._make_fixture(name='image1', id=image_id)
id=image_id)
expected = { expected = {
'checksum': None,
'container_format': None,
'created_at': None,
'deleted': None,
'deleted_at': None,
'disk_format': None,
'file': None,
'id': image_id, 'id': image_id,
'name': 'image1',
'is_public': True,
'size': None,
'min_disk': None, 'min_disk': None,
'min_ram': None, 'min_ram': None,
'disk_format': None, 'name': 'image1',
'container_format': None,
'checksum': None,
'created_at': None,
'updated_at': None,
'deleted_at': None,
'deleted': None,
'status': "active",
'properties': {},
'owner': None, 'owner': None,
'properties': {},
'protected': None,
'schema': None,
'size': None,
'status': "active",
'tags': None,
'updated_at': None,
'visibility': None,
} }
with mock.patch.object(self.service, 'call', return_value=image, with mock.patch.object(self.service, 'call', return_value=image,
autospec=True): autospec=True):
@ -172,8 +171,7 @@ class TestGlanceImageService(base.TestCase):
def test_show_raises_when_image_not_active(self): def test_show_raises_when_image_not_active(self):
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
image = self._make_fixture(name='image1', is_public=True, image = self._make_fixture(name='image1', id=image_id, status="queued")
id=image_id, status="queued")
with mock.patch.object(self.service, 'call', return_value=image, with mock.patch.object(self.service, 'call', return_value=image,
autospec=True): autospec=True):
self.assertRaises(exception.ImageUnacceptable, self.assertRaises(exception.ImageUnacceptable,
@ -196,7 +194,7 @@ class TestGlanceImageService(base.TestCase):
stub_context = context.RequestContext(auth_token=True) stub_context = context.RequestContext(auth_token=True)
stub_context.user_id = 'fake' stub_context.user_id = 'fake'
stub_context.project_id = 'fake' stub_context.project_id = 'fake'
stub_service = service.GlanceImageService(stub_client, 1, stub_context) stub_service = service.GlanceImageService(stub_client, stub_context)
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
writer = NullWriter() writer = NullWriter()
@ -243,8 +241,7 @@ class TestGlanceImageService(base.TestCase):
stub_client = MyGlanceStubClient() stub_client = MyGlanceStubClient()
stub_service = service.GlanceImageService(stub_client, stub_service = service.GlanceImageService(stub_client,
context=stub_context, context=stub_context)
version=2)
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
self.config(allowed_direct_url_schemes=['file'], group='glance') self.config(allowed_direct_url_schemes=['file'], group='glance')
@ -279,7 +276,7 @@ class TestGlanceImageService(base.TestCase):
stub_context = context.RequestContext(auth_token=True) stub_context = context.RequestContext(auth_token=True)
stub_context.user_id = 'fake' stub_context.user_id = 'fake'
stub_context.project_id = 'fake' stub_context.project_id = 'fake'
stub_service = service.GlanceImageService(stub_client, 1, stub_context) stub_service = service.GlanceImageService(stub_client, stub_context)
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
writer = NullWriter() writer = NullWriter()
self.assertRaises(exception.ImageNotAuthorized, stub_service.download, self.assertRaises(exception.ImageNotAuthorized, stub_service.download,
@ -295,7 +292,7 @@ class TestGlanceImageService(base.TestCase):
stub_context = context.RequestContext(auth_token=True) stub_context = context.RequestContext(auth_token=True)
stub_context.user_id = 'fake' stub_context.user_id = 'fake'
stub_context.project_id = 'fake' stub_context.project_id = 'fake'
stub_service = service.GlanceImageService(stub_client, 1, stub_context) stub_service = service.GlanceImageService(stub_client, stub_context)
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
writer = NullWriter() writer = NullWriter()
self.assertRaises(exception.ImageNotAuthorized, stub_service.download, self.assertRaises(exception.ImageNotAuthorized, stub_service.download,
@ -311,7 +308,7 @@ class TestGlanceImageService(base.TestCase):
stub_context = context.RequestContext(auth_token=True) stub_context = context.RequestContext(auth_token=True)
stub_context.user_id = 'fake' stub_context.user_id = 'fake'
stub_context.project_id = 'fake' stub_context.project_id = 'fake'
stub_service = service.GlanceImageService(stub_client, 1, stub_context) stub_service = service.GlanceImageService(stub_client, stub_context)
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
writer = NullWriter() writer = NullWriter()
self.assertRaises(exception.ImageNotFound, stub_service.download, self.assertRaises(exception.ImageNotFound, stub_service.download,
@ -327,7 +324,7 @@ class TestGlanceImageService(base.TestCase):
stub_context = context.RequestContext(auth_token=True) stub_context = context.RequestContext(auth_token=True)
stub_context.user_id = 'fake' stub_context.user_id = 'fake'
stub_context.project_id = 'fake' stub_context.project_id = 'fake'
stub_service = service.GlanceImageService(stub_client, 1, stub_context) stub_service = service.GlanceImageService(stub_client, stub_context)
image_id = uuidutils.generate_uuid() image_id = uuidutils.generate_uuid()
writer = NullWriter() writer = NullWriter()
self.assertRaises(exception.ImageNotFound, stub_service.download, self.assertRaises(exception.ImageNotFound, stub_service.download,
@ -346,7 +343,7 @@ class CheckImageServiceTestCase(base.TestCase):
def setUp(self): def setUp(self):
super(CheckImageServiceTestCase, self).setUp() super(CheckImageServiceTestCase, self).setUp()
self.context = context.RequestContext(global_request_id='global') self.context = context.RequestContext(global_request_id='global')
self.service = service.GlanceImageService(None, 1, self.context) self.service = service.GlanceImageService(None, self.context)
# NOTE(pas-ha) register keystoneauth dynamic options manually # NOTE(pas-ha) register keystoneauth dynamic options manually
plugin = kaloading.get_plugin_loader('password') plugin = kaloading.get_plugin_loader('password')
opts = kaloading.get_auth_plugin_conf_options(plugin) opts = kaloading.get_auth_plugin_conf_options(plugin)
@ -381,7 +378,7 @@ class CheckImageServiceTestCase(base.TestCase):
def _assert_client_call(self, mock_gclient, url, user=False): def _assert_client_call(self, mock_gclient, url, user=False):
mock_gclient.assert_called_once_with( mock_gclient.assert_called_once_with(
1, 2,
session=mock.sentinel.session, session=mock.sentinel.session,
global_request_id='global', global_request_id='global',
auth=mock.sentinel.sauth if user else mock.sentinel.auth, auth=mock.sentinel.sauth if user else mock.sentinel.auth,
@ -504,7 +501,7 @@ class TestGlanceSwiftTempURL(base.TestCase):
client = stubs.StubGlanceClient() client = stubs.StubGlanceClient()
self.context = context.RequestContext() self.context = context.RequestContext()
self.context.auth_token = 'fake' self.context.auth_token = 'fake'
self.service = service.GlanceImageService(client, 2, self.context) self.service = service.GlanceImageService(client, self.context)
self.config(swift_temp_url_key='correcthorsebatterystaple', self.config(swift_temp_url_key='correcthorsebatterystaple',
group='glance') group='glance')
self.config(swift_endpoint_url='https://swift.example.com', self.config(swift_endpoint_url='https://swift.example.com',
@ -769,7 +766,7 @@ class TestSwiftTempUrlCache(base.TestCase):
group='glance') group='glance')
self.config(swift_store_multiple_containers_seed=0, self.config(swift_store_multiple_containers_seed=0,
group='glance') group='glance')
self.glance_service = service.GlanceImageService(client, version=2, self.glance_service = service.GlanceImageService(client,
context=self.context) context=self.context)
@mock.patch('swiftclient.utils.generate_temp_url', autospec=True) @mock.patch('swiftclient.utils.generate_temp_url', autospec=True)
@ -1004,17 +1001,3 @@ class TestServiceUtils(base.TestCase):
self.assertFalse(service_utils.is_glance_image(image_href)) self.assertFalse(service_utils.is_glance_image(image_href))
image_href = None image_href = None
self.assertFalse(service_utils.is_glance_image(image_href)) self.assertFalse(service_utils.is_glance_image(image_href))
def test_is_image_href_ordinary_file_name_true(self):
image = u"\u0111eploy.iso"
result = service_utils.is_image_href_ordinary_file_name(image)
self.assertTrue(result)
def test_is_image_href_ordinary_file_name_false(self):
for image in ('733d1c44-a2ea-414b-aca7-69decf20d810',
u'glance://\u0111eploy_iso',
u'http://\u0111eploy_iso',
u'https://\u0111eploy_iso',
u'file://\u0111eploy_iso',):
result = service_utils.is_image_href_ordinary_file_name(image)
self.assertFalse(result)

View File

@ -24,7 +24,6 @@ import six.moves.builtins as __builtin__
from six.moves import http_client from six.moves import http_client
from ironic.common import exception from ironic.common import exception
from ironic.common.glance_service.v1 import image_service as glance_v1_service
from ironic.common.glance_service.v2 import image_service as glance_v2_service from ironic.common.glance_service.v2 import image_service as glance_v2_service
from ironic.common import image_service from ironic.common import image_service
from ironic.tests import base from ironic.tests import base
@ -271,16 +270,7 @@ class ServiceGetterTestCase(base.TestCase):
def test_get_glance_image_service(self, glance_service_mock): def test_get_glance_image_service(self, glance_service_mock):
image_href = uuidutils.generate_uuid() image_href = uuidutils.generate_uuid()
image_service.get_image_service(image_href, context=self.context) image_service.get_image_service(image_href, context=self.context)
glance_service_mock.assert_called_once_with(mock.ANY, None, 2, glance_service_mock.assert_called_once_with(mock.ANY, None,
self.context)
@mock.patch.object(glance_v1_service.GlanceImageService, '__init__',
return_value=None, autospec=True)
def test_get_glance_image_service_default_v1(self, glance_service_mock):
self.config(glance_api_version=1, group='glance')
image_href = uuidutils.generate_uuid()
image_service.get_image_service(image_href, context=self.context)
glance_service_mock.assert_called_once_with(mock.ANY, None, 1,
self.context) self.context)
@mock.patch.object(glance_v2_service.GlanceImageService, '__init__', @mock.patch.object(glance_v2_service.GlanceImageService, '__init__',
@ -288,7 +278,7 @@ class ServiceGetterTestCase(base.TestCase):
def test_get_glance_image_service_url(self, glance_service_mock): def test_get_glance_image_service_url(self, glance_service_mock):
image_href = 'glance://%s' % uuidutils.generate_uuid() image_href = 'glance://%s' % uuidutils.generate_uuid()
image_service.get_image_service(image_href, context=self.context) image_service.get_image_service(image_href, context=self.context)
glance_service_mock.assert_called_once_with(mock.ANY, None, 2, glance_service_mock.assert_called_once_with(mock.ANY, None,
self.context) self.context)
@mock.patch.object(image_service.HttpImageService, '__init__', @mock.patch.object(image_service.HttpImageService, '__init__',

View File

@ -40,6 +40,7 @@ from ironic.drivers.modules.irmc import boot as irmc_boot
from ironic.drivers.modules.irmc import common as irmc_common from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.irmc import management as irmc_management from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules import pxe from ironic.drivers.modules import pxe
from ironic.tests import base
from ironic.tests.unit.db import utils as db_utils from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.drivers.modules.irmc import test_common from ironic.tests.unit.drivers.modules.irmc import test_common
from ironic.tests.unit.drivers.modules import test_pxe from ironic.tests.unit.drivers.modules import test_pxe
@ -117,7 +118,7 @@ class IRMCDeployPrivateMethodsTestCase(test_common.BaseIRMCTest):
'/remote_image_share_root/deploy.iso') '/remote_image_share_root/deploy.iso')
self.assertEqual(driver_info_expected, driver_info_actual) self.assertEqual(driver_info_expected, driver_info_actual)
@mock.patch.object(service_utils, 'is_image_href_ordinary_file_name', @mock.patch.object(irmc_boot, '_is_image_href_ordinary_file_name',
spec_set=True, autospec=True) spec_set=True, autospec=True)
def test__parse_driver_info_not_in_share( def test__parse_driver_info_not_in_share(
self, is_image_href_ordinary_file_name_mock): self, is_image_href_ordinary_file_name_mock):
@ -427,7 +428,7 @@ class IRMCDeployPrivateMethodsTestCase(test_common.BaseIRMCTest):
autospec=True) autospec=True)
@mock.patch.object(irmc_boot, '_parse_deploy_info', spec_set=True, @mock.patch.object(irmc_boot, '_parse_deploy_info', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(service_utils, 'is_image_href_ordinary_file_name', @mock.patch.object(irmc_boot, '_is_image_href_ordinary_file_name',
spec_set=True, autospec=True) spec_set=True, autospec=True)
def test__prepare_boot_iso_fetch_ok(self, def test__prepare_boot_iso_fetch_ok(self,
is_image_href_ordinary_file_name_mock, is_image_href_ordinary_file_name_mock,
@ -1893,3 +1894,20 @@ class IRMCPXEBootBasicTestCase(test_pxe.PXEBootTestCase):
properties = task.driver.get_properties() properties = task.driver.get_properties()
for p in pxe.COMMON_PROPERTIES: for p in pxe.COMMON_PROPERTIES:
self.assertIn(p, properties) self.assertIn(p, properties)
class IsImageHrefOrdinaryFileNameTestCase(base.TestCase):
def test_is_image_href_ordinary_file_name_true(self):
image = u"\u0111eploy.iso"
result = irmc_boot._is_image_href_ordinary_file_name(image)
self.assertTrue(result)
def test_is_image_href_ordinary_file_name_false(self):
for image in ('733d1c44-a2ea-414b-aca7-69decf20d810',
u'glance://\u0111eploy_iso',
u'http://\u0111eploy_iso',
u'https://\u0111eploy_iso',
u'file://\u0111eploy_iso',):
result = irmc_boot._is_image_href_ordinary_file_name(image)
self.assertFalse(result)

View File

@ -2257,8 +2257,7 @@ class TestBuildInstanceInfoForDeploy(db_base.DbTestCase):
utils.build_instance_info_for_deploy(task) utils.build_instance_info_for_deploy(task)
glance_mock.assert_called_once_with(version=2, glance_mock.assert_called_once_with(context=task.context)
context=task.context)
glance_mock.return_value.show.assert_called_once_with( glance_mock.return_value.show.assert_called_once_with(
self.node.instance_info['image_source']) self.node.instance_info['image_source'])
glance_mock.return_value.swift_temp_url.assert_called_once_with( glance_mock.return_value.swift_temp_url.assert_called_once_with(
@ -2318,8 +2317,7 @@ class TestBuildInstanceInfoForDeploy(db_base.DbTestCase):
info = utils.build_instance_info_for_deploy(task) info = utils.build_instance_info_for_deploy(task)
glance_mock.assert_called_once_with(version=2, glance_mock.assert_called_once_with(context=task.context)
context=task.context)
glance_mock.return_value.show.assert_called_once_with( glance_mock.return_value.show.assert_called_once_with(
self.node.instance_info['image_source']) self.node.instance_info['image_source'])
glance_mock.return_value.swift_temp_url.assert_called_once_with( glance_mock.return_value.swift_temp_url.assert_called_once_with(
@ -2474,8 +2472,7 @@ class TestBuildInstanceInfoForHttpProvisioning(db_base.DbTestCase):
instance_info = utils.build_instance_info_for_deploy(task) instance_info = utils.build_instance_info_for_deploy(task)
glance_mock.assert_called_once_with(version=2, glance_mock.assert_called_once_with(context=task.context)
context=task.context)
glance_mock.return_value.show.assert_called_once_with( glance_mock.return_value.show.assert_called_once_with(
self.node.instance_info['image_source']) self.node.instance_info['image_source'])
self.cache_image_mock.assert_called_once_with(task.context, self.cache_image_mock.assert_called_once_with(task.context,
@ -2511,8 +2508,7 @@ class TestBuildInstanceInfoForHttpProvisioning(db_base.DbTestCase):
instance_info = utils.build_instance_info_for_deploy(task) instance_info = utils.build_instance_info_for_deploy(task)
glance_mock.assert_called_once_with(version=2, glance_mock.assert_called_once_with(context=task.context)
context=task.context)
glance_mock.return_value.show.assert_called_once_with( glance_mock.return_value.show.assert_called_once_with(
self.node.instance_info['image_source']) self.node.instance_info['image_source'])
self.cache_image_mock.assert_called_once_with(task.context, self.cache_image_mock.assert_called_once_with(task.context,

View File

@ -51,27 +51,27 @@ class StubGlanceClient(object):
return _GlanceWrapper(self.fake_wrapped) return _GlanceWrapper(self.fake_wrapped)
class FakeImage(object): class FakeImage(dict):
def __init__(self, metadata): def __init__(self, metadata):
IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner', IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner',
'container_format', 'checksum', 'id', 'container_format', 'checksum', 'id',
'name', 'name', 'deleted', 'status',
'deleted', 'status', 'min_disk', 'min_ram', 'tags', 'visibility',
'min_disk', 'min_ram', 'is_public'] 'protected', 'file', 'schema']
raw = dict.fromkeys(IMAGE_ATTRIBUTES) raw = dict.fromkeys(IMAGE_ATTRIBUTES)
raw.update(metadata) raw.update(metadata)
# raw['created_at'] = NOW_GLANCE_FORMAT # raw['created_at'] = NOW_GLANCE_FORMAT
# raw['updated_at'] = NOW_GLANCE_FORMAT # raw['updated_at'] = NOW_GLANCE_FORMAT
self.__dict__['raw'] = raw super(FakeImage, self).__init__(raw)
def __getattr__(self, key): def __getattr__(self, key):
try: try:
return self.__dict__['raw'][key] return self[key]
except KeyError: except KeyError:
raise AttributeError(key) raise AttributeError(key)
def __setattr__(self, key, value): def __setattr__(self, key, value):
try: if key in self:
self.__dict__['raw'][key] = value self[key] = value
except KeyError: else:
raise AttributeError(key) raise AttributeError(key)

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
Support for using the Image API v1 was removed. It was removed from Glance
in the Rocky release.
- |
The deprecated option ``[glance]glance_api_version`` was removed. Only v2
is now used.