mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-29 02:18:07 +01:00
d7b519a53c
Signed-off-by: Konstantina Chremmou <Konstantina.Chremmou@cloud.com>
480 lines
17 KiB
C#
Executable File
480 lines
17 KiB
C#
Executable File
/* Copyright (c) Cloud Software Group, Inc.
|
|
*
|
|
* 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.Linq;
|
|
using System.Windows.Forms;
|
|
using System.Drawing;
|
|
using XenAdmin.Core;
|
|
using XenAdmin.Network;
|
|
using XenAPI;
|
|
using XenAdmin.Commands;
|
|
using XenAdmin.Dialogs;
|
|
using XenAdmin.Controls.DataGridViewEx;
|
|
using XenCenterLib;
|
|
|
|
|
|
namespace XenAdmin.TabPages
|
|
{
|
|
public partial class PhysicalStoragePage : BaseTabPage
|
|
{
|
|
private IXenConnection connection;
|
|
private Host host;
|
|
|
|
private bool NeedBuildList;
|
|
|
|
private SelectionManager selectionManager;
|
|
private bool showTrimButton;
|
|
|
|
public PhysicalStoragePage()
|
|
{
|
|
InitializeComponent();
|
|
|
|
base.Text = Messages.STORAGE_TAB_TITLE;
|
|
PBD_CollectionChangedWithInvoke=Program.ProgramInvokeHandler(PBD_CollectionChanged);
|
|
|
|
trimButton.Command = new TrimSRCommand();
|
|
newSRButton.Command = new NewSRCommand();
|
|
}
|
|
|
|
public override string HelpID => "TabPageStorage";
|
|
|
|
/// <summary>
|
|
/// Make sure you set this before you set the connection,
|
|
/// as the connection is the one which rebuilds the list
|
|
/// </summary>
|
|
public Host Host
|
|
{
|
|
set
|
|
{
|
|
if (host != null)
|
|
{
|
|
host.PropertyChanged -= host_PropertyChanged;
|
|
}
|
|
|
|
host = value;
|
|
|
|
if (host != null)
|
|
{
|
|
host.PropertyChanged += host_PropertyChanged;
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly CollectionChangeEventHandler PBD_CollectionChangedWithInvoke;
|
|
public IXenConnection Connection
|
|
{
|
|
set
|
|
{
|
|
UnregisterHandlers();
|
|
|
|
connection = value;
|
|
|
|
if (connection != null)
|
|
{
|
|
connection.Cache.RegisterCollectionChanged<PBD>(PBD_CollectionChangedWithInvoke);
|
|
connection.XenObjectsUpdated += XenObjectUpdated;
|
|
|
|
Pool pool = Helpers.GetPoolOfOne(connection);
|
|
if (pool != null)
|
|
pool.PropertyChanged += pool_PropertyChanged;
|
|
}
|
|
|
|
RefreshTrimButton();
|
|
BuildList();
|
|
}
|
|
}
|
|
|
|
internal void SetSelectionBroadcaster(SelectionBroadcaster selectionBroadcaster, IMainWindow mainWindow)
|
|
{
|
|
selectionBroadcaster.BindTo(newSRButton, mainWindow);
|
|
}
|
|
|
|
private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
|
|
{
|
|
if (dataGridViewSr.SelectedRows.Count != 1)
|
|
{
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
contextMenuStrip.Items.Clear();
|
|
|
|
var row = dataGridViewSr.SelectedRows[0] as SRRow;
|
|
if (row != null)
|
|
{
|
|
contextMenuStrip.Items.AddRange(Program.MainWindow.ContextMenuBuilder.Build(row.SR));
|
|
}
|
|
|
|
}
|
|
|
|
private void XenObjectUpdated(object sender, EventArgs e)
|
|
{
|
|
if (NeedBuildList)
|
|
{
|
|
BuildList();
|
|
}
|
|
}
|
|
|
|
private void PBD_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
NeedBuildList = true;
|
|
BuildList();
|
|
}
|
|
|
|
private void pool_PropertyChanged(object sender1, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == "default_SR")
|
|
NeedBuildList = true;
|
|
}
|
|
|
|
private void host_PropertyChanged(object sender1, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == "PBDs")
|
|
NeedBuildList = true;
|
|
}
|
|
|
|
private void sr_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == "PBDs")
|
|
{
|
|
NeedBuildList = true;
|
|
}
|
|
else
|
|
{
|
|
Program.Invoke(Program.MainWindow, () => RefreshItem(sender as SR));
|
|
}
|
|
}
|
|
|
|
private void RefreshItem(SR sr=null)
|
|
{
|
|
if (sr == null)
|
|
return;
|
|
|
|
foreach (DataGridViewRow row in dataGridViewSr.Rows)
|
|
{
|
|
var srRow = row as SRRow;
|
|
if (srRow != null && sr.Equals(srRow.SR))
|
|
{
|
|
srRow.UpdateDetails();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UnregisterHandlers()
|
|
{
|
|
if (connection == null)
|
|
return;
|
|
|
|
connection.Cache.DeregisterCollectionChanged<PBD>(PBD_CollectionChangedWithInvoke);
|
|
connection.XenObjectsUpdated -= XenObjectUpdated;
|
|
|
|
foreach (SR sr in connection.Cache.SRs)
|
|
{
|
|
sr.PropertyChanged -= sr_PropertyChanged;
|
|
}
|
|
|
|
Pool pool = Helpers.GetPoolOfOne(connection);
|
|
if (pool != null)
|
|
pool.PropertyChanged -= pool_PropertyChanged;
|
|
}
|
|
|
|
public override void PageHidden()
|
|
{
|
|
UnregisterHandlers();
|
|
}
|
|
|
|
private void BuildList()
|
|
{
|
|
if (!this.Visible)
|
|
return;
|
|
int selectedIndex = dataGridViewSr.SelectedRows.Count == 1 ? dataGridViewSr.SelectedRows[0].Index : -1;
|
|
|
|
dataGridViewSr.SuspendLayout();
|
|
|
|
try
|
|
{
|
|
dataGridViewSr.Rows.Clear();
|
|
|
|
if (connection == null)
|
|
return;
|
|
|
|
List<PBD> pbds = host != null ? new List<PBD>(connection.ResolveAll(host.PBDs))
|
|
: new List<PBD>(connection.Cache.PBDs);
|
|
|
|
List<String> srs = new List<String>();
|
|
|
|
foreach (PBD pbd in pbds)
|
|
{
|
|
SR sr = pbd.Connection.Resolve(pbd.SR);
|
|
|
|
if (sr == null || sr.IsToolsSR() || !sr.Show(Properties.Settings.Default.ShowHiddenVMs))
|
|
continue;
|
|
|
|
// From MSDN:
|
|
// Returns the zero-based index of item in the sorted List<T>, if item is found;
|
|
// otherwise, a negative number that is the bitwise complement of the index of
|
|
// the next element that is larger than item or, if there is no larger element,
|
|
// the bitwise complement of Count.
|
|
int index = srs.BinarySearch(sr.opaque_ref);
|
|
|
|
// Don't allow duplicates
|
|
if (index >= 0)
|
|
continue;
|
|
|
|
sr.PropertyChanged -= sr_PropertyChanged;
|
|
sr.PropertyChanged += sr_PropertyChanged;
|
|
|
|
index = ~index;
|
|
srs.Insert(index, sr.opaque_ref);
|
|
|
|
SRRow row = new SRRow(sr);
|
|
dataGridViewSr.Rows.Add(row);
|
|
}
|
|
|
|
if (selectedIndex >= 0 && selectedIndex < dataGridViewSr.Rows.Count)
|
|
{
|
|
// Select previously selected item
|
|
dataGridViewSr.Rows[selectedIndex].Selected = true;
|
|
}
|
|
else
|
|
{
|
|
// Select first item
|
|
if (dataGridViewSr.Rows.Count > 0)
|
|
dataGridViewSr.Rows[0].Selected = true;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
dataGridViewSr.ResumeLayout();
|
|
}
|
|
RefreshButtons();
|
|
NeedBuildList = false;
|
|
}
|
|
|
|
private void dataGridViewSr_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
DataGridView.HitTestInfo hitTestInfo = dataGridViewSr.HitTest(e.X, e.Y);
|
|
|
|
if (hitTestInfo.Type == DataGridViewHitTestType.None)
|
|
{
|
|
dataGridViewSr.ClearSelection();
|
|
}
|
|
else if (hitTestInfo.Type == DataGridViewHitTestType.Cell && e.Button == MouseButtons.Right
|
|
&& 0 <= hitTestInfo.RowIndex && hitTestInfo.RowIndex < dataGridViewSr.Rows.Count
|
|
&& !dataGridViewSr.Rows[hitTestInfo.RowIndex].Selected)
|
|
{
|
|
if (dataGridViewSr.CurrentCell == dataGridViewSr[hitTestInfo.ColumnIndex, hitTestInfo.RowIndex])
|
|
dataGridViewSr.Rows[hitTestInfo.RowIndex].Selected = true;
|
|
else
|
|
dataGridViewSr.CurrentCell = dataGridViewSr[hitTestInfo.ColumnIndex, hitTestInfo.RowIndex];
|
|
}
|
|
|
|
if ((hitTestInfo.Type == DataGridViewHitTestType.None || hitTestInfo.Type == DataGridViewHitTestType.Cell)
|
|
&& e.Button == MouseButtons.Right)
|
|
{
|
|
contextMenuStrip.Show(dataGridViewSr, new Point(e.X, e.Y));
|
|
}
|
|
}
|
|
|
|
private void dataGridViewSr_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
|
|
{
|
|
var sr1 = ((SRRow)dataGridViewSr.Rows[e.RowIndex1]).SR;
|
|
var sr2 = ((SRRow)dataGridViewSr.Rows[e.RowIndex2]).SR;
|
|
|
|
string sr1CompStr = null;
|
|
string sr2CompStr = null;
|
|
|
|
if (e.Column.Index == columnUsage.Index)
|
|
{
|
|
int percent1 = sr1.physical_size == 0 ? 0 : (int)(100.0 * sr1.physical_utilisation / (double)sr1.physical_size);
|
|
int percent2 = sr2.physical_size == 0 ? 0 : (int)(100.0 * sr2.physical_utilisation / (double)sr2.physical_size);
|
|
long diff = percent1 - percent2;
|
|
e.SortResult = diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
else if (e.Column.Index == columnSize.Index)
|
|
{
|
|
long diff = sr1.physical_size - sr2.physical_size;
|
|
e.SortResult = diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
else if (e.Column.Index == columnVirtAlloc.Index)
|
|
{
|
|
long diff = sr1.virtual_allocation - sr2.virtual_allocation;
|
|
e.SortResult = diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
else if (e.Column.Index == columnName.Index)
|
|
{
|
|
sr1CompStr = sr1.Name();
|
|
sr2CompStr = sr2.Name();
|
|
}
|
|
else if (e.Column.Index == columnDescription.Index)
|
|
{
|
|
sr1CompStr = sr1.Description();
|
|
sr2CompStr = sr2.Description();
|
|
}
|
|
|
|
if (sr1CompStr != null && sr2CompStr != null)
|
|
{
|
|
var descCompare = StringUtility.NaturalCompare(sr1CompStr, sr2CompStr);
|
|
if (descCompare != 0)
|
|
{
|
|
e.SortResult = descCompare;
|
|
}
|
|
else
|
|
{
|
|
var refCompare = string.Compare(sr1.opaque_ref, sr2.opaque_ref, StringComparison.Ordinal);
|
|
e.SortResult = refCompare;
|
|
}
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
private void buttonProperties_Click(object sender, EventArgs e)
|
|
{
|
|
if (dataGridViewSr.SelectedRows.Count != 1)
|
|
return;
|
|
|
|
var srRow = dataGridViewSr.SelectedRows[0] as SRRow;
|
|
|
|
if (srRow != null)
|
|
{
|
|
SR sr = srRow.SR;
|
|
|
|
new PropertiesDialog(sr).ShowDialog(this);
|
|
}
|
|
}
|
|
|
|
void dataGridViewSrs_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
RefreshButtons();
|
|
if (showTrimButton)
|
|
{
|
|
List<SelectedItem> selectedSRs = (from SRRow row in dataGridViewSr.SelectedRows select new SelectedItem(row.SR)).ToList();
|
|
selectionManager.SetSelection(selectedSRs);
|
|
}
|
|
}
|
|
|
|
private void RefreshButtons()
|
|
{
|
|
buttonProperties.Enabled = dataGridViewSr.SelectedRows.Count == 1;
|
|
}
|
|
|
|
private void RefreshTrimButton()
|
|
{
|
|
showTrimButton = connection != null;
|
|
if (showTrimButton)
|
|
{
|
|
trimButtonContainer.Visible = true;
|
|
if (selectionManager == null)
|
|
selectionManager = new SelectionManager();
|
|
selectionManager.BindTo(trimButton, Program.MainWindow);
|
|
}
|
|
else
|
|
{
|
|
trimButtonContainer.Visible = false;
|
|
trimButton.SelectionBroadcaster = null;
|
|
}
|
|
}
|
|
|
|
protected class SRRow: DataGridViewRow
|
|
{
|
|
private SR sr;
|
|
public SR SR { get { return sr; } }
|
|
|
|
private DataGridViewExImageCell imageCell = new DataGridViewExImageCell();
|
|
private DataGridViewTextBoxCell nameCell = new DataGridViewTextBoxCell();
|
|
private DataGridViewTextBoxCell descriptionCell = new DataGridViewTextBoxCell();
|
|
private DataGridViewTextBoxCell typeCell = new DataGridViewTextBoxCell();
|
|
private DataGridViewTextBoxCell sharedCell = new DataGridViewTextBoxCell();
|
|
private DataGridViewTextBoxCell usageCell = new DataGridViewTextBoxCell();
|
|
private DataGridViewTextBoxCell sizeCell = new DataGridViewTextBoxCell();
|
|
private DataGridViewTextBoxCell virtAllocCell = new DataGridViewTextBoxCell();
|
|
|
|
|
|
public SRRow(SR sr)
|
|
{
|
|
this.sr = sr;
|
|
Cells.AddRange(imageCell,
|
|
nameCell,
|
|
descriptionCell,
|
|
typeCell,
|
|
sharedCell,
|
|
usageCell,
|
|
sizeCell,
|
|
virtAllocCell);
|
|
|
|
sr.PropertyChanged += sr_PropertyChanged;
|
|
UpdateDetails();
|
|
}
|
|
|
|
public void UpdateDetails()
|
|
{
|
|
if (this.sr != null)
|
|
{
|
|
// Work out percent usage
|
|
int percent = 0;
|
|
double ratio = 0;
|
|
if (this.sr.physical_size > 0)
|
|
{
|
|
ratio = this.sr.physical_utilisation / (double)this.sr.physical_size;
|
|
percent = (int)(100.0 * ratio);
|
|
}
|
|
string percentString = string.Format(Messages.DISK_PERCENT_USED, percent, Util.DiskSizeString(this.sr.physical_utilisation));
|
|
|
|
imageCell.Value = Images.GetImage16For(Images.GetIconFor(this.sr));
|
|
nameCell.Value = this.sr.Name();
|
|
descriptionCell.Value = this.sr.Description();
|
|
typeCell.Value = this.sr.FriendlyTypeName();
|
|
sharedCell.Value = this.sr.shared ? Messages.YES : Messages.NO;
|
|
usageCell.Value = percentString;
|
|
sizeCell.Value = Util.DiskSizeString(this.sr.physical_size);
|
|
virtAllocCell.Value = Util.DiskSizeString(this.sr.virtual_allocation);
|
|
}
|
|
}
|
|
|
|
void sr_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
Program.Invoke(Program.MainWindow, UpdateDetails);
|
|
}
|
|
}
|
|
}
|
|
}
|