Fixes Hyper-V boot from volume live migration
Live migration fails on Hyper-V when boot from volume is used with
CoW, as the target host tries to cache the root disk image in
pre_live_migration, but in this case the image_ref is empty.
This patch adds a check to handle the empty image_ref case.
Note(claudiub): test_livemigrationops.py change (1 line) was not
included, since the file did not exist in Juno.
Co-Authored-By: Claudiu Belu <cbelu@cloudbasesolutions.com>
Co-Authored-By: Adelina Tuvenie <atuvenie@cloudbasesolutions.com>
(cherry picked from commit d3758b6532
)
Conflicts:
nova/tests/unit/virt/hyperv/test_livemigrationops.py
Change-Id: I60cb60ccaeb0cb8c536906d897249e31ae396923
Closes-Bug: #1362075
This commit is contained in:
parent
abef68f183
commit
af3af7755b
|
@ -0,0 +1,119 @@
|
|||
# Copyright 2014 Cloudbase Solutions Srl
|
||||
# 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 mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests import fake_instance
|
||||
from nova.virt.hyperv import constants
|
||||
from nova.virt.hyperv import imagecache
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ImageCacheTestCase(test.NoDBTestCase):
|
||||
"""Unit tests for the Hyper-V ImageCache class."""
|
||||
|
||||
FAKE_BASE_DIR = 'fake/base/dir'
|
||||
FAKE_FORMAT = 'fake_format'
|
||||
FAKE_IMAGE_REF = 'fake_image_ref'
|
||||
|
||||
def setUp(self):
|
||||
super(ImageCacheTestCase, self).setUp()
|
||||
|
||||
self.context = 'fake-context'
|
||||
self.instance = fake_instance.fake_instance_obj(self.context)
|
||||
|
||||
# utilsfactory will check the host OS version via get_hostutils,
|
||||
# in order to return the proper Utils Class, so it must be mocked.
|
||||
patched_func = mock.patch.object(imagecache.utilsfactory,
|
||||
"get_hostutils")
|
||||
patched_get_pathutils = mock.patch.object(imagecache.utilsfactory,
|
||||
"get_pathutils")
|
||||
patched_func.start()
|
||||
patched_get_pathutils.start()
|
||||
self.addCleanup(patched_func.stop)
|
||||
self.addCleanup(patched_get_pathutils.stop)
|
||||
|
||||
self.imagecache = imagecache.ImageCache()
|
||||
self.imagecache._pathutils = mock.MagicMock()
|
||||
self.imagecache._vhdutils = mock.MagicMock()
|
||||
|
||||
def _prepare_get_cached_image(self, path_exists, use_cow):
|
||||
self.instance.image_ref = self.FAKE_IMAGE_REF
|
||||
self.imagecache._pathutils.get_base_vhd_dir.return_value = (
|
||||
self.FAKE_BASE_DIR)
|
||||
self.imagecache._pathutils.exists.return_value = path_exists
|
||||
self.imagecache._vhdutils.get_vhd_format.return_value = (
|
||||
constants.DISK_FORMAT_VHD)
|
||||
|
||||
CONF.set_override('use_cow_images', use_cow)
|
||||
|
||||
expected_path = os.path.join(self.FAKE_BASE_DIR,
|
||||
self.FAKE_IMAGE_REF)
|
||||
expected_vhd_path = "%s.%s" % (expected_path,
|
||||
constants.DISK_FORMAT_VHD.lower())
|
||||
return (expected_path, expected_vhd_path)
|
||||
|
||||
@mock.patch.object(imagecache.images, 'fetch')
|
||||
def test_get_cached_image_with_fetch(self, mock_fetch):
|
||||
(expected_path,
|
||||
expected_vhd_path) = self._prepare_get_cached_image(False, False)
|
||||
|
||||
result = self.imagecache.get_cached_image(self.context, self.instance)
|
||||
self.assertEqual(expected_vhd_path, result)
|
||||
|
||||
mock_fetch.assert_called_once_with(self.context, self.FAKE_IMAGE_REF,
|
||||
expected_path,
|
||||
self.instance['user_id'],
|
||||
self.instance['project_id'])
|
||||
self.imagecache._vhdutils.get_vhd_format.assert_called_once_with(
|
||||
expected_path)
|
||||
self.imagecache._pathutils.rename.assert_called_once_with(
|
||||
expected_path, expected_vhd_path)
|
||||
|
||||
@mock.patch.object(imagecache.images, 'fetch')
|
||||
def test_get_cached_image_with_fetch_exception(self, mock_fetch):
|
||||
(expected_path,
|
||||
expected_vhd_path) = self._prepare_get_cached_image(False, False)
|
||||
|
||||
# path doesn't exist until fetched.
|
||||
self.imagecache._pathutils.exists.side_effect = [False, False, True]
|
||||
mock_fetch.side_effect = exception.InvalidImageRef(
|
||||
image_href=self.FAKE_IMAGE_REF)
|
||||
|
||||
self.assertRaises(exception.InvalidImageRef,
|
||||
self.imagecache.get_cached_image,
|
||||
self.context, self.instance)
|
||||
|
||||
self.imagecache._pathutils.remove.assert_called_once_with(
|
||||
expected_path)
|
||||
|
||||
@mock.patch.object(imagecache.ImageCache, '_resize_and_cache_vhd')
|
||||
def test_get_cached_image_use_cow(self, mock_resize):
|
||||
(expected_path,
|
||||
expected_vhd_path) = self._prepare_get_cached_image(True, True)
|
||||
|
||||
expected_resized_vhd_path = expected_vhd_path + 'x'
|
||||
mock_resize.return_value = expected_resized_vhd_path
|
||||
|
||||
result = self.imagecache.get_cached_image(self.context, self.instance)
|
||||
self.assertEqual(expected_resized_vhd_path, result)
|
||||
|
||||
mock_resize.assert_called_once_with(self.instance, expected_vhd_path)
|
|
@ -98,7 +98,7 @@ class ImageCache(object):
|
|||
return resized_vhd_path
|
||||
|
||||
def get_cached_image(self, context, instance):
|
||||
image_id = instance['image_ref']
|
||||
image_id = instance.image_ref
|
||||
|
||||
base_vhd_dir = self._pathutils.get_base_vhd_dir()
|
||||
base_vhd_path = os.path.join(base_vhd_dir, image_id)
|
||||
|
@ -115,8 +115,8 @@ class ImageCache(object):
|
|||
if not vhd_path:
|
||||
try:
|
||||
images.fetch(context, image_id, base_vhd_path,
|
||||
instance['user_id'],
|
||||
instance['project_id'])
|
||||
instance.user_id,
|
||||
instance.project_id)
|
||||
|
||||
format_ext = self._vhdutils.get_vhd_format(base_vhd_path)
|
||||
vhd_path = base_vhd_path + '.' + format_ext.lower()
|
||||
|
|
|
@ -89,7 +89,7 @@ class LiveMigrationOps(object):
|
|||
if CONF.use_cow_images:
|
||||
boot_from_volume = self._volumeops.ebs_root_in_block_devices(
|
||||
block_device_info)
|
||||
if not boot_from_volume:
|
||||
if not boot_from_volume and instance.image_ref:
|
||||
self._imagecache.get_cached_image(context, instance)
|
||||
|
||||
self._volumeops.login_storage_targets(block_device_info)
|
||||
|
|
Loading…
Reference in New Issue