Merge "Refactoring: flatten the glance service module"

This commit is contained in:
Zuul 2019-06-19 12:15:56 +00:00 committed by Gerrit Code Review
commit f52b386b4c
9 changed files with 211 additions and 261 deletions

@ -1,199 +0,0 @@
# Copyright 2010 OpenStack Foundation
# 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 os
import sys
import time
from glanceclient import client
from glanceclient import exc as glance_exc
from oslo_log import log
import sendfile
import six
import six.moves.urllib.parse as urlparse
from ironic.common import exception
from ironic.common.glance_service import service_utils
from ironic.common.i18n import _
from ironic.common import keystone
from ironic.conf import CONF
LOG = log.getLogger(__name__)
_GLANCE_SESSION = None
def _translate_image_exception(image_id, exc_value):
if isinstance(exc_value, (glance_exc.Forbidden,
glance_exc.Unauthorized)):
return exception.ImageNotAuthorized(image_id=image_id)
if isinstance(exc_value, glance_exc.NotFound):
return exception.ImageNotFound(image_id=image_id)
if isinstance(exc_value, glance_exc.BadRequest):
return exception.Invalid(exc_value)
return exc_value
def check_image_service(func):
"""Creates a glance client if doesn't exists and calls the function."""
@six.wraps(func)
def wrapper(self, *args, **kwargs):
"""Wrapper around methods calls.
:param image_href: href that describes the location of an image
"""
if self.client:
return func(self, *args, **kwargs)
global _GLANCE_SESSION
if not _GLANCE_SESSION:
_GLANCE_SESSION = keystone.get_session('glance')
# NOTE(pas-ha) glanceclient uses Adapter-based SessionClient,
# so we can pass session and auth separately, makes things easier
service_auth = keystone.get_auth('glance')
adapter = keystone.get_adapter('glance', session=_GLANCE_SESSION,
auth=service_auth)
self.endpoint = adapter.get_endpoint()
user_auth = None
# NOTE(pas-ha) our ContextHook removes context.auth_token in noauth
# case, so when ironic is in noauth but glance is not, we will not
# enter the next if-block and use auth from [glance] config section
if self.context.auth_token:
user_auth = keystone.get_service_auth(self.context, self.endpoint,
service_auth)
self.client = client.Client(2, session=_GLANCE_SESSION,
auth=user_auth or service_auth,
endpoint_override=self.endpoint,
global_request_id=self.context.global_id)
return func(self, *args, **kwargs)
return wrapper
class BaseImageService(object):
def __init__(self, client=None, context=None):
self.client = client
self.context = context
self.endpoint = None
def call(self, method, *args, **kwargs):
"""Call a glance client method.
If we get a connection error,
retry the request according to CONF.num_retries.
:param context: The request context, for access checks.
:param method: The method requested to be called.
:param args: A list of positional arguments for the method called
:param kwargs: A dict of keyword arguments for the method called
:raises: GlanceConnectionFailed
"""
retry_excs = (glance_exc.ServiceUnavailable,
glance_exc.InvalidEndpoint,
glance_exc.CommunicationError)
image_excs = (glance_exc.Forbidden,
glance_exc.Unauthorized,
glance_exc.NotFound,
glance_exc.BadRequest)
num_attempts = 1 + CONF.glance.num_retries
# TODO(pas-ha) use retrying lib here
for attempt in range(1, num_attempts + 1):
try:
return getattr(self.client.images, method)(*args, **kwargs)
except retry_excs as e:
error_msg = ("Error contacting glance endpoint "
"%(endpoint)s for '%(method)s', attempt "
"%(attempt)s of %(num_attempts)s failed.")
LOG.exception(error_msg, {'endpoint': self.endpoint,
'num_attempts': num_attempts,
'attempt': attempt,
'method': method})
if attempt == num_attempts:
raise exception.GlanceConnectionFailed(
endpoint=self.endpoint, reason=e)
time.sleep(1)
except image_excs as e:
exc_type, exc_value, exc_trace = sys.exc_info()
new_exc = _translate_image_exception(
args[0], exc_value)
six.reraise(type(new_exc), new_exc, exc_trace)
@check_image_service
def _show(self, image_href, method='get'):
"""Returns a dict with image data for the given opaque image id.
:param image_href: The opaque image identifier.
:returns: A dict containing image metadata.
:raises: ImageNotFound
:raises: ImageUnacceptable if the image status is not active
"""
LOG.debug("Getting image metadata from glance. Image: %s",
image_href)
image_id = service_utils.parse_image_id(image_href)
image = self.call(method, image_id)
if not service_utils.is_image_active(image):
raise exception.ImageUnacceptable(
image_id=image_id,
reason=_("The image is required to be in an active state."))
if not service_utils.is_image_available(self.context, image):
raise exception.ImageNotFound(image_id=image_id)
base_image_meta = service_utils.translate_from_glance(image)
return base_image_meta
@check_image_service
def _download(self, image_href, data=None, method='data'):
"""Calls out to Glance for data and writes data.
:param image_href: The opaque image identifier.
:param data: (Optional) File object to write data to.
"""
image_id = service_utils.parse_image_id(image_href)
if 'file' in CONF.glance.allowed_direct_url_schemes:
location = self._get_location(image_id)
url = urlparse.urlparse(location)
if url.scheme == "file":
with open(url.path, "r") as f:
filesize = os.path.getsize(f.name)
sendfile.sendfile(data.fileno(), f.fileno(), 0, filesize)
return
image_chunks = self.call(method, image_id)
# NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper
# around real data, so we have to check the wrapped data for None.
if image_chunks.wrapped is None:
raise exception.ImageDownloadFailed(
image_href=image_href, reason=_('image contains no data.'))
if data is None:
return image_chunks
else:
for chunk in image_chunks:
data.write(chunk)

