mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-20 15:36:03 +01:00
361 lines
13 KiB
C#
361 lines
13 KiB
C#
|
/* 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;
|
|||
|
|
|||
|
|
|||
|
namespace XenAdmin.Controls
|
|||
|
{
|
|||
|
public partial class BondDetails : UserControl
|
|||
|
{
|
|||
|
private int bondSizeLimit = 2;
|
|||
|
|
|||
|
private int m_numberOfCheckedNics;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Style to use for checkable rows
|
|||
|
/// </summary>
|
|||
|
private DataGridViewCellStyle regStyle;
|
|||
|
/// <summary>
|
|||
|
/// Style to use for non-checkable rows (should appear grayed out)
|
|||
|
/// </summary>
|
|||
|
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<PIF> BondedPIFs
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
List<PIF> result = new List<PIF>();
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
numericUpDownMTU.Maximum = XenAPI.Network.MTU_MAX;
|
|||
|
numericUpDownMTU.Minimum = XenAPI.Network.MTU_MIN;
|
|||
|
numericUpDownMTU.Value = XenAPI.Network.MTU_DEFAULT;
|
|||
|
}
|
|||
|
|
|||
|
internal void SetHost(Host host)
|
|||
|
{
|
|||
|
Connection = host.Connection;
|
|||
|
bondSizeLimit = Helpers.BondSizeLimit(Connection);
|
|||
|
PopulateDataGridView(NetworkingHelper.GetAllPhysicalPIFs(host));
|
|||
|
ShowHideControls();
|
|||
|
}
|
|||
|
|
|||
|
internal void SetPool(Pool pool)
|
|||
|
{
|
|||
|
Connection = pool.Connection;
|
|||
|
bondSizeLimit = Helpers.BondSizeLimit(Connection);
|
|||
|
PopulateDataGridView(NetworkingHelper.GetAllPhysicalPIFs(pool));
|
|||
|
ShowHideControls();
|
|||
|
}
|
|||
|
|
|||
|
private void PopulateDataGridView(List<PIF> pifs)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
dataGridView1.SuspendLayout();
|
|||
|
dataGridView1.Rows.Clear();
|
|||
|
m_numberOfCheckedNics = 0;
|
|||
|
|
|||
|
foreach (PIF pif in pifs)
|
|||
|
{
|
|||
|
string description = PIFDescription(pif);
|
|||
|
XenAPI.Network network = Connection.Resolve<XenAPI.Network>(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()
|
|||
|
{
|
|||
|
labelMTU.Visible = numericUpDownMTU.Visible = Helpers.CowleyOrGreater(Connection);
|
|||
|
groupBoxBondMode.Visible = Helpers.BostonOrGreater(Connection);
|
|||
|
radioButtonLacpSrcMac.Visible = radioButtonLacpTcpudpPorts.Visible = Helpers.SupportsLinkAggregationBond(Connection);
|
|||
|
}
|
|||
|
|
|||
|
private string PIFDescription(PIF pif)
|
|||
|
{
|
|||
|
Bond bond = pif.BondSlaveOf;
|
|||
|
if (bond != null)
|
|||
|
return string.Format(Messages.ALREADY_IN_BOND, bond.Name);
|
|||
|
|
|||
|
XenAPI.Network network = Connection.Resolve(pif.network);
|
|||
|
if (network != null && !Helpers.BostonOrGreater(network.Connection) && network.HasActiveVIFs) // in Boston, can make a bond even if there are active VMs (PR-1006)
|
|||
|
return Messages.PIF_IN_USE_BY_VMS;
|
|||
|
|
|||
|
return "";
|
|||
|
}
|
|||
|
|
|||
|
internal DialogResult ShowCreationWarning()
|
|||
|
{
|
|||
|
List<PIF> pifs = BondedPIFs;
|
|||
|
|
|||
|
bool will_disturb_primary = NetworkingHelper.ContainsPrimaryManagement(pifs);
|
|||
|
bool will_disturb_secondary = NetworkingHelper.ContainsSecondaryManagement(pifs);
|
|||
|
|
|||
|
if (will_disturb_primary && will_disturb_secondary)
|
|||
|
{
|
|||
|
new ThreeButtonDialog(
|
|||
|
new ThreeButtonDialog.Details(
|
|||
|
SystemIcons.Error,
|
|||
|
Messages.BOND_CREATE_WILL_DISTURB_BOTH,
|
|||
|
Messages.BOND_CREATE)).ShowDialog(this);
|
|||
|
|
|||
|
return DialogResult.Cancel;
|
|||
|
}
|
|||
|
|
|||
|
if (will_disturb_primary)
|
|||
|
{
|
|||
|
Pool pool = Helpers.GetPool(Connection);
|
|||
|
if (pool != null && pool.ha_enabled)
|
|||
|
{
|
|||
|
new ThreeButtonDialog(
|
|||
|
new ThreeButtonDialog.Details(
|
|||
|
SystemIcons.Error,
|
|||
|
string.Format(Messages.BOND_CREATE_HA_ENABLED, pool.Name),
|
|||
|
Messages.BOND_CREATE)).ShowDialog(this);
|
|||
|
|
|||
|
return DialogResult.Cancel;
|
|||
|
}
|
|||
|
|
|||
|
return new ThreeButtonDialog(
|
|||
|
new ThreeButtonDialog.Details(SystemIcons.Warning, Messages.BOND_CREATE_WILL_DISTURB_PRIMARY, Messages.BOND_CREATE),
|
|||
|
"BondConfigError",
|
|||
|
new ThreeButtonDialog.TBDButton(Messages.BOND_CREATE_CONTINUE, DialogResult.OK),
|
|||
|
ThreeButtonDialog.ButtonCancel).ShowDialog(this);
|
|||
|
}
|
|||
|
|
|||
|
if (will_disturb_secondary)
|
|||
|
{
|
|||
|
return new ThreeButtonDialog(
|
|||
|
new ThreeButtonDialog.Details(
|
|||
|
SystemIcons.Warning,
|
|||
|
Messages.BOND_CREATE_WILL_DISTURB_SECONDARY,
|
|||
|
Messages.BOND_CREATE),
|
|||
|
ThreeButtonDialog.ButtonOK,
|
|||
|
ThreeButtonDialog.ButtonCancel).ShowDialog(this);
|
|||
|
}
|
|||
|
|
|||
|
return DialogResult.OK;
|
|||
|
}
|
|||
|
|
|||
|
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();
|
|||
|
SetValid();
|
|||
|
}
|
|||
|
|
|||
|
private void BondMode_CheckedChanged(object sender, EventArgs e)
|
|||
|
{
|
|||
|
ShowHideLacpWarning();
|
|||
|
}
|
|||
|
|
|||
|
private void ShowHideLacpWarning()
|
|||
|
{
|
|||
|
panelLACPWarning.Visible = radioButtonLacpSrcMac.Checked || radioButtonLacpTcpudpPorts.Checked;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|