/* 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 System.Linq;
using XenAdmin.Core;
using XenAdmin.Properties;
using XenAPI;
using XenAdmin.Dialogs;
namespace XenAdmin.Commands
{
///
/// 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.
///
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 _cantBootReasons = new Dictionary();
private readonly bool _noneCanBoot = true;
private readonly string _text;
private readonly GetHostForVM _getHostForVM;
public VMOperationHostCommand(IMainWindow mainWindow, IEnumerable 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);
if (reason == null)
_noneCanBoot = false;
else
_cantBootReasons[vm] = reason;
}
}
protected sealed override Host GetHost(VM vm)
{
return _getHostForVM(vm);
}
public override string MenuText
{
get
{
if (_noneCanBoot)
{
var uniqueReasons = _cantBootReasons.Values.Distinct().ToList();
if (uniqueReasons.Count == 1)
return string.Format(Messages.MAINWINDOW_CONTEXT_REASON, _text, uniqueReasons[0]);
}
return _text;
}
}
public override Image MenuImage
{
get
{
return Resources._000_TreeConnected_h32bit_16;
}
}
protected override bool CanExecute(VM vm)
{
return vm != null && !_cantBootReasons.ContainsKey(vm);
}
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(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 cantExecuteReasons)
{
return new CommandErrorDialog(ErrorDialogTitle, ErrorDialogText, cantExecuteReasons);
}
protected override string GetCantExecuteReasonCore(SelectedItem item)
{
VM vm = item.XenObject as VM;
if (vm != null && _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;
Host home = vm.Home();
if (home != null && !Helpers.DundeeOrGreater(home))
return false;
if (home == null && !Helpers.DundeeOrGreater(vm.Connection))
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.FewerFeatures(targetHost.cpu_info["features_hvm"], vm.last_boot_CPU_flags["features"]);
if (!vm.IsHVM && targetHost.cpu_info.ContainsKey("features_pv"))
return PoolJoinRules.FewerFeatures(targetHost.cpu_info["features_pv"], vm.last_boot_CPU_flags["features"]);
return false;
}
}
}