xenadmin/XenModel/WLB/WlbOptimizationRecommendation.cs
Konstantina Chremmou d93e40065a Various fixes on the EvacuateHostDialog (includes CA-292642, CA-284126, CP-34862).
- CP-34862: Do not allow nomination of new master if a pool secret rotation or
  other pool operations are in progress.
- CA-292642: Fixed button enablement after resolving VM problem.
- CA-284126: Added button to rescan running VMs on the server.
- Removed annoying, focus stealing, modal action progress dialog. When scanning
  for VMs show a spinner on the control instead.
- Fixed crash when the host combobox was updated due to cache changes.
- Logging in an elevated session was happening on the UI thread.
- Show the role elevation dialog before launching the host evacuation dialog or
  we end up with showing the user an unpopulated control.
- Stop deriving from animated progress dialog because it makes it difficult to
  get the resizing right when adding more controls.
- Stop using the progress bar for the VM solution actions; rely simply on the
  action's description and the VM's icon in the gridView.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2020-10-06 11:27:32 +01:00

287 lines
12 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 XenAPI;
namespace XenAdmin.Wlb
{
/// <summary>
/// Optimize pool recommendation properties
/// </summary>
// WLB: find optId, assume recommendation return string format is ["WLB"; host; optId; recID; reason]
public enum RecProperties
{
/// <summary>
/// WLB recommendation.
/// </summary>
WLB = 0,
/// <summary>
/// Recommend moving to host.
/// </summary>
ToHost = 1,
/// <summary>
/// Optimize VM set ID
/// </summary>
OptId = 2,
/// <summary>
/// VM recommendation ID
/// </summary>
RecId = 3,
/// <summary>
/// Recommend moving reason
/// </summary>
Reason = 4
}
/// <summary>
/// Tag each ListView row in WLBOptimizePool with one of these, and pass them to row update.
/// </summary>
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";
/// <summary>
/// Set optimization vm properties
/// </summary>
/// <param name="vm">vm from optimize pool recommendations.</param>
/// <param name="fromHost">host vm resides on.</param>
/// <param name="toHost">host to move vm to.</param>
/// <param name="reason">optimize reason.</param>
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<WlbOptimizationRecommendation>
{
public WlbOptimizationRecommendationCollection(Pool pool, Dictionary<XenRef<VM>, string[]> recommendations)
{
LoadSortedRecommendationList(pool, recommendations);
}
private void LoadSortedRecommendationList(Pool pool, Dictionary<XenRef<VM>, 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<Host> powerOnHosts = GetAllPowerOnHosts(pool, recommendations);
bool clearList = false;
foreach (KeyValuePair<XenRef<VM>, string[]> rec in recommendations)
{
if (rec.Value[0].Trim().ToLower() == "wlb")
{
int recId = 0;
int optId = 0;
int.TryParse(rec.Value[(int)RecProperties.RecId], out recId);
int.TryParse(rec.Value[(int)RecProperties.OptId], out optId);
//XenObject<Host> toHost = (!vm.Server.is_control_domain || (vm.Server.is_control_domain && (String.Compare(rec.Value[(int)WlbOptimizePool.RecProperties.Reason].ToString(), "PowerOn", true) == 0))) ? pool.Connection.Cache.Find_by_Uuid<Host>(rec.Value[(int)WlbOptimizePool.RecProperties.ToHost]) : null;
//XenObject<Host> fromHost = (!vm.Server.is_control_domain || (vm.Server.is_control_domain && (String.Compare(rec.Value[(int)WlbOptimizePool.RecProperties.Reason].ToString(), "PowerOff", true) == 0))) ? pool.Connection.Resolve(vm.Server.resident_on) : null;
VM vm = pool.Connection.Resolve(rec.Key);
Host toHost = (!vm.is_control_domain) ? pool.Connection.Cache.Find_By_Uuid<Host>(rec.Value[(int)RecProperties.ToHost]) : null;
Host fromHost = (!vm.is_control_domain) ? pool.Connection.Resolve(vm.resident_on) : pool.Connection.Cache.Find_By_Uuid<Host>(rec.Value[(int)RecProperties.ToHost]);
string powerOperation = Messages.ResourceManager.GetString(String.Format("WLB_OPT_OPERATION_HOST_{0}", rec.Value[(int)RecProperties.Reason].ToString().ToUpper()));
string resourcedReasonOutput = Messages.ResourceManager.GetString(String.Format("WLB_OPT_REASON_{0}", rec.Value[(int)RecProperties.Reason].ToString().ToUpper()));
if (resourcedReasonOutput == null)
{
resourcedReasonOutput = Messages.UNKNOWN;
}
/* Only vms or host have below criteria can be potentially added into recommendation collection
* - if it is a control_domain (powerOn/powerOff host)
* Or
* - if the moving vm is running, toHost not equal to fromHost, and toHost is disable but it's a powerOnHost or toHost is enabled
*/
if ((vm.is_control_domain && fromHost!=null)
|| (vm.power_state == XenAPI.vm_power_state.Running && toHost != fromHost && ((powerOnHosts.Count > 0 && !toHost.enabled && powerOnHosts.Contains(toHost)) || (toHost != null && toHost.enabled))))
{
// If it's a powerOn host, add it to the powerOnHosts list
//if (vm.Server.is_control_domain && (powerOperation == Messages.WLB_OPT_OPERATION_HOST_POWERON)&& !fromHost.Server.IsLive)
/*if(IsHostPowerOn(vm, powerOperation, fromHost))
{
powerOnHosts.Add(fromHost);
}
*/
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;
}
/* Add to the recommendation collection if:
* - it's a vm (not power on/off host)
* Or
* - it's a power on/off host
*/
//if (!vm.Server.is_control_domain || (vm.Server.is_control_domain && (((powerOperation == Messages.WLB_OPT_OPERATION_HOST_POWERON) && !fromHost.Server.IsLive) || ((powerOperation == Messages.WLB_OPT_OPERATION_HOST_POWEROFF) && fromHost.Server.IsLive))))
if (!vm.is_control_domain || IsHostPowerOnOff(vm, fromHost, powerOperation, recommendations))
{
this.Add(optVmSetting);
}
}
}
}
if (clearList)
{
this.Clear();
}
else
{
this.Sort(SortByRecommendationId);
}
}
private List<Host> GetAllPowerOnHosts(Pool pool, Dictionary<XenRef<VM>, string[]> recommendations)
{
List<Host> powerOnHosts = new List<Host>();
foreach (KeyValuePair<XenRef<VM>, 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<Host>(rec.Value[(int)RecProperties.ToHost]);
string powerOperation = Messages.ResourceManager.GetString(String.Format("WLB_OPT_OPERATION_HOST_{0}", rec.Value[(int)RecProperties.Reason].ToString().ToUpper()));
if (vm.is_control_domain && fromHost!=null && IsHostPowerOn(vm, powerOperation, fromHost))
{
powerOnHosts.Add(fromHost);
}
}
}
return powerOnHosts;
}
private int SortByRecommendationId(WlbOptimizationRecommendation x, WlbOptimizationRecommendation y)
{
return x.recId.CompareTo(y.recId);
}
private bool IsHostPowerOnOff(VM vm, Host fromHost, string powerOperation, Dictionary<XenRef<VM>, 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<XenRef<VM>, string[]> recommendations)
{
foreach (XenRef<VM> vm in fromHost.resident_VMs)
{
if(!(recommendations.ContainsKey(vm)))
{
return false;
}
}
return true;
}
}
}