/* 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.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using XenAdmin.Actions; using XenAdmin.Actions.Wlb; using XenAdmin.Commands; using XenAdmin.Controls; using XenAdmin.Core; using XenAdmin.Wlb; using XenAdmin.Dialogs; using XenAdmin.Dialogs.Wlb; using XenAdmin.Network; using XenAdmin.Help; using XenAdmin.TabPages; using XenAPI; namespace XenAdmin.TabPages { public partial class WlbPage : BaseTabPage { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private Pool _pool; private WlbPoolConfiguration _wlbPoolConfiguration; /// /// The pool that the panel is displaying WLB info for. Must be set on the event thread. /// public Pool Pool { set { Program.AssertOnEventThread(); if (null != value) { this.wlbOptimizePool.XenObject = value; if (_pool != value) { if (_pool != null) _pool.PropertyChanged -= Pool_PropertyChanged; _pool = value; _pool.PropertyChanged += Pool_PropertyChanged; } SetRetrievingConfiguration(); RetrieveConfiguration(); RefreshControls(); } } } public WlbPage() { InitializeComponent(); base.Text = Messages.WORKLOAD_BALANCING; pdSectionConfiguration.fixFirstColumnWidth(200); pictureBoxWarningTriangle.Image = SystemIcons.Warning.ToBitmap(); RefreshControls(); } #region ControlEvents private void buttonConfigure_Click(object sender, EventArgs e) { if (_pool == null) { return; } // if the _wlbPoolConfiguration is null, it means we cannot communicate with the WLB // server for some reason do nothing if (null != _wlbPoolConfiguration) { DisableButtons(); EditWLB(_pool); } } private void buttonEnableDisableWlb_Click(object sender, EventArgs e) { if (WlbServerState.GetState(_pool) != WlbServerState.ServerState.Enabled) { DisableButtons(); EnableWLB(); } else { DisableButtons(); DisableWLB(false); } } #endregion #region Wlb Actions private void EnableWLB() { // Enable WLB. EnableWLBAction action = new EnableWLBAction(_pool); // We will need to re-enable buttons when the action completes action.Completed += Program.MainWindow.action_Completed; action.Completed += this.action_Completed; action.RunAsync(); } private void DisableWLB(bool deconfigure) { DisableWLBAction action = new DisableWLBAction(_pool, deconfigure); action.Completed += Program.MainWindow.action_Completed; action.Completed += this.action_Completed; action.RunAsync(); } private void RetrieveConfiguration() { // only get the config if there are no other pending wlb actions if (null == HelpersGUI.FindActiveWLBAction(_pool.Connection)) { RetrieveWlbConfigurationAction action = new RetrieveWlbConfigurationAction(_pool); action.Completed += this.action_Completed; action.RunAsync(); } } private void SaveWLBConfig(WlbPoolConfiguration PoolConfiguration) { Dictionary completeConfiguration = PoolConfiguration.ToDictionary(); SendWlbConfigurationKind kind = SendWlbConfigurationKind.SetPoolConfiguration; // check for host configurations in the pool configuration if (PoolConfiguration.HostConfigurations.Count > 0) { // add the flag denoting that there are host configs to be saved kind |= SendWlbConfigurationKind.SetHostConfiguration; // get the key for each host config (host.uuid) foreach (string key in PoolConfiguration.HostConfigurations.ToDictionary().Keys) { //Drop any exising copy from the pool configuration if (completeConfiguration.ContainsKey(key)) { completeConfiguration.Remove(key); } // and add the task to the collection completeConfiguration.Add(key, PoolConfiguration.HostConfigurations.ToDictionary()[key]); } } // check for scheduled tasks in the pool configuration if (PoolConfiguration.ScheduledTasks.TaskList.Count > 0) { // add the flag denoting that there are scheduled tasks to be saved kind |= SendWlbConfigurationKind.SetScheduledTask; // get the key for each scheduled task foreach (string key in PoolConfiguration.ScheduledTasks.ToDictionary().Keys) { //Drop any exising copy from the pool configuration if (completeConfiguration.ContainsKey(key)) { completeConfiguration.Remove(key); } // and add the task to the collection completeConfiguration.Add(key, PoolConfiguration.ScheduledTasks.ToDictionary()[key]); } } SendWlbConfigurationAction action = new SendWlbConfigurationAction(_pool, PoolConfiguration.ToDictionary(), kind); using (var dialog = new ActionProgressDialog(action, ProgressBarStyle.Blocks)) { dialog.ShowCancel = true; dialog.ShowDialog(this); } Program.MainWindow.UpdateToolbars(); } #endregion #region Event Handlers protected void action_Completed(ActionBase sender) { // This seems to be called off the event thread AsyncAction action = (AsyncAction)sender; if (action.IsCompleted) { action.Completed -= action_Completed; if (action is EnableWLBAction || action is RetrieveWlbConfigurationAction || action is DisableWLBAction) { if (action is EnableWLBAction) { EnableWLBAction thisAction = (EnableWLBAction)action; _wlbPoolConfiguration = new WlbPoolConfiguration(thisAction.WlbConfiguration); } else if (action is RetrieveWlbConfigurationAction) { RetrieveWlbConfigurationAction thisAction = (RetrieveWlbConfigurationAction)action; if (thisAction.Succeeded) { _wlbPoolConfiguration = new WlbPoolConfiguration(thisAction.WlbConfiguration); } else { //_statusError = thisAction.Exception.Message; _wlbPoolConfiguration = null; } } else if (action is DisableWLBAction) { } Program.Invoke(Program.MainWindow, delegate() { if (_pool != null && _pool.Connection == action.Connection) { RefreshControls(); } }); } } } private void Pool_PropertyChanged(object sender, PropertyChangedEventArgs e) { // Refresh controls if a wlb pool preoprty changes, or if other_config changes if (e.PropertyName.StartsWith("wlb") || e.PropertyName == "other_config") { RefreshControls(); } } #endregion #region Private Methods /// /// Rebuilds the panel contents. Must be called on the event thread. /// private void RefreshControls() { Program.AssertOnEventThread(); if ((!this.Visible) || (null == _pool)) { return; } try { if (null != _wlbPoolConfiguration) { this.wlbOptimizePool.SuspendLayout(); // Update optimizePool control properties (versionSupport, apply button) this.wlbOptimizePool.SetOptControlProperties(_wlbPoolConfiguration.AutoBalanceEnabled, _wlbPoolConfiguration.PowerManagementEnabled); this.wlbOptimizePool.ResumeLayout(); } SetButtonState(); SetConfigurationItems(); } catch { //do nothing } } private void SetRetrievingConfiguration() { pdSectionConfiguration.PauseLayout(); pdSectionConfiguration.ClearData(); pdSectionConfiguration.AddEntry(Messages.WLB_RETRIEVING_CONFIGURATION, Messages.WLB_PLEASE_WAIT); pdSectionConfiguration.StartLayout(); pdSectionConfiguration.Expand(); } private void SetConfigurationItems() { // Populate the Configuration Items pdSectionConfiguration.PauseLayout(); pdSectionConfiguration.ClearData(); if (WlbServerState.GetState(_pool) == WlbServerState.ServerState.ConnectionError) { pdSectionConfiguration.AddEntry(Messages.WLB_STATUS, getPoolWLBStatus(_pool)); pdSectionConfiguration.AddEntry(Messages.WLB_SERVER_URL, getPoolWLBServer(_pool)); } else { pdSectionConfiguration.AddEntry(Messages.WLB_SERVER_URL, getPoolWLBServer(_pool)); pdSectionConfiguration.AddEntry(Messages.WLB_OPT_MODE, getPoolOptMode(_pool)); if (null != _wlbPoolConfiguration) { if (_wlbPoolConfiguration.AutomateOptimizationMode) { pdSectionConfiguration.AddEntry(Messages.WLB_NEXT_OPT_MODE_SCHEDULE_TEXT, getNextOptModeTask(_pool)); } pdSectionConfiguration.AddEntry(Messages.WLB_AUTO_OPT, getAutoOptimization(_pool)); pdSectionConfiguration.AddEntry(Messages.WLB_PWR_MGT, getPoolWlbPowerManagement(_pool)); } } pdSectionConfiguration.Expand(); pdSectionConfiguration.StartLayout(); } private RbacMethodList WLB_PERMISSION_CHECKS = new RbacMethodList( "pool.initialize_wlb", "pool.set_wlb_enabled", "pool.send_wlb_configuration" ); private bool PassedRbacChecks() { return Role.CanPerform(WLB_PERMISSION_CHECKS, _pool.Connection); } private void SetButtonState() { string statusMessage = string.Empty; string rbacUser = string.Empty; bool passedRbacChecks = PassedRbacChecks(); if (!passedRbacChecks) { List roleList = _pool.Connection.Session.Roles; roleList.Sort(); rbacUser = roleList[0].FriendlyName(); } AsyncAction thisAction = HelpersGUI.FindActiveWLBAction(_pool.Connection); if (thisAction is RetrieveWlbConfigurationAction) { DisableButtons(); return; } //if wlb is not configured, // disable the config button and change the text of the Enable button to Initialize WLB... //else if (!wlbConfigured) else if (WlbServerState.GetState(_pool) == WlbServerState.ServerState.NotConfigured) { buttonConnect.Visible = true; buttonConnect.Text = Messages.WLB_CONNECT; buttonConfigure.Visible = false; buttonEnableDisableWlb.Visible = false; buttonReports.Visible = false; pictureBoxWarningTriangle.Visible = false; if (passedRbacChecks) { statusMessage = string.Format(Messages.WLB_INITIALIZE_WLB_BLURB, _pool.Name()); } else { statusMessage = string.Format(Messages.WLB_INITIALIZE_WLB_BLURB_NO_PRIV, _pool.Name(), rbacUser); } labelStatus.Text = statusMessage; pictureBoxWarningTriangle.Visible = (WlbServerState.GetState(_pool) == WlbServerState.ServerState.ConnectionError); panelConfiguration.Visible = false; wlbOptimizePool.Visible = false; } //if there is an error contacting the WLB server, // disable the config button and change the text of the Enable button to Initialize WLB... //else if (!wlbConfigured) else if (WlbServerState.GetState(_pool) == WlbServerState.ServerState.ConnectionError) { buttonConnect.Visible = true; buttonConnect.Text = Messages.WLB_RECONNECT; buttonConfigure.Visible = false; buttonEnableDisableWlb.Visible = false; buttonReports.Visible = false; pictureBoxWarningTriangle.Visible = true; if (passedRbacChecks) { statusMessage = Messages.WLB_CONNECTION_ERROR_BLURB; } else { statusMessage = string.Format(Messages.WLB_CONNECTION_ERROR_BLURB_NO_PRIV, rbacUser); } labelStatus.Text = statusMessage; labelStatus.Visible = true; pictureBoxWarningTriangle.Visible = (WlbServerState.GetState(_pool) == WlbServerState.ServerState.ConnectionError); buttonReports.Enabled = false; panelConfiguration.Visible = false; wlbOptimizePool.Visible = false; } //if wlb is configured but not enabled, //enable the configure button and enble the enable button //else if (wlbConfigured && !wlbEnabled) else if (WlbServerState.GetState(_pool) == WlbServerState.ServerState.Disabled) { base.Text = Messages.WORKLOAD_BALANCING; buttonConnect.Visible = false; buttonConfigure.Visible = true; buttonEnableDisableWlb.Visible = true; buttonReports.Visible = true; pictureBoxWarningTriangle.Visible = true; buttonConfigure.Enabled = true; buttonEnableDisableWlb.Enabled = true; buttonEnableDisableWlb.Text = Messages.ENABLE_WLB_ELLIPSIS; buttonEnableDisableWlb.ImageIndex = 1; //Play arrow if (!passedRbacChecks) { statusMessage = string.Format(Messages.WLB_ENABLE_WLB_BLURB_NO_PRIV, _pool.Name(), rbacUser); } else { statusMessage = string.Format(Messages.WLB_ENABLE_WLB_BLURB, _pool.Name()); } labelStatus.Text = statusMessage; buttonReports.Enabled = true; panelConfiguration.Visible = true; wlbOptimizePool.Visible = true; } //otherwise, if wlb is configured and enabled, allow configuration, and show enable as Disable //else if (wlbEnabled) else if (WlbServerState.GetState(_pool) == WlbServerState.ServerState.Enabled) { buttonConnect.Visible = false; buttonConfigure.Visible = true; buttonEnableDisableWlb.Visible = true; buttonReports.Visible = true; pictureBoxWarningTriangle.Visible = false; buttonConfigure.Enabled = true; buttonEnableDisableWlb.Enabled = true; buttonEnableDisableWlb.Text = Messages.DISABLE_WLB_ELLIPSIS; buttonEnableDisableWlb.ImageIndex = 0; //Pause hashes if (!passedRbacChecks) { statusMessage = string.Format(Messages.WLB_NO_PERMISSION_BLURB, rbacUser); } else { statusMessage = string.Format(Messages.WLB_ENABLED_BLURB, _pool.Name()); } labelStatus.Text = statusMessage; buttonReports.Enabled = true; panelConfiguration.Visible = true; wlbOptimizePool.Visible = true; } if (PassedRbacChecks()) { //Show the buttons flowLayoutPanelLeftButtons.Visible = true; //panelButtons.Visible = true; } else { //disable and hide the buttons DisableButtons(); flowLayoutPanelLeftButtons.Visible = false; //panelButtons.Visible = false; } } private void DisableButtons() { buttonConfigure.Enabled = false; buttonEnableDisableWlb.Enabled = false; } internal void EditWLB(Pool pool) { // Do nothing if there is a WLB action in progress if (HelpersGUI.FindActiveWLBAction(pool.Connection) != null) { log.Debug("Not opening WLB dialog: an WLB action is in progress"); return; } if (!pool.Connection.IsConnected) { log.Debug("Not opening WLB dialog: the connection to the pool is now closed"); return; } try { WlbConfigurationDialog wlbConfigDialog = new WlbConfigurationDialog(pool); DialogResult dr = wlbConfigDialog.ShowDialog(); if (dr == DialogResult.OK) { _wlbPoolConfiguration = wlbConfigDialog.WlbPoolConfiguration; //check to see if the current opt mode matches the current schedule if (_wlbPoolConfiguration.AutomateOptimizationMode) { WlbPoolPerformanceMode scheduledPerfMode = _wlbPoolConfiguration.ScheduledTasks.GetCurrentScheduledPerformanceMode(); if (scheduledPerfMode != _wlbPoolConfiguration.PerformanceMode) { string blurb = string.Format(Messages.WLB_PROMPT_FOR_MODE_CHANGE_BLURB, getOptModeText(scheduledPerfMode), getOptModeText(_wlbPoolConfiguration.PerformanceMode)); DialogResult drModeCheck; using (var dlg = new ThreeButtonDialog( new ThreeButtonDialog.Details(null, blurb, Messages.WLB_PROMPT_FOR_MODE_CHANGE_CAPTION), ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)) { drModeCheck = dlg.ShowDialog(this); } if (drModeCheck == DialogResult.Yes) { _wlbPoolConfiguration.PerformanceMode = scheduledPerfMode; } } } SaveWLBConfig(_wlbPoolConfiguration); } } catch (Exception ex) { log.Debug("Unable to open the WLB configuration dialog.", ex); return; } if (!(WlbServerState.GetState(_pool) == WlbServerState.ServerState.NotConfigured)) { RetrieveConfiguration(); } } private string getPoolWLBStatus(Pool pool) { return String.Format(Messages.WLB_RECONFIGURE, String.IsNullOrEmpty(WlbServerState.GetFailureMessage(_pool)) ? string.Empty : WlbServerState.GetFailureMessage(_pool)); } private string getPoolWLBServer(Pool pool) { if (Helpers.WlbConfigured(pool.Connection)) { return pool.wlb_url; } return Messages.NONE_DEFINED; } private string getPoolOptMode(Pool pool) { string optMode = Messages.UNKNOWN; if (null != _wlbPoolConfiguration && Helpers.WlbConfigured(pool.Connection)) { if (_wlbPoolConfiguration.AutomateOptimizationMode) { optMode = string.Format(Messages.WLB_SCHEDULED_OPT_MODE, getOptModeText(_wlbPoolConfiguration.PerformanceMode)); } else { optMode = getOptModeText(_wlbPoolConfiguration.PerformanceMode); } } return optMode; } private string getOptModeText(WlbPoolPerformanceMode mode) { return (mode == WlbPoolPerformanceMode.MaximizePerformance) ? Messages.WLB_OPT_MODE_MAXIMIZEPERFORMANCE : Messages.WLB_OPT_MODE_MAXIMIZEDENSITY; } private string getNextOptModeTask(Pool pool) { //figure out the next task and return a nice string for display WlbScheduledTask nextTask = _wlbPoolConfiguration.ScheduledTasks.GetNextExecutingTask(); WlbScheduledTask.WlbTaskDaysOfWeek dayOfWeek; DateTime executeTime; WlbScheduledTask.GetLocalTaskTimes(nextTask.DaysOfWeek, nextTask.ExecuteTime, out dayOfWeek, out executeTime); string localDayOfWeek = WlbScheduledTask.DaysOfWeekL10N(dayOfWeek); return string.Format(Messages.WLB_NEXT_OPT_MODE_SCHEDULE_FORMAT, localDayOfWeek, HelpersGUI.DateTimeToString(executeTime, Messages.DATEFORMAT_HM, true)); } private string getAutoOptimization(Pool pool) { if (null != _wlbPoolConfiguration && Helpers.WlbConfigured(pool.Connection)) { return _wlbPoolConfiguration.AutoBalanceEnabled ? Messages.YES : Messages.NO; } return Messages.UNKNOWN; } private string getPoolWlbPowerManagement(Pool pool) { if (null != _wlbPoolConfiguration && Helpers.WlbConfigured(pool.Connection)) { return _wlbPoolConfiguration.PowerManagementEnabled ? Messages.YES : Messages.NO; } return Messages.UNKNOWN; } private void buttonConnect_Click(object sender, EventArgs e) { if (Helpers.WlbConfigured(_pool.Connection) && !(WlbServerState.GetState(_pool) == WlbServerState.ServerState.ConnectionError || WlbServerState.GetState(_pool) == WlbServerState.ServerState.NotConfigured)) { return; } DisableButtons(); WlbCredentialsDialog wlbCredentialsDialog = new WlbCredentialsDialog(_pool); DialogResult dr = wlbCredentialsDialog.ShowDialog(); if (dr == DialogResult.OK) { EnableWLB(); } else { SetButtonState(); } } private void buttonReports_Click(object sender, EventArgs e) { ViewWorkloadReportsCommand viewWorkloadReportsCommand = new ViewWorkloadReportsCommand(Program.MainWindow, _pool); viewWorkloadReportsCommand.Execute(); } #endregion } }