/* 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.Collections.ObjectModel; using System.Xml; using XenAdmin.Core; using XenAdmin.Network; using XenAPI; using XenAdmin.Network.StorageLink; using System.Threading; namespace XenAdmin.Actions { /// /// Performs a scan of a CSLG server for storage systems. /// public class SrCslgStorageSystemScanAction : SrCslgScanAction { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private ReadOnlyCollection _cslgSystemStorages; public StorageLinkConnection StorageLinkConnection { get; private set; } private List _SLConnections; private System.ComponentModel.ISynchronizeInvoke _invoker; private string _adapterid; /// /// Initializes a new instance of the class. /// /// The connection. /// The hostname. /// The username. /// The password secret. public SrCslgStorageSystemScanAction(System.ComponentModel.ISynchronizeInvoke invoker, IXenConnection connection, List storageLinkConnections, string hostname, string username, string passwordSecret) : base(connection, hostname, username, passwordSecret) { _SLConnections = storageLinkConnections; _invoker = invoker; } /// /// Boston or greater constructor /// /// public SrCslgStorageSystemScanAction(IXenConnection connection, string adapterid, string target, string user, string password) : base(connection, target, user, password) { if (!Helpers.BostonOrGreater(connection)) throw new ArgumentException(@"Invalid connection, it has to be a boston or greater connection", connection.Name); _adapterid = adapterid; } /// /// Gets the CSLG system storages. Returns null before the action has been run. /// /// The CSLG system storages. public ReadOnlyCollection CslgSystemStorages { get { return _cslgSystemStorages; } } /// /// Runs this instance. /// protected override void Run() { Dictionary dconf = GetAuthenticationDeviceConfig(); Log.DebugFormat("Attempting to find SRs on CSLG {0}.", dconf["target"]); if (Connection != null && Helpers.MidnightRideOrGreater(Connection) && !Helpers.CowleyOrGreater(Connection)) { RunProbe(dconf); _cslgSystemStorages = new ReadOnlyCollection(ParseStorageSystemsXml(Util.GetContentsOfValueNode(Result))); } else if (Connection != null && Helpers.BostonOrGreater(Connection)) { dconf["adapterid"] = _adapterid; RunProbe(dconf); if (!string.IsNullOrEmpty(Result)) _cslgSystemStorages = new ReadOnlyCollection(ParseStorageSystemsXml(Util.GetContentsOfValueNode(Result))); } else { bool created = false; if (Connection != null) { // There will be no connection if a storagelink-object is selected in the tree. string secretRef = Secret.get_by_uuid(Connection.Session, dconf["password_secret"]); string password = Secret.get_value(Connection.Session, secretRef); StorageLinkConnection = _SLConnections.Find(c => c.Host == dconf["target"] && c.Username == dconf["username"] && c.Password == password); if (StorageLinkConnection == null) { // the user has clicked the "test connection" button in the properties dialog then // the storagelink connection won't exist in the Program.StorageLinkConnections collection. StorageLinkConnection = new StorageLinkConnection(_invoker, dconf["target"], dconf["username"], password); StorageLinkConnection.BeginConnect(); created = true; } } else { StorageLinkConnection = _SLConnections.Find(c => c.Host == dconf["target"] && c.Username == dconf["username"]); } try { var list = new List(); // wait for storagelink connection to finish populating for (int i = 0; i < 600 && StorageLinkConnection.ConnectionState == StorageLinkConnectionState.Connecting; i++) { Thread.Sleep(100); } if (!string.IsNullOrEmpty(StorageLinkConnection.Error)) { throw new InvalidOperationException(StorageLinkConnection.Error); } if (StorageLinkConnection.ConnectionState != StorageLinkConnectionState.Connected) { throw new InvalidOperationException(string.Format(Messages.STORAGELINK_UNABLE_TO_CONNECT, dconf["target"])); } foreach (StorageLinkSystem s in StorageLinkConnection.Cache.StorageSystems) { var provisioningOptions = new List { new CslgParameter(null, Messages.NEWSR_CSLG_NONE) }; if ((s.Capabilities & StorageLinkEnums.StorageSystemCapabilities.POOL_LEVEL_DEDUPLICATION) != 0) { provisioningOptions.Add(new CslgParameter("DEDUP", Messages.NEWSR_CSLG_DEDUPLICATION)); } var protocols = new List { new CslgParameter(null, Messages.NEWSR_CSLG_AUTO) }; if ((s.Capabilities & StorageLinkEnums.StorageSystemCapabilities.ISCSI) != 0) { protocols.Add(new CslgParameter("ISCSI", Messages.NEWSR_CSLG_ISCSI)); } if ((s.Capabilities & StorageLinkEnums.StorageSystemCapabilities.FIBRE_CHANNEL) != 0) { protocols.Add(new CslgParameter("FC", Messages.NEWSR_CSLG_FC)); } list.Add(new CslgSystemStorage(s.ToString(), s.StorageSystemId, protocols, provisioningOptions, false, s)); } _cslgSystemStorages = new ReadOnlyCollection(list); } finally { if (created) { StorageLinkConnection.EndConnect(); } } } } private List ParseStorageSystemsXml(String xml) { List output = new List(); XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); foreach (XmlNode storageSystemInfo in doc.GetElementsByTagName("csl__storageSystemInfo")) { string displayName = GetXmlNodeInnerText(storageSystemInfo, "displayName"); string storageSystemId = GetXmlNodeInnerText(storageSystemInfo, "storageSystemId"); List protocols = new List(); protocols.Add(new CslgParameter(null, Messages.NEWSR_CSLG_AUTO)); List provisioningOptions = new List(); provisioningOptions.Add(new CslgParameter(null, Messages.NEWSR_CSLG_NONE)); bool supportsCHAP = false; foreach (string innerText in GetXmlChildNodeInnerTexts(storageSystemInfo, "systemCapabilities/capabilities")) { if (innerText == "DEDUPLICATION") { provisioningOptions.Add(new CslgParameter(innerText, Messages.NEWSR_CSLG_DEDUPLICATION)); } if (innerText == "SUPPORTS_CHAP") { supportsCHAP = true; } } foreach (string innerText in GetXmlChildNodeInnerTexts(storageSystemInfo, "protocolSupport/capabilities")) { if (innerText == "ISCSI") { protocols.Add(new CslgParameter(innerText, Messages.NEWSR_CSLG_ISCSI)); } else if (innerText == "FC") { protocols.Add(new CslgParameter(innerText, Messages.NEWSR_CSLG_FC)); } } output.Add(new CslgSystemStorage(displayName, storageSystemId, protocols, provisioningOptions, supportsCHAP, null)); } return output; } } #region CslgSystemStorage class /// /// Represents a System Storage from a CSLG server. /// public class CslgSystemStorage { private readonly string _displayName; private readonly string _storageSystemId; private readonly ReadOnlyCollection _protocols; private readonly ReadOnlyCollection _provisioningOptions; private readonly bool _supportsCHAP; public StorageLinkSystem StorageLinkSystem { get; private set; } /// /// Initializes a new instance of the class. /// /// The display name. /// The storage system id. /// The available protocols. /// The available provisioning options. /// Indicates whether CHAP is supported. public CslgSystemStorage(string displayName, string storageSystemId, IEnumerable protocols, IEnumerable provisioningOptions, bool supportsCHAP, StorageLinkSystem storageLinkSystem) { Util.ThrowIfStringParameterNullOrEmpty(displayName, "displayName"); Util.ThrowIfStringParameterNullOrEmpty(storageSystemId, "storageSystemId"); Util.ThrowIfParameterNull(protocols, "protocols"); Util.ThrowIfParameterNull(protocols, "provisioningOptions"); _displayName = displayName; _storageSystemId = storageSystemId; _protocols = new ReadOnlyCollection(new List(protocols)); _provisioningOptions = new ReadOnlyCollection(new List(provisioningOptions)); _supportsCHAP = supportsCHAP; StorageLinkSystem = storageLinkSystem; } /// /// Gets the display name. /// /// The display name. public string DisplayName { get { return _displayName; } } /// /// Gets the available protocols. /// /// The available protocols. public ReadOnlyCollection Protocols { get { return _protocols; } } /// /// Gets the storage system id. /// /// The storage system id. public string StorageSystemId { get { return _storageSystemId; } } public override string ToString() { return DisplayName; } /// /// Gets the available provisioning options. /// /// The available provisioning options. public ReadOnlyCollection ProvisioningOptions { get { return _provisioningOptions; } } /// /// Gets supports CHAP value. /// /// Indicates whether CHAP is supported. public bool SupportsCHAP { get { return _supportsCHAP; } } public override bool Equals(object obj) { var other = obj as CslgSystemStorage; return other != null && other.StorageSystemId == StorageSystemId; } public override int GetHashCode() { return StorageSystemId.GetHashCode(); } } #endregion #region CslgParameter class /// /// Represents a parameter required to configure CSLG storage repository which has a string value and string display-name for that value. /// public class CslgParameter { private readonly string _name; private readonly string _displayName; /// /// Initializes a new instance of the class. /// /// The name. /// The display name. public CslgParameter(string name, string displayName) { Util.ThrowIfStringParameterNullOrEmpty(displayName, "displayName"); _name = name; _displayName = displayName; } /// /// Gets the name. /// /// The name. public string Name { get { return _name; } } /// /// Gets the display name. /// /// The display name. public string DisplayName { get { return _displayName; } } public override string ToString() { return _displayName; } } #endregion }