/* 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.Drawing; using System.Windows.Forms; using XenAPI; using XenAdmin.Network; using XenAdmin.Dialogs; using XenAdmin.Core; using System.Linq; namespace XenAdmin.Controls { public partial class BondDetails : UserControl { private int bondSizeLimit = 2; private int m_numberOfCheckedNics; /// /// Style to use for checkable rows /// private DataGridViewCellStyle regStyle; /// /// Style to use for non-checkable rows (should appear grayed out) /// private DataGridViewCellStyle dimmedStyle; public event EventHandler ValidChanged; internal IXenConnection Connection; private bool valid = false; internal bool Valid { get { return valid; } set { if (valid != value) { valid = value; if (ValidChanged != null) ValidChanged(this, null); } } } internal string BondName { get { return string.Format(Messages.PIF_BOND, PIF.BondSuffix(BondedPIFs)); } } internal List BondedPIFs { get { List result = new List(); foreach (DataGridViewRow row in dataGridView1.Rows) { if (IsRowChecked(row)) result.Add((PIF)row.Tag); } return result; } } internal bond_mode BondMode { get { return radioButtonActiveBackup.Checked ? bond_mode.active_backup : radioButtonBalanceSlb.Checked ? bond_mode.balance_slb : bond_mode.lacp; } } internal Bond.hashing_algoritm HashingAlgorithm { get { return radioButtonLacpSrcMac.Checked ? Bond.hashing_algoritm.src_mac : radioButtonLacpTcpudpPorts.Checked ? Bond.hashing_algoritm.tcpudp_ports : Bond.hashing_algoritm.unknown; } } internal long MTU { get { return (long)numericUpDownMTU.Value; } } internal bool AutoPlug { get { return cbxAutomatic.Checked; } } public BondDetails() { InitializeComponent(); //setup cell styles regStyle = dataGridView1.DefaultCellStyle.Clone(); dimmedStyle = dataGridView1.DefaultCellStyle.Clone(); dimmedStyle.ForeColor = SystemColors.GrayText; } internal void SetHost(Host host) { Connection = host.Connection; bondSizeLimit = Helpers.BondSizeLimit(Connection); PopulateDataGridView(NetworkingHelper.GetAllPhysicalPIFs(host)); SetDefaultBoundariesOfMtuDropDown(); ShowHideControls(); } internal void SetPool(Pool pool) { Connection = pool.Connection; bondSizeLimit = Helpers.BondSizeLimit(Connection); PopulateDataGridView(NetworkingHelper.GetAllPhysicalPIFs(pool)); SetDefaultBoundariesOfMtuDropDown(); ShowHideControls(); } private void PopulateDataGridView(List pifs) { try { dataGridView1.SuspendLayout(); dataGridView1.Rows.Clear(); m_numberOfCheckedNics = 0; foreach (PIF pif in pifs) { string description = PIFDescription(pif); XenAPI.Network network = Connection.Resolve(pif.network); PIF_metrics metrics = pif.PIFMetrics(); int rowIndex = dataGridView1.Rows.Add(new object[] { false, String.Format("{0} {1}", pif.Name(), description), pif.MAC, (network.PIFs.Count > 1) ? network.LinkStatusString() : pif.LinkStatusString(), pif.Carrier() ? pif.Speed() : Messages.HYPHEN, pif.Carrier() ? pif.Duplex() : Messages.HYPHEN, metrics == null ? "" : metrics.vendor_name, metrics == null ? "" : metrics.device_name, metrics == null ? "" : metrics.pci_bus_path }); DataGridViewRow row = dataGridView1.Rows[rowIndex]; row.Tag = pif; ToggleNICRowCheckable(row); } //CA-47050: the ColumnPci 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. ColumnPci.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; int storedWidth = ColumnPci.Width; ColumnPci.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; ColumnPci.MinimumWidth = storedWidth; } finally { dataGridView1.ResumeLayout(); } SetValid(); } private void ShowHideControls() { radioButtonLacpSrcMac.Visible = radioButtonLacpTcpudpPorts.Visible = Helpers.SupportsLinkAggregationBond(Connection); } private string PIFDescription(PIF pif) { Bond bond = pif.BondMemberOf(); return bond == null ? "" : string.Format(Messages.ALREADY_IN_BOND, bond.Name()); } internal bool CanCreateBond() { List pifs = BondedPIFs; bool will_disturb_primary = NetworkingHelper.ContainsPrimaryManagement(pifs); bool will_disturb_secondary = NetworkingHelper.ContainsSecondaryManagement(pifs); bool will_disturb_clustering = NetworkingHelper.ContainsClusteringPif(pifs); // It is not allowed to bond primary and secondary interfaces together. if (will_disturb_primary && will_disturb_secondary) { using (var dlg = new ErrorDialog(Messages.BOND_CREATE_WILL_DISTURB_BOTH) {WindowTitle = Messages.BOND_CREATE}) { dlg.ShowDialog(this); } return false; } // Only primary management interface. // In this case, clustering interface warning is hidden if it happens to be the management interface. if (will_disturb_primary) { Pool pool = Helpers.GetPool(Connection); if (pool != null && pool.ha_enabled) { using (var dlg = new ErrorDialog(string.Format(Messages.BOND_CREATE_HA_ENABLED, pool.Name())) {WindowTitle = Messages.BOND_CREATE}) { dlg.ShowDialog(this); } return false; } DialogResult dialogResult; using (var dlg = new WarningDialog(string.Format(Messages.BOND_CREATE_WILL_DISTURB_PRIMARY, BrandManager.BrandConsole), new ThreeButtonDialog.TBDButton(Messages.BOND_CREATE_CONTINUE, DialogResult.OK), ThreeButtonDialog.ButtonCancel) { HelpNameSetter = "BondConfigError", WindowTitle = Messages.BOND_CREATE }) { dialogResult = dlg.ShowDialog(this); } return dialogResult == DialogResult.OK; } // Only secondary interface. // If there is clustering interface, shows clustering warning. Otherwise, shows secondary interface warning. if (will_disturb_secondary) { DialogResult dialogResult; if (will_disturb_clustering) { using (var dlg = new WarningDialog(Messages.BOND_CREATE_WILL_DISTURB_CLUSTERING, ThreeButtonDialog.ButtonOK, ThreeButtonDialog.ButtonCancel) {WindowTitle = Messages.BOND_CREATE}) { dialogResult = dlg.ShowDialog(this); } } else { using (var dlg = new WarningDialog(Messages.BOND_CREATE_WILL_DISTURB_SECONDARY, ThreeButtonDialog.ButtonOK, ThreeButtonDialog.ButtonCancel) {WindowTitle = Messages.BOND_CREATE}) { dialogResult = dlg.ShowDialog(this); } } return dialogResult == DialogResult.OK; } return true; } private void UpdateCellsReadOnlyState() { foreach (DataGridViewRow row in dataGridView1.Rows) ToggleNICRowCheckable(row); } private void ToggleNICRowCheckable(DataGridViewRow row) { PIF pif = row.Tag as PIF; if (pif != null) { string description = PIFDescription(pif); bool checkable = String.IsNullOrEmpty(description) && m_numberOfCheckedNics < bondSizeLimit; //if it's already checked do not consider it if (IsRowChecked(row)) return; row.Cells[0].ReadOnly = !checkable; row.DefaultCellStyle = checkable ? regStyle : dimmedStyle; } } private bool IsRowChecked(DataGridViewRow row) { return (bool)(row.Cells[0] as DataGridViewCheckBoxCell).Value; } private bool IsRowChecked(int rowIndex) { return IsRowChecked(dataGridView1.Rows[rowIndex]); } private void SetValid() { Valid = (2 <= m_numberOfCheckedNics && m_numberOfCheckedNics <= bondSizeLimit); } private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e) { if (dataGridView1.IsCurrentCellDirty) dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit); } private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { if (e.ColumnIndex != 0) return; DataGridViewCheckBoxCell cell = (DataGridViewCheckBoxCell)dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex]; if (IsRowChecked(e.RowIndex)) m_numberOfCheckedNics++; else m_numberOfCheckedNics--; UpdateCellsReadOnlyState(); UpdateMtuControls(); SetValid(); } private void UpdateMtuControls() { if (BondedPIFs.Count > 0) numericUpDownMTU.Maximum = Math.Min(BondedPIFs.Select(p => p.MTU).DefaultIfEmpty(XenAPI.Network.MTU_MAX).Min(), XenAPI.Network.MTU_MAX); else SetDefaultBoundariesOfMtuDropDown(); ShowOrHideMtuInfo(); } private void SetDefaultBoundariesOfMtuDropDown() { numericUpDownMTU.Maximum = XenAPI.Network.MTU_MAX;// Math.Min(AllPIFs.Select(p => p.MTU).DefaultIfEmpty(XenAPI.Network.MTU_MAX).Max(), XenAPI.Network.MTU_MAX); numericUpDownMTU.Minimum = XenAPI.Network.MTU_MIN; numericUpDownMTU.Value = XenAPI.Network.MTU_DEFAULT; ShowOrHideMtuInfo(); } private void ShowOrHideMtuInfo() { numericUpDownMTU.Enabled = numericUpDownMTU.Minimum != numericUpDownMTU.Maximum; infoMtuMessage.Text = numericUpDownMTU.Minimum == numericUpDownMTU.Maximum ? string.Format(Messages.ALLOWED_MTU_VALUE, numericUpDownMTU.Minimum) : string.Format(Messages.ALLOWED_MTU_RANGE, numericUpDownMTU.Minimum, numericUpDownMTU.Maximum); } private void BondMode_CheckedChanged(object sender, EventArgs e) { ShowHideLacpWarning(); } private void ShowHideLacpWarning() { panelLACPWarning.Visible = radioButtonLacpSrcMac.Checked || radioButtonLacpTcpudpPorts.Checked; } } }