/* 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";
///
/// Make sure you set this before you set the connection,
/// as the connection is the one which rebuilds the list
///
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_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_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 pbds = host != null ? new List(connection.ResolveAll(host.PBDs))
: new List(connection.Cache.PBDs);
List srs = new List();
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, 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 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);
}
}
}
}