@ -14,15 +14,21 @@
# under the License.
import collections
import os
import re
import sys
import time
from glanceclient import client
from glanceclient import exc as glance_exc
from oslo_log import log
from oslo_utils import uuidutils
import sendfile
import six
from six.moves.urllib import parse as urlparse
from swiftclient import utils as swift_utils
from ironic.common import exception as exc
from ironic.common.glance_service import base_image_service
from ironic.common import exception
from ironic.common.glance_service import service_utils
from ironic.common.i18n import _
from ironic.common import keystone
@ -33,7 +39,62 @@ TempUrlCacheElement = collections.namedtuple('TempUrlCacheElement',
['url', 'url_expires_at'])
class GlanceImageService(base_image_service.BaseImageService):
LOG = log.getLogger(__name__)
_GLANCE_SESSION = None
def _translate_image_exception(image_id, exc_value):
if isinstance(exc_value, (glance_exc.Forbidden,
glance_exc.Unauthorized)):
return exception.ImageNotAuthorized(image_id=image_id)
if isinstance(exc_value, glance_exc.NotFound):
return exception.ImageNotFound(image_id=image_id)
if isinstance(exc_value, glance_exc.BadRequest):
return exception.Invalid(exc_value)
return exc_value
def check_image_service(func):
"""Creates a glance client if doesn't exists and calls the function."""
@six.wraps(func)
def wrapper(self, *args, **kwargs):
"""Wrapper around methods calls.
:param image_href: href that describes the location of an image
"""
if self.client:
return func(self, *args, **kwargs)
global _GLANCE_SESSION
if not _GLANCE_SESSION:
_GLANCE_SESSION = keystone.get_session('glance')
# NOTE(pas-ha) glanceclient uses Adapter-based SessionClient,
# so we can pass session and auth separately, makes things easier
service_auth = keystone.get_auth('glance')
adapter = keystone.get_adapter('glance', session=_GLANCE_SESSION,
auth=service_auth)
self.endpoint = adapter.get_endpoint()
user_auth = None
# NOTE(pas-ha) our ContextHook removes context.auth_token in noauth
# case, so when ironic is in noauth but glance is not, we will not
# enter the next if-block and use auth from [glance] config section
if self.context.auth_token:
user_auth = keystone.get_service_auth(self.context, self.endpoint,
service_auth)
self.client = client.Client(2, session=_GLANCE_SESSION,
auth=user_auth or service_auth,
endpoint_override=self.endpoint,
global_request_id=self.context.global_id)
return func(self, *args, **kwargs)
return wrapper
class GlanceImageService(object):
# A dictionary containing cached temp URLs in namedtuples
# in format:
@ -45,11 +106,112 @@ class GlanceImageService(base_image_service.BaseImageService):
# }
_cache = {}
def show(self, image_id):
return self._show(image_id, method='get')
def __init__(self, client=None, context=None):
self.client = client
self.context = context
self.endpoint = None
def download(self, image_id, data=None):
return self._download(image_id, method='data', data=data)
def call(self, method, *args, **kwargs):
"""Call a glance client method.
If we get a connection error,
retry the request according to CONF.num_retries.
:param context: The request context, for access checks.
:param method: The method requested to be called.
:param args: A list of positional arguments for the method called
:param kwargs: A dict of keyword arguments for the method called
:raises: GlanceConnectionFailed
"""
retry_excs = (glance_exc.ServiceUnavailable,
glance_exc.InvalidEndpoint,
glance_exc.CommunicationError)
image_excs = (glance_exc.Forbidden,
glance_exc.Unauthorized,
glance_exc.NotFound,
glance_exc.BadRequest)
num_attempts = 1 + CONF.glance.num_retries
# TODO(pas-ha) use retrying lib here
for attempt in range(1, num_attempts + 1):
try:
return getattr(self.client.images, method)(*args, **kwargs)
except retry_excs as e:
error_msg = ("Error contacting glance endpoint "
"%(endpoint)s for '%(method)s', attempt "
"%(attempt)s of %(num_attempts)s failed.")
LOG.exception(error_msg, {'endpoint': self.endpoint,
'num_attempts': num_attempts,
'attempt': attempt,
'method': method})
if attempt == num_attempts:
raise exception.GlanceConnectionFailed(
endpoint=self.endpoint, reason=e)
time.sleep(1)
except image_excs:
exc_type, exc_value, exc_trace = sys.exc_info()
new_exc = _translate_image_exception(
args[0], exc_value)
six.reraise(type(new_exc), new_exc, exc_trace)
@check_image_service
def show(self, image_href):
"""Returns a dict with image data for the given opaque image id.
:param image_href: The opaque image identifier.
:returns: A dict containing image metadata.
:raises: ImageNotFound
:raises: ImageUnacceptable if the image status is not active
"""
LOG.debug("Getting image metadata from glance. Image: %s",
image_href)
image_id = service_utils.parse_image_id(image_href)
image = self.call('get', image_id)
if not service_utils.is_image_active(image):
raise exception.ImageUnacceptable(
image_id=image_id,
reason=_("The image is required to be in an active state."))
if not service_utils.is_image_available(self.context, image):
raise exception.ImageNotFound(image_id=image_id)
base_image_meta = service_utils.translate_from_glance(image)
return base_image_meta
@check_image_service
def download(self, image_href, data=None):
"""Calls out to Glance for data and writes data.
:param image_href: The opaque image identifier.
:param data: (Optional) File object to write data to.
"""
image_id = service_utils.parse_image_id(image_href)
if 'file' in CONF.glance.allowed_direct_url_schemes:
location = self._get_location(image_id)
url = urlparse.urlparse(location)
if url.scheme == "file":
with open(url.path, "r") as f:
filesize = os.path.getsize(f.name)
sendfile.sendfile(data.fileno(), f.fileno(), 0, filesize)
return
image_chunks = self.call('data', image_id)
# NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper
# around real data, so we have to check the wrapped data for None.
if image_chunks.wrapped is None:
raise exception.ImageDownloadFailed(
image_href=image_href, reason=_('image contains no data.'))
if data is None:
return image_chunks
else:
for chunk in image_chunks:
data.write(chunk)
def _generate_temp_url(self, path, seconds, key, method, endpoint,
image_id):
@ -119,7 +281,7 @@ class GlanceImageService(base_image_service.BaseImageService):
if ('id' not in image_info or not
uuidutils.is_uuid_like(image_info['id'])):
raise exc.ImageUnacceptable(_(
raise exception.ImageUnacceptable(_(
'The given image info does not have a valid image id: %s')
% image_info)
@ -138,7 +300,7 @@ class GlanceImageService(base_image_service.BaseImageService):
endpoint_url = adapter.get_endpoint()
if not endpoint_url:
raise exc.MissingParameterValue(_(
raise exception.MissingParameterValue(_(
'Swift temporary URLs require a Swift endpoint URL, but it '
'was not found in the service catalog. '
'You must provide "swift_endpoint_url" as a config option.'))
@ -159,7 +321,7 @@ class GlanceImageService(base_image_service.BaseImageService):
key = swift_api.connection.head_account().get(key_header)
if not key:
raise exc.MissingParameterValue(_(
raise exception.MissingParameterValue(_(
'Swift temporary URLs require a shared secret to be '
'created. You must provide "swift_temp_url_key" as a '
'config option or pre-generate the key on the project '
@ -183,7 +345,7 @@ class GlanceImageService(base_image_service.BaseImageService):
"""Validate the required settings for a temporary URL."""
if (CONF.glance.swift_temp_url_duration
< CONF.glance.swift_temp_url_expected_download_start_delay):
raise exc.InvalidParameterValue(_(
raise exception.InvalidParameterValue(_(
'"swift_temp_url_duration" must be greater than or equal to '
'"[glance]swift_temp_url_expected_download_start_delay" '
'option, otherwise the Swift temporary URL may expire before '
@ -191,7 +353,7 @@ class GlanceImageService(base_image_service.BaseImageService):
seed_num_chars = CONF.glance.swift_store_multiple_containers_seed
if (seed_num_chars is None or seed_num_chars < 0
or seed_num_chars > 32):
raise exc.InvalidParameterValue(_(
raise exception.InvalidParameterValue(_(
"An integer value between 0 and 32 is required for"
" swift_store_multiple_containers_seed."))
@ -238,7 +400,7 @@ class GlanceImageService(base_image_service.BaseImageService):
image_meta = self.call('get', image_id)
if not service_utils.is_image_available(self.context, image_meta):
raise exc.ImageNotFound(image_id=image_id)
raise exception.ImageNotFound(image_id=image_id)
return getattr(image_meta, 'direct_url', None)

@ -29,7 +29,7 @@ from six.moves import http_client
import six.moves.urllib.parse as urlparse
from ironic.common import exception
from ironic.common.glance_service.v2 import image_service
from ironic.common.glance_service import image_service
from ironic.common.i18n import _
from ironic.common import utils

@ -27,9 +27,8 @@ import testtools
from ironic.common import context
from ironic.common import exception
from ironic.common.glance_service import base_image_service
from ironic.common.glance_service import image_service
from ironic.common.glance_service import service_utils
from ironic.common.glance_service.v2 import image_service as glance_v2
from ironic.common import image_service as service
from ironic.tests import base
from ironic.tests.unit import stubs
@ -245,9 +244,9 @@ class TestGlanceImageService(base.TestCase):
self.config(allowed_direct_url_schemes=['file'], group='glance')
# patching open in base_image_service module namespace
# patching open in image_service module namespace
# to make call-spec assertions
with mock.patch('ironic.common.glance_service.base_image_service.open',
with mock.patch('ironic.common.glance_service.image_service.open',
new=mock.mock_open(), create=True) as mock_ironic_open:
with open('/whatever/target', 'w') as mock_target_fd:
stub_service.download(image_id, mock_target_fd)
@ -356,7 +355,7 @@ class CheckImageServiceTestCase(base.TestCase):
region_name='SomeRegion',
interface='internal',
group='glance')
base_image_service._GLANCE_SESSION = None
image_service._GLANCE_SESSION = None
def test_check_image_service_client_already_set(self, mock_gclient,
mock_sess, mock_adapter,
@ -366,7 +365,7 @@ class CheckImageServiceTestCase(base.TestCase):
self.service.client = True
wrapped_func = base_image_service.check_image_service(func)
wrapped_func = image_service.check_image_service(func)
self.assertTrue(wrapped_func(self.service))
self.assertEqual(0, mock_gclient.call_count)
self.assertEqual(0, mock_sess.call_count)
@ -393,7 +392,7 @@ class CheckImageServiceTestCase(base.TestCase):
uuid = uuidutils.generate_uuid()
params = {'image_href': uuid}
wrapped_func = base_image_service.check_image_service(func)
wrapped_func = image_service.check_image_service(func)
self.assertEqual(((), params), wrapped_func(self.service, **params))
self._assert_client_call(mock_gclient, 'glance_url')
mock_auth.assert_called_once_with('glance')
@ -417,7 +416,7 @@ class CheckImageServiceTestCase(base.TestCase):
uuid = uuidutils.generate_uuid()
params = {'image_href': uuid}
wrapped_func = base_image_service.check_image_service(func)
wrapped_func = image_service.check_image_service(func)
self.assertEqual(((), params), wrapped_func(self.service, **params))
self._assert_client_call(mock_gclient, 'glance_url', user=True)
mock_sess.assert_called_once_with('glance')
@ -441,9 +440,9 @@ class CheckImageServiceTestCase(base.TestCase):
uuid = uuidutils.generate_uuid()
params = {'image_href': uuid}
wrapped_func = base_image_service.check_image_service(func)
wrapped_func = image_service.check_image_service(func)
self.assertEqual(((), params), wrapped_func(self.service, **params))
self.assertEqual('none', base_image_service.CONF.glance.auth_type)
self.assertEqual('none', image_service.CONF.glance.auth_type)
self._assert_client_call(mock_gclient, 'foo')
mock_sess.assert_called_once_with('glance')
mock_adapter.assert_called_once_with('glance',
@ -785,8 +784,8 @@ class TestSwiftTempUrlCache(base.TestCase):
{'uuid': fake_image['id'], 'exp_time': exp_time}
)
self.glance_service._cache[fake_image['id']] = (
glance_v2.TempUrlCacheElement(url=temp_url,
url_expires_at=exp_time)
image_service.TempUrlCacheElement(url=temp_url,
url_expires_at=exp_time)
)
cleanup_mock = mock.Mock()
@ -813,7 +812,7 @@ class TestSwiftTempUrlCache(base.TestCase):
)
query = '?temp_url_sig=hmacsig&temp_url_expires=%s'
self.glance_service._cache[fake_image['id']] = (
glance_v2.TempUrlCacheElement(
image_service.TempUrlCacheElement(
url=(CONF.glance.swift_endpoint_url + path
+ query % old_exp_time),
url_expires_at=old_exp_time)
@ -842,21 +841,21 @@ class TestSwiftTempUrlCache(base.TestCase):
def test_remove_expired_items_from_cache(self):
expired_items = {
uuidutils.generate_uuid(): glance_v2.TempUrlCacheElement(
uuidutils.generate_uuid(): image_service.TempUrlCacheElement(
'fake-url-1',
int(time.time()) - 10
),
uuidutils.generate_uuid(): glance_v2.TempUrlCacheElement(
uuidutils.generate_uuid(): image_service.TempUrlCacheElement(
'fake-url-2',
int(time.time()) + 90 # Agent won't be able to start in time
)
}
valid_items = {
uuidutils.generate_uuid(): glance_v2.TempUrlCacheElement(
uuidutils.generate_uuid(): image_service.TempUrlCacheElement(
'fake-url-3',
int(time.time()) + 1000
),
uuidutils.generate_uuid(): glance_v2.TempUrlCacheElement(
uuidutils.generate_uuid(): image_service.TempUrlCacheElement(
'fake-url-4',
int(time.time()) + 2000
)

@ -23,7 +23,7 @@ import six.moves.builtins as __builtin__
from six.moves import http_client
from ironic.common import exception
from ironic.common.glance_service.v2 import image_service as glance_v2_service
from ironic.common.glance_service import image_service as glance_v2_service
from ironic.common import image_service
from ironic.tests import base

@ -25,7 +25,7 @@ from oslo_utils import uuidutils
import six
from ironic.common import exception
from ironic.common.glance_service import base_image_service
from ironic.common.glance_service import image_service
from ironic.common import pxe_utils
from ironic.common import states
from ironic.common import utils
@ -1126,8 +1126,7 @@ class PXEInterfacesTestCase(db_base.DbTestCase):
self.assertRaises(exception.MissingParameterValue,
pxe_utils.get_image_info, self.node)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def _test_get_instance_image_info(self, show_mock):
properties = {'properties': {u'kernel_id': u'instance_kernel_uuid',
u'ramdisk_id': u'instance_ramdisk_uuid'}}
@ -1147,8 +1146,7 @@ class PXEInterfacesTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
image_info = pxe_utils.get_instance_image_info(task)
show_mock.assert_called_once_with(mock.ANY, 'glance://image_uuid',
method='get')
show_mock.assert_called_once_with(mock.ANY, 'glance://image_uuid')
self.assertEqual(expected_info, image_info)
# test with saved info
@ -1183,8 +1181,7 @@ class PXEInterfacesTestCase(db_base.DbTestCase):
self.assertEqual({}, image_info)
boot_opt_mock.assert_called_once_with(task.node)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_get_instance_image_info_whole_disk_image(self, show_mock):
properties = {'properties': None}
show_mock.return_value = properties

@ -26,7 +26,7 @@ from ironic.common import boot_devices
from ironic.common import boot_modes
from ironic.common import dhcp_factory
from ironic.common import exception
from ironic.common.glance_service import base_image_service
from ironic.common.glance_service import image_service
from ironic.common import pxe_utils
from ironic.common import states
from ironic.common import utils as common_utils
@ -91,8 +91,7 @@ class iPXEBootTestCase(db_base.DbTestCase):
shared=True) as task:
self.assertEqual(expected, task.driver.get_properties())
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_good(self, mock_glance):
mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
'ramdisk_id': 'fake-initr'}}
@ -100,16 +99,14 @@ class iPXEBootTestCase(db_base.DbTestCase):
shared=True) as task:
task.driver.boot.validate(task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_good_whole_disk_image(self, mock_glance):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_internal_info['is_whole_disk_image'] = True
task.driver.boot.validate(task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
@mock.patch.object(noop_storage.NoopStorage, 'should_write_image',
autospec=True)
def test_validate_skip_check_write_image_false(self, mock_write,
@ -178,7 +175,7 @@ class iPXEBootTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
task.driver.boot.validate, task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
@mock.patch.object(image_service.GlanceImageService, 'show',
autospec=True)
def test_validate_fail_no_image_kernel_ramdisk_props(self, mock_glance):
mock_glance.return_value = {'properties': {}}
@ -188,7 +185,7 @@ class iPXEBootTestCase(db_base.DbTestCase):
task.driver.boot.validate,
task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
@mock.patch.object(image_service.GlanceImageService, 'show',
autospec=True)
def test_validate_fail_glance_image_doesnt_exists(self, mock_glance):
mock_glance.side_effect = exception.ImageNotFound('not found')
@ -197,7 +194,7 @@ class iPXEBootTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
task.driver.boot.validate, task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
@mock.patch.object(image_service.GlanceImageService, 'show',
autospec=True)
def test_validate_fail_glance_conn_problem(self, mock_glance):
exceptions = (exception.GlanceConnectionFailed('connection fail'),

@ -27,7 +27,7 @@ from ironic.common import boot_devices
from ironic.common import boot_modes
from ironic.common import dhcp_factory
from ironic.common import exception
from ironic.common.glance_service import base_image_service
from ironic.common.glance_service import image_service
from ironic.common import pxe_utils
from ironic.common import states
from ironic.common import utils as common_utils
@ -93,8 +93,7 @@ class PXEBootTestCase(db_base.DbTestCase):
shared=True) as task:
self.assertEqual(expected, task.driver.get_properties())
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_good(self, mock_glance):
mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
'ramdisk_id': 'fake-initr'}}
@ -102,16 +101,14 @@ class PXEBootTestCase(db_base.DbTestCase):
shared=True) as task:
task.driver.boot.validate(task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_good_whole_disk_image(self, mock_glance):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_internal_info['is_whole_disk_image'] = True
task.driver.boot.validate(task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
@mock.patch.object(noop_storage.NoopStorage, 'should_write_image',
autospec=True)
def test_validate_skip_check_write_image_false(self, mock_write,
@ -180,8 +177,7 @@ class PXEBootTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
task.driver.boot.validate, task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_fail_no_image_kernel_ramdisk_props(self, mock_glance):
mock_glance.return_value = {'properties': {}}
with task_manager.acquire(self.context, self.node.uuid,
@ -190,8 +186,7 @@ class PXEBootTestCase(db_base.DbTestCase):
task.driver.boot.validate,
task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_fail_glance_image_doesnt_exists(self, mock_glance):
mock_glance.side_effect = exception.ImageNotFound('not found')
with task_manager.acquire(self.context, self.node.uuid,
@ -199,8 +194,7 @@ class PXEBootTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
task.driver.boot.validate, task)
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
def test_validate_fail_glance_conn_problem(self, mock_glance):
exceptions = (exception.GlanceConnectionFailed('connection fail'),
exception.ImageNotAuthorized('not authorized'),