summaryrefslogtreecommitdiff
path: root/nova/virt/hyperv/livemigrationops.py
blob: 1b0f81eae04115cd44ab8166f44efd0f4f867ba7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# 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

import nova.conf
from nova import exception
from nova.i18n import _
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,
                migrate_disks=not shared_storage)
        except Exception:
            LOG.exception("Live migration failed. Attempting rollback.",
                          instance=instance_ref)
            recover_method(context, instance_ref, dest, migrate_data)
            return

        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.connect_volumes(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)
        self._vmops.plug_vifs(instance_ref, network_info)

    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()

        try:
            # The remote instance dir might not exist or other issue to cause
            # OSError in check_remote_instances_dir_shared function
            migrate_data.is_shared_instance_path = (
                self._pathutils.check_remote_instances_dir_shared(
                    instance_ref.host))
        except exception.FileNotFound as e:
            reason = _('Unavailable instance location: %s') % e
            raise exception.MigrationPreCheckError(reason=reason)
        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=instance_ref)
        return dest_check_data