/* 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 { /// /// Performs the WLB recommendations for the Start-On, Resume-On and Migrate menu items. /// internal class WlbRecommendations { private readonly static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private readonly ReadOnlyCollection _vms; private readonly Session _session; private bool _initialized; private readonly Dictionary, string[]>> _recommendations = new Dictionary, string[]>>(); private bool _isError; /// /// Initializes a new instance of the class. /// /// The VMs that the recommendations are required for. /// The session. public WlbRecommendations(IEnumerable vms, Session session) { Util.ThrowIfEnumerableParameterNullOrEmpty(vms, "vms"); Util.ThrowIfParameterNull(session, "session"); _vms = new ReadOnlyCollection(new List(vms)); _session = session; } /// /// Calls VM.retrieve_wlb_recommendations for each of the VMs specified in the constructor. /// 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; } } /// /// Gets a value indicating whether an exception was thrown when calling VM.retrieve_wlb_recommendations. /// /// true if an exception was thrown; otherwise, false. 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, 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 starRatings = new List(); Dictionary canExecutes = new Dictionary(); Dictionary cantExecuteReasons = new Dictionary(); 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.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 CanExecuteByVM; public readonly double StarRating; public readonly Dictionary CantExecuteReasons; public WlbRecommendation(Dictionary canExecuteByVM, double starRating, Dictionary cantExecuteReasons) { CanExecuteByVM = canExecuteByVM; StarRating = starRating; CantExecuteReasons = cantExecuteReasons; } } } }