mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-22 16:36:03 +01:00
077cb17deb
NewAction to use the Action<ActionBase> delegate instead of EventHandler. Thus there is no need to fire them with Empty or null EventArgs and on several occasions we avoid casting objects in the event handlers. Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
539 lines
20 KiB
C#
539 lines
20 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.Threading;
|
|
using System.Windows.Forms;
|
|
using XenAdmin.Actions;
|
|
using XenAdmin.Core;
|
|
using XenAdmin.Network;
|
|
using XenAPI;
|
|
|
|
|
|
namespace XenAdmin.Dialogs
|
|
{
|
|
public partial class IscsiDeviceConfigDialog : XenDialogBase
|
|
{
|
|
private ISCSIPopulateLunsAction IscsiPopulateLunsAction;
|
|
private ISCSIPopulateIQNsAction IscsiPopulateIqnsAction;
|
|
private bool LunInUse = false;
|
|
|
|
private readonly Dictionary<String, ISCSIInfo> LunMap = new Dictionary<String, ISCSIInfo>();
|
|
|
|
private readonly ToolTip TargetIqnToolTip = new ToolTip();
|
|
private readonly ToolTip TargetLunToolTip = new ToolTip();
|
|
|
|
public IscsiDeviceConfigDialog()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
public IscsiDeviceConfigDialog(IXenConnection connection)
|
|
: base(connection)
|
|
{
|
|
InitializeComponent();
|
|
labelIscsiInvalidHost.Visible = false;
|
|
|
|
// IQN's can be very long, so we will show the value as a mouse over tooltip.
|
|
// Initialize the tooltip here.
|
|
TargetIqnToolTip.Active = true;
|
|
TargetIqnToolTip.AutomaticDelay = 0;
|
|
TargetIqnToolTip.AutoPopDelay = 50000;
|
|
TargetIqnToolTip.InitialDelay = 50;
|
|
TargetIqnToolTip.ReshowDelay = 50;
|
|
TargetIqnToolTip.ShowAlways = true;
|
|
|
|
|
|
// Initialize LUN's tooltip here.
|
|
TargetLunToolTip.Active = true;
|
|
TargetLunToolTip.AutomaticDelay = 0;
|
|
TargetLunToolTip.AutoPopDelay = 50000;
|
|
TargetLunToolTip.InitialDelay = 50;
|
|
TargetLunToolTip.ReshowDelay = 50;
|
|
TargetLunToolTip.ShowAlways = true;
|
|
}
|
|
|
|
private void textBoxIscsiHost_TextChanged(object sender, EventArgs e)
|
|
{
|
|
labelIscsiInvalidHost.Visible = false;
|
|
IScsiParams_TextChanged(null, null);
|
|
}
|
|
|
|
private void IscsiUseChapCheckBox_CheckedChanged(object sender, System.EventArgs e)
|
|
{
|
|
groupBoxChap.Enabled = IscsiUseChapCheckBox.Checked;
|
|
foreach (Control c in groupBoxChap.Controls)
|
|
{
|
|
c.Enabled = IscsiUseChapCheckBox.Checked;
|
|
}
|
|
lunInUseLabel.Text = "";
|
|
ChapSettings_Changed(null, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when any of the iSCSI filer params change: resets the IQNs/LUNs.
|
|
/// Must be called on the event thread.
|
|
/// </summary>
|
|
private void IScsiParams_TextChanged(object sender, EventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
// User has changed filer hostname/username/password - clear IQN/LUN boxes
|
|
comboBoxIscsiIqns.Items.Clear();
|
|
comboBoxIscsiIqns.Enabled = false;
|
|
|
|
// Cancel pending IQN/LUN scans
|
|
if (IscsiPopulateIqnsAction != null)
|
|
{
|
|
IscsiPopulateIqnsAction.Cancel();
|
|
}
|
|
|
|
ChapSettings_Changed(null, null);
|
|
}
|
|
|
|
private void ChapSettings_Changed(object sender, EventArgs e)
|
|
{
|
|
comboBoxIscsiLuns.Items.Clear();
|
|
comboBoxIscsiLuns.Text = "";
|
|
comboBoxIscsiLuns.Enabled = false;
|
|
|
|
if (IscsiPopulateLunsAction != null)
|
|
{
|
|
IscsiPopulateLunsAction.Cancel();
|
|
}
|
|
|
|
UpdateButtons();
|
|
}
|
|
|
|
private void UpdateButtons()
|
|
{
|
|
UInt16 i;
|
|
bool portValid = UInt16.TryParse(textBoxIscsiPort.Text, out i);
|
|
|
|
buttonIscsiPopulateIQNs.Enabled =
|
|
!String.IsNullOrEmpty(getIscsiHost())
|
|
&& portValid;
|
|
|
|
buttonIscsiPopulateLUNs.Enabled =
|
|
!String.IsNullOrEmpty(getIscsiHost())
|
|
&& !String.IsNullOrEmpty(getIscsiIQN())
|
|
&& portValid;
|
|
|
|
buttonOk.Enabled = !String.IsNullOrEmpty(getIscsiLUN()) && !LunInUse;
|
|
}
|
|
|
|
private String getIscsiHost()
|
|
{
|
|
// If the user has selected an IQN, use the host from that IQN (due to multi-homing,
|
|
// this may differ from the host they first entered). Otherwise use the host
|
|
// they first entered,
|
|
ToStringWrapper<IScsiIqnInfo> wrapper = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
if (wrapper != null)
|
|
return wrapper.item.IpAddress;
|
|
return textBoxIscsiHost.Text.Trim();
|
|
}
|
|
|
|
private UInt16 getIscsiPort()
|
|
{
|
|
ToStringWrapper<IScsiIqnInfo> wrapper = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
if (wrapper != null)
|
|
return wrapper.item.Port;
|
|
|
|
// No combobox item was selected
|
|
UInt16 port;
|
|
if (UInt16.TryParse(textBoxIscsiPort.Text, out port))
|
|
{
|
|
return port;
|
|
}
|
|
return Util.DEFAULT_ISCSI_PORT;
|
|
}
|
|
|
|
private String getIscsiIQN()
|
|
{
|
|
ToStringWrapper<IScsiIqnInfo> wrapper = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
if (wrapper == null)
|
|
return "";
|
|
return wrapper.item.TargetIQN;
|
|
}
|
|
|
|
private String getIscsiLUN()
|
|
{
|
|
return comboBoxIscsiLuns.Text;
|
|
}
|
|
|
|
private void comboBoxIscsiIqns_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
ToStringWrapper<IScsiIqnInfo> wrapper = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
// Keep the IScsiTargetIqnComboBox tooltip in sync with the selected item
|
|
if (wrapper != null)
|
|
{
|
|
TargetIqnToolTip.SetToolTip(comboBoxIscsiIqns, wrapper.ToString());
|
|
}
|
|
else
|
|
{
|
|
TargetIqnToolTip.SetToolTip(comboBoxIscsiIqns, "");
|
|
}
|
|
// Clear the LUN map and ComboBox because the user has changed the IQN. The user
|
|
// must re-discover the LUNs for the new IQN.
|
|
ClearLunMapAndCombo();
|
|
}
|
|
|
|
private void comboBoxIscsiLuns_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
var selectedLun = comboBoxIscsiLuns.SelectedItem as string;
|
|
// Keep the tooltip in sync with the selected item
|
|
if (selectedLun != null)
|
|
{
|
|
TargetLunToolTip.SetToolTip(comboBoxIscsiLuns, selectedLun);
|
|
}
|
|
else
|
|
{
|
|
TargetLunToolTip.SetToolTip(comboBoxIscsiLuns, "");
|
|
}
|
|
|
|
try
|
|
{
|
|
SR sr = UniquenessCheck(ConnectionsManager.XenConnectionsCopy);
|
|
|
|
// LUN is not in use if sr != null
|
|
LunInUse = sr != null;
|
|
|
|
if (sr == null)
|
|
{
|
|
lunInUseLabel.Text = "";
|
|
return;
|
|
}
|
|
|
|
Pool pool = Helpers.GetPool(sr.Connection);
|
|
if (pool != null)
|
|
{
|
|
lunInUseLabel.Text = String.Format(Messages.NEWSR_LUN_IN_USE_ON_POOL,
|
|
sr.Name, pool.Name);
|
|
return;
|
|
}
|
|
|
|
Host master = Helpers.GetMaster(sr.Connection);
|
|
if (master != null)
|
|
{
|
|
lunInUseLabel.Text = String.Format(Messages.NEWSR_LUN_IN_USE_ON_SERVER,
|
|
sr.Name, master.Name);
|
|
return;
|
|
}
|
|
|
|
lunInUseLabel.Text = Messages.NEWSR_LUN_IN_USE;
|
|
}
|
|
finally
|
|
{
|
|
UpdateButtons();
|
|
}
|
|
}
|
|
|
|
private void ClearLunMapAndCombo()
|
|
{
|
|
// Clear LUNs as they may no longer be valid
|
|
comboBoxIscsiLuns.Items.Clear();
|
|
comboBoxIscsiLuns.Text = "";
|
|
comboBoxIscsiLuns.Enabled = false;
|
|
LunMap.Clear();
|
|
LunInUse = false;
|
|
lunInUseLabel.Text = "";
|
|
|
|
UpdateButtons();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check the current config of the iSCSI sr in the wizard is unique across
|
|
/// all active connections.
|
|
/// </summary>
|
|
/// <returns>SR that uses this config if not unique, null if unique</returns>
|
|
private SR UniquenessCheck(List<IXenConnection> connections)
|
|
{
|
|
// Check currently selected lun is unique amongst other connected hosts.
|
|
foreach (IXenConnection connection in connections)
|
|
{
|
|
foreach (SR sr in connection.Cache.SRs)
|
|
{
|
|
if (sr.GetSRType(false) != SR.SRTypes.lvmoiscsi)
|
|
continue;
|
|
|
|
if (sr.PBDs.Count < 1)
|
|
continue;
|
|
|
|
PBD pbd = connection.Resolve(sr.PBDs[0]);
|
|
|
|
if (pbd == null)
|
|
continue;
|
|
|
|
if (UniquenessCheckMiami(connection, pbd))
|
|
return sr;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check currently LUN against miami host
|
|
/// </summary>
|
|
/// <param name="connection"></param>
|
|
/// <param name="pbd"></param>
|
|
/// <returns></returns>
|
|
private bool UniquenessCheckMiami(IXenConnection connection, PBD pbd)
|
|
{
|
|
if (!pbd.device_config.ContainsKey(SCSIID))
|
|
return false;
|
|
|
|
String scsiID = pbd.device_config[SCSIID];
|
|
String myLUN = getIscsiLUN();
|
|
|
|
if (!LunMap.ContainsKey(myLUN))
|
|
return false;
|
|
|
|
ISCSIInfo info = LunMap[myLUN];
|
|
|
|
return info.ScsiID == scsiID;
|
|
}
|
|
|
|
private const String TARGET = "target";
|
|
private const String PORT = "port";
|
|
private const String TARGETIQN = "targetIQN";
|
|
private const String LUNSERIAL = "LUNSerial";
|
|
private const String SCSIID = "SCSIid";
|
|
private const String LUNID = "LUNid";
|
|
private const String CHAPUSER = "chapuser";
|
|
private const String CHAPPASSWORD = "chappassword";
|
|
|
|
public Dictionary<String, String> DeviceConfig
|
|
{
|
|
get
|
|
{
|
|
Dictionary<String, String> dconf = new Dictionary<String, String>();
|
|
ToStringWrapper<IScsiIqnInfo> iqn = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
if (iqn == null)
|
|
return null;
|
|
|
|
// Reset target IP address to home address specified in IQN scan.
|
|
// Allows multi-homing - see CA-11607
|
|
dconf[TARGET] = iqn.item.IpAddress;
|
|
dconf[PORT] = iqn.item.Port.ToString();
|
|
dconf[TARGETIQN] = getIscsiIQN();
|
|
|
|
if (!LunMap.ContainsKey(getIscsiLUN()))
|
|
return null;
|
|
|
|
ISCSIInfo info = LunMap[getIscsiLUN()];
|
|
if (info.LunID == -1)
|
|
{
|
|
dconf[LUNSERIAL] = info.Serial;
|
|
}
|
|
else
|
|
{
|
|
dconf[SCSIID] = info.ScsiID;
|
|
}
|
|
|
|
if (IscsiUseChapCheckBox.Checked)
|
|
{
|
|
dconf[CHAPUSER] = IScsiChapUserTextBox.Text;
|
|
dconf[CHAPPASSWORD] = IScsiChapSecretTextBox.Text;
|
|
}
|
|
|
|
return dconf;
|
|
}
|
|
}
|
|
|
|
private void buttonIscsiPopulateIQNs_Click(object sender, EventArgs e)
|
|
{
|
|
buttonIscsiPopulateIQNs.Enabled = false;
|
|
// For this button to be enabled, we must be Miami or newer
|
|
comboBoxIscsiIqns.Items.Clear();
|
|
// Clear LUNs as they may no longer be valid
|
|
ClearLunMapAndCombo();
|
|
// Disable the Discover LUNs button because we have no IQNs
|
|
buttonIscsiPopulateLUNs.Enabled = false;
|
|
// Cancel any LUN scan in progress, as it is no longer meaningful
|
|
if (IscsiPopulateLunsAction != null)
|
|
{
|
|
IscsiPopulateLunsAction.Cancel();
|
|
}
|
|
|
|
if (IscsiUseChapCheckBox.Checked)
|
|
{
|
|
IscsiPopulateIqnsAction = new ISCSIPopulateIQNsAction(connection,
|
|
getIscsiHost(), getIscsiPort(), IScsiChapUserTextBox.Text, IScsiChapSecretTextBox.Text);
|
|
}
|
|
else
|
|
{
|
|
IscsiPopulateIqnsAction = new ISCSIPopulateIQNsAction(connection,
|
|
getIscsiHost(), getIscsiPort(), null, null);
|
|
}
|
|
|
|
IscsiPopulateIqnsAction.Completed += IscsiPopulateIqnsAction_Completed;
|
|
Dialogs.ActionProgressDialog dialog = new Dialogs.ActionProgressDialog(
|
|
IscsiPopulateIqnsAction, ProgressBarStyle.Marquee);
|
|
dialog.ShowCancel = true;
|
|
dialog.ShowDialog(this);
|
|
}
|
|
|
|
private void IscsiPopulateIqnsAction_Completed(ActionBase sender)
|
|
{
|
|
Program.Invoke(this, (System.Threading.WaitCallback)IscsiPopulateIqnsAction_Completed_, sender);
|
|
}
|
|
|
|
private void IscsiPopulateIqnsAction_Completed_(object o)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
ISCSIPopulateIQNsAction action = (ISCSIPopulateIQNsAction)o;
|
|
|
|
if (action.Succeeded)
|
|
{
|
|
if (action.IQNs.Length == 0)
|
|
{
|
|
// Do nothing: ActionProgressDialog will show Messages.NEWSR_NO_IQNS_FOUND
|
|
}
|
|
else
|
|
{
|
|
int width = comboBoxIscsiIqns.Width;
|
|
foreach (Actions.IScsiIqnInfo iqnInfo in action.IQNs)
|
|
{
|
|
if (!String.IsNullOrEmpty(iqnInfo.TargetIQN))
|
|
{
|
|
String toString = String.Format("{0} ({1}:{2})", iqnInfo.TargetIQN, iqnInfo.IpAddress, iqnInfo.Port);
|
|
comboBoxIscsiIqns.Items.Add(new ToStringWrapper<IScsiIqnInfo>(iqnInfo, toString));
|
|
width = Math.Max(width, Drawing.MeasureText(toString, comboBoxIscsiIqns.Font).Width);
|
|
}
|
|
}
|
|
// Set the combo box dropdown width to accommodate the widest item (within reason)
|
|
comboBoxIscsiIqns.DropDownWidth = Math.Min(width, Int16.MaxValue);
|
|
|
|
if (comboBoxIscsiIqns.Items.Count > 0)
|
|
{
|
|
comboBoxIscsiIqns.SelectedItem = comboBoxIscsiIqns.Items[0];
|
|
comboBoxIscsiIqns.Enabled = true;
|
|
buttonIscsiPopulateLUNs.Enabled = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Failure failure = action.Exception as Failure;
|
|
if (failure != null && failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_140")
|
|
{
|
|
labelIscsiInvalidHost.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
}
|
|
buttonIscsiPopulateIQNs.Enabled = true;
|
|
}
|
|
|
|
private void buttonIscsiPopulateLUNs_Click(object sender, EventArgs e)
|
|
{
|
|
buttonIscsiPopulateLUNs.Enabled = false;
|
|
comboBoxIscsiLuns.Items.Clear();
|
|
LunMap.Clear();
|
|
|
|
if (IscsiUseChapCheckBox.Checked)
|
|
{
|
|
IscsiPopulateLunsAction = new Actions.ISCSIPopulateLunsAction(connection,
|
|
getIscsiHost(), getIscsiPort(), getIscsiIQN(), IScsiChapUserTextBox.Text, IScsiChapSecretTextBox.Text);
|
|
}
|
|
else
|
|
{
|
|
IscsiPopulateLunsAction = new Actions.ISCSIPopulateLunsAction(connection,
|
|
getIscsiHost(), getIscsiPort(), getIscsiIQN(), null, null);
|
|
}
|
|
|
|
IscsiPopulateLunsAction.Completed += IscsiPopulateLunsAction_Completed;
|
|
ActionProgressDialog dialog =
|
|
new ActionProgressDialog(IscsiPopulateLunsAction, ProgressBarStyle.Marquee);
|
|
dialog.ShowCancel = true;
|
|
dialog.ShowDialog(this);
|
|
}
|
|
|
|
private void IscsiPopulateLunsAction_Completed(ActionBase sender)
|
|
{
|
|
Program.Invoke(this, (WaitCallback)IscsiPopulateLunsAction_Completed_, sender);
|
|
}
|
|
|
|
private void IscsiPopulateLunsAction_Completed_(object o)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
ISCSIPopulateLunsAction action = (ISCSIPopulateLunsAction)o;
|
|
|
|
buttonIscsiPopulateLUNs.Enabled = true;
|
|
|
|
if (!action.Succeeded)
|
|
{
|
|
Failure failure = action.Exception as Failure;
|
|
if (failure != null && failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_140")
|
|
{
|
|
labelIscsiInvalidHost.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (action.LUNs.Length == 0)
|
|
{
|
|
// Do nothing: ActionProgressDialog will show Messages.NEWSR_NO_LUNS_FOUND
|
|
}
|
|
else
|
|
{
|
|
int width = comboBoxIscsiLuns.Width;
|
|
|
|
foreach (Actions.ISCSIInfo i in action.LUNs)
|
|
{
|
|
String label = "LUN";
|
|
if (i.LunID != -1)
|
|
label += String.Format(" {0}", i.LunID);
|
|
if (i.Serial != "")
|
|
label += String.Format(": {0}", i.Serial);
|
|
if (i.Size >= 0)
|
|
label += String.Format(": {0}", Util.DiskSizeString(i.Size));
|
|
if (i.Vendor != "")
|
|
label += String.Format(" ({0})", i.Vendor);
|
|
comboBoxIscsiLuns.Items.Add(label);
|
|
LunMap.Add(label, i);
|
|
|
|
width = Math.Max(width, Drawing.MeasureText(label, comboBoxIscsiLuns.Font).Width);
|
|
}
|
|
comboBoxIscsiLuns.SelectedItem = comboBoxIscsiLuns.Items[0];
|
|
comboBoxIscsiLuns.Enabled = true;
|
|
|
|
// Set the combo box dropdown width to accommodate the widest item (within reason)
|
|
comboBoxIscsiLuns.DropDownWidth = Math.Min(width, Int16.MaxValue);
|
|
}
|
|
|
|
UpdateButtons();
|
|
}
|
|
}
|
|
}
|