/* 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
{
///
/// 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,
///
/// Recommendate moving to host.
///
ToHost = 1,
///
/// Optimize VM set ID
///
OptId = 2,
///
/// VM recommendation ID
///
RecId = 3,
///
/// Recommendate 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 optimizal 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 recId = 0;
int optId = 0;
int.TryParse(rec.Value[(int)RecProperties.RecId], out recId);
int.TryParse(rec.Value[(int)RecProperties.OptId], out optId);
//XenObject 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(rec.Value[(int)WlbOptimizePool.RecProperties.ToHost]) : null;
//XenObject 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(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]);
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 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]);
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, 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;
}
}
}