Files
deb-nova/nova/virt/hyperv/pathutils.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

198 lines
8.2 KiB
Python

# Copyright 2013 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 tempfile
import time
from os_win.utils import pathutils
from oslo_log import log as logging
import nova.conf
from nova import exception
from nova.i18n import _
from nova.virt.hyperv import constants
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
ERROR_INVALID_NAME = 123
# NOTE(claudiub): part of the pre-existing PathUtils is nova-specific and
# it does not belong in the os-win library. In order to ensure the same
# functionality with the least amount of changes necessary, adding as a mixin
# the os_win.pathutils.PathUtils class into this PathUtils.
class PathUtils(pathutils.PathUtils):
def get_instances_dir(self, remote_server=None):
local_instance_path = os.path.normpath(CONF.instances_path)
if remote_server and not local_instance_path.startswith(r'\\'):
if CONF.hyperv.instances_path_share:
path = CONF.hyperv.instances_path_share
else:
# Use an administrative share
path = local_instance_path.replace(':', '$')
return ('\\\\%(remote_server)s\\%(path)s' %
{'remote_server': remote_server, 'path': path})
else:
return local_instance_path
def _get_instances_sub_dir(self, dir_name, remote_server=None,
create_dir=True, remove_dir=False):
instances_path = self.get_instances_dir(remote_server)
path = os.path.join(instances_path, dir_name)
try:
if remove_dir:
self.check_remove_dir(path)
if create_dir:
self.check_create_dir(path)
return path
except WindowsError as ex:
if ex.winerror == ERROR_INVALID_NAME:
raise exception.AdminRequired(_(
"Cannot access \"%(instances_path)s\", make sure the "
"path exists and that you have the proper permissions. "
"In particular Nova-Compute must not be executed with the "
"builtin SYSTEM account or other accounts unable to "
"authenticate on a remote host.") %
{'instances_path': instances_path})
raise
def get_instance_migr_revert_dir(self, instance_name, create_dir=False,
remove_dir=False):
dir_name = '%s_revert' % instance_name
return self._get_instances_sub_dir(dir_name, None, create_dir,
remove_dir)
def get_instance_dir(self, instance_name, remote_server=None,
create_dir=True, remove_dir=False):
return self._get_instances_sub_dir(instance_name, remote_server,
create_dir, remove_dir)
def _lookup_vhd_path(self, instance_name, vhd_path_func,
*args, **kwargs):
vhd_path = None
for format_ext in ['vhd', 'vhdx']:
test_path = vhd_path_func(instance_name, format_ext,
*args, **kwargs)
if self.exists(test_path):
vhd_path = test_path
break
return vhd_path
def lookup_root_vhd_path(self, instance_name, rescue=False):
return self._lookup_vhd_path(instance_name, self.get_root_vhd_path,
rescue)
def lookup_configdrive_path(self, instance_name, rescue=False):
configdrive_path = None
for format_ext in constants.DISK_FORMAT_MAP:
test_path = self.get_configdrive_path(instance_name, format_ext,
rescue=rescue)
if self.exists(test_path):
configdrive_path = test_path
break
return configdrive_path
def lookup_ephemeral_vhd_path(self, instance_name, eph_name):
return self._lookup_vhd_path(instance_name,
self.get_ephemeral_vhd_path,
eph_name)
def get_root_vhd_path(self, instance_name, format_ext, rescue=False):
instance_path = self.get_instance_dir(instance_name)
image_name = 'root'
if rescue:
image_name += '-rescue'
return os.path.join(instance_path,
image_name + '.' + format_ext.lower())
def get_configdrive_path(self, instance_name, format_ext,
remote_server=None, rescue=False):
instance_path = self.get_instance_dir(instance_name, remote_server)
configdrive_image_name = 'configdrive'
if rescue:
configdrive_image_name += '-rescue'
return os.path.join(instance_path,
configdrive_image_name + '.' + format_ext.lower())
def get_ephemeral_vhd_path(self, instance_name, format_ext, eph_name):
instance_path = self.get_instance_dir(instance_name)
return os.path.join(instance_path, eph_name + '.' + format_ext.lower())
def get_base_vhd_dir(self):
return self._get_instances_sub_dir('_base')
def get_export_dir(self, instance_name):
dir_name = os.path.join('export', instance_name)
return self._get_instances_sub_dir(dir_name, create_dir=True,
remove_dir=True)
def get_vm_console_log_paths(self, instance_name, remote_server=None):
instance_dir = self.get_instance_dir(instance_name,
remote_server)
console_log_path = os.path.join(instance_dir, 'console.log')
return console_log_path, console_log_path + '.1'
def copy_vm_console_logs(self, instance_name, dest_host):
local_log_paths = self.get_vm_console_log_paths(
instance_name)
remote_log_paths = self.get_vm_console_log_paths(
instance_name, remote_server=dest_host)
for local_log_path, remote_log_path in zip(local_log_paths,
remote_log_paths):
if self.exists(local_log_path):
self.copy(local_log_path, remote_log_path)
def get_image_path(self, image_name):
# Note: it is possible that the path doesn't exist
base_dir = self.get_base_vhd_dir()
for ext in ['vhd', 'vhdx']:
file_path = os.path.join(base_dir,
image_name + '.' + ext.lower())
if self.exists(file_path):
return file_path
return None
def get_age_of_file(self, file_name):
return time.time() - os.path.getmtime(file_name)
def check_dirs_shared_storage(self, src_dir, dest_dir):
# Check if shared storage is being used by creating a temporary
# file at the destination path and checking if it exists at the
# source path.
LOG.debug("Checking if %(src_dir)s and %(dest_dir)s point "
"to the same location.",
dict(src_dir=src_dir, dest_dir=dest_dir))
with tempfile.NamedTemporaryFile(dir=dest_dir) as tmp_file:
src_path = os.path.join(src_dir,
os.path.basename(tmp_file.name))
shared_storage = os.path.exists(src_path)
return shared_storage
def check_remote_instances_dir_shared(self, dest):
# Checks if the instances dir from a remote host points
# to the same storage location as the local instances dir.
local_inst_dir = self.get_instances_dir()
remote_inst_dir = self.get_instances_dir(dest)
return self.check_dirs_shared_storage(local_inst_dir,
remote_inst_dir)