xenadmin/XenAdmin/Dialogs/IscsiDeviceConfigDialog.cs
Konstantina Chremmou ae22560ce8 Converted all extension get properties of the API classes to methods in order to
prevent them from being serialised alongside the API properties. This will also
be useful for moving the API bindings out of XenModel.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2017-09-03 03:35:30 +01:00

540 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;
using (var 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();
}
}
}