mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-30 07:40:13 +01:00
92f0499911
We observed that some threads can reach deadlock-ish state after they have Invoked into a control's UI thread. When it happens they are all in a waiting for join or in sleep state for very long time, although there should not be any deadlock situations.
It seems this has something to do with multiple parent controls and with which control we invoked on. This should not make a difference, because we have got one UI thread (for MainWindow) they should wait for, but we have seen it does.
The solution that fixed this issue was to invoke on the MainWindow instead of various controls (see a4fe507adf
).
This changeset is changing all our Invokes to invoke into MainWindow
instead of a control itself. (MainWindow's UI thread is the only UI thread
all Control is using in XenCenter)
This changeset should be in place until we have found the root cause or the exact reason for the above.
888 lines
34 KiB
C#
888 lines
34 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.Windows.Forms;
|
|
using XenAPI;
|
|
using XenAdmin.Actions;
|
|
using XenAdmin.Network;
|
|
using XenAdmin.Core;
|
|
using System.Threading;
|
|
using XenAdmin.Controls;
|
|
using XenAdmin.Dialogs;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using XenAdmin.Utils;
|
|
|
|
namespace XenAdmin.Wizards.NewSRWizard_Pages.Frontends
|
|
{
|
|
public partial class LVMoISCSI : XenTabPage
|
|
{
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
/// <summary>
|
|
/// If an SR already exists on this LUN, this will point to the SR info which indicates that
|
|
/// the SR should be reattached.
|
|
///
|
|
/// If this is null, indicates the LUN should be formatted into a new SR.
|
|
/// </summary>
|
|
private SR.SRInfo _srToIntroduce;
|
|
|
|
private ISCSIPopulateLunsAction IscsiPopulateLunsAction;
|
|
private ISCSIPopulateIQNsAction IscsiPopulateIqnsAction;
|
|
|
|
private readonly Dictionary<String, ISCSIInfo> LunMap = new Dictionary<String, ISCSIInfo>();
|
|
|
|
private readonly ToolTip TargetIqnToolTip = new ToolTip();
|
|
|
|
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 CHAPUSER = "chapuser";
|
|
private const string CHAPPASSWORD = "chappassword";
|
|
|
|
private IEnumerable<Control> ErrorIcons
|
|
{
|
|
get { return new Control[] {errorIconAtCHAPPassword, errorIconAtHostOrIP, errorIconAtTargetLUN }; }
|
|
}
|
|
|
|
private IEnumerable<Control> ErrorLabels
|
|
{
|
|
get { return new Control[] { errorLabelAtHostname, errorLabelAtCHAPPassword, errorLabelAtTargetLUN }; }
|
|
}
|
|
|
|
private IEnumerable<Control> SpinnerControls
|
|
{
|
|
get { return new Control[] { spinnerIconAtScanTargetHostButton, spinnerIconAtTargetIqn, spinnerIconAtTargetLun }; }
|
|
}
|
|
|
|
private IEnumerable<Control> UserInputControls
|
|
{
|
|
get
|
|
{
|
|
return new Control[]
|
|
{
|
|
textBoxIscsiHost, textBoxIscsiPort, IscsiUseChapCheckBox, IScsiChapUserTextBox,
|
|
IScsiChapSecretTextBox, scanTargetHostButton, comboBoxIscsiIqns, comboBoxIscsiLuns
|
|
};
|
|
}
|
|
}
|
|
|
|
private readonly TemporaryDisablerForControls controlDisabler = new TemporaryDisablerForControls();
|
|
|
|
public LVMoISCSI()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
#region XentabPage overrides
|
|
|
|
public override string PageTitle { get { return Messages.NEWSR_PATH_ISCSI; } }
|
|
|
|
public override string Text { get { return Messages.NEWSR_LOCATION; } }
|
|
|
|
public override string HelpID { get { return "Location_ISCSI"; } }
|
|
|
|
public override void PageLoaded(PageLoadedDirection direction)
|
|
{
|
|
base.PageLoaded(direction);
|
|
HelpersGUI.PerformIQNCheck();
|
|
|
|
if (direction == PageLoadedDirection.Forward)
|
|
HelpersGUI.FocusFirstControl(Controls);
|
|
}
|
|
|
|
public override void PageLeave(PageLoadedDirection direction, ref bool cancel)
|
|
{
|
|
if (direction == PageLoadedDirection.Back)
|
|
return;
|
|
|
|
// For Miami hosts we need to ensure an SR.probe()
|
|
// has been performed, and that the user has made a decision. Show the iSCSI choices dialog until
|
|
// they click something other than 'Cancel'. For earlier host versions, warn that data loss may
|
|
// occur.
|
|
|
|
Host master = Helpers.GetMaster(Connection);
|
|
if (master == null)
|
|
{
|
|
cancel = true;
|
|
return;
|
|
}
|
|
|
|
Dictionary<String, String> dconf = DeviceConfig;
|
|
if (dconf == null)
|
|
{
|
|
cancel = true;
|
|
return;
|
|
}
|
|
|
|
// Start probe
|
|
SrProbeAction IscsiProbeAction = new SrProbeAction(Connection, master, SR.SRTypes.lvmoiscsi, dconf);
|
|
ActionProgressDialog dialog = new ActionProgressDialog(IscsiProbeAction, ProgressBarStyle.Marquee);
|
|
dialog.ShowCancel = true;
|
|
dialog.ShowDialog(this);
|
|
|
|
// Probe has been performed. Now ask the user if they want to Reattach/Format/Cancel.
|
|
// Will return false on cancel
|
|
cancel = !ExamineIscsiProbeResults(IscsiProbeAction);
|
|
|
|
base.PageLeave(direction, ref cancel);
|
|
}
|
|
|
|
public override bool EnableNext()
|
|
{
|
|
UInt16 i;
|
|
bool portValid = UInt16.TryParse(textBoxIscsiPort.Text, out i);
|
|
|
|
return !String.IsNullOrEmpty(getIscsiHost())
|
|
&& portValid
|
|
&& !(IscsiUseChapCheckBox.Checked && String.IsNullOrEmpty(IScsiChapUserTextBox.Text))
|
|
&& comboBoxIscsiLuns.SelectedItem != null && comboBoxIscsiLuns.SelectedItem as string != Messages.SELECT_TARGET_LUN
|
|
&& !IsLunInUse();
|
|
}
|
|
|
|
public override bool EnablePrevious()
|
|
{
|
|
if (SrWizardType.DisasterRecoveryTask && SrWizardType.SrToReattach == null)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public override void PopulatePage()
|
|
{
|
|
HideAllErrorIconsAndLabels();
|
|
|
|
// Enable IQN scanning
|
|
comboBoxIscsiIqns.Visible = true;
|
|
|
|
// 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;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void UpdateButtons()
|
|
{
|
|
UInt16 i;
|
|
bool portValid = UInt16.TryParse(textBoxIscsiPort.Text, out i);
|
|
|
|
scanTargetHostButton.Enabled =
|
|
!String.IsNullOrEmpty(getIscsiHost())
|
|
&& portValid;
|
|
|
|
// Cause wizards next etc to update
|
|
OnPageUpdated();
|
|
}
|
|
|
|
private void textBoxIscsiHost_TextChanged(object sender, EventArgs e)
|
|
{
|
|
HideAllErrorIconsAndLabels();
|
|
HideAllSpinnerIcons();
|
|
IScsiParams_TextChanged(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();
|
|
|
|
spinnerIconAtScanTargetHostButton.Visible = false;
|
|
|
|
// User has changed filer hostname/username/password - clear IQN/LUN boxes
|
|
comboBoxIscsiIqns.Items.Clear();
|
|
comboBoxIscsiIqns.Enabled = false;
|
|
labelIscsiIQN.Enabled = false;
|
|
iSCSITargetGroupBox.Enabled = false;
|
|
|
|
// Cancel pending IQN/LUN scans
|
|
if (IscsiPopulateIqnsAction != null)
|
|
{
|
|
IscsiPopulateIqnsAction.Cancel();
|
|
}
|
|
|
|
ChapSettings_Changed(null, null);
|
|
}
|
|
|
|
private bool IsLunInUse()
|
|
{
|
|
SR sr = UniquenessCheck();
|
|
|
|
// LUN is not in use iff sr != null
|
|
|
|
if (sr == null)
|
|
{
|
|
HideAllErrorIconsAndLabels();
|
|
return false;
|
|
}
|
|
|
|
spinnerIconAtTargetLun.Visible = false;
|
|
|
|
Pool pool = Helpers.GetPool(sr.Connection);
|
|
if (pool != null)
|
|
{
|
|
errorIconAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Text = String.Format(Messages.NEWSR_LUN_IN_USE_ON_POOL, sr.Name, pool.Name);
|
|
return true;
|
|
}
|
|
|
|
Host master = Helpers.GetMaster(sr.Connection);
|
|
if (master != null)
|
|
{
|
|
errorIconAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Text = String.Format(Messages.NEWSR_LUN_IN_USE_ON_SERVER, sr.Name, master.Name);
|
|
return true;
|
|
}
|
|
|
|
errorIconAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Text = Messages.NEWSR_LUN_IN_USE;
|
|
return true;
|
|
}
|
|
|
|
private void comboBoxIscsiLuns_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
if (comboBoxIscsiLuns.SelectedItem as string != Messages.SELECT_TARGET_LUN)
|
|
{
|
|
spinnerIconAtTargetLun.DisplaySucceededImage();
|
|
}
|
|
else
|
|
{
|
|
spinnerIconAtTargetLun.Visible = false;
|
|
HideAllErrorIconsAndLabels();
|
|
}
|
|
|
|
UpdateButtons();
|
|
}
|
|
|
|
private void ChapSettings_Changed(object sender, EventArgs e)
|
|
{
|
|
comboBoxIscsiLuns.Items.Clear();
|
|
comboBoxIscsiLuns.Text = "";
|
|
comboBoxIscsiLuns.Enabled = false;
|
|
targetLunLabel.Enabled = false;
|
|
|
|
if (IscsiPopulateLunsAction != null)
|
|
{
|
|
IscsiPopulateLunsAction.Cancel();
|
|
}
|
|
|
|
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()
|
|
{
|
|
// Check currently selected lun is unique amongst other connected hosts.
|
|
foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
|
|
{
|
|
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 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;
|
|
}
|
|
else
|
|
{
|
|
return Util.DEFAULT_ISCSI_PORT;
|
|
}
|
|
}
|
|
|
|
private String getIscsiIQN()
|
|
{
|
|
ToStringWrapper<IScsiIqnInfo> wrapper = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
if (wrapper == null)
|
|
return "";
|
|
else
|
|
return wrapper.item.TargetIQN;
|
|
}
|
|
|
|
private void IScsiTargetIqnComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
ToStringWrapper<IScsiIqnInfo> wrapper = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
|
|
ClearLunMapAndCombo();
|
|
HideAllErrorIconsAndLabels();
|
|
|
|
if (wrapper != null)
|
|
{
|
|
TargetIqnToolTip.SetToolTip(comboBoxIscsiIqns, wrapper.ToString());
|
|
IscsiPopulateLUNs();
|
|
}
|
|
else
|
|
{
|
|
TargetIqnToolTip.SetToolTip(comboBoxIscsiIqns, Messages.SELECT_TARGET_IQN);
|
|
}
|
|
}
|
|
|
|
private String getIscsiLUN()
|
|
{
|
|
return comboBoxIscsiLuns.Text;
|
|
}
|
|
|
|
private void IscsiUseChapCheckBox_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
bool enabled = IscsiUseChapCheckBox.Checked;
|
|
|
|
IScsiChapUserTextBox.Enabled = enabled;
|
|
IScsiChapSecretTextBox.Enabled = enabled;
|
|
labelCHAPuser.Enabled = enabled;
|
|
IScsiChapSecretLabel.Enabled = enabled;
|
|
|
|
HideAllErrorIconsAndLabels();
|
|
ChapSettings_Changed(null, null);
|
|
}
|
|
|
|
private void scanTargetHostButton_Click(object sender, EventArgs e)
|
|
{
|
|
HideAllErrorIconsAndLabels();
|
|
spinnerIconAtTargetIqn.Visible = false;
|
|
spinnerIconAtTargetLun.Visible = false;
|
|
|
|
spinnerIconAtScanTargetHostButton.StartSpinning();
|
|
|
|
scanTargetHostButton.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();
|
|
// Cancel any LUN scan in progress, as it is no longer meaningful
|
|
if (IscsiPopulateLunsAction != null)
|
|
{
|
|
IscsiPopulateLunsAction.Cancel();
|
|
}
|
|
|
|
UpdateButtons();
|
|
|
|
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;
|
|
|
|
controlDisabler.Reset();
|
|
controlDisabler.SaveOrUpdateEnabledStates(UserInputControls);
|
|
controlDisabler.DisableAllControls();
|
|
|
|
scanTargetHostButton.Enabled = false;
|
|
IscsiPopulateIqnsAction.RunAsync();
|
|
}
|
|
|
|
private void ClearLunMapAndCombo()
|
|
{
|
|
// Clear LUNs as they may no longer be valid
|
|
comboBoxIscsiLuns.Items.Clear();
|
|
comboBoxIscsiLuns.Text = "";
|
|
comboBoxIscsiLuns.Enabled = false;
|
|
targetLunLabel.Enabled = false;
|
|
LunMap.Clear();
|
|
spinnerIconAtTargetIqn.Visible = false;
|
|
spinnerIconAtTargetLun.Visible = false;
|
|
|
|
UpdateButtons();
|
|
}
|
|
|
|
private void IscsiPopulateIqnsAction_Completed(ActionBase sender)
|
|
{
|
|
Program.Invoke(Program.MainWindow, (System.Threading.WaitCallback)IscsiPopulateIqnsAction_Completed_, sender);
|
|
}
|
|
|
|
private void IscsiPopulateIqnsAction_Completed_(object o)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
ISCSIPopulateIQNsAction action = (ISCSIPopulateIQNsAction)o;
|
|
|
|
controlDisabler.RestoreEnabledOnAllControls();
|
|
|
|
if (action.Succeeded)
|
|
{
|
|
if (action.IQNs.Length == 0)
|
|
{
|
|
// Do nothing: ActionProgressDialog will show Messages.NEWSR_NO_IQNS_FOUND
|
|
}
|
|
else
|
|
{
|
|
int width = comboBoxIscsiIqns.Width;
|
|
|
|
comboBoxIscsiIqns.Items.Add(Messages.SELECT_TARGET_IQN);
|
|
|
|
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 = Messages.SELECT_TARGET_IQN;
|
|
comboBoxIscsiIqns.Enabled = true;
|
|
labelIscsiIQN.Enabled = true;
|
|
iSCSITargetGroupBox.Enabled = true;
|
|
}
|
|
|
|
spinnerIconAtScanTargetHostButton.DisplaySucceededImage();
|
|
|
|
comboBoxIscsiIqns.Focus();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
spinnerIconAtScanTargetHostButton.Visible = false;
|
|
|
|
Failure failure = action.Exception as Failure;
|
|
if (failure != null && failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_140")
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Text = Messages.INVALID_HOST;
|
|
errorLabelAtHostname.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
else if (failure != null && failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_141")
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Text = Messages.SR_UNABLE_TO_CONNECT_TO_SCSI_TARGET;
|
|
errorLabelAtHostname.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
else if (failure != null && failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_68")
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Text = Messages.LOGGING_IN_TO_THE_ISCSI_TARGET_FAILED;
|
|
errorLabelAtHostname.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
else
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Text = failure.ErrorDescription.Count > 2 ? failure.ErrorDescription[2] : failure.ErrorDescription[0];
|
|
errorLabelAtHostname.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
|
|
}
|
|
}
|
|
scanTargetHostButton.Enabled = true;
|
|
}
|
|
|
|
private void IscsiPopulateLUNs()
|
|
{
|
|
spinnerIconAtTargetIqn.StartSpinning();
|
|
|
|
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;
|
|
|
|
controlDisabler.Reset();
|
|
controlDisabler.SaveOrUpdateEnabledStates(UserInputControls);
|
|
controlDisabler.DisableAllControls();
|
|
|
|
IscsiPopulateLunsAction.RunAsync();
|
|
}
|
|
|
|
private void IscsiPopulateLunsAction_Completed(ActionBase sender)
|
|
{
|
|
Program.Invoke(Program.MainWindow, (WaitCallback)IscsiPopulateLunsAction_Completed_, sender);
|
|
}
|
|
|
|
private void IscsiPopulateLunsAction_Completed_(object o)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
controlDisabler.RestoreEnabledOnAllControls();
|
|
|
|
ISCSIPopulateLunsAction action = (ISCSIPopulateLunsAction)o;
|
|
|
|
if (!action.Succeeded)
|
|
{
|
|
spinnerIconAtTargetIqn.Visible = false;
|
|
|
|
Failure failure = action.Exception as Failure;
|
|
|
|
if (failure != null && failure.ErrorDescription != null && failure.ErrorDescription.Count > 0)
|
|
{
|
|
if (failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_140")
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Text = Messages.INVALID_HOST;
|
|
errorLabelAtHostname.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
else if (failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_68")
|
|
{
|
|
errorIconAtCHAPPassword.Visible = true;
|
|
errorLabelAtCHAPPassword.Text = Messages.LOGGING_IN_TO_THE_ISCSI_TARGET_FAILED;
|
|
errorLabelAtCHAPPassword.Visible = true;
|
|
IScsiChapUserTextBox.Focus();
|
|
}
|
|
else
|
|
{
|
|
errorIconAtTargetLUN.Visible = true;
|
|
errorIconAtTargetLUN.Text = failure.ErrorDescription.Count > 2 ? failure.ErrorDescription[2] : failure.ErrorDescription[0];
|
|
errorIconAtTargetLUN.Visible = true;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (action.LUNs.Length == 0)
|
|
{
|
|
// Do nothing: ActionProgressDialog will show Messages.NEWSR_NO_LUNS_FOUND
|
|
}
|
|
else
|
|
{
|
|
comboBoxIscsiLuns.Items.Add(Messages.SELECT_TARGET_LUN);
|
|
|
|
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);
|
|
}
|
|
comboBoxIscsiLuns.SelectedItem = comboBoxIscsiLuns.Items.Count == 2 ? comboBoxIscsiLuns.Items[1] : Messages.SELECT_TARGET_LUN; //if there is only one choice, select that one by default
|
|
comboBoxIscsiLuns.Enabled = true;
|
|
targetLunLabel.Enabled = true;
|
|
comboBoxIscsiLuns.Focus();
|
|
|
|
spinnerIconAtTargetIqn.DisplaySucceededImage();
|
|
}
|
|
|
|
comboBoxIscsiLuns.Enabled = true;
|
|
comboBoxIscsiIqns.Enabled = true;
|
|
|
|
UpdateButtons();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called with the results of an iSCSI SR.probe(), either immediately after the scan, or after the
|
|
/// user has performed a scan, clicked 'cancel' on a dialog, and then clicked 'next' again (this
|
|
/// avoids duplicate probing if none of the settings have changed).
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Whether to continue or not - wheter to format or not is stored in
|
|
/// iScsiFormatLUN.
|
|
/// </returns>
|
|
private bool ExamineIscsiProbeResults(SrProbeAction action)
|
|
{
|
|
_srToIntroduce = null;
|
|
|
|
if (!action.Succeeded)
|
|
{
|
|
Exception exn = action.Exception;
|
|
log.Warn(exn, exn);
|
|
Failure failure = exn as Failure;
|
|
if (failure != null && failure.ErrorDescription[0] == "SR_BACKEND_FAILURE_140")
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Visible = true;
|
|
errorLabelAtHostname.Text = Messages.INVALID_HOST;
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
else if (failure != null)
|
|
{
|
|
errorIconAtHostOrIP.Visible = true;
|
|
errorLabelAtHostname.Visible = true;
|
|
errorLabelAtHostname.Text = failure.ErrorDescription.Count > 2 ? failure.ErrorDescription[2] : failure.ErrorDescription[0];
|
|
textBoxIscsiHost.Focus();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
List<SR.SRInfo> SRs = SR.ParseSRListXML(action.Result);
|
|
|
|
if (!String.IsNullOrEmpty(SrWizardType.UUID))
|
|
{
|
|
// Check LUN contains correct SR
|
|
if (SRs.Count == 1 && SRs[0].UUID == SrWizardType.UUID)
|
|
{
|
|
_srToIntroduce = SRs[0];
|
|
return true;
|
|
}
|
|
|
|
errorIconAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Visible = true;
|
|
errorLabelAtTargetLUN.Text = String.Format(Messages.INCORRECT_LUN_FOR_SR, SrWizardType.SrName);
|
|
|
|
return false;
|
|
}
|
|
else if (SRs.Count == 0)
|
|
{
|
|
// No existing SRs were found on this LUN. If allowed to create new SR, ask the user if they want to proceed and format.
|
|
if (!SrWizardType.AllowToCreateNewSr)
|
|
{
|
|
new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(SystemIcons.Error, Messages.NEWSR_LUN_HAS_NO_SRS, Messages.XENCENTER)).ShowDialog(this);
|
|
|
|
return false;
|
|
}
|
|
DialogResult result = Program.RunInAutomatedTestMode ? DialogResult.Yes :
|
|
new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(SystemIcons.Warning, Messages.NEWSR_ISCSI_FORMAT_WARNING, this.Text),
|
|
ThreeButtonDialog.ButtonYes,
|
|
new ThreeButtonDialog.TBDButton(Messages.NO_BUTTON_CAPTION, DialogResult.No, ThreeButtonDialog.ButtonType.CANCEL, true)).ShowDialog(this);
|
|
|
|
return result == DialogResult.Yes;
|
|
}
|
|
else
|
|
{
|
|
// There should be 0 or 1 SRs on the LUN
|
|
System.Diagnostics.Trace.Assert(SRs.Count == 1);
|
|
|
|
// CA-17230
|
|
// Check this isn't a detached SR
|
|
SR.SRInfo info = SRs[0];
|
|
SR sr = SrWizardHelpers.SrInUse(info.UUID);
|
|
if (sr != null)
|
|
{
|
|
ThreeButtonDialog d = new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(null, string.Format(Messages.DETACHED_ISCI_DETECTED, Helpers.GetName(sr.Connection))),
|
|
new ThreeButtonDialog.TBDButton(Messages.ATTACH_SR, DialogResult.OK),
|
|
ThreeButtonDialog.ButtonCancel);
|
|
|
|
DialogResult res = d.ShowDialog(Program.MainWindow);
|
|
if (res == DialogResult.Cancel)
|
|
return false;
|
|
|
|
_srToIntroduce = info;
|
|
return true;
|
|
}
|
|
|
|
// An SR exists on this LUN. Ask the user if they want to attach it, format it and
|
|
// create a new SR, or cancel.
|
|
DialogResult result = Program.RunInAutomatedTestMode ? DialogResult.Yes :
|
|
new IscsiChoicesDialog(Connection, info).ShowDialog(this);
|
|
|
|
switch (result)
|
|
{
|
|
case DialogResult.Yes:
|
|
// Reattach
|
|
_srToIntroduce = SRs[0];
|
|
return true;
|
|
|
|
case DialogResult.No:
|
|
// Format - SrToIntroduce is already null
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// We really want to prevent the user getting to the next step if there is any kind of
|
|
// exception here, since clicking 'finish' might destroy data: require another probe.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void HideAllErrorIconsAndLabels()
|
|
{
|
|
foreach (var c in ErrorIcons.Concat(ErrorLabels))
|
|
c.Visible = false;
|
|
}
|
|
|
|
private void HideAllSpinnerIcons()
|
|
{
|
|
|
|
foreach (var c in SpinnerControls)
|
|
c.Visible = false;
|
|
}
|
|
|
|
#region Accessors
|
|
|
|
public SrWizardType SrWizardType { private get; set; }
|
|
|
|
public string UUID { get { return _srToIntroduce == null ? null : _srToIntroduce.UUID; } }
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
public string SrDescription
|
|
{
|
|
get
|
|
{
|
|
ToStringWrapper<IScsiIqnInfo> iqn = comboBoxIscsiIqns.SelectedItem as ToStringWrapper<IScsiIqnInfo>;
|
|
return iqn == null ? null : string.Format(Messages.NEWSR_ISCSI_DESCRIPTION, iqn.item.IpAddress, getIscsiIQN(), getIscsiLUN());
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|