/* Copyright (c) Cloud Software Group, Inc. * * 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 XenAPI; namespace XenAdmin.Wlb { /// /// Optimize pool recommendation properties /// // WLB: find optId, assume recommendation return string format is ["WLB"; host; optId; recID; reason] public enum RecProperties { /// /// WLB recommendation. /// WLB = 0, /// /// Recommend moving to host. /// ToHost = 1, /// /// Optimize VM set ID /// OptId = 2, /// /// VM recommendation ID /// RecId = 3, /// /// Recommend moving reason /// Reason = 4 } /// /// Tag each ListView row in WLBOptimizePool with one of these, and pass them to row update. /// public class WlbOptimizationRecommendation { public readonly VM vm; public readonly string reason; public readonly Host fromHost; public readonly Host toHost; public readonly int recId; public readonly int optId; public readonly string powerOperation; public const string OPTIMIZINGPOOL = "wlb_optimizing_pool"; /// /// Set optimization vm properties /// /// vm from optimize pool recommendations. /// host vm resides on. /// host to move vm to. /// optimize reason. internal WlbOptimizationRecommendation(VM vm, Host fromHost, Host toHost, string reason, int recId, int optId, string powerOperation) { this.vm = vm; this.fromHost = fromHost; this.toHost = toHost; this.reason = reason; this.recId = recId; this.optId = optId; this.powerOperation = powerOperation; } } public class WlbOptimizationRecommendationCollection : List { public WlbOptimizationRecommendationCollection(Pool pool, Dictionary, string[]> recommendations) { LoadSortedRecommendationList(pool, recommendations); } private void LoadSortedRecommendationList(Pool pool, Dictionary, string[]> recommendations) { // When there are host powerOn recommendations, the toHost of the recommended move vms are disabled, // We use the powerOhHosts list to check if the toHost of the recommended move vms is a powerOnHost when it's disabled List powerOnHosts = GetAllPowerOnHosts(pool, recommendations); bool clearList = false; foreach (KeyValuePair, string[]> rec in recommendations) { if (rec.Value[0].Trim().ToLower() == "wlb") { int.TryParse(rec.Value[(int)RecProperties.RecId], out var recId); int.TryParse(rec.Value[(int)RecProperties.OptId], out var optId); VM vm = pool.Connection.Resolve(rec.Key); Host toHost = !vm.is_control_domain ? pool.Connection.Cache.Find_By_Uuid(rec.Value[(int)RecProperties.ToHost]) : null; Host fromHost = !vm.is_control_domain ? pool.Connection.Resolve(vm.resident_on) : pool.Connection.Cache.Find_By_Uuid(rec.Value[(int)RecProperties.ToHost]); ParseRecommendationReason(rec.Value[(int)RecProperties.Reason], out var powerOperation, out var resourcedReasonOutput); if ((vm.is_control_domain && fromHost != null) || (vm.power_state == vm_power_state.Running && toHost != fromHost && ((powerOnHosts.Count > 0 && toHost != null && !toHost.enabled && powerOnHosts.Contains(toHost)) || (toHost != null && toHost.enabled)))) { WlbOptimizationRecommendation optVmSetting = new WlbOptimizationRecommendation(vm, fromHost, toHost, resourcedReasonOutput, recId, optId, powerOperation); // Clear the recommendation collection if the number of vms on a power off host // doesn't match the vms in the recommendations if (IsHostPowerOff(vm, powerOperation, fromHost) && !VmsOnPowerOffHostAreInRecommendations(fromHost, recommendations)) { clearList = true; break; } if (!vm.is_control_domain || IsHostPowerOnOff(vm, fromHost, powerOperation, recommendations)) { Add(optVmSetting); } } } } if (clearList) { Clear(); } else { Sort(SortByRecommendationId); } } private List GetAllPowerOnHosts(Pool pool, Dictionary, string[]> recommendations) { List powerOnHosts = new List(); foreach (KeyValuePair, string[]> rec in recommendations) { if (rec.Value[0].Trim().ToLower() == "wlb") { VM vm = pool.Connection.Resolve(rec.Key); Host fromHost = !vm.is_control_domain ? pool.Connection.Resolve(vm.resident_on) : pool.Connection.Cache.Find_By_Uuid(rec.Value[(int)RecProperties.ToHost]); ParseRecommendationReason(rec.Value[(int)RecProperties.Reason], out var powerOperation, out _); if (vm.is_control_domain && fromHost != null && IsHostPowerOn(vm, powerOperation, fromHost)) { powerOnHosts.Add(fromHost); } } } return powerOnHosts; } private void ParseRecommendationReason(string reason, out string powerOperation, out string resourcedReasonOutput) { powerOperation = null; switch (reason.ToUpper()) { case "POWERON": powerOperation = Messages.WLB_OPT_OPERATION_HOST_POWERON; resourcedReasonOutput = Messages.WLB_OPT_REASON_POWERON; break; case "POWEROFF": powerOperation = Messages.WLB_OPT_OPERATION_HOST_POWEROFF; resourcedReasonOutput = Messages.WLB_OPT_REASON_POWEROFF; break; case "CONSOLIDATE": resourcedReasonOutput = Messages.WLB_OPT_REASON_CONSOLIDATE; break; case "CPU": resourcedReasonOutput = Messages.WLB_OPT_REASON_CPU; break; case "DISK": resourcedReasonOutput = Messages.WLB_OPT_REASON_DISK; break; case "DISKREAD": resourcedReasonOutput = Messages.WLB_OPT_REASON_DISKREAD; break; case "DISKWRITE": resourcedReasonOutput = Messages.WLB_OPT_REASON_DISKWRITE; break; case "LOADAVERAGE": resourcedReasonOutput = Messages.WLB_OPT_REASON_LOADAVERAGE; break; case "MEMORY": resourcedReasonOutput = Messages.WLB_OPT_REASON_MEMORY; break; case "NETWORK": resourcedReasonOutput = Messages.WLB_OPT_REASON_NETWORK; break; case "NETWORKREAD": resourcedReasonOutput = Messages.WLB_OPT_REASON_NETWORKREAD; break; case "NETWORKWRITE": resourcedReasonOutput = Messages.WLB_OPT_REASON_NETWORKWRITE; break; case "NONE": resourcedReasonOutput = Messages.NONE_UPPER; break; default: resourcedReasonOutput = Messages.UNKNOWN; break; } } private int SortByRecommendationId(WlbOptimizationRecommendation x, WlbOptimizationRecommendation y) { return x.recId.CompareTo(y.recId); } private bool IsHostPowerOnOff(VM vm, Host fromHost, string powerOperation, Dictionary, string[]> recommendations) { if (vm.is_control_domain && fromHost!=null) { if(IsHostPowerOn(vm, powerOperation, fromHost)) { return true; } if(IsHostPowerOff(vm, powerOperation, fromHost) && VmsOnPowerOffHostAreInRecommendations(fromHost, recommendations)) { return true; } } return false; } private bool IsHostPowerOn(VM vm, string powerOperation, Host fromHost) { if (vm != null && !String.IsNullOrEmpty(powerOperation) && fromHost != null) { return vm.is_control_domain && (powerOperation == Messages.WLB_OPT_OPERATION_HOST_POWERON) && !fromHost.IsLive(); } return false; } private bool IsHostPowerOff(VM vm, string powerOperation, Host fromHost) { if (vm != null && !String.IsNullOrEmpty(powerOperation) && fromHost != null) { return vm.is_control_domain && (powerOperation == Messages.WLB_OPT_OPERATION_HOST_POWEROFF) && fromHost.IsLive(); } return false; } private bool VmsOnPowerOffHostAreInRecommendations(Host fromHost, Dictionary, string[]> recommendations) { foreach (XenRef vm in fromHost.resident_VMs) { if (!recommendations.ContainsKey(vm)) { return false; } } return true; } } }