xenadmin/XenAdmin/Commands/DeleteVirtualDiskCommand.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

501 lines
21 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 System.Linq;
using XenAPI;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Core;
using XenAdmin.Commands.Controls;
using XenAdmin.Dialogs;
namespace XenAdmin.Commands
{
class DeleteVirtualDiskCommand : Command
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Allows auto unplug and delete on running VMs
/// </summary>
public bool AllowRunningVMDelete = false;
/// <summary>
/// Allows deletion of the VDI when multiple VMs are using this VDI. If the VMs are running you also need to set AllowRunningVMDelete
/// </summary>
public bool AllowMultipleVBDDelete = false;
public DeleteVirtualDiskCommand(IMainWindow mainWindow, IEnumerable<SelectedItem> selection)
: base(mainWindow, selection)
{
}
public override string ContextMenuText
{
get
{
var selection = GetSelection();
if (selection.Count > 1)
return Messages.MAINWINDOW_DELETE_OBJECTS;
return selection.AsXenObjects<VDI>().All(v => v.is_a_snapshot)
? Messages.DELETE_SNAPSHOT_MENU_ITEM
: Messages.DELETE_VIRTUAL_DISK;
}
}
protected override bool CanExecuteCore(SelectedItemCollection selection)
{
return selection.AllItemsAre<VDI>(CanExecute);
}
protected override bool ConfirmationRequired
{
get
{
// We always do some sort of confirmation for this delete
return true;
}
}
protected override string ConfirmationDialogText
{
get
{
if (!NeedMultipleWarnings)
{
// In the single warning type case we use the in built confirmation dialog to show a single warning
// if there are mixed vdi types then we will use the multiple warning dialog in confirm() override
SelectedItemCollection selectedItems = GetSelection();
VDI vdi = selectedItems[0].XenObject as VDI;
SR sr = vdi.Connection.Resolve<SR>(vdi.SR);
bool single = selectedItems.Count == 1;
var typ = vdi.VDIType();
switch (typ)
{
case VDI.FriendlyType.SNAPSHOT:
return single ? Messages.MESSAGEBOX_DELETE_SNAPSHOT : Messages.MESSAGEBOX_DELETE_SNAPSHOT_MULTIPLE;
case VDI.FriendlyType.ISO:
return single ? string.Format(Messages.MESSAGEBOX_DELETE_ISO, Helpers.GetName(vdi)) : Messages.MESSAGEBOX_DELETE_ISO_MULTIPLE;
case VDI.FriendlyType.SYSTEM_DISK:
return single ? Messages.MESSAGEBOX_DELETE_SYS_DISK : Messages.MESSAGEBOX_DELETE_SYS_DISK_MULTIPLE;
case VDI.FriendlyType.VIRTUAL_DISK:
return single ? Messages.MESSAGEBOX_DELETE_VD : Messages.MESSAGEBOX_DELETE_VD_MULTIPLE;
case VDI.FriendlyType.NONE:
return "";
}
}
return base.ConfirmationDialogText;
}
}
protected override string ConfirmationDialogTitle
{
get
{
if (!NeedMultipleWarnings)
{
// In the single warning type case we use the in built confirmation dialog to show a single warning
// if there are mixed vdi types then we will use the multiple warning dialog in confirm() override
SelectedItemCollection selectedItems = GetSelection();
VDI vdi = selectedItems[0].XenObject as VDI;
bool single = selectedItems.Count == 1;
var typ = vdi.VDIType();
switch (typ)
{
case VDI.FriendlyType.SNAPSHOT:
return single ? Messages.MESSAGEBOX_DELETE_SNAPSHOT_TITLE : Messages.MESSAGEBOX_DELETE_SNAPSHOTS_TITLE_MULTIPLE;
case VDI.FriendlyType.ISO:
return single ? Messages.MESSAGEBOX_DELETE_ISO_TITLE : Messages.MESSAGEBOX_DELETE_ISO_TITLE_MULTIPLE;
case VDI.FriendlyType.SYSTEM_DISK:
return single ? Messages.MESSAGEBOX_DELETE_SYS_DISK_TITLE : Messages.MESSAGEBOX_DELETE_SYS_DISK_TITLE_MULTIPLE;
case VDI.FriendlyType.VIRTUAL_DISK:
return single ? Messages.MESSAGEBOX_DELETE_VD_TITLE : Messages.MESSAGEBOX_DELETE_VD_TITLE_MUTLIPLE;
case VDI.FriendlyType.NONE:
return "";
}
}
return base.ConfirmationDialogTitle;
}
}
private bool? _needMultipleWarnings = null;
private bool NeedMultipleWarnings
{
get
{
// While just O(n) it does cast an entire list, so we cache the first result.
if (_needMultipleWarnings.HasValue)
return _needMultipleWarnings.Value;
_needMultipleWarnings = false;
SelectedItemCollection selection = GetSelection();
VDI.FriendlyType current = VDI.FriendlyType.NONE;
VDI.FriendlyType previous = VDI.FriendlyType.NONE;
if (selection.Count > 1)
{
for (int i = 0; i < selection.Count; i++)
{
VDI v = selection[i].XenObject as VDI;
if (v == null)
current = VDI.FriendlyType.NONE;
else
current = v.VDIType();
if (i > 0 && current != previous)
{
_needMultipleWarnings = true;
break;
}
previous = current;
}
}
return _needMultipleWarnings.Value;
}
}
protected override bool Confirm()
{
if (Program.RunInAutomatedTestMode)
return true;
if (NeedMultipleWarnings)
{
MultipleWarningDialog warningDialog = new MultipleWarningDialog(
Messages.MESSAGEBOX_DELETE_VD_TITLE_MUTLIPLE,
Messages.MULTI_VDI_DELETE_WARNING,
Messages.DELETE_ALL_BUTTON_LABEL);
SelectedItemCollection selectedItems = GetSelection();
List<VDI> snapshots = new List<VDI>();
List<VDI> isos = new List<VDI>();
List<VDI> systemVDisks = new List<VDI>();
List<VDI> virtualDisks = new List<VDI>();
foreach (VDI vdi in selectedItems.AsXenObjects<VDI>())
{
var typ = vdi.VDIType();
switch (typ)
{
case VDI.FriendlyType.SNAPSHOT:
snapshots.Add(vdi);
break;
case VDI.FriendlyType.ISO:
isos.Add(vdi);
break;
case VDI.FriendlyType.SYSTEM_DISK:
systemVDisks.Add(vdi);
break;
case VDI.FriendlyType.VIRTUAL_DISK:
virtualDisks.Add(vdi);
break;
case VDI.FriendlyType.NONE:
break;
}
}
if (snapshots.Count == 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_SNAPSHOT_TITLE,
Messages.WARNING_DELETE_SNAPSHOT,
snapshots.ConvertAll<IXenObject>(delegate(VDI v){return (IXenObject)v;}));
}
else if (snapshots.Count > 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_SNAPSHOTS_TITLE_MULTIPLE,
Messages.WARNING_DELETE_SNAPSHOT_MULTIPLE,
snapshots.ConvertAll<IXenObject>(delegate(VDI v){return (IXenObject)v;}));
}
if (isos.Count == 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_ISO_TITLE,
Messages.WARNING_DELETE_ISO,
isos.ConvertAll<IXenObject>(delegate(VDI v) { return (IXenObject)v; }));
}
else if (isos.Count > 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_ISO_TITLE_MULTIPLE,
Messages.WARNING_DELETE_ISO_MULTIPLE,
isos.ConvertAll<IXenObject>(delegate(VDI v) { return (IXenObject)v; }));
}
if (systemVDisks.Count == 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_SYS_DISK_TITLE,
Messages.WARNING_DELETE_SYS_DISK,
systemVDisks.ConvertAll<IXenObject>(delegate(VDI v) { return (IXenObject)v; }));
}
else if (systemVDisks.Count > 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_SYS_DISK_TITLE_MULTIPLE,
Messages.WARNING_DELETE_SYS_DISK_MULTIPLE,
systemVDisks.ConvertAll<IXenObject>(delegate(VDI v) { return (IXenObject)v; }));
}
if (virtualDisks.Count == 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_VD_TITLE,
Messages.WARNING_DELETE_VD,
virtualDisks.ConvertAll<IXenObject>(delegate(VDI v) { return (IXenObject)v; }));
}
else if (virtualDisks.Count > 1)
{
warningDialog.AddWarningMessage(
Messages.MESSAGEBOX_DELETE_VD_TITLE_MUTLIPLE,
Messages.WARNING_DELETE_VD_MULTIPLE,
virtualDisks.ConvertAll<IXenObject>(delegate(VDI v) { return (IXenObject)v; }));
}
return warningDialog.ShowDialog(Parent) == DialogResult.Yes;
}
else
{
return base.Confirm();
}
}
protected bool CanExecute(VDI vdi)
{
if (vdi == null)
return false;
SR sr = vdi.Connection.Resolve<SR>(vdi.SR);
if (sr == null)
return false;
if (vdi.Locked)
return false;
if (sr.Physical())
return false;
if (sr.IsToolsSR())
return false;
if (vdi.IsUsedByHA())
{
return false;
}
List<VBD> vbds = vdi.Connection.ResolveAll<VBD>(vdi.VBDs);
if (vbds.Count > 1 && !AllowMultipleVBDDelete)
return false;
foreach (VBD vbd in vbds)
{
VM vm = vdi.Connection.Resolve<VM>(vbd.VM);
if (vdi.type == vdi_type.system)
{
if (vm.power_state == vm_power_state.Running)
return false;
}
if (vbd.Locked)
return false;
if (vbd.currently_attached)
{
//Check if we can unplug
DeactivateVBDCommand cmd = new DeactivateVBDCommand(Program.MainWindow, vbd);
if (!AllowRunningVMDelete || !cmd.CanExecute())
return false;
}
}
if (sr.HBALunPerVDI())
return true;
if (!vdi.allowed_operations.Contains(vdi_operations.destroy))
{
if (AllowRunningVMDelete)
{
// We deliberately DONT call allowed operations because we assume we know better :)
// Xapi will think we can't delete because VBDs are plugged. We are going to unplug them.
// Known risks of this method that will make us fail because we are disrespecting xapi:
// - someone else is calling a delete on this vdi already, altering the allowed ops
// - the storage manager cannot perform a delete on the SR due to drivers
return true;
}
return false;
}
return true;
}
protected override string GetCantExecuteReasonCore(SelectedItem item)
{
VDI vdi = item.XenObject as VDI;
if (vdi == null)
return base.GetCantExecuteReasonCore(item);
SR sr = vdi.Connection.Resolve<SR>(vdi.SR);
if (sr == null)
return Messages.SR_COULD_NOT_BE_CONTACTED;
VDI.FriendlyType vdiType = vdi.VDIType();
if (vdi.Locked)
return vdiType == VDI.FriendlyType.SNAPSHOT ? Messages.CANNOT_DELETE_SNAPSHOT_IN_USE
: vdiType == VDI.FriendlyType.ISO ? Messages.CANNOT_DELETE_ISO_IN_USE
: Messages.CANNOT_DELETE_VD_IN_USE;
if (sr.Physical())
return FriendlyErrorNames.VDI_IS_A_PHYSICAL_DEVICE;
if (sr.IsToolsSR())
return Messages.CANNOT_DELETE_TOOLS_SR;
if (vdi.IsUsedByHA())
return Messages.CANNOT_DELETE_HA_VD;
if (vdi.IsMetadataForDR())
return Messages.CANNOT_DELETE_DR_VD;
List<VBD> vbds = vdi.Connection.ResolveAll<VBD>(vdi.VBDs);
if (vbds.Count > 1 && !AllowMultipleVBDDelete)
return Messages.CANNOT_DELETE_VDI_MULTIPLE_VBDS;
foreach (VBD vbd in vbds)
{
VM vm = vdi.Connection.Resolve<VM>(vbd.VM);
if (vdiType == VDI.FriendlyType.SYSTEM_DISK)
{
if (vm.power_state == vm_power_state.Running)
return string.Format(
Messages.CANNOT_DELETE_IN_USE_SYS_VD,
Helpers.GetName(vm).Ellipsise(20));
}
if (vbd.Locked)
return vdiType == VDI.FriendlyType.SNAPSHOT ? Messages.CANNOT_DELETE_SNAPSHOT_IN_USE
: vdiType == VDI.FriendlyType.ISO ? Messages.CANNOT_DELETE_ISO_IN_USE
: Messages.CANNOT_DELETE_VD_IN_USE;
if (vbd.currently_attached)
{
if (!AllowRunningVMDelete)
{
return string.Format(Messages.CANNOT_DELETE_VDI_ACTIVE_ON,
Helpers.GetName(vm).Ellipsise(20));
}
DeactivateVBDCommand cmd = new DeactivateVBDCommand(Program.MainWindow, vbd);
if (!cmd.CanExecute())
{
var reasons = cmd.GetCantExecuteReasons();
return reasons.Count > 0
? string.Format(Messages.CANNOT_DELETE_CANNOT_DEACTIVATE_REASON,
Helpers.GetName(vm).Ellipsise(20), reasons.ElementAt(0).Value)
: Messages.UNKNOWN;
}
}
}
// This is a necessary final check, there are other blocking reasons non covered in this method
// Known examples:
// - someone else is calling a delete on this vdi already, altering the allowed ops
// - the storage manager cannot perform a delete on the SR due to drivers
if (!vdi.allowed_operations.Contains(vdi_operations.destroy))
return vdiType == VDI.FriendlyType.SNAPSHOT ? Messages.CANNOT_DELETE_SNAPSHOT_GENERIC
: vdiType == VDI.FriendlyType.ISO ? Messages.CANNOT_DELETE_ISO_GENERIC
: Messages.CANNOT_DELETE_VD_GENERIC;
return base.GetCantExecuteReasonCore(item);
}
protected override XenAdmin.Dialogs.CommandErrorDialog GetErrorDialogCore(IDictionary<SelectedItem, string> cantExecuteReasons)
{
return new CommandErrorDialog(Messages.ERROR_DESTROYING_STORAGE_ITEMS_TITLE, Messages.ERROR_DESTROYING_STORAGE_ITEMS_MESSAGE, cantExecuteReasons);
}
protected override void ExecuteCore(SelectedItemCollection selection)
{
List<AsyncAction> actionsToComplete = new List<AsyncAction>();
List<VM> deletedVMSnapshots = new List<VM>();
foreach (VDI vdi in selection.AsXenObjects<VDI>())
{
if (vdi.Locked || !vdi.Show(XenAdmin.Properties.Settings.Default.ShowHiddenVMs))
continue;
actionsToComplete.AddRange(getDestroyVDIAction(vdi, deletedVMSnapshots));
}
if (actionsToComplete.Count == 0)
return;
if (actionsToComplete.Count > 1)
RunMultipleActions(actionsToComplete, Messages.ACTION_DELETING_MULTIPLE_STORAGE_ITEMS_TITLE, Messages.ACTION_DELETING_MULTIPLE_STORAGE_ITEMS_STATUS, Messages.COMPLETED, true);
else
actionsToComplete[0].RunAsync();
}
private List<AsyncAction> getDestroyVDIAction(VDI vdi, List<VM> deletedVMSnapshots)
{
List<AsyncAction> actions = new List<AsyncAction>();
// Destroy the entire snapshot if it exists. Else destroy disk
if (vdi.is_a_snapshot && vdi.GetVMs().Count >= 1)
{
foreach (VM vm in vdi.GetVMs())
{
if (!vm.is_a_snapshot || deletedVMSnapshots.Contains(vm))
continue;
AsyncAction action = new VMSnapshotDeleteAction(vm);
actions.Add(action);
deletedVMSnapshots.Add(vm);
}
}
else
{
SR sr = vdi.Connection.Resolve(vdi.SR);
if (sr == null)
{
// Nothing we can do here, but this should have been caught in the getcantexecutereason method and prompted
return actions;
}
DestroyDiskAction a = new DestroyDiskAction(vdi);
a.AllowRunningVMDelete = AllowRunningVMDelete;
actions.Add(a);
}
return actions;
}
}
}