/* 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.Text; using System.Threading; using System.Xml; using XenAdmin.Core; using XenAdmin.Network; using XenAPI; namespace XenAdmin.Actions { /// /// Asks the server for a list of LUNs on a particular iSCSI target. /// public class ISCSIPopulateLunsAction : AsyncAction { private readonly string targetHost; private readonly UInt16 targetPort; private readonly string targetIQN; private readonly string chapUsername; private readonly string chapPassword; private ISCSIInfo[] _luns; /// /// Will be null if the scan has not yet successfully returned. /// public ISCSIInfo[] LUNs { get { return _luns; } } public ISCSIPopulateLunsAction(IXenConnection connection, string targetHost, UInt16 targetPort, string targetIQN, string chapUsername, string chapPassword) : base(connection, string.Format(Messages.ACTION_ISCSI_LUN_SCANNING, targetHost)) { this.targetHost = targetHost; this.targetPort = targetPort; this.targetIQN = targetIQN; this.chapUsername = chapUsername; this.chapPassword = chapPassword; } public class NoLUNsFoundException : Exception { private string host; public NoLUNsFoundException(string host) { this.host = host; } public override string Message { get { return string.Format(Messages.NEWSR_NO_LUNS_FOUND, host); } } } protected override void Run() { Pool pool = Helpers.GetPoolOfOne(Connection); if (pool == null) throw new XenAPI.Failure(XenAPI.Failure.INTERNAL_ERROR, Messages.POOL_GONE); Dictionary settings = new Dictionary(); settings["target"] = targetHost; settings["port"] = targetPort.ToString(System.Globalization.CultureInfo.InvariantCulture); settings["targetIQN"] = targetIQN; if (!string.IsNullOrEmpty(this.chapUsername)) { settings["chapuser"] = this.chapUsername; settings["chappassword"] = this.chapPassword; } try { RelatedTask = XenAPI.SR.async_create(Session, pool.master, settings, 0, Helpers.GuiTempObjectPrefix, Messages.ISCSI_SHOULD_NO_BE_CREATED, XenAPI.SR.SRTypes.lvmoiscsi.ToString(), "user", true, new Dictionary()); this.PollToCompletion(); throw new InvalidOperationException(Messages.ISCSI_FAIL); } catch (XenAPI.Failure exn) { if (exn.ErrorDescription.Count < 1) throw new BadServerResponse(targetHost); // We expect an SR_BACKEND_FAILURE_96 error, with a message from // xapi, stdout, and then stderr. // stderr will be an XML-encoded description of the iSCSI IQNs. if (exn.ErrorDescription[0] != "SR_BACKEND_FAILURE_107" && exn.ErrorDescription[0] != "SR_BACKEND_FAILURE_87") throw; // We want a custom error if the server returns no aggregates. if (exn.ErrorDescription.Count < 4 || exn.ErrorDescription[3].Length == 0) throw new NoLUNsFoundException(targetHost); try { List results = new List(); XmlDocument doc = new XmlDocument(); doc.LoadXml(exn.ErrorDescription[3].ToString()); foreach (XmlNode target_node in doc.GetElementsByTagName("iscsi-target")) { foreach (XmlNode lun_node in target_node.ChildNodes) { int lunid = -1; string scsiid = ""; string vendor = ""; string serial = ""; long size = -1; foreach (XmlNode n in lun_node.ChildNodes) { if (n.Name.ToLowerInvariant() == "lunid") { lunid = int.Parse(n.InnerText.Trim(), System.Globalization.CultureInfo.InvariantCulture); } else if (n.Name.ToLowerInvariant() == "vendor") { vendor = n.InnerText.Trim(); } else if (n.Name.ToLowerInvariant() == "serial") { serial = n.InnerText.Trim(); } else if (n.Name.ToLowerInvariant() == "size") { long.TryParse(n.InnerText.Trim(), out size); } else if (n.Name.ToLowerInvariant() == "scsiid") { scsiid = n.InnerText.Trim(); } } results.Add(new ISCSIInfo(scsiid,lunid, vendor, serial, size)); } } results.Sort(); _luns = results.ToArray(); } catch { throw new BadServerResponse(targetHost); } if (_luns.Length < 1) throw new NoLUNsFoundException(targetHost); } } } public struct ISCSIInfo : IComparable { /// /// May be -1 when running against earlier Rio builds -- should always be set to a valid LUN ID otherwise. /// public int LunID; /// /// New identifier -- dont use LinID /// public string ScsiID; /// /// May be the empty string if no vendor field was supplied. /// public string Vendor; /// /// May be the empty string if no serial number was supplied. /// public string Serial; /// /// May be -1 if no size was supplied. /// public long Size; internal ISCSIInfo(string scsiid, int lunid, string vendor, string serial, long size) { ScsiID = scsiid; LunID = lunid; Vendor = vendor; Serial = serial; Size = size; } public int CompareTo(ISCSIInfo other) { return LunID - other.LunID; } } }