nova/nova/virt/hyperv/livemigrationops.py
Lucian Petrut 5ee7f3b30c Hyper-V: properly handle shared storage during migrations
In case of migrations, we attempt to move the instance files without
checking whether shared storage is used. Note that the block_migration
flag is ignored.

After a live migration is performed, we always try to delete the
instance files. Also, if the migration fails, the destination node is
not cleaned up, as the HyperVLiveMigrateData object is not used at the
moment.

This change addresses those issues.

Partial-Bug: #1565895

Change-Id: I0ac0a2d2e7a8771024a486dd5931bd05b1ecd074
2016-08-24 17:53:40 +03:00

144 lines
6.1 KiB
Python

# Copyright 2012 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.
"""
Management class for live migration VM operations.
"""
from os_win import utilsfactory
from oslo_log import log as logging
from oslo_utils import excutils
import nova.conf
from nova.objects import migrate_data as migrate_data_obj
from nova.virt.hyperv import block_device_manager
from nova.virt.hyperv import imagecache
from nova.virt.hyperv import pathutils
from nova.virt.hyperv import serialconsoleops
from nova.virt.hyperv import vmops
from nova.virt.hyperv import volumeops
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
class LiveMigrationOps(object):
def __init__(self):
self._livemigrutils = utilsfactory.get_livemigrationutils()
self._pathutils = pathutils.PathUtils()
self._vmops = vmops.VMOps()
self._volumeops = volumeops.VolumeOps()
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
self._imagecache = imagecache.ImageCache()
self._vmutils = utilsfactory.get_vmutils()
self._block_dev_man = block_device_manager.BlockDeviceInfoManager()
def live_migration(self, context, instance_ref, dest, post_method,
recover_method, block_migration=False,
migrate_data=None):
LOG.debug("live_migration called", instance=instance_ref)
instance_name = instance_ref["name"]
if migrate_data and 'is_shared_instance_path' in migrate_data:
shared_storage = migrate_data.is_shared_instance_path
else:
shared_storage = (
self._pathutils.check_remote_instances_dir_shared(dest))
if migrate_data:
migrate_data.is_shared_instance_path = shared_storage
else:
migrate_data = migrate_data_obj.HyperVLiveMigrateData(
is_shared_instance_path=shared_storage)
try:
# We must make sure that the console log workers are stopped,
# otherwise we won't be able to delete / move VM log files.
self._serial_console_ops.stop_console_handler(instance_name)
if not shared_storage:
self._pathutils.copy_vm_console_logs(instance_name, dest)
self._vmops.copy_vm_dvd_disks(instance_name, dest)
self._livemigrutils.live_migrate_vm(instance_name,
dest)
except Exception:
with excutils.save_and_reraise_exception():
LOG.debug("Calling live migration recover_method "
"for instance: %s", instance_name)
recover_method(context, instance_ref, dest, block_migration,
migrate_data)
LOG.debug("Calling live migration post_method for instance: %s",
instance_name)
post_method(context, instance_ref, dest,
block_migration, migrate_data)
def pre_live_migration(self, context, instance, block_device_info,
network_info):
LOG.debug("pre_live_migration called", instance=instance)
self._livemigrutils.check_live_migration_config()
if CONF.use_cow_images:
boot_from_volume = self._block_dev_man.is_boot_from_volume(
block_device_info)
if not boot_from_volume and instance.image_ref:
self._imagecache.get_cached_image(context, instance)
self._volumeops.initialize_volumes_connection(block_device_info)
disk_path_mapping = self._volumeops.get_disk_path_mapping(
block_device_info)
if disk_path_mapping:
# We create a planned VM, ensuring that volumes will remain
# attached after the VM is migrated.
self._livemigrutils.create_planned_vm(instance.name,
instance.host,
disk_path_mapping)
def post_live_migration(self, context, instance, block_device_info,
migrate_data):
self._volumeops.disconnect_volumes(block_device_info)
if not migrate_data.is_shared_instance_path:
self._pathutils.get_instance_dir(instance.name,
create_dir=False,
remove_dir=True)
def post_live_migration_at_destination(self, ctxt, instance_ref,
network_info, block_migration):
LOG.debug("post_live_migration_at_destination called",
instance=instance_ref)
def check_can_live_migrate_destination(self, ctxt, instance_ref,
src_compute_info, dst_compute_info,
block_migration=False,
disk_over_commit=False):
LOG.debug("check_can_live_migrate_destination called",
instance=instance_ref)
migrate_data = migrate_data_obj.HyperVLiveMigrateData()
migrate_data.is_shared_instance_path = (
self._pathutils.check_remote_instances_dir_shared(
instance_ref.host))
return migrate_data
def cleanup_live_migration_destination_check(self, ctxt, dest_check_data):
LOG.debug("cleanup_live_migration_destination_check called")
def check_can_live_migrate_source(self, ctxt, instance_ref,
dest_check_data):
LOG.debug("check_can_live_migrate_source called", instance_ref)
return dest_check_data