xenadmin/XenAdmin/Commands/VMOperationHostCommand.cs
Mihaela Stoica 366e6e4ec2 CP-14644: Check feature level on VM migration
Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
2015-11-02 15:55:52 +00:00

213 lines
8.3 KiB
C#

/* Copyright (c) Citrix Systems Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using XenAdmin.Core;
using XenAdmin.Properties;
using XenAPI;
using XenAdmin.Dialogs;
namespace XenAdmin.Commands
{
/// <summary>
/// The Command for the menu-items displayed for each Host in the start-on, resume-on and migrate sub-menu when WLB isn't enabled.
/// </summary>
internal class VMOperationHostCommand : VMOperationCommand
{
public delegate Host GetHostForVM(VM vm);
private readonly static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly Dictionary<VM, string> _cantBootReasons = new Dictionary<VM, string>();
private readonly bool _noneCanBoot = true;
private readonly string _text;
private readonly GetHostForVM _getHostForVM;
public VMOperationHostCommand(IMainWindow mainWindow, IEnumerable<SelectedItem> vms, GetHostForVM getHostForVM, string text, vm_operations operation, Session session)
: base(mainWindow, vms, operation)
{
Util.ThrowIfParameterNull(session, "session");
Util.ThrowIfParameterNull(getHostForVM, "getHostForVM");
Util.ThrowIfParameterNull(text, "text");
_text = text;
_getHostForVM = getHostForVM;
foreach (SelectedItem item in vms)
{
VM vm = (VM)item.XenObject;
string reason = GetVmCannotBootOnHostReason(vm, GetHost(vm), session, operation);
_cantBootReasons[vm] = reason;
if (reason == null)
_noneCanBoot = false;
}
}
protected sealed override Host GetHost(VM vm)
{
return _getHostForVM(vm);
}
public override string MenuText
{
get
{
List<string> uniqueReasons = new List<string>();
foreach (VM vm in _cantBootReasons.Keys)
{
if (!uniqueReasons.Contains(_cantBootReasons[vm]))
{
uniqueReasons.Add(_cantBootReasons[vm]);
}
}
if (_noneCanBoot && uniqueReasons.Count == 1)
{
return string.Format(Messages.MAINWINDOW_CONTEXT_REASON, _text, uniqueReasons[0]);
}
else
{
return _text;
}
}
}
public override Image MenuImage
{
get
{
return _noneCanBoot ? Resources._000_ServerDisconnected_h32bit_16 : Resources._000_TreeConnected_h32bit_16;
}
}
protected override bool CanExecute(VM vm)
{
return vm != null && _cantBootReasons.ContainsKey(vm) && _cantBootReasons[vm] == null;
}
private static string GetVmCannotBootOnHostReason(VM vm, Host host, Session session, vm_operations operation)
{
Host residentHost = vm.Connection.Resolve(vm.resident_on);
if (host == null)
return Messages.NO_HOME_SERVER;
if (vm.power_state == vm_power_state.Running && residentHost != null
&& Helpers.productVersionCompare(Helpers.HostProductVersion(host), Helpers.HostProductVersion(residentHost)) < 0)
{
// This will be a migrate menu if powerstate if running
return Messages.OLDER_THAN_CURRENT_SERVER;
}
if (vm.power_state == vm_power_state.Running && residentHost != null && host.opaque_ref == residentHost.opaque_ref)
return Messages.HOST_MENU_CURRENT_SERVER;
if ((operation == vm_operations.pool_migrate || operation == vm_operations.resume_on) && VmCpuFeaturesIncompatibleWithHost(host, vm))
{
return FriendlyErrorNames.VM_INCOMPATIBLE_WITH_THIS_HOST;
}
try
{
VM.assert_can_boot_here(session, vm.opaque_ref, host.opaque_ref);
}
catch (Failure f)
{
if (f.ErrorDescription.Count > 2 && f.ErrorDescription[0] == Failure.VM_REQUIRES_SR)
{
SR sr = host.Connection.Resolve((new XenRef<SR>(f.ErrorDescription[2])));
if (sr != null && sr.content_type == SR.Content_Type_ISO)
return Messages.MIGRATE_PLEASE_EJECT_YOUR_CD;
}
return f.ShortMessage;
}
catch (Exception e)
{
log.ErrorFormat("There was an error calling assert_can_boot_here on host {0}", host.Name);
log.Error(e, e);
return Messages.HOST_MENU_UNKNOWN_ERROR;
}
return null;
}
protected override CommandErrorDialog GetErrorDialogCore(IDictionary<SelectedItem, string> cantExecuteReasons)
{
return new CommandErrorDialog(ErrorDialogTitle, ErrorDialogText, cantExecuteReasons);
}
protected override string GetCantExecuteReasonCore(SelectedItem item)
{
VM vm = item.XenObject as VM;
if (vm == null)
base.GetCantExecuteReasonCore(item);
if (_cantBootReasons.ContainsKey(vm))
{
return _cantBootReasons[vm];
}
return base.GetCantExecuteReasonCore(item);
}
public static bool VmCpuFeaturesIncompatibleWithHost(Host targetHost, VM vm)
{
// check the CPU feature compatibility for Dundee and higher hosts
if (!Helpers.DundeeOrGreater(targetHost))
return false;
// only for running or suspended VMs
if (vm.power_state != vm_power_state.Running && vm.power_state != vm_power_state.Suspended)
return false;
if (vm.last_boot_CPU_flags == null || !vm.last_boot_CPU_flags.ContainsKey("vendor") || !vm.last_boot_CPU_flags.ContainsKey("features")
|| targetHost.cpu_info == null || !targetHost.cpu_info.ContainsKey("vendor"))
return false;
if (vm.last_boot_CPU_flags["vendor"] != targetHost.cpu_info["vendor"])
return true;
if (vm.IsHVM && targetHost.cpu_info.ContainsKey("features_hvm"))
return PoolJoinRules.LessFeatures(targetHost.cpu_info["features_hvm"], vm.last_boot_CPU_flags["features"]);
if (!vm.IsHVM && targetHost.cpu_info.ContainsKey("features_pv"))
return PoolJoinRules.LessFeatures(targetHost.cpu_info["features_pv"], vm.last_boot_CPU_flags["features"]);
return false;
}
}
}