/* 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 Images.StaticImages._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; } } }