Align internal image API with volume and network

First part of standardizing the nova internal image API with the API
objects for volume and network. This patch creates a nova.image.api.API
object that matches the signature of nova.network.api.API and
nova.volume.cinder.API for CRUD operations on image records. The
existing unit tests were enough to fully cover the new code paths, since
much of this patch simply aligned the internal image API calls and API
object with that of the other submodules.

The next in this patch series will work on cleaning up the remainder of
places in nova that directly call on nova.image.glance methods, as well
as the untouched download/upload handling.

Change-Id: I67e7c9d841e5bad82cdca8f7048eaa57f0cfbb2f
Partially-implements: blueprint standardize-nova-image
This commit is contained in:
Jay Pipes
2014-05-05 23:42:55 -04:00
parent 209c0a29ec
commit 5122269147
13 changed files with 256 additions and 177 deletions
@@ -903,13 +903,11 @@ class ServersController(wsgi.Controller):
img = instance['image_ref']
if not img:
props = bdms.root_metadata(
context, self.compute_api.image_service,
context, self.compute_api.image_api,
self.compute_api.volume_api)
image_meta = {'properties': props}
else:
src_image = self.compute_api.\
image_service.show(context, img)
image_meta = dict(src_image)
image_meta = self.compute_api.image_api.get(context, img)
image = self.compute_api.snapshot_volume_backed(
context,
+2 -4
View File
@@ -1439,13 +1439,11 @@ class Controller(wsgi.Controller):
img = instance['image_ref']
if not img:
props = bdms.root_metadata(
context, self.compute_api.image_service,
context, self.compute_api.image_api,
self.compute_api.volume_api)
image_meta = {'properties': props}
else:
src_image = self.compute_api.image_service.\
show(context, img)
image_meta = dict(src_image)
image_meta = self.compute_api.image_api.get(context, img)
image = self.compute_api.snapshot_volume_backed(
context,
+23 -22
View File
@@ -43,7 +43,7 @@ from nova import crypto
from nova.db import base
from nova import exception
from nova import hooks
from nova.image import glance
from nova import image
from nova import network
from nova.network import model as network_model
from nova.network.security_group import openstack_driver
@@ -240,11 +240,9 @@ def _diff_dict(orig, new):
class API(base.Base):
"""API for interacting with the compute manager."""
def __init__(self, image_service=None, network_api=None, volume_api=None,
def __init__(self, image_api=None, network_api=None, volume_api=None,
security_group_api=None, **kwargs):
self.image_service = (image_service or
glance.get_default_image_service())
self.image_api = image_api or image.API()
self.network_api = network_api or network.API()
self.volume_api = volume_api or volume.API()
self.security_group_api = (security_group_api or
@@ -462,8 +460,8 @@ class API(base.Base):
return self.network_api.validate_networks(context, requested_networks,
max_count)
@staticmethod
def _handle_kernel_and_ramdisk(context, kernel_id, ramdisk_id, image):
def _handle_kernel_and_ramdisk(self, context, kernel_id, ramdisk_id,
image):
"""Choose kernel and ramdisk appropriate for the instance.
The kernel and ramdisk can be chosen in one of three ways:
@@ -490,14 +488,19 @@ class API(base.Base):
# Verify kernel and ramdisk exist (fail-fast)
if kernel_id is not None:
image_service, kernel_id = glance.get_remote_image_service(
context, kernel_id)
image_service.show(context, kernel_id)
kernel_image = self.image_api.get(context, kernel_id)
# kernel_id could have been a URI, not a UUID, so to keep behaviour
# from before, which leaked that implementation detail out to the
# caller, we return the image UUID of the kernel image and ramdisk
# image (below) and not any image URIs that might have been
# supplied.
# TODO(jaypipes): Get rid of this silliness once we move to a real
# Image object and hide all of that stuff within nova.image.api.
kernel_id = kernel_image['id']
if ramdisk_id is not None:
image_service, ramdisk_id = glance.get_remote_image_service(
context, ramdisk_id)
image_service.show(context, ramdisk_id)
ramdisk_image = self.image_api.get(context, ramdisk_id)
ramdisk_id = ramdisk_image['id']
return kernel_id, ramdisk_id
@@ -682,10 +685,8 @@ class API(base.Base):
if not image_href:
return None, {}
(image_service, image_id) = glance.get_remote_image_service(
context, image_href)
image = image_service.show(context, image_id)
return image_id, image
image = self.image_api.get(context, image_href)
return image['id'], image
def _checks_for_create_and_rebuild(self, context, image_id, image,
instance_type, metadata,
@@ -869,7 +870,7 @@ class API(base.Base):
if bdm.get('image_id'):
try:
image_id = bdm['image_id']
image_meta = self.image_service.show(context, image_id)
image_meta = self.image_api.get(context, image_id)
return image_meta.get('properties', {})
except Exception:
raise exception.InvalidBDMImage(id=image_id)
@@ -1410,7 +1411,7 @@ class API(base.Base):
"from shelved instance..."),
snapshot_id, instance=instance)
try:
self.image_service.delete(context, snapshot_id)
self.image_api.delete(context, snapshot_id)
except (exception.ImageNotFound,
exception.ImageNotAuthorized) as exc:
LOG.warning(_("Failed to delete snapshot "
@@ -1990,7 +1991,7 @@ class API(base.Base):
}
image_ref = instance.image_ref
sent_meta = compute_utils.get_image_metadata(
context, self.image_service, image_ref, instance)
context, self.image_api, image_ref, instance)
sent_meta['name'] = name
sent_meta['is_public'] = False
@@ -1999,7 +2000,7 @@ class API(base.Base):
properties.update(extra_properties or {})
sent_meta['properties'].update(properties)
return self.image_service.create(context, sent_meta)
return self.image_api.create(context, sent_meta)
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def snapshot_volume_backed(self, context, instance, image_meta, name,
@@ -2064,7 +2065,7 @@ class API(base.Base):
# hence the zero size
image_meta['size'] = 0
return self.image_service.create(context, image_meta, data='')
return self.image_api.create(context, image_meta)
@wrap_check_policy
@check_instance_lock
+11 -21
View File
@@ -56,6 +56,7 @@ from nova import consoleauth
import nova.context
from nova import exception
from nova import hooks
from nova import image
from nova.image import glance
from nova import manager
from nova import network
@@ -354,8 +355,7 @@ def delete_image_on_error(function):
LOG.debug("Cleaning up image %s" % image_id,
exc_info=True, instance=instance)
try:
image_service = glance.get_default_image_service()
image_service.delete(context, image_id)
self.image_api.delete(context, image_id)
except Exception:
LOG.exception(_("Error while trying to clean up image %s")
% image_id, instance=instance)
@@ -419,12 +419,6 @@ def aggregate_object_compat(function):
return decorated_function
def _get_image_meta(context, image_ref):
image_service, image_id = glance.get_remote_image_service(context,
image_ref)
return image_service.show(context, image_id)
class InstanceEvents(object):
def __init__(self):
self._events = {}
@@ -575,6 +569,7 @@ class ComputeManager(manager.Manager):
self.virtapi = ComputeVirtAPI(self)
self.network_api = network.API()
self.volume_api = volume.API()
self.image_api = image.API()
self._last_host_check = 0
self._last_bw_usage_poll = 0
self._bw_usage_supported = True
@@ -2496,13 +2491,14 @@ class ComputeManager(manager.Manager):
instance.save()
if image_ref:
image_meta = _get_image_meta(context, image_ref)
image_meta = self.image_api.get(context, image_ref)
else:
image_meta = {}
# This instance.exists message should contain the original
# image_ref, not the new one. Since the DB has been updated
# to point to the new one... we have to override it.
#TODO(jaypipes): Move generate_image_url() into the nova.image.api
orig_image_ref_url = glance.generate_image_url(orig_image_ref)
extra_usage_info = {'image_ref_url': orig_image_ref_url}
self.conductor_api.notify_usage_exists(context,
@@ -2831,13 +2827,12 @@ class ComputeManager(manager.Manager):
:param rotation: int representing how many backups to keep around;
None if rotation shouldn't be used (as in the case of snapshots)
"""
image_service = glance.get_default_image_service()
filters = {'property-image_type': 'backup',
'property-backup_type': backup_type,
'property-instance_uuid': instance.uuid}
images = image_service.detail(context, filters=filters,
sort_key='created_at', sort_dir='desc')
images = self.image_api.get_all(context, filters=filters,
sort_key='created_at', sort_dir='desc')
num_images = len(images)
LOG.debug("Found %(num_images)d images (rotation: %(rotation)d)",
{'num_images': num_images, 'rotation': rotation},
@@ -2854,7 +2849,7 @@ class ComputeManager(manager.Manager):
image_id = image['id']
LOG.debug("Deleting image %s", image_id,
instance=instance)
image_service.delete(context, image_id)
self.image_api.delete(context, image_id)
@object_compat
@wrap_exception()
@@ -2954,9 +2949,7 @@ class ComputeManager(manager.Manager):
' using instance\'s current image'))
rescue_image_ref = instance['image_ref']
image_service, image_id = glance.get_remote_image_service(
context, rescue_image_ref)
image_meta = compute_utils.get_image_metadata(context, image_service,
image_meta = compute_utils.get_image_metadata(context, self.image_api,
rescue_image_ref,
instance)
# NOTE(belliott) bug #1227350 - xenapi needs the actual image id
@@ -3934,8 +3927,7 @@ class ComputeManager(manager.Manager):
if image:
instance.image_ref = shelved_image_ref
image_service = glance.get_default_image_service()
image_service.delete(context, image['id'])
self.image_api.delete(context, image['id'])
self._unshelve_instance_key_restore(instance, scrubbed_keys)
instance.power_state = self._get_power_state(context, instance)
@@ -4393,10 +4385,8 @@ class ComputeManager(manager.Manager):
% dict(ports=len(network_info)))
raise exception.InterfaceAttachFailed(instance=instance)
image_ref = instance.get('image_ref')
image_service, image_id = glance.get_remote_image_service(
context, image_ref)
image_meta = compute_utils.get_image_metadata(
context, image_service, image_ref, instance)
context, self.image_api, image_ref, instance)
self.driver.attach_interface(instance, image_meta, network_info[0])
return network_info[0]
+4 -3
View File
@@ -199,13 +199,14 @@ def _get_unused_letter(used_letters):
return letters[0]
def get_image_metadata(context, image_service, image_id, instance):
def get_image_metadata(context, image_api, image_id_or_uri, instance):
# If the base image is still available, get its metadata
try:
image = image_service.show(context, image_id)
image = image_api.get(context, image_id_or_uri)
except Exception as e:
LOG.warning(_("Can't access image %(image_id)s: %(error)s"),
{"image_id": image_id, "error": e}, instance=instance)
{"image_id": image_id_or_uri, "error": e},
instance=instance)
image_system_meta = {}
else:
flavor = flavors.extract_flavor(instance)
+17
View File
@@ -0,0 +1,17 @@
# 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.
def API():
# Needed to prevent circular import...
import nova.image.api
return nova.image.api.API()
+130
View File
@@ -0,0 +1,130 @@
# 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.
"""
Main abstraction layer for retrieving and storing information about disk
images used by the compute layer.
"""
from nova.image import glance
class API(object):
"""Responsible for exposing a relatively stable internal API for other
modules in Nova to retrieve information about disk images. This API
attempts to match the nova.volume.api and nova.network.api calling
interface.
"""
def _get_session_and_image_id(self, context, id_or_uri):
"""Returns a tuple of (session, image_id). If the supplied `id_or_uri`
is an image ID, then the default client session will be returned
for the context's user, along with the image ID. If the supplied
`id_or_uri` parameter is a URI, then a client session connecting to
the URI's image service endpoint will be returned along with a
parsed image ID from that URI.
:param context: The `nova.context.Context` object for the request
:param id_or_uri: A UUID identifier or an image URI to look up image
information for.
"""
return glance.get_remote_image_service(context, id_or_uri)
def _get_session(self, _context):
"""Returns a client session that can be used to query for image
information.
:param context: The `nova.context.Context` object for the request
"""
#TODO(jaypipes): Refactor glance.get_remote_image_service and
# glance.get_default_image_service into a single
# method that takes a context and actually respects
# it, returning a real session object that keeps
# the context alive...
return glance.get_default_image_service()
def get_all(self, context, **kwargs):
"""Retrieves all information records about all disk images available
to show to the requesting user. If the requesting user is an admin,
all images in an ACTIVE status are returned. If the requesting user
is not an admin, the all public images and all private images that
are owned by the requesting user in the ACTIVE status are returned.
:param context: The `nova.context.Context` object for the request
:param **kwargs: A dictionary of filter and pagination values that
may be passed to the underlying image info driver.
"""
session = self._get_session(context)
return session.detail(context, **kwargs)
def get(self, context, id_or_uri):
"""Retrieves the information record for a single disk image. If the
supplied identifier parameter is a UUID, the default driver will
be used to return information about the image. If the supplied
identifier is a URI, then the driver that matches that URI endpoint
will be used to query for image information.
:param context: The `nova.context.Context` object for the request
:param id_or_uri: A UUID identifier or an image URI to look up image
information for.
"""
session, image_id = self._get_session_and_image_id(context, id_or_uri)
return session.show(context, image_id)
def create(self, context, image_info, data=None):
"""Creates a new image record, optionally passing the image bits to
backend storage.
:param context: The `nova.context.Context` object for the request
:param image_info: A dict of information about the image that is
passed to the image registry.
:param data: Optional file handle or bytestream iterator that is
passed to backend storage.
"""
session = self._get_session(context)
return session.create(context, image_info, data=data)
def update(self, context, id_or_uri, image_info,
data=None, purge_props=False):
"""Update the information about an image, optionally along with a file
handle or bytestream iterator for image bits. If the optional file
handle for updated image bits is supplied, the image may not have
already uploaded bits for the image.
:param context: The `nova.context.Context` object for the request
:param id_or_uri: A UUID identifier or an image URI to look up image
information for.
:param image_info: A dict of information about the image that is
passed to the image registry.
:param data: Optional file handle or bytestream iterator that is
passed to backend storage.
:param purge_props: Optional, defaults to True. If set, the backend
image registry will clear all image properties
and replace them the image properties supplied
in the image_info dictionary's 'properties'
collection.
"""
session, image_id = self._get_session_and_image_id(context, id_or_uri)
return session.update(context, image_id, image_info, data=data,
purge_props=purge_props)
def delete(self, context, id_or_uri):
"""Delete the information about an image and mark the image bits for
deletion.
:param context: The `nova.context.Context` object for the request
:param id_or_uri: A UUID identifier or an image URI to look up image
information for.
"""
session, image_id = self._get_session_and_image_id(context, id_or_uri)
return session.delete(context, image_id)
+6 -17
View File
@@ -547,7 +547,7 @@ class ComputeVolumeTestCase(BaseTestCase):
self.test_boot_volume_metadata(metadata=False)
def test_boot_image_metadata(self, metadata=True):
def image_api_show(*args, **kwargs):
def image_api_get(*args, **kwargs):
if metadata:
return {
'properties': {'img_test_key': 'img_test_value'}
@@ -555,7 +555,7 @@ class ComputeVolumeTestCase(BaseTestCase):
else:
return {}
self.stubs.Set(self.compute_api.image_service, 'show', image_api_show)
self.stubs.Set(self.compute_api.image_api, 'get', image_api_get)
block_device_mapping = [{
'boot_index': 0,
@@ -2007,10 +2007,9 @@ class ComputeTestCase(BaseTestCase):
self.assertEqual('some_random_state', inst_obj['vm_state'])
@mock.patch.object(nova.compute.utils, "get_image_metadata")
@mock.patch.object(nova.image.glance, "get_remote_image_service")
@mock.patch.object(nova.virt.fake.FakeDriver, "rescue")
def test_rescue_with_image_specified(self, mock_rescue,
mock_get_remote_image_service, mock_get_image_metadata):
mock_get_image_metadata):
image_ref = "image-ref"
rescue_image_meta = {}
@@ -2023,16 +2022,12 @@ class ComputeTestCase(BaseTestCase):
mock_context.elevated.return_value = ctxt
mock_get_image_metadata.return_value = rescue_image_meta
mock_image_service = "image_service"
mock_get_remote_image_service.return_value = (mock_image_service, "id")
self.compute.rescue_instance(mock_context, instance=instance,
rescue_password="password", rescue_image_ref=image_ref)
mock_get_remote_image_service.assert_called_with(ctxt,
image_ref)
mock_get_image_metadata.assert_called_with(ctxt,
mock_image_service,
self.compute.image_api,
image_ref, instance)
mock_rescue.assert_called_with(ctxt, instance, [],
rescue_image_meta, 'password')
@@ -2040,11 +2035,9 @@ class ComputeTestCase(BaseTestCase):
self._objectify(instance), [], [])
@mock.patch.object(nova.compute.utils, "get_image_metadata")
@mock.patch.object(nova.image.glance, "get_remote_image_service")
@mock.patch.object(nova.virt.fake.FakeDriver, "rescue")
def test_rescue_with_base_image_when_image_not_specified(self,
mock_rescue, mock_get_remote_image_service,
mock_get_image_metadata):
mock_rescue, mock_get_image_metadata):
image_ref = "image-ref"
system_meta = {"image_base_image_ref": image_ref}
@@ -2059,16 +2052,12 @@ class ComputeTestCase(BaseTestCase):
mock_context.elevated.return_value = ctxt
mock_get_image_metadata.return_value = rescue_image_meta
mock_image_service = "image_service"
mock_get_remote_image_service.return_value = (mock_image_service, "id")
self.compute.rescue_instance(mock_context, instance=instance,
rescue_password="password")
mock_get_remote_image_service.assert_called_with(ctxt,
image_ref)
mock_get_image_metadata.assert_called_with(ctxt,
mock_image_service,
self.compute.image_api,
image_ref, instance)
mock_rescue.assert_called_with(ctxt, instance, [],
rescue_image_meta, 'password')
+11 -11
View File
@@ -436,20 +436,20 @@ class _ComputeAPIUnitTestMixIn(object):
migration['source_compute'], fake_quotas.reservations, cast=False)
def _test_delete_shelved_part(self, inst):
image_service = self.compute_api.image_service
self.mox.StubOutWithMock(image_service, 'delete')
image_api = self.compute_api.image_api
self.mox.StubOutWithMock(image_api, 'delete')
snapshot_id = inst.system_metadata.get('shelved_image_id')
if snapshot_id == SHELVED_IMAGE:
image_service.delete(self.context, snapshot_id).AndReturn(True)
image_api.delete(self.context, snapshot_id).AndReturn(True)
elif snapshot_id == SHELVED_IMAGE_NOT_FOUND:
image_service.delete(self.context, snapshot_id).AndRaise(
image_api.delete(self.context, snapshot_id).AndRaise(
exception.ImageNotFound(image_id=snapshot_id))
elif snapshot_id == SHELVED_IMAGE_NOT_AUTHORIZED:
image_service.delete(self.context, snapshot_id).AndRaise(
image_api.delete(self.context, snapshot_id).AndRaise(
exception.ImageNotAuthorized(image_id=snapshot_id))
elif snapshot_id == SHELVED_IMAGE_EXCEPTION:
image_service.delete(self.context, snapshot_id).AndRaise(
image_api.delete(self.context, snapshot_id).AndRaise(
test.TestingException("Unexpected error"))
def _test_downed_host_part(self, inst, updates, delete_time, delete_type):
@@ -1363,7 +1363,7 @@ class _ComputeAPIUnitTestMixIn(object):
extra_props = dict(cow='moo', cat='meow')
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(self.compute_api.image_service,
self.mox.StubOutWithMock(self.compute_api.image_api,
'create')
self.mox.StubOutWithMock(instance, 'save')
self.mox.StubOutWithMock(self.compute_api.compute_rpcapi,
@@ -1399,11 +1399,11 @@ class _ComputeAPIUnitTestMixIn(object):
expected_props['backup_type'] = 'fake-backup-type'
compute_utils.get_image_metadata(
self.context, self.compute_api.image_service,
self.context, self.compute_api.image_api,
FAKE_IMAGE_REF, instance).AndReturn(expected_meta)
fake_image = dict(id='fake-image-id')
mock_method = self.compute_api.image_service.create(
mock_method = self.compute_api.image_api.create(
self.context, expected_meta)
if create_fails:
mock_method.AndRaise(test.TestingException())
@@ -1534,7 +1534,7 @@ class _ComputeAPIUnitTestMixIn(object):
def fake_get_all_by_instance(context, instance, use_slave=False):
return copy.deepcopy(instance_bdms)
def fake_image_create(context, image_meta, data):
def fake_image_create(context, image_meta, data=None):
self.assertThat(image_meta, matchers.DictMatches(expect_meta))
def fake_volume_get(context, volume_id):
@@ -1545,7 +1545,7 @@ class _ComputeAPIUnitTestMixIn(object):
self.stubs.Set(db, 'block_device_mapping_get_all_by_instance',
fake_get_all_by_instance)
self.stubs.Set(self.compute_api.image_service, 'create',
self.stubs.Set(self.compute_api.image_api, 'create',
fake_image_create)
self.stubs.Set(self.compute_api.volume_api, 'get',
fake_volume_get)
+10 -17
View File
@@ -627,8 +627,8 @@ class ComputeGetImageMetadataTestCase(test.TestCase):
"properties": {},
}
self.image_service = nova.tests.image.fake._FakeImageService()
self.stubs.Set(self.image_service, 'show', self._fake_show)
self.mock_image_api = mock.Mock()
self.mock_image_api.get.return_value = self.image
self.ctx = context.RequestContext('fake', 'fake')
@@ -659,24 +659,19 @@ class ComputeGetImageMetadataTestCase(test.TestCase):
self.ctx, instance_obj.Instance(), self.instance,
expected_attrs=instance_obj.INSTANCE_DEFAULT_FIELDS)
def _fake_show(self, ctx, image_id):
return self.image
def test_get_image_meta(self):
image_meta = compute_utils.get_image_metadata(
self.ctx, self.image_service, 'fake-image', self.instance_obj)
self.ctx, self.mock_image_api, 'fake-image', self.instance_obj)
self.image['properties'] = 'DONTCARE'
self.assertThat(self.image, matchers.DictMatches(image_meta))
def test_get_image_meta_no_image(self):
def fake_show(ctx, image_id):
raise exception.ImageNotFound(image_id='fake-image')
self.stubs.Set(self.image_service, 'show', fake_show)
e = exception.ImageNotFound(image_id='fake-image')
self.mock_image_api.get.side_effect = e
image_meta = compute_utils.get_image_metadata(
self.ctx, self.image_service, 'fake-image', self.instance_obj)
self.ctx, self.mock_image_api, 'fake-image', self.instance_obj)
self.image['properties'] = 'DONTCARE'
# NOTE(danms): The trip through system_metadata will stringify things
@@ -690,23 +685,21 @@ class ComputeGetImageMetadataTestCase(test.TestCase):
del self.instance['system_metadata'][k]
image_meta = compute_utils.get_image_metadata(
self.ctx, self.image_service, 'fake-image', self.instance_obj)
self.ctx, self.mock_image_api, 'fake-image', self.instance_obj)
self.image['properties'] = 'DONTCARE'
self.assertThat(self.image, matchers.DictMatches(image_meta))
def test_get_image_meta_no_image_no_image_system_meta(self):
def fake_show(ctx, image_id):
raise exception.ImageNotFound(image_id='fake-image')
self.stubs.Set(self.image_service, 'show', fake_show)
e = exception.ImageNotFound(image_id='fake-image')
self.mock_image_api.get.side_effect = e
for k in self.instance['system_metadata'].keys():
if k.startswith('image_'):
del self.instance['system_metadata'][k]
image_meta = compute_utils.get_image_metadata(
self.ctx, self.image_service, 'fake-image', self.instance_obj)
self.ctx, self.mock_image_api, 'fake-image', self.instance_obj)
expected = {'properties': 'DONTCARE'}
self.assertThat(expected, matchers.DictMatches(image_meta))
+1 -1
View File
@@ -374,7 +374,7 @@ class ShelveComputeAPITestCase(test_compute.BaseTestCase):
# sets a fake method.
self2.images = {}
def fake_create(self2, ctxt, metadata):
def fake_create(self2, ctxt, metadata, data=None):
self.assertEqual(metadata['name'], 'vm01-shelved')
metadata['id'] = '8b24ed3f-ee57-43bc-bc2e-fb2e9482bc42'
return metadata
+26 -59
View File
@@ -461,7 +461,8 @@ class LibvirtConnTestCase(test.TestCase):
flavor = db.flavor_get(self.context, 5)
sys_meta = flavors.save_flavor_info({}, flavor)
nova.tests.image.fake.stub_out_image_service(self.stubs)
self.image_service = nova.tests.image.fake.stub_out_image_service(
self.stubs)
self.test_instance = {
'uuid': '32dfcb37-5af1-552b-357c-be8c3aa38310',
'memory_kb': '1024000',
@@ -2666,9 +2667,6 @@ class LibvirtConnTestCase(test.TestCase):
self.flags(snapshots_directory='./', group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assign different image_ref from nova/images/fakes for testing ami
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
@@ -2682,7 +2680,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -2696,7 +2694,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -2718,9 +2716,6 @@ class LibvirtConnTestCase(test.TestCase):
virt_type='lxc',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assign different image_ref from nova/images/fakes for testing ami
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
@@ -2734,7 +2729,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -2748,7 +2743,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -2768,9 +2763,6 @@ class LibvirtConnTestCase(test.TestCase):
self.flags(snapshots_directory='./', group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assuming that base image already exists in image_service
instance_ref = db.instance_create(self.context, self.test_instance)
properties = {'instance_id': instance_ref['id'],
@@ -2780,7 +2772,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -2799,7 +2791,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -2821,9 +2813,6 @@ class LibvirtConnTestCase(test.TestCase):
virt_type='lxc',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assuming that base image already exists in image_service
instance_ref = db.instance_create(self.context, self.test_instance)
properties = {'instance_id': instance_ref['id'],
@@ -2833,7 +2822,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -2852,7 +2841,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -2874,9 +2863,6 @@ class LibvirtConnTestCase(test.TestCase):
snapshots_directory='./',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assuming that base image already exists in image_service
instance_ref = db.instance_create(self.context, self.test_instance)
properties = {'instance_id': instance_ref['id'],
@@ -2886,7 +2872,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -2900,7 +2886,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -2923,9 +2909,6 @@ class LibvirtConnTestCase(test.TestCase):
virt_type='lxc',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assuming that base image already exists in image_service
instance_ref = db.instance_create(self.context, self.test_instance)
properties = {'instance_id': instance_ref['id'],
@@ -2935,7 +2918,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -2949,7 +2932,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -2970,9 +2953,6 @@ class LibvirtConnTestCase(test.TestCase):
self.flags(snapshots_directory='./',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assign different image_ref from nova/images/fakes for
# testing different base image
test_instance = copy.deepcopy(self.test_instance)
@@ -2987,7 +2967,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -3000,7 +2980,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -3021,9 +3001,6 @@ class LibvirtConnTestCase(test.TestCase):
virt_type='lxc',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assign different image_ref from nova/images/fakes for
# testing different base image
test_instance = copy.deepcopy(self.test_instance)
@@ -3038,7 +3015,7 @@ class LibvirtConnTestCase(test.TestCase):
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -3051,7 +3028,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -3071,9 +3048,6 @@ class LibvirtConnTestCase(test.TestCase):
self.flags(snapshots_directory='./',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assign a non-existent image
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = '661122aa-1234-dede-fefe-babababababa'
@@ -3084,7 +3058,7 @@ class LibvirtConnTestCase(test.TestCase):
snapshot_name = 'test-snap'
sent_meta = {'name': snapshot_name, 'is_public': False,
'status': 'creating', 'properties': properties}
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -3097,7 +3071,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -3118,9 +3092,6 @@ class LibvirtConnTestCase(test.TestCase):
virt_type='lxc',
group='libvirt')
# Start test
image_service = nova.tests.image.fake.FakeImageService()
# Assign a non-existent image
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = '661122aa-1234-dede-fefe-babababababa'
@@ -3131,7 +3102,7 @@ class LibvirtConnTestCase(test.TestCase):
snapshot_name = 'test-snap'
sent_meta = {'name': snapshot_name, 'is_public': False,
'status': 'creating', 'properties': properties}
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -3144,7 +3115,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['status'], 'active')
@@ -3164,8 +3135,6 @@ class LibvirtConnTestCase(test.TestCase):
self.flags(snapshots_directory='./',
group='libvirt')
image_service = nova.tests.image.fake.FakeImageService()
# Assign an image with an architecture defined (x86_64)
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = 'a440c04b-79fa-479c-bed1-0b816eaec379'
@@ -3179,7 +3148,7 @@ class LibvirtConnTestCase(test.TestCase):
snapshot_name = 'test-snap'
sent_meta = {'name': snapshot_name, 'is_public': False,
'status': 'creating', 'properties': properties}
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -3192,7 +3161,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['properties']['architecture'], 'fake_arch')
@@ -3215,8 +3184,6 @@ class LibvirtConnTestCase(test.TestCase):
self.flags(snapshots_directory='./',
group='libvirt')
image_service = nova.tests.image.fake.FakeImageService()
# Assign a non-existent image
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = '661122aa-1234-dede-fefe-babababababa'
@@ -3229,7 +3196,7 @@ class LibvirtConnTestCase(test.TestCase):
snapshot_name = 'test-snap'
sent_meta = {'name': snapshot_name, 'is_public': False,
'status': 'creating', 'properties': properties}
recv_meta = image_service.create(context, sent_meta)
recv_meta = self.image_service.create(context, sent_meta)
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn')
libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup
@@ -3242,7 +3209,7 @@ class LibvirtConnTestCase(test.TestCase):
conn.snapshot(self.context, instance_ref, recv_meta['id'],
func_call_matcher.call)
snapshot = image_service.show(context, recv_meta['id'])
snapshot = self.image_service.show(context, recv_meta['id'])
self.assertIsNone(func_call_matcher.match())
self.assertEqual(snapshot['properties']['image_state'], 'available')
self.assertEqual(snapshot['properties']['os_type'],
+13 -18
View File
@@ -69,7 +69,7 @@ from nova.compute import utils as compute_utils
from nova.compute import vm_mode
from nova import context as nova_context
from nova import exception
from nova.image import glance
from nova import image
from nova.objects import block_device as block_device_obj
from nova.objects import flavor as flavor_obj
from nova.objects import instance as instance_obj
@@ -415,6 +415,7 @@ class LibvirtDriver(driver.ComputeDriver):
self.disk_cachemodes[disk_type] = cache_mode
self._volume_api = volume.API()
self._image_api = image.API()
@property
def disk_cachemode(self):
@@ -1478,15 +1479,12 @@ class LibvirtDriver(driver.ComputeDriver):
except exception.InstanceNotFound:
raise exception.InstanceNotRunning(instance_id=instance['uuid'])
(image_service, image_id) = glance.get_remote_image_service(
context, instance['image_ref'])
base_image_ref = instance['image_ref']
base = compute_utils.get_image_metadata(
context, image_service, image_id, instance)
context, self._image_api, base_image_ref, instance)
_image_service = glance.get_remote_image_service(context, image_href)
snapshot_image_service, snapshot_image_id = _image_service
snapshot = snapshot_image_service.show(context, snapshot_image_id)
snapshot = self._image_api.get(context, image_href)
disk_path = libvirt_utils.find_disk(virt_dom)
source_format = libvirt_utils.get_disk_type(disk_path)
@@ -1586,10 +1584,10 @@ class LibvirtDriver(driver.ComputeDriver):
update_task_state(task_state=task_states.IMAGE_UPLOADING,
expected_state=task_states.IMAGE_PENDING_UPLOAD)
with libvirt_utils.file_open(out_path) as image_file:
image_service.update(context,
image_href,
metadata,
image_file)
self._image_api.update(context,
image_href,
metadata,
image_file)
LOG.info(_("Snapshot image upload complete"),
instance=instance)
@@ -2083,11 +2081,9 @@ class LibvirtDriver(driver.ComputeDriver):
image_meta = utils.get_image_from_system_metadata(system_meta)
if not image_meta:
image_ref = instance.get('image_ref')
service, image_id = glance.get_remote_image_service(context,
image_ref)
image_meta = compute_utils.get_image_metadata(context,
service,
image_id,
self._image_api,
image_ref,
instance)
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
@@ -3451,10 +3447,9 @@ class LibvirtDriver(driver.ComputeDriver):
block_device_info=None, write_to_disk=False):
# We should get image metadata every time for generating xml
if image_meta is None:
(image_service, image_id) = glance.get_remote_image_service(
context, instance['image_ref'])
image_ref = instance['image_ref']
image_meta = compute_utils.get_image_metadata(
context, image_service, image_id, instance)
context, self._image_api, image_ref, instance)
# NOTE(danms): Stringifying a NetworkInfo will take a lock. Do
# this ahead of time so that we don't acquire it while also
# holding the logging lock.