/* 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.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); } } } }