mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-23 08:56:02 +01:00
7ba8546929
- added an action for the API call that retrieves the WLB VM recommendations - removed the thread used for updating the menu items, so that we always access the menu items on the UI thread and do only the server calls on separate threads (for WLB, getting the recommendations now is done on the new action, for non-WLB we have the `workerQueueWithoutWlb`) Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
229 lines
8.4 KiB
C#
229 lines
8.4 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 System.Text;
|
|
using XenAPI;
|
|
using System.Collections.ObjectModel;
|
|
using XenAdmin.Core;
|
|
|
|
|
|
namespace XenAdmin.Commands
|
|
{
|
|
/// <summary>
|
|
/// Performs the WLB recommendations for the Start-On, Resume-On and Migrate menu items.
|
|
/// </summary>
|
|
internal class WlbRecommendations
|
|
{
|
|
private readonly ReadOnlyCollection<VM> _vms;
|
|
private readonly bool _initialized;
|
|
private readonly Dictionary<VM, Dictionary<XenRef<Host>, string[]>> _recommendations;
|
|
private readonly bool _isError;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="WlbRecommendations"/> class.
|
|
/// </summary>
|
|
/// <param name="vms">The VMs that the recommendations are required for.</param>
|
|
/// <param name="recommendations"></param>
|
|
public WlbRecommendations(List<VM> vms, Dictionary<VM, Dictionary<XenRef<Host>, string[]>> recommendations)
|
|
{
|
|
Util.ThrowIfEnumerableParameterNullOrEmpty(vms, "vms");
|
|
|
|
_vms = new ReadOnlyCollection<VM>(vms);
|
|
_recommendations = recommendations;
|
|
_isError = recommendations == null;
|
|
_initialized = recommendations != null;
|
|
}
|
|
/*
|
|
/// <summary>
|
|
/// Calls VM.retrieve_wlb_recommendations for each of the VMs specified in the constructor.
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
if (_initialized)
|
|
{
|
|
throw new InvalidOperationException("Already initialized");
|
|
}
|
|
|
|
_initialized = true;
|
|
|
|
if (Helpers.WlbEnabled(_vms[0].Connection))
|
|
{
|
|
try
|
|
{
|
|
foreach (VM vm in _vms)
|
|
{
|
|
_recommendations[vm] = VM.retrieve_wlb_recommendations(_session, vm.opaque_ref);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.Error("Error getting WLB recommendations", e);
|
|
_isError = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_isError = true;
|
|
}
|
|
}*/
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether an exception was thrown when calling VM.retrieve_wlb_recommendations.
|
|
/// </summary>
|
|
/// <value><c>true</c> if an exception was thrown; otherwise, <c>false</c>.</value>
|
|
public bool IsError
|
|
{
|
|
get
|
|
{
|
|
return _isError;
|
|
}
|
|
}
|
|
|
|
private void Verify()
|
|
{
|
|
if (_isError)
|
|
{
|
|
throw new InvalidOperationException("There was an error getting the WLB recommendations.");
|
|
}
|
|
if (!_initialized)
|
|
{
|
|
throw new InvalidOperationException("Initialize() has not been called.");
|
|
}
|
|
}
|
|
|
|
public Host GetOptimalServer(VM vm)
|
|
{
|
|
Verify();
|
|
|
|
double highStars = 0;
|
|
Host recHost = null;
|
|
foreach (KeyValuePair<XenRef<Host>, string[]> rec in _recommendations[vm])
|
|
{
|
|
if (rec.Value.Length > 0 && rec.Value[0].Trim().ToLower() == "wlb")
|
|
{
|
|
// WLB: stars equal to highStars handles when there are only two hosts and vm resides on a host with high stars
|
|
double stars = Helpers.ParseStringToDouble(rec.Value[1].Trim(), 0);
|
|
if ((stars >= highStars) && (rec.Key.opaque_ref != vm.resident_on.opaque_ref))
|
|
{
|
|
highStars = stars;
|
|
recHost = vm.Connection.Resolve(rec.Key);
|
|
}
|
|
}
|
|
}
|
|
|
|
return recHost;
|
|
}
|
|
|
|
public WlbRecommendation GetStarRating(Host host)
|
|
{
|
|
Verify();
|
|
|
|
List<double> starRatings = new List<double>();
|
|
Dictionary<VM, bool> canExecutes = new Dictionary<VM, bool>();
|
|
Dictionary<VM, string> cantExecuteReasons = new Dictionary<VM, string>();
|
|
|
|
foreach (VM vm in _vms)
|
|
{
|
|
Host residentHost = vm.Connection.Resolve(vm.resident_on);
|
|
string[] rec = new string[0];
|
|
|
|
if (residentHost != null && residentHost.opaque_ref == host.opaque_ref)
|
|
{
|
|
cantExecuteReasons[vm] = Messages.HOST_MENU_CURRENT_SERVER;
|
|
canExecutes[vm] = false;
|
|
}
|
|
else if (_recommendations[vm].TryGetValue(new XenRef<Host>(host.opaque_ref), out rec))
|
|
{
|
|
if (rec.Length > 0 && rec[0].Trim().ToLower() == "wlb")
|
|
{
|
|
double stars = 0;
|
|
ParseStarRating(rec, out stars);
|
|
canExecutes[vm] = true;
|
|
starRatings.Add(stars);
|
|
}
|
|
else
|
|
{
|
|
cantExecuteReasons[vm] = new Failure(rec).ShortMessage;
|
|
canExecutes[vm] = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cantExecuteReasons[vm] = FriendlyErrorNames.HOST_NOT_LIVE_SHORT;
|
|
canExecutes[vm] = false;
|
|
}
|
|
}
|
|
|
|
double averageStarRating = 0.0;
|
|
|
|
foreach (double s in starRatings)
|
|
{
|
|
averageStarRating += s;
|
|
}
|
|
averageStarRating /= starRatings.Count;
|
|
|
|
return new WlbRecommendation(canExecutes, averageStarRating, cantExecuteReasons);
|
|
}
|
|
|
|
private static bool ParseStarRating(string[] rec, out double starRating)
|
|
{
|
|
// recommedation string[] format examples:
|
|
// WLB; 0.0; zero_score_reason
|
|
// WLB; 3.4
|
|
// xapiError(such as the assert can boot failsthe);detail;detail
|
|
|
|
starRating = 0;
|
|
if (rec != null && rec.Length > 1 && rec[0].Trim().ToLower() == "wlb")
|
|
{
|
|
starRating = Helpers.ParseStringToDouble(rec[1], 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal class WlbRecommendation
|
|
{
|
|
public readonly Dictionary<VM, bool> CanExecuteByVM;
|
|
public readonly double StarRating;
|
|
public readonly Dictionary<VM, string> CantExecuteReasons;
|
|
|
|
public WlbRecommendation(Dictionary<VM, bool> canExecuteByVM, double starRating, Dictionary<VM, string> cantExecuteReasons)
|
|
{
|
|
CanExecuteByVM = canExecuteByVM;
|
|
StarRating = starRating;
|
|
CantExecuteReasons = cantExecuteReasons;
|
|
}
|
|
}
|
|
}
|
|
}
|