2017-01-16 20:59:50 +01:00
|
|
|
|
/* Copyright (c) Citrix Systems, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
* 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.Diagnostics;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
|
|
|
|
using XenAdmin.Core;
|
|
|
|
|
using XenAdmin.Dialogs;
|
|
|
|
|
using XenAPI;
|
|
|
|
|
using XenAdmin.Commands;
|
2017-11-17 02:04:45 +01:00
|
|
|
|
using XenCenterLib;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace XenAdmin.TabPages
|
|
|
|
|
{
|
2017-01-25 13:38:33 +01:00
|
|
|
|
internal partial class VMStoragePage : BaseTabPage
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
|
|
2015-10-28 19:12:31 +01:00
|
|
|
|
private readonly DataGridViewColumn storageLinkColumn;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
private VM vm;
|
|
|
|
|
|
|
|
|
|
public VMStoragePage()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
2015-10-28 19:12:31 +01:00
|
|
|
|
storageLinkColumn = ColumnSRVolume;
|
|
|
|
|
|
2017-01-25 13:38:33 +01:00
|
|
|
|
Text = Messages.VIRTUAL_DISKS;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
dataGridViewStorage.Sort(ColumnDevicePosition, ListSortDirection.Ascending);
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-13 18:47:27 +02:00
|
|
|
|
public override string HelpID => "TabPageStorage";
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public VM VM
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Program.AssertOnEventThread();
|
|
|
|
|
|
2017-01-25 13:38:33 +01:00
|
|
|
|
// de-register old listeners...
|
|
|
|
|
UnregisterHandlers();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
vm = value;
|
|
|
|
|
multipleDvdIsoList1.VM = vm;
|
|
|
|
|
|
|
|
|
|
if (vm != null)
|
|
|
|
|
vm.PropertyChanged += vm_PropertyChanged;
|
|
|
|
|
|
|
|
|
|
BuildList();
|
|
|
|
|
|
|
|
|
|
// set all columns to be preferred width but allow user to resize.
|
|
|
|
|
foreach (DataGridViewTextBoxColumn col in dataGridViewStorage.Columns)
|
2015-10-28 19:12:31 +01:00
|
|
|
|
{
|
|
|
|
|
if (col != storageLinkColumn)
|
|
|
|
|
{
|
|
|
|
|
col.Width = col.GetPreferredWidth(DataGridViewAutoSizeColumnMode.AllCells, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void vdi_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// Nothing changing on any of the vdi's will require a rebuild of the list...
|
2016-09-13 02:29:47 +02:00
|
|
|
|
Program.Invoke(this, () =>
|
|
|
|
|
{
|
|
|
|
|
if (e.PropertyName == "allowed_operations")
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
else
|
|
|
|
|
UpdateData();
|
|
|
|
|
});
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void vbd_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// Only rebuild list if VDIs changed - otherwise just refresh
|
|
|
|
|
|
|
|
|
|
if (e.PropertyName == "VDI" || e.PropertyName == "empty" || e.PropertyName == "device")
|
|
|
|
|
{
|
|
|
|
|
BuildList();
|
|
|
|
|
}
|
|
|
|
|
else if (e.PropertyName == "allowed_operations")
|
|
|
|
|
{
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
}
|
|
|
|
|
else if (e.PropertyName == "currently_attached")
|
|
|
|
|
{
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
UpdateData();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UpdateData();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-25 13:38:33 +01:00
|
|
|
|
private void UnregisterHandlers()
|
|
|
|
|
{
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (VBD vbd in vm.Connection.ResolveAll(vm.VBDs))
|
|
|
|
|
{
|
|
|
|
|
vbd.PropertyChanged -= vbd_PropertyChanged;
|
|
|
|
|
|
|
|
|
|
VDI vdi = vm.Connection.Resolve(vbd.VDI);
|
|
|
|
|
if (vdi == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
vdi.PropertyChanged -= vdi_PropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vm.PropertyChanged -= vm_PropertyChanged;
|
2017-01-31 15:35:59 +01:00
|
|
|
|
|
|
|
|
|
multipleDvdIsoList1.DeregisterEvents();
|
2017-01-25 13:38:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void PageHidden()
|
|
|
|
|
{
|
|
|
|
|
UnregisterHandlers();
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private void UpdateData()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
dataGridViewStorage.SuspendLayout();
|
|
|
|
|
foreach (DataGridViewRow r in dataGridViewStorage.Rows)
|
|
|
|
|
{
|
|
|
|
|
VBDRow row = r as VBDRow;
|
|
|
|
|
row.UpdateCells();
|
|
|
|
|
}
|
|
|
|
|
dataGridViewStorage.Sort(dataGridViewStorage.SortedColumn,
|
|
|
|
|
dataGridViewStorage.SortOrder == SortOrder.Ascending
|
|
|
|
|
? ListSortDirection.Ascending
|
|
|
|
|
: ListSortDirection.Descending);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
log.Error("Error updating vm storage list.", e);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
dataGridViewStorage.ResumeLayout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void vm_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (e.PropertyName == "VBDs")
|
|
|
|
|
BuildList();
|
|
|
|
|
else if (e.PropertyName == "power_state" || e.PropertyName == "Locked")
|
2015-04-16 15:17:02 +02:00
|
|
|
|
Program.Invoke(this, UpdateButtons);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void BuildList()
|
|
|
|
|
{
|
|
|
|
|
Program.AssertOnEventThread();
|
|
|
|
|
if (!this.Visible)
|
|
|
|
|
return;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
dataGridViewStorage.SuspendLayout();
|
|
|
|
|
DataGridViewSelectedRowCollection vbdSavedItems = dataGridViewStorage.SelectedRows;
|
|
|
|
|
|
|
|
|
|
dataGridViewStorage.Rows.Clear();
|
|
|
|
|
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-10-28 19:12:31 +01:00
|
|
|
|
bool storageLinkColumnVisible = false;
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
List<bool> devices_in_use = new List<bool>();
|
|
|
|
|
foreach (VBD vbd in vm.Connection.ResolveAll(vm.VBDs))
|
|
|
|
|
{
|
|
|
|
|
vbd.PropertyChanged -= new PropertyChangedEventHandler(vbd_PropertyChanged);
|
|
|
|
|
vbd.PropertyChanged += new PropertyChangedEventHandler(vbd_PropertyChanged);
|
|
|
|
|
|
2017-09-03 04:33:29 +02:00
|
|
|
|
if (!vbd.IsCDROM() && !vbd.IsFloppyDrive())
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
VDI vdi = vm.Connection.Resolve(vbd.VDI);
|
|
|
|
|
if (vdi == null || !vdi.Show(Properties.Settings.Default.ShowHiddenVMs))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
SR sr = vm.Connection.Resolve(vdi.SR);
|
2017-09-03 04:33:29 +02:00
|
|
|
|
if (sr == null || sr.IsToolsSR())
|
2013-06-24 13:41:48 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2015-10-28 19:12:31 +01:00
|
|
|
|
storageLinkColumnVisible = vdi.sm_config.ContainsKey("SVID");
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
vdi.PropertyChanged -= new PropertyChangedEventHandler(vdi_PropertyChanged);
|
|
|
|
|
vdi.PropertyChanged += new PropertyChangedEventHandler(vdi_PropertyChanged);
|
|
|
|
|
|
|
|
|
|
dataGridViewStorage.Rows.Add(new VBDRow(vbd, vdi, sr));
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
if (int.TryParse(vbd.userdevice, out i))
|
|
|
|
|
{
|
|
|
|
|
while (devices_in_use.Count <= i)
|
|
|
|
|
{
|
|
|
|
|
devices_in_use.Add(false);
|
|
|
|
|
}
|
|
|
|
|
devices_in_use[i] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//CA-47050: the dnsColumn should be autosized to Fill, but should not become smaller than a minimum
|
|
|
|
|
//width, which is chosen to be the column's contents (including header) width. To find what this is
|
|
|
|
|
//set temporarily the column's autosize mode to AllCells.
|
2018-06-21 15:41:19 +02:00
|
|
|
|
HelpersGUI.ResizeGridViewColumnToAllCells(ColumnDevicePath);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-28 19:12:31 +01:00
|
|
|
|
|
|
|
|
|
storageLinkColumn.Visible = storageLinkColumnVisible;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
dataGridViewStorage.Sort(dataGridViewStorage.SortedColumn, dataGridViewStorage.SortOrder == SortOrder.Ascending ? ListSortDirection.Ascending : ListSortDirection.Descending);
|
|
|
|
|
|
|
|
|
|
IEnumerable<VBD> vbdsSelected = from VBDRow row in vbdSavedItems select row.VBD;
|
|
|
|
|
foreach (VBDRow row in dataGridViewStorage.Rows)
|
|
|
|
|
{
|
|
|
|
|
row.Selected = vbdsSelected.Contains(row.VBD);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
log.ErrorFormat("Exception building VM storage list: {0}", e.Message);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
dataGridViewStorage.ResumeLayout();
|
|
|
|
|
}
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<VBDRow> SelectedVBDRows
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (dataGridViewStorage.SelectedRows.Count == 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
List<VBDRow> rows = new List<VBDRow>();
|
|
|
|
|
foreach (DataGridViewRow r in dataGridViewStorage.SelectedRows)
|
|
|
|
|
rows.Add(r as VBDRow);
|
|
|
|
|
|
|
|
|
|
return rows;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateButtons()
|
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
AttachVirtualDiskCommand attachCmd = new AttachVirtualDiskCommand(Program.MainWindow, vm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
AttachButton.Enabled = attachCmd.CanExecute();
|
|
|
|
|
AddButton.Enabled = attachCmd.CanExecute();
|
|
|
|
|
|
|
|
|
|
List<VBDRow> vbdRows = SelectedVBDRows;
|
|
|
|
|
|
|
|
|
|
if (dataGridViewStorage.Rows.Count == 0 || vbdRows == null || vm == null)
|
|
|
|
|
{
|
|
|
|
|
DeactivateButton.Enabled = false;
|
2016-09-13 02:29:47 +02:00
|
|
|
|
DetachButton.Enabled = false;
|
|
|
|
|
DeleteButton.Enabled = false;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
EditButton.Enabled = false;
|
|
|
|
|
MoveButton.Enabled = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
EditButton.Enabled = vbdRows.Count == 1 && !vbdRows[0].VBD.Locked && !vbdRows[0].VDI.Locked;
|
|
|
|
|
|
|
|
|
|
List<SelectedItem> selectedVDIs = new List<SelectedItem>();
|
|
|
|
|
List<SelectedItem> selectedVBDs = new List<SelectedItem>();
|
|
|
|
|
|
|
|
|
|
foreach (VBDRow r in vbdRows)
|
|
|
|
|
{
|
|
|
|
|
selectedVDIs.Add(new SelectedItem(r.VDI));
|
|
|
|
|
selectedVBDs.Add(new SelectedItem(r.VBD));
|
|
|
|
|
}
|
2014-01-22 13:39:32 +01:00
|
|
|
|
DeleteVirtualDiskCommand deleteCmd = new DeleteVirtualDiskCommand(Program.MainWindow, selectedVDIs);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// User has visibility that this disk in use by this VM. Allow unplug + delete in single step (non default behaviour),
|
|
|
|
|
// but only if we are the only VBD (default behaviour)
|
|
|
|
|
deleteCmd.AllowRunningVMDelete = true;
|
|
|
|
|
if (deleteCmd.CanExecute())
|
|
|
|
|
{
|
|
|
|
|
DeleteButtonContainer.RemoveAll();
|
2016-09-13 02:29:47 +02:00
|
|
|
|
DeleteButton.Enabled = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DeleteButtonContainer.SetToolTip(deleteCmd.ToolTipText);
|
2016-09-13 02:29:47 +02:00
|
|
|
|
DeleteButton.Enabled = false;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Command activationCmd = null;
|
|
|
|
|
|
|
|
|
|
SelectedItemCollection vbdCol = new SelectedItemCollection(selectedVBDs);
|
|
|
|
|
if (vbdCol.AsXenObjects<VBD>().Find(delegate(VBD vbd) { return !vbd.currently_attached; }) == null)
|
|
|
|
|
{
|
|
|
|
|
// no VBDs are attached so we are deactivating
|
2016-09-13 02:29:47 +02:00
|
|
|
|
toolStripMenuItemDeactivate.Text = DeactivateButton.Text = Messages.DEACTIVATE;
|
2014-01-22 13:39:32 +01:00
|
|
|
|
activationCmd = new DeactivateVBDCommand(Program.MainWindow, selectedVBDs);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-09-06 15:53:46 +02:00
|
|
|
|
// this is the default cause in the mixed attached/detached scenario. We try to activate all the selection
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// The command error reports afterwards about the ones which are already attached
|
2016-09-13 02:29:47 +02:00
|
|
|
|
toolStripMenuItemDeactivate.Text = DeactivateButton.Text = Messages.ACTIVATE;
|
2014-01-22 13:39:32 +01:00
|
|
|
|
activationCmd = new ActivateVBDCommand(Program.MainWindow, selectedVBDs);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (activationCmd.CanExecute())
|
|
|
|
|
{
|
|
|
|
|
DeactivateButtonContainer.RemoveAll();
|
|
|
|
|
DeactivateButton.Enabled = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DeactivateButtonContainer.SetToolTip(activationCmd.ToolTipText);
|
|
|
|
|
DeactivateButton.Enabled = false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-22 13:39:32 +01:00
|
|
|
|
DetachVirtualDiskCommand detachCmd = new DetachVirtualDiskCommand(Program.MainWindow, selectedVDIs, vm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (detachCmd.CanExecute())
|
|
|
|
|
{
|
|
|
|
|
DetachButtonContainer.RemoveAll();
|
2016-09-13 02:29:47 +02:00
|
|
|
|
DetachButton.Enabled = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DetachButtonContainer.SetToolTip(detachCmd.ToolTipText);
|
2016-09-13 02:29:47 +02:00
|
|
|
|
DetachButton.Enabled = false;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move button
|
2018-10-05 16:02:39 +02:00
|
|
|
|
Command moveCmd = MoveVirtualDiskDialog.MoveMigrateCommand(Program.MainWindow, new SelectedItemCollection(selectedVDIs));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (moveCmd.CanExecute())
|
|
|
|
|
{
|
|
|
|
|
MoveButton.Enabled = true;
|
|
|
|
|
MoveButtonContainer.RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MoveButton.Enabled = false;
|
|
|
|
|
MoveButtonContainer.SetToolTip(moveCmd.ToolTipText);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
#region Actions on VDIs
|
|
|
|
|
|
|
|
|
|
private void AddVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
var cmd = new AddVirtualDiskCommand(Program.MainWindow, vm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (cmd.CanExecute())
|
|
|
|
|
cmd.Execute();
|
|
|
|
|
|
|
|
|
|
// don't wait for the property change to trigger it, as this can take a while
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void AttachVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2014-01-22 13:39:32 +01:00
|
|
|
|
AttachVirtualDiskCommand cmd = new AttachVirtualDiskCommand(Program.MainWindow, vm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (cmd.CanExecute())
|
|
|
|
|
cmd.Execute();
|
|
|
|
|
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void MoveVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
List<VBDRow> rows = SelectedVBDRows;
|
|
|
|
|
if (rows == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-10-05 16:02:39 +02:00
|
|
|
|
var vdis = (from VBDRow r in rows select new SelectedItem(r.VDI)).ToList();
|
|
|
|
|
|
|
|
|
|
var cmd = MoveVirtualDiskDialog.MoveMigrateCommand(Program.MainWindow, new SelectedItemCollection(vdis));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (cmd.CanExecute())
|
|
|
|
|
cmd.Execute();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void DetachVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
List<VBDRow> rows = SelectedVBDRows;
|
|
|
|
|
if (rows == null)
|
|
|
|
|
return;
|
|
|
|
|
List<SelectedItem> l = new List<SelectedItem>();
|
|
|
|
|
foreach (VBDRow r in rows)
|
|
|
|
|
l.Add(new SelectedItem(r.VDI));
|
|
|
|
|
|
2014-01-22 13:39:32 +01:00
|
|
|
|
DetachVirtualDiskCommand cmd = new DetachVirtualDiskCommand(Program.MainWindow, l, vm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (cmd.CanExecute())
|
|
|
|
|
cmd.Execute();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void DeleteVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
List<VBDRow> rows = SelectedVBDRows;
|
|
|
|
|
if (rows == null)
|
|
|
|
|
return;
|
|
|
|
|
List<SelectedItem> l = new List<SelectedItem>();
|
|
|
|
|
foreach (VBDRow r in rows)
|
|
|
|
|
l.Add(new SelectedItem(r.VDI));
|
|
|
|
|
|
2014-01-22 13:39:32 +01:00
|
|
|
|
DeleteVirtualDiskCommand cmd = new DeleteVirtualDiskCommand(Program.MainWindow, l);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// User has visibility that this disk in use by this VM. Allow unplug + delete in single step (non default behaviour),
|
|
|
|
|
// but only if we are the only VBD (default behaviour)
|
|
|
|
|
cmd.AllowRunningVMDelete = true;
|
|
|
|
|
if (cmd.CanExecute())
|
|
|
|
|
cmd.Execute();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void EditVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (vm == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
List<VBDRow> rows = SelectedVBDRows;
|
|
|
|
|
|
|
|
|
|
if (rows == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
new PropertiesDialog(rows[0].VDI).ShowDialog(this);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void ActivateDeactivateVdi()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2016-09-13 02:29:47 +02:00
|
|
|
|
List<VBDRow> rows = SelectedVBDRows;
|
|
|
|
|
if (rows == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var selection = from VBDRow r in rows select new SelectedItem(r.VBD);
|
|
|
|
|
SelectedItemCollection col = new SelectedItemCollection(selection);
|
|
|
|
|
|
|
|
|
|
Command cmd = null;
|
|
|
|
|
if (col.AsXenObjects<VBD>().Find(vbd => !vbd.currently_attached) == null)
|
|
|
|
|
cmd = new DeactivateVBDCommand(Program.MainWindow, col);
|
|
|
|
|
else
|
|
|
|
|
cmd = new ActivateVBDCommand(Program.MainWindow, col);
|
|
|
|
|
|
|
|
|
|
if (cmd.CanExecute())
|
|
|
|
|
cmd.Execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Datagridview event handlers
|
|
|
|
|
|
|
|
|
|
private void dataGridViewStorage_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.Column == ColumnDevicePosition)
|
|
|
|
|
{
|
|
|
|
|
e.SortResult = StringUtility.NaturalCompare(e.CellValue1.ToString(), e.CellValue2.ToString());
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
e.Handled = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void dataGridViewStorage_SelectedIndexChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void dataGridViewStorage_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.RowIndex >= 0 && EditButton.Enabled)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// If double-click was on data row (not including header), launch the storage edit box
|
2016-09-13 02:29:47 +02:00
|
|
|
|
EditVdi();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
private void dataGridViewStorage_MouseUp(object sender, MouseEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2016-09-13 02:29:47 +02:00
|
|
|
|
DataGridView.HitTestInfo hitTestInfo = dataGridViewStorage.HitTest(e.X, e.Y);
|
|
|
|
|
|
|
|
|
|
if (hitTestInfo.Type == DataGridViewHitTestType.None)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2016-09-13 02:29:47 +02:00
|
|
|
|
dataGridViewStorage.ClearSelection();
|
|
|
|
|
}
|
|
|
|
|
else if (hitTestInfo.Type == DataGridViewHitTestType.Cell && e.Button == MouseButtons.Right
|
|
|
|
|
&& 0 <= hitTestInfo.RowIndex && hitTestInfo.RowIndex < dataGridViewStorage.Rows.Count
|
|
|
|
|
&& !dataGridViewStorage.Rows[hitTestInfo.RowIndex].Selected)
|
|
|
|
|
{
|
|
|
|
|
// Select the row that the user right clicked on (similiar to outlook) if it's not already in the selection
|
|
|
|
|
// (avoids clearing a multiselect if you right click inside it)
|
|
|
|
|
// Check if the CurrentCell is the cell the user right clicked on (but the row is not Selected) [CA-64954]
|
|
|
|
|
// This happens when the grid is initially shown: the current cell is the first cell in the first column, but the row is not selected
|
|
|
|
|
|
|
|
|
|
if (dataGridViewStorage.CurrentCell == dataGridViewStorage[hitTestInfo.ColumnIndex, hitTestInfo.RowIndex])
|
|
|
|
|
dataGridViewStorage.Rows[hitTestInfo.RowIndex].Selected = true;
|
|
|
|
|
else
|
|
|
|
|
dataGridViewStorage.CurrentCell = dataGridViewStorage[hitTestInfo.ColumnIndex, hitTestInfo.RowIndex];
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
if ((hitTestInfo.Type == DataGridViewHitTestType.None || hitTestInfo.Type == DataGridViewHitTestType.Cell)
|
|
|
|
|
&& e.Button == MouseButtons.Right)
|
|
|
|
|
{
|
|
|
|
|
contextMenuStrip1.Show(dataGridViewStorage, new Point(e.X, e.Y));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void dataGridViewStorage_KeyUp(object sender, KeyEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2016-09-13 02:29:47 +02:00
|
|
|
|
if (e.KeyCode != Keys.Apps)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (dataGridViewStorage.SelectedRows.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
// 3 is the defaul control margin
|
|
|
|
|
contextMenuStrip1.Show(dataGridViewStorage, 3, dataGridViewStorage.ColumnHeadersHeight + 3);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DataGridViewRow row = dataGridViewStorage.SelectedRows[0];
|
|
|
|
|
contextMenuStrip1.Show(dataGridViewStorage, 3, row.Height * (row.Index + 2));
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Button and Toolstrip event handlers
|
|
|
|
|
|
|
|
|
|
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2016-09-13 02:29:47 +02:00
|
|
|
|
bool add = AddButton.Visible && AddButton.Enabled;
|
|
|
|
|
bool attach = AttachButton.Visible && AttachButton.Enabled;
|
|
|
|
|
bool deactivate = DeactivateButton.Enabled;
|
|
|
|
|
bool move = MoveButton.Enabled;
|
|
|
|
|
bool delete = DeleteButton.Visible && DeleteButton.Enabled;
|
|
|
|
|
bool detach = DetachButton.Visible && DetachButton.Enabled;
|
|
|
|
|
bool edit = EditButton.Visible && EditButton.Enabled;
|
|
|
|
|
|
|
|
|
|
if (!(add || attach || deactivate || move || delete || detach || edit))
|
|
|
|
|
{
|
|
|
|
|
e.Cancel = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return;
|
2016-09-13 02:29:47 +02:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
toolStripMenuItemAdd.Visible = add;
|
|
|
|
|
toolStripMenuItemAttach.Visible = attach;
|
|
|
|
|
toolStripMenuItemDeactivate.Visible = deactivate;
|
|
|
|
|
toolStripMenuItemMove.Visible = move;
|
|
|
|
|
toolStripMenuItemDelete.Visible = delete;
|
|
|
|
|
toolStripMenuItemDetach.Visible = detach;
|
|
|
|
|
toolStripMenuItemProperties.Visible = edit;
|
|
|
|
|
toolStripSeparator1.Visible = (add || attach || deactivate || move || delete || detach) && edit;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2016-09-13 02:29:47 +02:00
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemAdd_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
AddVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemAttach_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
AttachVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemDeactivate_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
ActivateDeactivateVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemMove_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
MoveVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemDelete_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
DeleteVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemDetach_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
DetachVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void toolStripMenuItemProperties_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
EditVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AddButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
AddVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AttachButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
AttachVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeactivateButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
ActivateDeactivateVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MoveButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
MoveVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeleteDriveButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
DeleteVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DetachButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
DetachVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EditButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
EditVdi();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class VBDRow : DataGridViewRow
|
|
|
|
|
{
|
|
|
|
|
public readonly VBD VBD;
|
|
|
|
|
|
|
|
|
|
public readonly VDI VDI;
|
|
|
|
|
|
|
|
|
|
public readonly SR SR;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Arguments should never be null
|
|
|
|
|
/// </summary>
|
|
|
|
|
public VBDRow(VBD vbd, VDI vdi, SR sr)
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(vbd != null && vdi != null && sr != null, "vbd, vdi and sr must be set to a non-null value");
|
|
|
|
|
|
|
|
|
|
VBD = vbd;
|
|
|
|
|
VDI = vdi;
|
|
|
|
|
SR = sr;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
|
{
|
|
|
|
|
DataGridViewTextBoxCell c = new DataGridViewTextBoxCell();
|
|
|
|
|
c.Value = CellValue(i);
|
|
|
|
|
Cells.Add(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int CompareTo(object o)
|
|
|
|
|
{
|
|
|
|
|
VBDRow r = (VBDRow)o;
|
|
|
|
|
string c1 = this.VBD.userdevice;
|
|
|
|
|
string c2 = r.VBD.userdevice;
|
|
|
|
|
int i1;
|
|
|
|
|
int i2;
|
|
|
|
|
if (int.TryParse(c1, out i1) && int.TryParse(c2, out i2))
|
|
|
|
|
{
|
|
|
|
|
return i1 - i2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return c1.CompareTo(c2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private object CellValue(int col)
|
|
|
|
|
{
|
|
|
|
|
switch (col)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
return VBD.userdevice;
|
|
|
|
|
case 1:
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return VDI.Name();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 2:
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return VDI.Description();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 3:
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return SR.Name();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 4:
|
2015-10-26 17:01:55 +01:00
|
|
|
|
string name;
|
|
|
|
|
return VDI.sm_config.TryGetValue("displayname", out name) ? name : "";
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 5:
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return VDI.SizeText();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 6:
|
2017-09-05 03:15:38 +02:00
|
|
|
|
return VBD.IsReadOnly() ? Messages.YES : Messages.NO;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 7:
|
2017-09-05 03:15:38 +02:00
|
|
|
|
return GetPriorityString(VBD.GetIoNice());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case 8:
|
|
|
|
|
return VBD.currently_attached ? Messages.YES : Messages.NO;
|
|
|
|
|
case 9:
|
|
|
|
|
return VBD.device == "" ? Messages.STORAGE_PANEL_UNKNOWN : string.Format("/dev/{0}", VBD.device);
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentException(String.Format("Invalid column number {0} in VBDRenderer.CellValue()", col));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateCells()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
|
{
|
|
|
|
|
Cells[i].Value = CellValue(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetPriorityString(int p)
|
|
|
|
|
{
|
|
|
|
|
switch (p)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
return Messages.STORAGE_WORST;
|
|
|
|
|
case 7:
|
|
|
|
|
return Messages.STORAGE_BEST;
|
|
|
|
|
default:
|
|
|
|
|
return p.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|