xenadmin/XenAdmin/Dialogs/EvacuateHostDialog.cs
Konstantina Chremmou ae22560ce8 Converted all extension get properties of the API classes to methods in order to
prevent them from being serialised alongside the API properties. This will also
be useful for moving the API bindings out of XenModel.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2017-09-03 03:35:30 +01:00

947 lines
37 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.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using XenAPI;
using XenAdmin.Actions;
using XenAdmin.Core;
using XenAdmin.Wlb;
using XenAdmin.Commands;
using XenAdmin.Actions.VMActions;
namespace XenAdmin.Dialogs
{
public partial class EvacuateHostDialog : DialogWithProgress
{
#region Private fields
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly Host host;
private EvacuateHostAction hostAction;
private Dictionary<string, VmPrecheckRow> vms;
private Dictionary<XenRef<VM>, String[]> reasons;
private string elevatedUName;
private string elevatedPass;
private Session elevatedSession;
private readonly string[] rbacMethods = new[]
{
"host.remove_from_other_config", // save VM list
"host.add_to_other_config",
"host.disable", // disable the host
"pbd.plug", // Repair SR and install tools
"pbd.create",
"vbd.eject",
"vbd.insert",
"vm.suspend", // Suspend VMs
"vbd.async_eject", // Change ISO
"vbd.async_insert"
};
#endregion
/* README
*
* This dialog inherits from DialogWithProgress. Be extremely careful about resizing it, as the DialogWithProgress
* class asserts that the controls which inherit it must match its dimensions... otherwise the expanding progress
* bar may not show.
*
*/
public EvacuateHostDialog(Host host)
: base(host.Connection)
{
this.host = host;
InitializeComponent();
Shrink();
NewMasterComboBox.DrawMode = DrawMode.OwnerDrawFixed;
NewMasterComboBox.DrawItem += new DrawItemEventHandler(NewMasterComboBox_DrawItem);
NewMasterComboBox.ItemHeight = 16;
if (!host.IsMaster() || connection.Cache.HostCount <= 1)
{
NewMasterComboBox.Enabled = false;
NewMasterComboBox.Visible = false;
NewMasterLabel.Visible = false;
labelMasterBlurb.Visible = false;
// Move the panel containing the VM listbox up to fill the gap where the labels were
const int pad = 6;
int extraSize = panel2.Top - labelMasterBlurb.Top - pad;
panel2.Top = labelMasterBlurb.Top + pad;
panel2.Height += extraSize;
}
Pool pool = Core.Helpers.GetPool(host.Connection);
if (Helpers.WlbEnabled(host.Connection) && WlbServerState.GetState(pool) == WlbServerState.ServerState.Enabled)
lableWLBEnabled.Visible = true;
vms = new Dictionary<string, VmPrecheckRow>();
this.host.PropertyChanged += new PropertyChangedEventHandler(hostUpdate);
ActiveControl = CloseButton;
}
void NewMasterComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (NewMasterComboBox.Enabled)
{
using (SolidBrush backBrush = new SolidBrush(NewMasterComboBox.BackColor))
{
e.Graphics.FillRectangle(backBrush, e.Bounds);
}
if (e.Index == -1)
return;
ToStringWrapper<Host> host = NewMasterComboBox.Items[e.Index] as ToStringWrapper<Host>;
if (host == null)
return;
Graphics g = e.Graphics;
Rectangle bounds = e.Bounds;
Image icon = Images.GetImage16For(host.item);
// Now draw the image
g.DrawImage(icon, bounds.Left + 1, bounds.Top + 1, bounds.Height - 2, bounds.Height - 2);
Rectangle bump = new Rectangle(bounds.Height, bounds.Y, bounds.Width - bounds.Height - RIGHT_PADDING, bounds.Height);
// And the text
Drawing.DrawText(g, host.ToString(), e.Font,
bump, e.ForeColor, e.BackColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
}
else
{
using (SolidBrush backBrush = new SolidBrush(SystemColors.Control))
{
e.Graphics.FillRectangle(backBrush, e.Bounds);
}
if (e.Index == -1)
return;
ToStringWrapper<Host> host = NewMasterComboBox.Items[e.Index] as ToStringWrapper<Host>;
if (host == null)
return;
Graphics g = e.Graphics;
Rectangle bounds = e.Bounds;
Image icon = Images.GetImage16For(host.item);
// Now draw the image
g.DrawImage(icon, new Rectangle(bounds.Left + 1, bounds.Top + 1, bounds.Height - 2, bounds.Height - 2), 0, 0, icon.Width, icon.Height, GraphicsUnit.Pixel, Core.Drawing.GreyScaleAttributes);
Rectangle bump = new Rectangle(bounds.Height, bounds.Y, bounds.Width - bounds.Height - RIGHT_PADDING, bounds.Height);
// And the text
Drawing.DrawText(g, host.ToString(), e.Font,
bump, SystemColors.GrayText, SystemColors.Control, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
}
}
const int RIGHT_PADDING = 5;
private void dataGridViewVms_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex >= 0 && e.RowIndex < dataGridViewVms.RowCount
&& e.ColumnIndex == columnAction.Index)
{
var row = dataGridViewVms.Rows[e.RowIndex] as VmPrecheckRow;
if (row != null && row.hasSolution())
{
Cursor.Current = Cursors.Hand;
return;
}
}
Cursor.Current = Cursors.Default;
}
private void dataGridViewVms_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex >= 0 && e.RowIndex < dataGridViewVms.RowCount
&& e.ColumnIndex == columnAction.Index)
{
var row = dataGridViewVms.Rows[e.RowIndex] as VmPrecheckRow;
if (row != null && row.hasSolution())
{
AsyncAction a = row.Solve();
if (a != null)
a.Completed += solveActionCompleted;
row.Update();
}
}
}
void solveActionCompleted(ActionBase sender)
{
// this should rescan the vm errors and update the dialog.
Program.Invoke(this, update);
}
private void hostUpdate(object o, PropertyChangedEventArgs args)
{
if (args == null || args.PropertyName == "name_label" || args.PropertyName == "resident_VMs")
{
Program.Invoke(this, update);
}
}
private void update()
{
Program.AssertOnEventThread();
populateVMs();
populateHosts();
Scan();
}
private void Scan()
{
DelegatedAsyncAction saveVMsAction = new DelegatedAsyncAction(connection, Messages.SAVING_VMS_ACTION_TITLE,
Messages.SAVING_VMS_ACTION_DESC, Messages.COMPLETED, delegate(Session session)
{
//Save Evacuated VMs for later
host.SaveEvacuatedVMs(session);
}, "host.remove_from_other_config", "host.add_to_other_config");
DelegatedAsyncAction action = new DelegatedAsyncAction(connection, Messages.MAINTENANCE_MODE,
Messages.SCANNING_VMS, Messages.SCANNING_VMS, delegate(Session session)
{
reasons = new Dictionary<XenRef<VM>, string[]>();
// WLB: get host wlb evacuate recommendation if wlb is enabled
if (Helpers.WlbEnabled(host.Connection))
{
try
{
reasons = XenAPI.Host.retrieve_wlb_evacuate_recommendations(session, host.opaque_ref);
}
catch (Exception ex)
{
log.Debug(ex.Message, ex);
}
}
// WLB: in case wlb has no recommendations or get errors when retrieve recommendation,
// assume retrieve_wlb_evacuate_recommendations returns 0 recommendation
// or return recommendations for all running vms on this host
if (reasons.Count == 0 || !ValidRecommendation(reasons))
reasons = Host.get_vms_which_prevent_evacuation(session, host.opaque_ref);
// take care of errors
Program.Invoke(this, delegate()
{
foreach (KeyValuePair<XenRef<VM>, String[]> kvp in reasons)
{
//WLB: filter out errors
if (string.Compare(kvp.Value[0].Trim(), "wlb", true) != 0)
ProcessError(kvp.Key.opaque_ref, kvp.Value);
//Update NewMasterComboBox for host power on recommendation
if ((session.Connection.Resolve(kvp.Key)).is_control_domain)
{
Host powerOnHost = session.Connection.Cache.Find_By_Uuid<Host>(kvp.Value[(int)RecProperties.ToHost]);
if (powerOnHost != null)
{
var previousSelection = NewMasterComboBox.SelectedItem as ToStringWrapper<Host>;
var hostToAdd = new ToStringWrapper<Host>(powerOnHost, powerOnHost.Name());
if (NewMasterComboBox.Items.Count == 0)
{
powerOnHost.PropertyChanged -= new PropertyChangedEventHandler(host_PropertyChanged);
powerOnHost.PropertyChanged += new PropertyChangedEventHandler(host_PropertyChanged);
NewMasterComboBox.Items.Add(hostToAdd);
}
else
{
foreach (ToStringWrapper<Host> tswh in NewMasterComboBox.Items)
{
if (tswh.item.CompareTo(powerOnHost) != 0)
{
powerOnHost.PropertyChanged -= new PropertyChangedEventHandler(host_PropertyChanged);
powerOnHost.PropertyChanged += new PropertyChangedEventHandler(host_PropertyChanged);
NewMasterComboBox.Items.Add(hostToAdd);
}
}
}
SelectProperItemInNewMasterComboBox(previousSelection);
}
}
}
});
}, true);
SetSession(saveVMsAction);
SetSession(action);
saveVMsAction.RunAsync();
using (var dlg = new ActionProgressDialog(action, ProgressBarStyle.Blocks))
dlg.ShowDialog(this);
RefreshEntermaintenanceButton();
}
private void VM_PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "resident_on" || args.PropertyName == "allowed_operations" || args.PropertyName == "current_operations")
{
Program.Invoke(this, () => RefreshVM(sender as VM));
}
else if (args.PropertyName == "virtualisation_status")
{
Program.Invoke(this, update);
}
else if (args.PropertyName == "guest_metrics")
{
VM v = sender as VM;
VM_guest_metrics gm = connection.Resolve(v.guest_metrics);
if (gm == null)
return;
gm.PropertyChanged -= new PropertyChangedEventHandler(gm_PropertyChanged);
gm.PropertyChanged += new PropertyChangedEventHandler(gm_PropertyChanged);
}
}
void gm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "PV_drivers_version")
Program.Invoke(this, update);
}
private void host_PropertyChanged(object sender, PropertyChangedEventArgs args)
{
populateHosts();
Program.Invoke(this, NewMasterComboBox.Refresh);
}
private void populateVMs()
{
Program.AssertOnEventThread();
deregisterVMEvents();
Dictionary<string, AsyncAction> solveActionsByUuid = new Dictionary<string,AsyncAction>();
foreach (VmPrecheckRow i in vms.Values)
solveActionsByUuid.Add(i.vm.uuid, i.solutionAction);
vms = new Dictionary<string, VmPrecheckRow>();
try
{
dataGridViewVms.SuspendLayout();
dataGridViewVms.Rows.Clear();
foreach (VM vm in connection.ResolveAll(host.resident_VMs))
{
if (vm.is_control_domain || vm.is_a_template)
continue;
vm.PropertyChanged += VM_PropertyChanged;
var row = new VmPrecheckRow(this, vm);
if (solveActionsByUuid.ContainsKey(vm.uuid))
row.UpdateSolutionAction(solveActionsByUuid[vm.uuid]);
vms.Add(vm.opaque_ref, row);
dataGridViewVms.Rows.Add(row);
}
}
finally
{
dataGridViewVms.ResumeLayout();
RefreshEntermaintenanceButton();
}
}
private void RefreshEntermaintenanceButton()
{
EvacuateButton.Enabled = true;
foreach (var row in dataGridViewVms.Rows)
{
var precheckRow = row as VmPrecheckRow;
if (precheckRow != null && (precheckRow.hasSolution() || precheckRow.hasSolutionActionInProgress()))
{
EvacuateButton.Enabled = false;
break;
}
}
}
private void deregisterVMEvents()
{
//Deregister event handlers from these VMs
foreach (var row in vms.Values)
{
VM v = row.vm;
v.PropertyChanged -= VM_PropertyChanged;
VM_guest_metrics gm = connection.Resolve(v.guest_metrics);
if (gm == null)
return;
gm.PropertyChanged -= gm_PropertyChanged;
}
}
private void populateHosts()
{
Program.AssertOnEventThread();
ToStringWrapper<Host> previousSelection = NewMasterComboBox.SelectedItem as ToStringWrapper<Host>;
NewMasterComboBox.BeginUpdate();
try
{
NewMasterComboBox.Items.Clear();
foreach (Host host in connection.Cache.Hosts)
{
Host_metrics metrics = connection.Resolve<Host_metrics>(host.metrics);
if (host.opaque_ref == this.host.opaque_ref)
continue;
host.PropertyChanged -= new PropertyChangedEventHandler(host_PropertyChanged);
host.PropertyChanged += new PropertyChangedEventHandler(host_PropertyChanged);
if (host.enabled && metrics != null && metrics.live)
NewMasterComboBox.Items.Add(new ToStringWrapper<Host>(host, host.Name()));
}
SelectProperItemInNewMasterComboBox(previousSelection);
}
finally
{
NewMasterComboBox.EndUpdate();
}
}
private void RefreshVM(VM vm)
{
foreach (var row in dataGridViewVms.Rows)
{
var precheckRow = row as VmPrecheckRow;
if (precheckRow != null && (precheckRow.vm == vm))
{
precheckRow.Update();
break;
}
}
dataGridViewVms.Refresh();
}
private class VmPrecheckRow : DataGridViewRow, IComparable<VmPrecheckRow>
{
public readonly VM vm;
EvacuateHostDialog dialog;
Solution solution;
public AsyncAction solutionAction;
public string error { get; private set; }
private DataGridViewImageCell cellImage = new DataGridViewImageCell();
private DataGridViewTextBoxCell cellVm = new DataGridViewTextBoxCell();
private DataGridViewTextBoxCell cellAction = new DataGridViewTextBoxCell();
public VmPrecheckRow(EvacuateHostDialog dialog, VM vm)
{
this.dialog = dialog;
this.vm = vm;
this.error = "";
this.solution = Solution.None;
Cells.AddRange(cellImage, cellVm, cellAction);
Update();
}
public void Update()
{
cellImage.Value = Images.GetImage16For(vm);
cellVm.Value = ToString();
cellAction.Value = error;
if (hasSolution())
{
cellAction.Style.Font = new Font(Program.DefaultFont, FontStyle.Underline);
cellAction.Style.ForeColor = Color.Blue;
}
else
{
cellAction.Style.Font = Program.DefaultFont;
cellAction.Style.ForeColor = DefaultForeColor;
}
cellAction.Style.SelectionForeColor = cellAction.Style.ForeColor;
cellAction.Style.SelectionBackColor = cellAction.Style.BackColor;
}
public override string ToString()
{
return vm.Name();
}
public int CompareTo(VmPrecheckRow otherVM)
{
return vm.CompareTo(otherVM.vm);
}
public void UpdateError(string message, Solution solution)
{
// still running action to solve a previous error, no point in overwriting or we could end up 'solving' it twice
if (hasSolutionActionInProgress())
{
this.error = Messages.EVACUATE_SOLUTION_IN_PROGRESS;
return;
}
this.solution = solution;
switch (solution)
{
case Solution.EjectCD:
error = String.Format(Messages.EVACUATE_HOST_EJECT_CD_PROMPT, message);
break;
case Solution.Suspend:
error = String.Format(Messages.EVACUATE_HOST_SUSPEND_VM_PROMPT, message);
break;
case Solution.Shutdown:
error = String.Format(Messages.EVACUATE_HOST_SHUTDOWN_VM_PROMPT, message);
break;
case Solution.InstallPVDrivers:
error = String.Format(vm.HasNewVirtualisationStates() ? Messages.EVACUATE_HOST_INSTALL_MGMNT_PROMPT : Messages.EVACUATE_HOST_INSTALL_TOOLS_PROMPT, message);
break;
case Solution.InstallPVDriversNoSolution:
// if the state is not unknown we have metrics and can show a detailed message.
// Otherwise go with the server and just say they aren't installed
error = !vm.GetVirtualisationStatus().HasFlag(XenAPI.VM.VirtualisationStatus.UNKNOWN)
? vm.GetVirtualisationWarningMessages()
: Messages.PV_DRIVERS_NOT_INSTALLED;
break;
}
Update();
}
public void UpdateSolutionAction(AsyncAction action)
{
this.solutionAction = action;
this.error = hasSolutionActionInProgress() ? Messages.EVACUATE_SOLUTION_IN_PROGRESS : "";
Update();
}
public AsyncAction Solve()
{
AsyncAction a = null;
switch (solution)
{
case Solution.EjectCD:
a = new ChangeVMISOAction(vm.Connection, vm, null, vm.FindVMCDROM());
dialog.SetSession(a);
dialog.DoAction(a);
break;
case Solution.Suspend:
a = new VMSuspendAction(vm);
dialog.SetSession(a);
dialog.DoAction(a);
break;
case Solution.Shutdown:
a = new VMHardShutdown(vm);
dialog.SetSession(a);
dialog.DoAction(a);
break;
case Solution.InstallPVDrivers:
a = new InstallToolsCommand(Program.MainWindow, vm).ExecuteGetAction();
// The install pv tools action is marked as complete after they have taken the user to the console and loaded the disc
// Rescanning when the action is 'complete' in this case doesn't gain us anything then. Keep showing the "Click here to install PV drivers" text.
dialog.SetSession(a);
solutionAction = a;
return a;
}
solutionAction = a;
solution = Solution.None;
// we show this, then register an event handler on the action completed to re-update the text/solution
this.error = Messages.EVACUATE_SOLUTION_IN_PROGRESS;
return a;
}
internal bool hasSolution()
{
return solution != Solution.None
&& solution != Solution.InstallPVDriversNoSolution;
}
internal bool hasSolutionActionInProgress()
{
return solutionAction != null && !solutionAction.IsCompleted;
}
}
private void RepairButton_Click(object sender, EventArgs e)
{
CloseButton.Text = Messages.CLOSE;
NewMasterComboBox.Enabled = false;
ToStringWrapper<Host> newMaster = NewMasterComboBox.SelectedItem as ToStringWrapper<Host>;
hostAction = new EvacuateHostAction(host, newMaster != null ? newMaster.item : null, reasons ?? new Dictionary<XenRef<VM>, string[]>(), AddHostToPoolCommand.NtolDialog, AddHostToPoolCommand.EnableNtolDialog);
hostAction.Completed += Program.MainWindow.action_Completed;
SetSession(hostAction);
Program.MainWindow.UpdateToolbars();
//Closes all per-Connection and per-VM wizards for the given connection.
Program.MainWindow.CloseActiveWizards(host.Connection);
EvacuateButton.Enabled = false; // disable evac button, it will get re-enabled when action completes
DoAction(hostAction);
}
private void SetSession(AsyncAction action)
{
Program.AssertOnEventThread();
if (elevatedUName == null)
return;
action.sudoPassword = elevatedPass;
action.sudoUsername = elevatedUName;
if (elevatedSession != null)
{
// we still have the session from the role elevation dialog
// use this first
action.Session = elevatedSession;
elevatedSession = null;
}
else
action.Session = action.NewSession();
}
private void DoAction(AsyncAction action)
{
action.Changed += action_Changed;
action.Completed += action_Completed;
Grow(action.RunAsync);
}
private void action_Changed(ActionBase action)
{
Program.Invoke(this, () => UpdateProgressControls(action));
}
private void action_Completed(ActionBase sender)
{
Program.Invoke(this, delegate()
{
if (sender == null)
return;
if (sender != hostAction)
{
//re-enable the buttons
this.EvacuateButton.Enabled = true;
this.CloseButton.Enabled = true;
return;
}
if (hostAction.Exception == null)
{
Close();
}
this.EvacuateButton.Enabled = true;
this.CloseButton.Enabled = true;
this.NewMasterComboBox.Enabled = true;
Failure failure = hostAction.Exception as Failure;
if (failure == null)
return;
if (failure.ErrorDescription.Count > 0 &&
failure.ErrorDescription[0] == Failure.HOST_NOT_ENOUGH_FREE_MEMORY)
{
failure.ErrorDescription[0] = Failure.HA_NO_PLAN;
failure.Setup();
}
ProcessError(null, failure.ErrorDescription.ToArray());
});
Program.Invoke(this, () => FinalizeProgressControls(sender));
}
private bool CanSuspendVm(String vmRef)
{
if (vmRef == null)
return false;
VM vm = connection.Resolve(new XenRef<VM>(vmRef));
return vm != null && vm.allowed_operations != null && vm.allowed_operations.Contains(vm_operations.suspend);
}
private void ProcessError(String vmRef, String[] ErrorDescription)
{
try
{
if (ErrorDescription.Length == 0)
return;
switch (ErrorDescription[0])
{
case Failure.VM_REQUIRES_SR:
vmRef = ErrorDescription[1];
SR sr = connection.Resolve(new XenRef<SR>(ErrorDescription[2]));
if (sr == null)
return;
if (sr.content_type == SR.Content_Type_ISO)
{
UpdateVMWithError(vmRef, Messages.EVACUATE_HOST_LOCAL_CD, Solution.EjectCD);
}
else
{
UpdateVMWithError(vmRef, Messages.EVACUATE_HOST_LOCAL_STORAGE, CanSuspendVm(vmRef) ? Solution.Suspend : Solution.Shutdown);
}
break;
case Failure.VM_MISSING_PV_DRIVERS:
vmRef = ErrorDescription[1];
VM vm = connection.Resolve(new XenRef<VM>(vmRef));
if (vm != null && InstallToolsCommand.CanExecute(vm))
UpdateVMWithError(vmRef, String.Empty, Solution.InstallPVDrivers);
else
UpdateVMWithError(vmRef, String.Empty, Solution.InstallPVDriversNoSolution);
break;
case Failure.HOST_NOT_ENOUGH_FREE_MEMORY:
if (vmRef == null)
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
Messages.EVACUATE_HOST_NOT_ENOUGH_MEMORY,
Messages.EVACUATE_HOST_NOT_ENOUGH_MEMORY_TITLE)))
{
dlg.ShowDialog(this);
}
vmRef = ErrorDescription[1];
UpdateVMWithError(vmRef, String.Empty, CanSuspendVm(vmRef) ? Solution.Suspend : Solution.Shutdown);
break;
case Failure.HA_NO_PLAN:
foreach (string _vmRef in vms.Keys)
{
UpdateVMWithError(_vmRef, String.Empty, CanSuspendVm(_vmRef) ? Solution.Suspend : Solution.Shutdown);
}
if (vmRef == null)
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
Messages.EVACUATE_HOST_NO_OTHER_HOSTS,
Messages.EVACUATE_HOST_NO_OTHER_HOSTS_TITLE)))
{
dlg.ShowDialog(this);
}
break;
default:
AddDefaultSuspendOperation(vmRef);
break;
}
}
catch (Exception e)
{
log.Debug("Exception processing exception", e);
log.Debug(e, e);
AddDefaultSuspendOperation(vmRef);
}
}
private void AddDefaultSuspendOperation(String vmRef)
{
if (vmRef == null)
return;
UpdateVMWithError(vmRef, String.Empty, CanSuspendVm(vmRef) ? Solution.Suspend : Solution.Shutdown);
}
enum Solution { None, EjectCD, Suspend, InstallPVDrivers, InstallPVDriversNoSolution, Shutdown };
void UpdateVMWithError(string opaqueRef, string message, Solution solution)
{
if (opaqueRef == null || !vms.ContainsKey(opaqueRef))
return;
var row = vms[opaqueRef];
row.UpdateError(message, solution);
Program.Invoke(this, dataGridViewVms.Refresh);
}
private void CloseButton_Click(object sender, EventArgs e)
{
this.Close();
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
Text = string.Format(Messages.EVACUATE_HOST_DIALOG_TITLE, host.Name());
//This dialog uses several different actions all of which might need an elevated session
//We sudo once for all of them and store the session, or close the dialog.
List<Role> validRoles = new List<Role>();
if (!connection.Session.IsLocalSuperuser
&& !Registry.DontSudo
&& !Role.CanPerform(new RbacMethodList(rbacMethods), connection, out validRoles))
{
var sudoDialog = XenAdminConfigManager.Provider.SudoDialogDelegate;
var result = sudoDialog(validRoles, connection, Text);
if (!result.Result)
{
Close();
return;
}
elevatedPass = result.ElevatedPassword;
elevatedUName = result.ElevatedUsername;
elevatedSession = result.ElevatedSession;
}
update();
}
private void SelectProperItemInNewMasterComboBox(ToStringWrapper<Host> previousSelection)
{
bool selected = false;
if (previousSelection != null)
{
foreach (ToStringWrapper<Host> host in NewMasterComboBox.Items)
{
if (host.item.opaque_ref == previousSelection.item.opaque_ref)
{
NewMasterComboBox.SelectedItem = host;
selected = true;
break;
}
}
}
if (NewMasterComboBox.Items.Count > 0 && !selected)
{
NewMasterComboBox.SelectedIndex = 0;
}
}
private bool ValidRecommendation(Dictionary<XenRef<VM>, String[]> reasons)
{
bool valid = true;
List<Host> controlDomain = new List<Host>();
foreach (KeyValuePair<XenRef<VM>, String[]> kvp in reasons)
{
if ((this.connection.Resolve(kvp.Key)).is_control_domain)
{
controlDomain.Add(this.connection.Cache.Find_By_Uuid<Host>(kvp.Value[(int)RecProperties.ToHost]));
}
}
foreach (KeyValuePair<XenRef<VM>, String[]> kvp in reasons)
{
if (string.Compare(kvp.Value[0].Trim(), "wlb", true) == 0)
{
Host toHost = this.connection.Cache.Find_By_Uuid<Host>(kvp.Value[(int)RecProperties.ToHost]);
if (!(this.connection.Resolve(kvp.Key)).is_control_domain && !toHost.IsLive() && !controlDomain.Contains(toHost))
{
valid = false;
break;
}
}
}
return valid;
}
private void EvacuateHostDialog_FormClosed(object sender, FormClosedEventArgs e)
{
if (host != null)
host.PropertyChanged -= hostUpdate;
foreach (var h in connection.Cache.Hosts)
h.PropertyChanged -= host_PropertyChanged;
deregisterVMEvents();
if (elevatedSession != null && elevatedSession.uuid != null)
{
// NOTE: This doesnt happen currently, as we always scan once. Here as cheap insurance.
// we still have the session from the role elevation dialog
// it hasn't been used by an action so needs to be logged out
elevatedSession.logout();
}
}
}
}