xenadmin/XenAdmin/Wizards/ImportWizard/NetworkPickerPage.cs
Konstantina Chremmou ee648135f6 Since it is easy to forget calling the base class method at the beginning of the
PageLoaded override in derived classes, enforce it by wrapping the page specific
code in a new virtual method, which the derived classes can override and PageLoaded
can call after its own logic.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2018-03-09 11:22:26 +00:00

520 lines
16 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.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAdmin.Network;
using XenAPI;
using XenCenterLib;
namespace XenAdmin.Wizards.ImportWizard
{
public partial class NetworkPickerPage : XenTabPage
{
#region Private fields
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IXenConnection m_selectedConnection;
private Host m_selectedAffinity;
private VM m_vm;
private bool m_buttonNextEnabled;
#endregion
public NetworkPickerPage()
{
InitializeComponent();
m_invalidMacLabel.Visible = false;
}
#region Base class (XenTabPage) overrides
/// <summary>
/// Gets the page's title (headline)
/// </summary>
public override string PageTitle { get { return Messages.IMPORT_SELECT_NETWORK_PAGE_TITLE; } }
/// <summary>
/// Gets the page's label in the (left hand side) wizard progress panel
/// </summary>
public override string Text { get { return Messages.IMPORT_SELECT_NETWORK_PAGE_TEXT; } }
/// <summary>
/// Gets the value by which the help files section for this page is identified
/// </summary>
public override string HelpID { get { return "NetworkPicker"; } }
protected override bool ImplementsIsDirty()
{
return true;
}
protected override void PageLoadedCore(PageLoadedDirection direction)
{
m_buttonNextEnabled = true;
}
public override void PopulatePage()
{
SetNetworkList();
BuildVIFList();
}
public override bool EnablePrevious()
{
return false;
}
public override bool EnableNext()
{
return m_buttonNextEnabled;
}
#endregion
#region Accessors
public List<VIF> VIFs
{
get
{
var vifs = new List<VIF>();
foreach (DataGridViewRow row in m_networkGridView.Rows)
{
var vifRow = row as VifRow;
if (vifRow == null)
continue;
VIF vif = vifRow.Vif;
if (vif == null)
continue;
if (vif.MAC == Messages.MAC_AUTOGENERATE)
vif.MAC = "";
vifs.Add(vif);
}
return vifs;
}
}
#endregion
public void SetAffinity(Host host)
{
m_selectedAffinity = host;
}
public void SetVm(VM vm)
{
m_vm = vm;
}
/// <summary>
/// Should be called before the Affinity is set.
/// </summary>
public void SetConnection(IXenConnection con)
{
m_selectedConnection = con;
}
#region Private methods
private void UpdateControlsEnabledState(bool enabled)
{
m_buttonNextEnabled = enabled;
m_invalidMacLabel.Visible = !enabled;
OnPageUpdated();
}
private void SetNetworkList()
{
DataGridViewComboBoxColumn col = (DataGridViewComboBoxColumn)m_networkGridView.Columns["NetworkNetworkColumn"];
col.Items.Clear();
var networks = m_selectedConnection.Cache.Networks.Where(ShowNetwork);
foreach (XenAPI.Network network in networks)
col.Items.Add(new ToStringWrapper<XenAPI.Network>(network, network.Name()));
col.DisplayMember = ToStringWrapper<XenAPI.Network>.DisplayMember;
col.ValueMember = ToStringWrapper<XenAPI.Network>.ValueMember;
col.Sorted = true;
}
private void BuildVIFList()
{
try
{
m_networkGridView.SuspendLayout();
m_networkGridView.Rows.Clear();
if (m_vm.is_a_template && m_vm.VIFs.Count < 1)
{
// We need to automatically generate VIFs for Networks marked AutoPlug=true
var networks = m_selectedConnection.Cache.Networks;
foreach (XenAPI.Network network in networks)
{
if (m_networkGridView.Rows.Count < m_vm.MaxVIFsAllowed() && ShowNetwork(network) && network.GetAutoPlug())
{
AddVIFRow(new VIF
{
Connection = m_selectedConnection,
device = m_networkGridView.Rows.Count.ToString(),
network = new XenRef<XenAPI.Network>(network.opaque_ref),
MAC = Messages.MAC_AUTOGENERATE
});
}
}
}
else if (m_vm.is_a_template)
{
// We need to create off the _vmTemplate
var vifs = m_selectedConnection.ResolveAll(m_vm.VIFs);
foreach (VIF vif in vifs)
{
AddVIFRow(new VIF
{
Connection = m_selectedConnection,
device = vif.device,
network = vif.network,
MAC = Messages.MAC_AUTOGENERATE
});
}
}
else
{
//We need to recreate off vm
var vifs = m_selectedConnection.ResolveAll(m_vm.VIFs);
foreach (VIF vif in vifs)
AddVIFRow(vif);
}
m_networkGridView.Sort(m_networkGridView.Columns[0], ListSortDirection.Ascending);
}
finally
{
m_networkGridView.ResumeLayout();
m_buttonDeleteNetwork.Enabled = m_networkGridView.SelectedRows.Count > 0;
}
}
private XenAPI.Network GetDefaultNetwork()
{
foreach (XenAPI.Network network in m_selectedConnection.Cache.Networks)
if (ShowNetwork(network))
return network;
return null;
}
/// <summary>
/// Function tells you when you can / cannot show the network based on the following rules
/// 1) Don't show the guest installer network or networks with HideFromXenCenter==true.
/// 2) If you selected an affinity, only show networks that host can see
/// 3) If you haven't selected an affinity, only show networks all hosts can see
/// </summary>
private bool ShowNetwork(XenAPI.Network network)
{
if (!network.Show(Properties.Settings.Default.ShowHiddenVMs))
return false;
if (network.IsSlave())
return false;
if (m_selectedAffinity != null && !m_selectedAffinity.CanSeeNetwork(network))
return false;
if (m_selectedAffinity == null && !network.AllHostsCanSeeNetwork())
return false;
return true;
}
private void AddVIFRow(VIF vif)
{
var row = new VifRow(vif);
XenAPI.Network network = m_selectedConnection.Resolve(vif.network);
bool isGuestInstallerNetwork = network != null && network.IsGuestInstallerNetwork();
ToStringWrapper<XenAPI.Network> comboBoxEntry = FindComboBoxEntryForNetwork(network);
// CA-66962: Don't choose disallowed networks: choose a better one instead.
// CA-79930/CA-73056: Except for the guest installer network, which we let
// through for now, but hide it below.
if (comboBoxEntry == null && !isGuestInstallerNetwork)
{
network = GetDefaultNetwork();
comboBoxEntry = FindComboBoxEntryForNetwork(network);
vif.network = new XenRef<XenAPI.Network>(network.opaque_ref);
}
row.CreateCells(m_networkGridView,
String.Format(Messages.NETWORKPICKER_INTERFACE, vif.device),
vif.MAC,
comboBoxEntry);
row.Cells[0].ReadOnly = true;
// CA-73056: A row for the guest installer network shouldn't show up. But we still need
// it present but invisible, otherwise the corresponding VIF doesn't get created at all.
// CA-218956 - Expose HIMN when showing hidden objects
if (isGuestInstallerNetwork && !XenAdmin.Properties.Settings.Default.ShowHiddenVMs)
row.Visible = false;
m_networkGridView.Rows.Add(row);
}
private ToStringWrapper<XenAPI.Network> FindComboBoxEntryForNetwork(XenAPI.Network network)
{
if (network == null)
return null;
DataGridViewComboBoxColumn column = (DataGridViewComboBoxColumn)m_networkGridView.Columns["NetworkNetworkColumn"];
foreach (ToStringWrapper<XenAPI.Network> entry in column.Items)
{
if (entry.item.uuid == network.uuid)
return entry;
}
return null;
}
private bool AllMacAddressesValid()
{
foreach (var row in m_networkGridView.Rows)
{
var vifRow = row as VifRow;
if (vifRow != null && !vifRow.HasValidMac)
return false;
}
return true;
}
#endregion
#region Event handlers
private void m_buttonDeleteNetwork_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow row in m_networkGridView.SelectedRows)
m_networkGridView.Rows.Remove(row);
UpdateControlsEnabledState(AllMacAddressesValid());
}
private void m_buttonAddNetwork_Click(object sender, EventArgs e)
{
if (m_networkGridView.Rows.Count >= m_vm.MaxVIFsAllowed())
{
using (var dlg = new ThreeButtonDialog(new ThreeButtonDialog.Details(
SystemIcons.Error,
FriendlyErrorNames.VIFS_MAX_ALLOWED, FriendlyErrorNames.VIFS_MAX_ALLOWED_TITLE)))
{
dlg.ShowDialog(Program.MainWindow);
}
return;
}
VIF vif = new VIF {Connection = m_selectedConnection};
int i = 0;
while (true)
{
bool exists = false;
foreach (DataGridViewRow row in m_networkGridView.Rows)
{
VifRow vifRow = row as VifRow;
if (vifRow == null)
continue;
VIF v = vifRow.Vif;
if (v == null)
continue;
if (int.Parse(v.device) == i)
exists = true;
}
if (exists)
i++;
else
break;
}
vif.device = i.ToString();
vif.MAC = Messages.MAC_AUTOGENERATE;
if (GetDefaultNetwork() != null)
vif.network = new XenRef<XenAPI.Network>(GetDefaultNetwork().opaque_ref);
AddVIFRow(vif);
}
private void m_networkGridView_SelectionChanged(object sender, EventArgs e)
{
m_buttonDeleteNetwork.Enabled = m_networkGridView.SelectedRows.Count > 0;
}
private void m_networkGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
VifRow row = m_networkGridView.Rows[e.RowIndex] as VifRow;
if (row == null)
return;
VIF vif = row.Vif;
if (vif == null)
return;
if (e.ColumnIndex == MACAddressNetworkColumn.Index)
{
row.HasValidMac = true;
string mac = (string)row.Cells[e.ColumnIndex].Value;
if (mac == null || mac.Trim().Length == 0)
{
// We take it that zero-length mac means the user wants to auto-generate
row.Cells[e.ColumnIndex].Value = Messages.MAC_AUTOGENERATE;
}
else if (mac.Trim() == Messages.MAC_AUTOGENERATE)
{
row.Cells[e.ColumnIndex].Value = Messages.MAC_AUTOGENERATE;
vif.MAC = Messages.MAC_AUTOGENERATE;
}
else if (Helpers.IsValidMAC(mac))
{
vif.MAC = mac;
}
else
{
row.HasValidMac = false;
}
UpdateControlsEnabledState(AllMacAddressesValid());
}
else if (e.ColumnIndex == NetworkNetworkColumn.Index)
{
object enteredValue = row.Cells[e.ColumnIndex].Value;
var entry = enteredValue as ToStringWrapper<XenAPI.Network>;
XenAPI.Network network = (entry == null) ? (XenAPI.Network)enteredValue : entry.item;
if (network != null)
vif.network = new XenRef<XenAPI.Network>(network.opaque_ref);
}
}
private void m_networkGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || e.RowIndex >= m_networkGridView.RowCount)
return;
if (e.ColumnIndex == MACAddressNetworkColumn.Index)
{
m_networkGridView.BeginEdit(false);
return;
}
if (e.ColumnIndex == NetworkNetworkColumn.Index)
{
m_networkGridView.BeginEdit(false);
var combobox = m_networkGridView.EditingControl as ComboBox;
if (combobox != null)
combobox.DroppedDown = true;
}
}
private void m_networkGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
m_networkGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
IsDirty = true;
}
private void m_networkGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.ThrowException = false;
log.Error(String.Format(Messages.NETWORKPICKER_LOG_VIF_ERROR, e.Exception.Message));
}
private void m_networkGridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
if (e.RowIndex < 0 || e.RowIndex >= m_networkGridView.RowCount)
return;
if (e.ColumnIndex == MACAddressNetworkColumn.Index)
{
bool enable = true;
foreach (DataGridViewRow row in m_networkGridView.Rows)
{
if (row.Index == e.RowIndex)
continue;
var vifRow = row as VifRow;
if (vifRow != null && !vifRow.HasValidMac)
{
enable = false;
break;
}
}
UpdateControlsEnabledState(enable);
}
}
#endregion
#region Nested classes
private class VifRow : DataGridViewRow
{
public readonly VIF Vif;
public bool HasValidMac = true;
public VifRow(VIF vif)
{
Vif = vif;
}
}
#endregion
}
}