/* 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.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using XenAdmin.Commands; using XenAdmin.Model; using XenAPI; using XenAdmin.Core; using XenAdmin.Wizards; using XenAdmin.Actions; using XenAdmin.Dialogs; using XenAdmin.Controls.DataGridViewEx; namespace XenAdmin.Controls.NetworkingTab { public partial class NetworkList : UserControl { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public NetworkList() { InitializeComponent(); Properties.Settings.Default.PropertyChanged += Default_PropertyChanged; } private IXenObject _xenObject = null; public IXenObject XenObject { get { Program.AssertOnEventThread(); return _xenObject; } set { Program.AssertOnEventThread(); // Remove all old property change listeners. They will get added back in the // population of the DataGridView that follows (see BuildList). DegregisterEventsOnXmo(); DeregisterEventsOnGridRows(); if (_xenObject is Host || _xenObject is Pool) { _xenObject.Connection.Cache.DeregisterBatchCollectionChanged(NetworkCollectionChanged); _xenObject.Connection.Cache.DeregisterBatchCollectionChanged(PIFCollectionChanged); } else if (_xenObject is VM) { _xenObject.Connection.Cache.DeregisterBatchCollectionChanged(CollectionChanged); _xenObject.Connection.Cache.DeregisterBatchCollectionChanged(VM_guest_metrics_BatchCollectionChanged); } _xenObject = value; if (_xenObject != null) _xenObject.PropertyChanged += Server_PropertyChanged; if (_xenObject is Host || _xenObject is Pool) { AddNetworkColumns(); _xenObject.Connection.Cache.RegisterBatchCollectionChanged(NetworkCollectionChanged); _xenObject.Connection.Cache.RegisterBatchCollectionChanged(PIFCollectionChanged); AddNetworkButton.Text = Messages.HOST_NETWORK_TAB_ADD_BUTTON_LABEL; EditNetworkButton.Text = Messages.HOST_NETWORK_TAB_EDIT_BUTTON_LABEL; RemoveNetworkButton.Text = Messages.HOST_NETWORK_TAB_REMOVE_BUTTON_LABEL; // This is the divider for the activate button, hide this as well as the button groupBox1.Visible = false; buttonActivateToggle.Visible = false; } else if (_xenObject is VM) { AddVifColumns(); _xenObject.Connection.Cache.RegisterBatchCollectionChanged(CollectionChanged); // update the list when we get new metrics _xenObject.Connection.Cache.RegisterBatchCollectionChanged(VM_guest_metrics_BatchCollectionChanged); AddNetworkButton.Text = Messages.VM_NETWORK_TAB_ADD_BUTTON_LABEL; EditNetworkButton.Text = Messages.VM_NETWORK_TAB_EDIT_BUTTON_LABEL; RemoveNetworkButton.Text = Messages.VM_NETWORK_TAB_REMOVE_BUTTON_LABEL; groupBox1.Visible = true; buttonActivateToggle.Visible = true; } BuildList(); } } private void DegregisterEventsOnXmo() { if (XenObject == null) return; XenObject.PropertyChanged -= Server_PropertyChanged; if (XenObject is VM) { VM vm = (VM)XenObject; VM_guest_metrics vmGuestMetrics = vm.Connection.Resolve(vm.guest_metrics); if (vmGuestMetrics != null) vmGuestMetrics.PropertyChanged -= Server_PropertyChanged; } } private void AddNetworkColumns() { // catch the sorting column and sorting order, so they could be restored after the columns are added to the grid (SCTX-501) DataGridViewColumn previousSortColumn = NetworksGridView.SortedColumn; SortOrder previousSortOrder = NetworksGridView.SortOrder == SortOrder.None ? SortOrder.Ascending : NetworksGridView.SortOrder; try { NetworksGridView.SuspendLayout(); NetworksGridView.Columns.Clear(); NetworksGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.ImageColumn, this.NameColumn, this.DescriptionColumn, this.NicColumn, this.VlanColumn, this.AutoColumn, this.LinkStatusColumn, this.NetworkMacColumn, this.MtuColumn}); //CA-47050: the Description column should be autosized to Fill, but should not become smaller than a minimum //width, which here is chosen to be the column header width. To find what this width is set temporarily the //column's autosize mode to ColumnHeader. this.DescriptionColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader; int storedWidth = this.DescriptionColumn.Width; this.DescriptionColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; this.DescriptionColumn.MinimumWidth = storedWidth; } finally { NetworksGridView.ResumeLayout(); } // restore the sorting column (SCTX-501) RestoreSortingColumn(previousSortColumn, previousSortOrder, NicColumn); } private void AddVifColumns() { // catch the sorting column and sorting order, so they could be restored after the columns are added to the grid (SCTX-501) DataGridViewColumn previousSortColumn = NetworksGridView.SortedColumn; SortOrder previousSortOrder = NetworksGridView.SortOrder == SortOrder.None ? SortOrder.Ascending : NetworksGridView.SortOrder; NetworksGridView.Columns.Clear(); NetworksGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.ImageColumn, this.DeviceColumn, this.VifMacColumn, this.LimitColumn, this.NetworkColumn, this.IpColumn, this.ActiveColumn}); //the IP column should be autosized to Fill, but should not become smaller than a minimum //width, which here is chosen to be the column header width. To find what this width is set temporarily the //column's autosize mode to ColumnHeader. this.IpColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader; int storedWidth = this.DescriptionColumn.Width; this.IpColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; this.IpColumn.MinimumWidth = storedWidth; // restore the sorting column (SCTX-501) RestoreSortingColumn(previousSortColumn, previousSortOrder, DeviceColumn); } private void RestoreSortingColumn(DataGridViewColumn previousSortColumn, SortOrder previousSortOrder, DataGridViewColumn defaultSortColumn) { // SCTX-501 preserve the sorting column if ((previousSortColumn == null) || !NetworksGridView.Columns.Contains(previousSortColumn)) { previousSortColumn = defaultSortColumn; previousSortOrder = SortOrder.Ascending; // try to get the old sort order from the SortGlyph foreach (System.Windows.Forms.DataGridViewColumn column in NetworksGridView.Columns) if (column.HeaderCell.SortGlyphDirection != SortOrder.None) { previousSortColumn = column; previousSortOrder = column.HeaderCell.SortGlyphDirection; break; } } if (previousSortColumn != null) { // clear any old sort glyphs; this is necessary as a result of the // change for CA-45643 (MTU column only added for Cowley or greater pools) foreach (System.Windows.Forms.DataGridViewColumn column in NetworksGridView.Columns) column.HeaderCell.SortGlyphDirection = SortOrder.None; NetworksGridView.Sort( previousSortColumn, previousSortOrder == SortOrder.Ascending ? ListSortDirection.Ascending : ListSortDirection.Descending); } } void VM_guest_metrics_BatchCollectionChanged(object sender, EventArgs e) { Program.Invoke(this, BuildList); } void Default_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowHiddenVMs") { BuildList(); } } public bool InBuildList = false; public void BuildList() { Program.AssertOnEventThread(); if (!this.Visible) return; if (InBuildList) return; InBuildList = true; try { if (XenObject == null || XenObject.Locked) return; if (!XenObject.Connection.CacheIsPopulated) return; if (XenObject is VM) { DeregisterEventsOnGridRows(); VIF selectedVIF = SelectedVif; VM vm = XenObject as VM; NetworksGridView.SuspendLayout(); NetworksGridView.Rows.Clear(); List vifs = vm.Connection.ResolveAll(vm.VIFs); vifs.Sort(); // CA-8981 - Listen for guest metric changes which is necessary for IP Address updates VM_guest_metrics vmGuestMetrics = vm.Connection.Resolve(vm.guest_metrics); if (vmGuestMetrics != null) vmGuestMetrics.PropertyChanged += Server_PropertyChanged; var vifRowsToAdd = new List(); foreach (var vif in vifs) { var network = vif.Connection.Resolve(vif.network); if (network != null && network.IsGuestInstallerNetwork) continue; // Don't show the guest installer network in the network tab (CA-73056) vifRowsToAdd.Add(new VifRow(vif)); } NetworksGridView.Rows.AddRange(vifRowsToAdd.ToArray()); bool selected = true; if (selectedVIF != null) { foreach (VifRow row in NetworksGridView.Rows) { // Cannot compare opaque_ref as VIFs get destroyed / recreated on each edit. if (row.Vif.device == selectedVIF.device) { row.Selected = true; break; } } } if (!selected && NetworksGridView.Rows.Count > 0) { NetworksGridView.Rows[0].Selected = true; } } else if (XenObject is Host || XenObject is Pool) { DeregisterEventsOnGridRows(); XenAPI.Network selectedNetwork = SelectedNetwork; NetworksGridView.SuspendLayout(); NetworksGridView.Rows.Clear(); XenAPI.Network[] networks = XenObject.Connection.Cache.Networks; Array.Sort < XenAPI.Network>(networks); List networkRowsToAdd = new List(); for (int i = 0; i < networks.Length; i++) { if (!networks[i].Show(XenAdmin.Properties.Settings.Default.ShowHiddenVMs)) continue; networkRowsToAdd.Add(new NetworkRow(networks[i], XenObject)); } NetworksGridView.Rows.AddRange(networkRowsToAdd.ToArray()); // The following update causes this to be a lot slower with many networks. Alot! CA-43944 //foreach(NetworkRow r in NetworksGridView.Rows) //r.UpdateDefaultCellStyle(); // Has to be done again after adding to the grid view, even though it's already called in the constructor if (selectedNetwork != null) { foreach (NetworkRow row in NetworksGridView.Rows) { if (row.Network.opaque_ref == selectedNetwork.opaque_ref && selectedNetwork.Show(XenAdmin.Properties.Settings.Default.ShowHiddenVMs)) { row.Selected = true; break; } } } } } finally { if (NetworksGridView.SortedColumn != null) { NetworksGridView.Sort( NetworksGridView.SortedColumn, NetworksGridView.SortOrder == SortOrder.Ascending ? ListSortDirection.Ascending : ListSortDirection.Descending); } NetworksGridView.ResumeLayout(); InBuildList = false; } } public void DeregisterEventsOnGridRows() { foreach (DataGridViewExRow row in NetworksGridView.Rows) { if (row is VifRow) ((VifRow)row).DeregisterEvents(); else if (row is NetworkRow) ((NetworkRow)row).DeregisterEvents(); } } private void NetworksGridView_SelectionChanged(object sender, EventArgs e) { UpdateEnablement(); } private void UpdateEnablement() { bool locked = _xenObject.Locked; if (SelectedNetwork != null) { XenAPI.Network TheNetwork = SelectedNetwork; AddNetworkButton.Enabled = !locked; EditNetworkButton.Enabled = !locked && !TheNetwork.Locked && !TheNetwork.IsSlave && !TheNetwork.CreateInProgress; if (HasPhysicalNonBondNIC(TheNetwork)) { RemoveNetworkButton.Enabled = false; RemoveButtonContainer.SetToolTip(Messages.TOOLTIP_REMOVE_PIF); } else { RemoveNetworkButton.Enabled = !locked && !TheNetwork.Locked && !TheNetwork.IsSlave && !TheNetwork.CreateInProgress; RemoveButtonContainer.SetToolTip(""); } } else if (SelectedVif != null) { VIF vif = SelectedVif; AddNetworkButton.Enabled = !locked; // In this case read vif.currently_attached as is-it-plugged RemoveNetworkButton.Enabled = !locked && (vif.allowed_operations.Contains(vif_operations.unplug) || !vif.currently_attached); EditNetworkButton.Enabled = !locked && (vif.allowed_operations.Contains(vif_operations.unplug) || !vif.currently_attached); buttonActivateToggle.Enabled = !locked && ( vif.currently_attached && vif.allowed_operations.Contains(vif_operations.unplug) || !vif.currently_attached && vif.allowed_operations.Contains(vif_operations.plug)); buttonActivateToggle.Text = vif.currently_attached ? Messages.VM_NETWORK_TAB_DEACTIVATE_BUTTON_LABEL : Messages.VM_NETWORK_TAB_ACTIVATE_BUTTON_LABEL; VM vm = (VM)XenObject; if (vm.power_state == vm_power_state.Suspended) { RemoveButtonContainer.SetToolTip(Messages.TOOLTIP_REMOVE_NETWORK_SUSPENDED); EditButtonContainer.SetToolTip(vm.HasNewVirtualisationStates ? Messages.TOOLTIP_EDIT_NETWORK_IO_DRIVERS : Messages.TOOLTIP_EDIT_NETWORK_TOOLS); toolTipContainerActivateToggle.SetToolTip(vif.currently_attached ? Messages.TOOLTIP_DEACTIVATE_VIF_SUSPENDED : Messages.TOOLTIP_ACTIVATE_VIF_SUSPENDED); } else { if (vm.power_state == vm_power_state.Running && !vm.GetVirtualisationStatus.HasFlag(VM.VirtualisationStatus.IO_DRIVERS_INSTALLED)) { RemoveButtonContainer.SetToolTip(vm.HasNewVirtualisationStates ? Messages.TOOLTIP_REMOVE_NETWORK_IO_DRIVERS : Messages.TOOLTIP_REMOVE_NETWORK_TOOLS); EditButtonContainer.SetToolTip(vm.HasNewVirtualisationStates ? Messages.TOOLTIP_EDIT_NETWORK_IO_DRIVERS : Messages.TOOLTIP_EDIT_NETWORK_TOOLS); toolTipContainerActivateToggle.SetToolTip(vif.currently_attached ? Messages.TOOLTIP_DEACTIVATE_VIF_TOOLS : Messages.TOOLTIP_ACTIVATE_VIF_TOOLS); } else { RemoveButtonContainer.RemoveAll(); EditButtonContainer.RemoveAll(); toolTipContainerActivateToggle.RemoveAll(); } } } else { AddNetworkButton.Enabled = !locked; RemoveNetworkButton.Enabled = false; EditNetworkButton.Enabled = false; buttonActivateToggle.Enabled = false; } } /// /// Return true if the given network has a PIF that represents a physical NIC -- i.e. /// IsPhysical == true and IsBondNIC == false. /// /// /// private static bool HasPhysicalNonBondNIC(XenAPI.Network network) { foreach (PIF pif in network.Connection.ResolveAll(network.PIFs)) { if (pif.IsPhysical && !pif.IsBondNIC) return true; } return false; } private void AddNetworkButton_Click(object sender, EventArgs e) { if (XenObject is VM) { VM vm = (VM)_xenObject; if (NetworksGridView.Rows.Count >= vm.MaxVIFsAllowed) { new ThreeButtonDialog( new ThreeButtonDialog.Details( SystemIcons.Error, FriendlyErrorNames.VIFS_MAX_ALLOWED, FriendlyErrorNames.VIFS_MAX_ALLOWED_TITLE)).ShowDialog(Program.MainWindow); return; } Host master = Helpers.GetMaster(vm.Connection); if (master == null) { // Cache populating? return; } VIFDialog d = new VIFDialog(vm.Connection, null, VIF.GetDeviceId(vm)); if (d.ShowDialog(this) != DialogResult.OK) return; Proxy_VIF pVif = d.GetNewSettings(); pVif.VM = vm.opaque_ref; CreateVIFCommand action = new CreateVIFCommand(Program.MainWindow, vm, pVif); action.Execute(); } else if (XenObject is Host) { Host host = (Host)_xenObject; Program.MainWindow.ShowPerConnectionWizard(_xenObject.Connection, new NewNetworkWizard(_xenObject.Connection, null, host)); } else if (XenObject is Pool) { Pool pool = (Pool)_xenObject; Host host = pool.Connection.Resolve(pool.master); if (host != null) { Program.MainWindow.ShowPerConnectionWizard(_xenObject.Connection, new NewNetworkWizard(_xenObject.Connection, pool, host)); } } } private XenAPI.Network SelectedNetwork { get { if (NetworksGridView.SelectedRows.Count == 0) return null; NetworkRow row = NetworksGridView.SelectedRows[0] as NetworkRow; if (row == null) return null; return row.Network; } } private VIF SelectedVif { get { if (NetworksGridView.SelectedRows.Count == 0) return null; VifRow row = NetworksGridView.SelectedRows[0] as VifRow; if (row == null) return null; return row.Vif; } } private void RemoveNetworkButton_Click(object sender, EventArgs e) { if (NetworksGridView.SelectedRows.Count == 0) return; XenAPI.Network network = SelectedNetwork; if (network != null && network.IsBond) { var destroyBondCommand = new DestroyBondCommand(Program.MainWindow, network); destroyBondCommand.Execute(); } else { // Check and see if the system is running in automation test mode. If so, then // do not launch the popup Y/N dialog. DialogResult result; if (Program.RunInAutomatedTestMode) { result = DialogResult.Yes; } else if (XenObject is VM) { // Deleting a VIF, not a Network. result = new ThreeButtonDialog( new ThreeButtonDialog.Details(SystemIcons.Warning, Messages.MESSAGEBOX_VIF_DELETE, Messages.MESSAGEBOX_VIF_DELETE_TITLE), ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo).ShowDialog(Program.MainWindow); } else { result = new ThreeButtonDialog( new ThreeButtonDialog.Details(SystemIcons.Warning, Messages.MESSAGEBOX_NETWORK_DELETE, Messages.MESSAGEBOX_NETWORK_DELETE_TITLE), ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo).ShowDialog(Program.MainWindow); } if (result == DialogResult.Yes) { DoRemoveNetwork(); } } } private void DoRemoveNetwork() { if (SelectedVif != null) { VM vm = XenObject as VM; var action = new DeleteVIFAction(SelectedVif); action.Completed += action_Completed; action.RunAsync(); } else if(SelectedNetwork != null) { NetworkAction action = new NetworkAction(XenObject.Connection, SelectedNetwork,false); action.Completed += action_Completed; action.RunAsync(); } } private void buttonActivateToggle_Click(object sender, EventArgs e) { if (SelectedVif != null) { VM vm = XenObject as VM; AsyncAction action; if (SelectedVif.currently_attached) action = new UnplugVIFAction(SelectedVif); else action = new PlugVIFAction(SelectedVif); action.Completed += action_Completed; action.RunAsync(); } } void NetworkCollectionChanged(object sender, EventArgs e) { Program.Invoke(this, BuildList); } void PIFCollectionChanged(object sender, EventArgs e) { Program.Invoke(this, BuildList); } void CollectionChanged(object sender, EventArgs e) { Program.Invoke(this, BuildList); } void action_Completed(ActionBase sender) { Program.Invoke(this, BuildList); } void Server_PropertyChanged(object sender, PropertyChangedEventArgs e) { // JJN- Provide a temporary fix to the bug reported in CA-9322 where the // editing of the MAC and Limit fields is barely usable due to the continuous // reconstruction of the list within this view. Ensure the button enablement // does not digress with this fix. if (shouldRefreshBuildList(e)) { Program.Invoke(this, RefreshAllItems); } } private void RefreshAllItems() { foreach (DataGridViewExRow row in NetworksGridView.Rows) { if (row is VifRow) ((VifRow)row).UpdateDetails(); else if (row is NetworkRow) ((NetworkRow)row).UpdateDetails(); } } public static bool shouldRefreshBuildList(PropertyChangedEventArgs e) { return (e.PropertyName != "allowed_operations" && e.PropertyName != "current_operations" && e.PropertyName != "io_read_kbs" && e.PropertyName != "io_write_kbs" && e.PropertyName != "last_updated"); } private void EditNetworkButton_Click(object sender, EventArgs e) { if (NetworksGridView.SelectedRows.Count > 0) { if (XenObject is VM) { launchVmNetworkSettingsDialog(); } else { launchHostOrPoolNetworkSettingsDialog(); } } } private void launchVmNetworkSettingsDialog() { VM vm = XenObject as VM; VIF vif = SelectedVif; if (vm == null || vif == null) return; int device; VIFDialog d; if (int.TryParse(vif.device, out device)) { d = new VIFDialog(vm.Connection, vif, device); } else { log.ErrorFormat("Aborting vif edit. Could not parse existing vif device to int. Value is: '{0}'", vif.device); return; } if (d.ShowDialog() != DialogResult.OK) return; Proxy_VIF proxyVIF = d.GetNewSettings(); UpdateVIFCommand command = new UpdateVIFCommand(Program.MainWindow, vm, vif, proxyVIF); InBuildList = true; command.Completed += new EventHandler((s, f) => Program.Invoke(this, () => { InBuildList = false; BuildList(); })); command.Execute(); } private void launchHostOrPoolNetworkSettingsDialog() { XenAPI.Network network = SelectedNetwork; if (network == null) return; new PropertiesDialog(network).ShowDialog(this); } private void EditMenuItemHandler(object sender, EventArgs e) { EditNetworkButton_Click(null, null); } private void AddMenuItemHandler(object sender, EventArgs e) { AddNetworkButton_Click(null, null); } private void RemoveMenuItemHandler(object sender, EventArgs e) { RemoveNetworkButton_Click(null, null); } private void NetworksGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) { UpdateEnablement(); } private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { Point pt = NetworksGridView.PointToClient(new Point(contextMenuStrip1.Left, contextMenuStrip1.Top)); DataGridView.HitTestInfo info = NetworksGridView.HitTest(pt.X, pt.Y); if (info != null && info.RowIndex >= 0 && info.RowIndex < NetworksGridView.Rows.Count) { DataGridViewRow row = NetworksGridView.Rows[info.RowIndex]; if (row != null) { row.Selected = true; copyToolStripMenuItem.Visible = true; addToolStripMenuItem.Visible = AddNetworkButton.Visible && AddNetworkButton.Enabled; propertiesToolStripMenuItem.Visible = EditNetworkButton.Visible && EditNetworkButton.Enabled; removeToolStripMenuItem.Visible = RemoveNetworkButton.Visible && RemoveNetworkButton.Enabled; return; } } // else just show add button copyToolStripMenuItem.Visible = false; addToolStripMenuItem.Visible = AddNetworkButton.Visible && AddNetworkButton.Enabled; propertiesToolStripMenuItem.Visible = false; removeToolStripMenuItem.Visible = false; } private void copyToolStripMenuItem_Click(object sender, EventArgs e) { Point pt = NetworksGridView.PointToClient(new Point(contextMenuStrip1.Left, contextMenuStrip1.Top)); DataGridView.HitTestInfo info = NetworksGridView.HitTest(pt.X, pt.Y); if (info != null && info.RowIndex >= 0 && info.RowIndex < NetworksGridView.Rows.Count) { DataGridViewRow row = NetworksGridView.Rows[info.RowIndex]; if (row != null) { string t = row.Cells[info.ColumnIndex].Value.ToString(); if (String.IsNullOrEmpty(t)) return; Clipboard.SetText(t); } } } class VifRow : DataGridViewExRow { public VIF Vif; private DataGridViewExImageCell ImageCell = new DataGridViewExImageCell(); private DataGridViewTextBoxCell DeviceCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell MacCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell LimitCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell NetworkCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell IpCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell AttachedCell = new DataGridViewTextBoxCell(); private VIF_metrics Metrics; public VifRow(VIF vif) { Vif = vif; Cells.AddRange(ImageCell, DeviceCell, MacCell, LimitCell, NetworkCell, IpCell, AttachedCell); Vif.PropertyChanged += Server_PropertyChanged; UpdateDetails(); } public void UpdateDetails() { if(Metrics != null) Metrics.PropertyChanged -= Server_PropertyChanged; Metrics = Vif.Connection.Resolve(Vif.metrics); if (Metrics != null) Metrics.PropertyChanged += Server_PropertyChanged; ImageCell.Value = Properties.Resources._000_Network_h32bit_16; DeviceCell.Value = Vif.device; MacCell.Value = Helpers.GetMacString(Vif.MAC); LimitCell.Value = Vif.qos_algorithm_type != ""? Vif.LimitString:""; NetworkCell.Value = Vif.NetworkName(); IpCell.Value = Vif.IPAddressesAsString(); AttachedCell.Value = Vif.currently_attached ? Messages.YES : Messages.NO; } public void DeregisterEvents() { Vif.PropertyChanged -= Server_PropertyChanged; if (Metrics != null) Metrics.PropertyChanged -= Server_PropertyChanged; } private void Server_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (NetworkList.shouldRefreshBuildList(e)) Program.Invoke(Program.MainWindow, UpdateDetails); } } class NetworkRow : DataGridViewExRow { public XenAPI.Network Network; private DataGridViewExImageCell ImageCell = new DataGridViewExImageCell(); private DataGridViewTextBoxCell NameCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell DescriptionCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell NicCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell VlanCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell AutoCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell LinkStatusCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell MacCell = new DataGridViewTextBoxCell(); private DataGridViewTextBoxCell MtuCell = new DataGridViewTextBoxCell(); private IXenObject Xmo; PIF Pif; public NetworkRow(XenAPI.Network network, IXenObject xmo) { Network = network; Xmo = xmo; Cells.AddRange(ImageCell, NameCell, DescriptionCell, NicCell, VlanCell, AutoCell, LinkStatusCell, MacCell, MtuCell); Network.PropertyChanged += Server_PropertyChanged; Program.Invoke(Program.MainWindow, UpdateDetails); } public void UpdateDetails() { Enabled = !Network.IsSlave; DeregisterPifEvents(); Pif = Helpers.FindPIF(Network, Xmo as Host); RegisterPifEvents(); ImageCell.Value = Properties.Resources._000_Network_h32bit_16; NameCell.Value = NetworkName(); DescriptionCell.Value = Network.Description; NicCell.Value = Helpers.GetName(Pif); VlanCell.Value = Helpers.VlanString(Pif); AutoCell.Value = Network.AutoPlug ? Messages.YES : Messages.NO; LinkStatusCell.Value = Xmo is Pool ? Network.LinkStatusString : Pif == null ? Messages.NONE : Pif.LinkStatusString; MacCell.Value = Pif != null && Pif.IsPhysical ? Pif.MAC : Messages.SPACED_HYPHEN; MtuCell.Value = Network.CanUseJumboFrames ? Network.MTU.ToString() : Messages.SPACED_HYPHEN; } public void DeregisterEvents() { Network.PropertyChanged -= Server_PropertyChanged; DeregisterPifEvents(); } private object NetworkName() { if (Network.Show(XenAdmin.Properties.Settings.Default.ShowHiddenVMs) && !Network.IsSlave) return Helpers.GetName(Network); else if (Network.IsSlave && Properties.Settings.Default.ShowHiddenVMs) return string.Format(Messages.NIC_SLAVE, Helpers.GetName(Network)); else if (Properties.Settings.Default.ShowHiddenVMs) return string.Format(Messages.NIC_HIDDEN, Helpers.GetName(Network)); else return string.Empty; } private void Server_PropertyChanged(object sender, PropertyChangedEventArgs e) { if(NetworkList.shouldRefreshBuildList(e)) Program.Invoke(Program.MainWindow, UpdateDetails); } private void RegisterPifEvents() { if (Pif != null) { Pif.PropertyChanged += Server_PropertyChanged; // Listen for Tunnel and PIF_metrics changes which is necessary for Link Status updates (CA-46103) if (Pif.IsTunnelAccessPIF) { Tunnel tunnel = Pif.Connection.Resolve(Pif.tunnel_access_PIF_of[0]); if (tunnel != null) tunnel.PropertyChanged += Pif_PropertyChanged; } else { PIF_metrics metrics = Pif.PIFMetrics; if (metrics != null) metrics.PropertyChanged += Pif_PropertyChanged; } } } private void DeregisterPifEvents() { if (Pif != null) { Pif.PropertyChanged -= Server_PropertyChanged; // Remove Tunnel and PIF_metrics property change listeners (CA-46103) if (Pif.IsTunnelAccessPIF) { Tunnel tunnel = Pif.Connection.Resolve(Pif.tunnel_access_PIF_of[0]); if (tunnel != null) tunnel.PropertyChanged -= Pif_PropertyChanged; } else { PIF_metrics metrics = Pif.PIFMetrics; if (metrics != null) metrics.PropertyChanged -= Pif_PropertyChanged; } } } private void Pif_PropertyChanged(object sender, PropertyChangedEventArgs e) { // Tunnel.status or PIF_metrics.carrier (CA-46103) if (e.PropertyName == "status" || e.PropertyName == "carrier") Program.Invoke(Program.MainWindow, UpdateDetails); } } private void NetworksGridView_SortCompare(object sender, DataGridViewSortCompareEventArgs e) { if (XenObject != null && XenObject is VM && e.Column.Index == DeviceColumn.Index) { int val1 = 0; int val2 = 0; if (int.TryParse(e.CellValue1.ToString(), out val1) && int.TryParse(e.CellValue2.ToString(), out val2)) { e.SortResult = val1.CompareTo(val2); e.Handled = true; return; } } e.SortResult = StringUtility.NaturalCompare(e.CellValue1.ToString(), e.CellValue2.ToString()); e.Handled = true; } } }