/* 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.Linq;
using System.Xml;
using XenAdmin;
using XenAdmin.Core;
using XenAdmin.Network;
using XenAdmin.Network.StorageLink;


namespace XenAPI
{
    public partial class SR : IComparable<SR>, IEquatable<SR>
    {
        /// <summary>
        /// The SR types. Note that the names of these enum members correspond exactly to the SR type string as
        /// recognised by the server. If you add anything here, check whether it should also be added to
        /// SR.CanCreateWithXenCenter. Also add it to XenAPI/FriendlyNames.resx under
        /// Label-SR.SRTypes-*. Don't change the lower-casing!
        /// </summary>
        public enum SRTypes
        {
            local, ext, lvmoiscsi, iso, nfs, lvm, netapp, udev, lvmofc,
            lvmohba, egenera, egeneracd, dummy, unknown, equal, cslg, shm,
            iscsi,
            ebs, rawhba,
            cifs, lvmofcoe
        }

        public const string Content_Type_ISO = "iso";
        public const string SM_Config_Type_CD = "cd";

        private const string XenServer_Tools_Label = "XenServer Tools";

        public override string ToString()
        {
            return Name;
        }

        /// <returns>A friendly name for the SR.</returns>
        public override string Name
        {
            get
            {
                return _name(true);
            }
        }

        /// <summary>
        /// Get the given SR's home, i.e. the host under which we are going to display it.  May return null, if this SR should live
        /// at the pool level.
        /// </summary>
        public Host Home
        {
            get
            {
                if (shared || PBDs.Count != 1)
                    return null;

                PBD pbd = Connection.Resolve(PBDs[0]);
                if (pbd == null)
                    return null;

                return Connection.Resolve(pbd.host);
            }
        }


        public string NameWithoutHost
        {
            get
            {
                return _name(false);
            }
        }

        private string _name(bool with_host)
        {
            return I18N("name_label", name_label, with_host);
        }

        /// <returns>A friendly description for the SR.</returns>
        public override string Description
        {
            get
            {
                return I18N("name_description", name_description, true);
            }
        }

        public bool Physical
        {
            get
            {
                SRTypes type = GetSRType(false);
                return type == SRTypes.local || (type == SRTypes.udev && SMConfigType == SM_Config_Type_CD);
            }
        }

        public string SMConfigType
        {
            get { return Get(sm_config, "type"); }
        }

        public bool IsToolsSR
        {
            get
            {
                return name_label == SR.XenServer_Tools_Label;
            }
        }

        public string FriendlyTypeName
        {
            get
            {
                return getFriendlyTypeName(GetSRType(false));
            }
        }

        /// <summary>
        /// A friendly (internationalized) name for the SR type.
        /// </summary>
        public static string getFriendlyTypeName(SRTypes srType)
        {
            return PropertyManager.GetFriendlyName(string.Format("Label-SR.SRTypes-{0}", srType.ToString()),
                                                       "Label-SR.SRTypes-unknown");
        }

        /// <summary>
        /// Use this instead of type
        /// </summary>
        public SRTypes GetSRType(bool ignoreCase)
        {
            try
            {
                return (SRTypes)Enum.Parse(typeof(SRTypes), type, ignoreCase);
            }
            catch
            {
                return SRTypes.unknown;
            }
        }

        public string ConfigType
        {
            get { return Get(sm_config, "type"); }
        }

        private string I18N(string field_name, string field_value, bool with_host)
        {
            if (!other_config.ContainsKey("i18n-key"))
            {
                return field_value;
            }

            string i18n_key = other_config["i18n-key"];

            string original_value_key = "i18n-original-value-" + field_name;
            string original_value =
                other_config.ContainsKey(original_value_key) ? other_config[original_value_key] : "";

            if (original_value != field_value)
                return field_value;

            string hostname = with_host ? GetHostName() : null;
            if (hostname == null)
            {
                string pattern = PropertyManager.GetFriendlyName(string.Format("SR.{0}-{1}", field_name, i18n_key));
                return pattern == null ? field_value : pattern;
            }
            else
            {
                string pattern = PropertyManager.GetFriendlyName(string.Format("SR.{0}-{1}-host", field_name, i18n_key));
                return pattern == null ? field_value : string.Format(pattern, hostname);
            }
        }

        /// <returns>The name of the host to which this SR is attached, or null if the storage is shared
        /// or unattached.</returns>
        private string GetHostName()
        {
            if (Connection == null)
                return null;
            Host host = GetStorageHost();
            return host == null ? null : host.Name;
        }



        /// <returns>The host to which the given SR belongs, or null if the SR is shared or completely disconnected.</returns>
        public Host GetStorageHost()
        {
            if (shared || PBDs.Count != 1)
                return null;

            PBD pbd = Connection.Resolve(PBDs[0]);
            return pbd == null ? null : Connection.Resolve(pbd.host);
        }

        /// <summary>
        /// Iterating through the PBDs, this will return the storage host of the first PBD that is currently_attached.
        /// This will return null if there are no PBDs or none of them is currently_attached
        /// </summary>
        /// <returns></returns>
        public Host GetFirstAttachedStorageHost()
        {
            if (PBDs.Count == 0)
                return null;

            var currentlyAttachedPBDs = PBDs.Select(pbdref => Connection.Resolve(pbdref)).Where(p => p != null && p.currently_attached);

            if (currentlyAttachedPBDs.FirstOrDefault() != null)
                return currentlyAttachedPBDs.Select(p => Connection.Resolve(p.host)).Where(h => h != null).FirstOrDefault();

            return null;
        }

        public bool IsDetachable()
        {
            return !IsDetached && !HasRunningVMs() && CanCreateWithXenCenter;
        }

        /// <summary>
        /// Can create with XC, or is citrix storage link gateway. Special case alert!
        /// </summary>
        public bool CanCreateWithXenCenter
        {
            get
            {
                SRTypes type = GetSRType(false);
                return type == SRTypes.iso
                    || type == SRTypes.lvmoiscsi
                    || type == SRTypes.nfs
                    || type == SRTypes.equal
                    || type == SRTypes.netapp
                    || type == SRTypes.lvmohba
                    || type == SRTypes.cslg
                    || type == SRTypes.cifs
                    || type == SRTypes.lvmofcoe;
            }
        }

        public bool IsLocalSR
        {
            get
            {
                SRTypes type = GetSRType(false);
                return type == SRTypes.local
                    || type == SRTypes.ext
                    || type == SRTypes.lvm
                    || type == SRTypes.udev
                    || type == SRTypes.egeneracd
                    || type == SRTypes.dummy;
            }
        }

        public bool ShowForgetWarning
        {
            get
            {
                return GetSRType(false) != SRTypes.iso;
            }
        }

        /// <summary>
        /// Internal helper function. True if all the PBDs for this SR are currently_attached.
        /// </summary>
        /// <returns></returns>
        private bool AllPBDsAttached()
        {
            return Connection.ResolveAll(this.PBDs).All(pbd => pbd.currently_attached);
        }

        /// <summary>
        /// Internal helper function. True if any of the PBDs for this SR is currently_attached.
        /// </summary>
        /// <returns></returns>
        private bool AnyPBDAttached()
        {
            return Connection.ResolveAll(this.PBDs).Any(pbd => pbd.currently_attached);
        }

        /// <summary>
        /// Returns true if there are any Running or Suspended VMs attached to VDIs on this SR.
        /// </summary>
        /// <param name="connection"></param>
        /// <returns></returns>
        public bool HasRunningVMs()
        {
            foreach (VDI vdi in Connection.ResolveAll(VDIs))
            {
                foreach (VBD vbd in Connection.ResolveAll(vdi.VBDs))
                {
                    VM vm = Connection.Resolve(vbd.VM);
                    if (vm == null)
                        continue;
                    // PR-1223: ignore control domain VM on metadata VDIs, so that the SR can be detached if there are no other running VMs
                    if (vdi.type == vdi_type.metadata && vm.is_control_domain)
                        continue;
                    if (vm.power_state == vm_power_state.Running)
                        return true;
                }
            }
            return false;
        }

        /// <summary>
        /// If host is non-null, return whether this storage can be seen from the given host.
        /// If host is null, return whether the storage is shared, with a PBD for each host and at least one PBD plugged.
        /// (See CA-36285 for why this is the right test when looking for SRs on which to create a new VM).
        /// </summary>
        public virtual bool CanBeSeenFrom(Host host)
        {
            if (host == null)
            {
                return shared && Connection != null && !IsBroken(false) && AnyPBDAttached();
            }

            foreach (PBD pbd in host.Connection.ResolveAll(PBDs))
                if (pbd.currently_attached && pbd.host.opaque_ref == host.opaque_ref)
                    return true;

            return false;
        }

        public PBD GetPBDFor(Host host)
        {
            foreach (PBD pbd in host.Connection.ResolveAll(PBDs))
                if (pbd.host.opaque_ref == host.opaque_ref)
                    return pbd;

            return null;
        }

        /// <summary>
        /// True if there is less than 0.5GB free. Always false for dummy and ebs SRs.
        /// </summary>
        public bool IsFull
        {
            get
            {
                SRTypes t = GetSRType(false);
                return t != SRTypes.dummy && t != SRTypes.ebs && FreeSpace < XenAdmin.Util.BINARY_GIGA / 2;
            }
        }

        public virtual long FreeSpace
        {
            get
            {
                return physical_size - physical_utilisation;
            }
        }

        public virtual bool ShowInVDISRList(bool showHiddenVMs)
        {
            if (content_type == Content_Type_ISO)
                return false;
            return Show(showHiddenVMs);

        }

        public bool IsDetached
        {
            get
            {
                // SR is detached when it has no PBDs or when all its PBDs are unplugged
                return !HasPBDs || !AnyPBDAttached();
            }
        }

        public bool HasPBDs
        {
            get
            {
                // CA-15188: Show SRs with no PBDs on Orlando, as pool-eject bug has been fixed.
                // SRs are detached if they have no PBDs

                return PBDs.Count > 0;
            }
        }

        public override bool Show(bool showHiddenVMs)
        {
            if (name_label.StartsWith(Helpers.GuiTempObjectPrefix))
                return false;

            // CA-15012 - dont show cd drives of type local on miami (if dont get destroyed properly on upgrade)
            if (GetSRType(false) == SRTypes.local)
                return false;

            if (showHiddenVMs)
                return true;

			//CP-2458: hide SRs that were introduced by a DR_task
			if (Helpers.BostonOrGreater(Connection) && introduced_by != null && introduced_by.opaque_ref != Helper.NullOpaqueRef)
				return false;

            return !IsHidden;
        }

        public override bool IsHidden
        {
            get
            {
                return BoolKey(other_config, HIDE_FROM_XENCENTER);
            }
        }

        public const String USE_VHD = "use_vhd";

        public bool NeedsUpgrading
        {
            get
            {
                SRTypes type = GetSRType(false);
                if (!(type == SRTypes.lvm ||
                    type == SRTypes.lvmofc ||
                    type == SRTypes.lvmohba ||
                    type == SRTypes.lvmoiscsi))
                    return false;

                if (!Helpers.GeorgeOrGreater(Connection))
                    return false;

                return !BoolKey(sm_config, USE_VHD);
            }
        }

        /// <summary>
        /// The SR is broken when it has the wrong number of PBDs, or (optionally) not all the PBDs are attached.
        /// </summary>
        /// <param name="checkAttached">Whether to check that all the PBDs are attached</param>
        /// <returns></returns>
        public virtual bool IsBroken(bool checkAttached)
        {
            if (PBDs == null || PBDs.Count == 0 ||
                checkAttached && !AllPBDsAttached())
            {
                return true;
            }
            Pool pool = Helpers.GetPoolOfOne(Connection);
            if (pool == null || !shared)
            {
                if (PBDs.Count != 1)
                {
                    // There should be exactly one PBD, since this is a non-shared SR
                    return true;
                }
            }
            else
            {
                if (PBDs.Count != Connection.Cache.HostCount)
                {
                    // There isn't a PBD for each host
                    return true;
                }
            }
            return false;
        }

        public bool IsBroken()
        {
            return IsBroken(true);
        }

        public static bool IsDefaultSr(SR sr)
        {
            Pool pool = Helpers.GetPoolOfOne(sr.Connection);
            return pool != null && pool.default_SR != null && pool.default_SR.opaque_ref == sr.opaque_ref;
        }

        /// <summary>
        /// Returns true if a new VM may be created on this SR: the SR supports VDI_CREATE, has the right number of PBDs, and is not full.
        /// </summary>
        /// <param name="myConnection">The IXenConnection whose cache this XenObject belongs to. May not be null.</param>
        /// <returns></returns>
        public bool CanCreateVmOn()
        {
            System.Diagnostics.Trace.Assert(Connection != null, "Connection must not be null");

            return SupportsVdiCreate() && !IsBroken(false) && !IsFull;
        }


        /// <summary>
        /// Whether the underlying SR backend supports VDI_CREATE. Will return true even if the SR is full.
        /// </summary>
        /// <param name="connection"></param>
        /// <returns></returns>
        public virtual bool SupportsVdiCreate()
        {
            // ISO SRs are deemed not to support VDI create in the GUI, even though the back end
            // knows that they do. See CA-40119.
            if (content_type == SR.Content_Type_ISO)
                return false;

            Host master = Helpers.GetMaster(Connection);
            if (master == null)
                return false;

            SM sm = SM.GetByType(Connection, type);
            return sm != null && -1 != Array.IndexOf(sm.capabilities, "VDI_CREATE");
        }

        /// <summary>
        /// Parses an XML list of SRs (as returned by the SR.probe() call) into a list of SRInfos.
        /// </summary>
        public static List<SRInfo> ParseSRListXML(string xml)
        {
            List<SRInfo> results = new List<SRInfo>();
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            // If we've got this from an async task result, then it will be wrapped
            // in a <value> element.  Parse the contents instead.
            foreach (XmlNode node in doc.GetElementsByTagName("value"))
            {
                xml = node.InnerText;
                doc = new XmlDocument();
                doc.LoadXml(xml);
                break;
            }

            foreach (XmlNode node in doc.GetElementsByTagName("SR"))
            {
                string uuid = "";
                long size = 0;
                string aggr = "";
                string name_label = "";
                string name_description = "";
                bool pool_metadata_detected = false;

                foreach (XmlNode info in node.ChildNodes)
                {
                    if (info.Name.ToLowerInvariant() == "uuid")
                    {
                        uuid = info.InnerText.Trim();
                    }
                    else if (info.Name.ToLowerInvariant() == "size")
                    {
                        size = long.Parse(info.InnerText.Trim());
                    }
                    else if (info.Name.ToLowerInvariant() == "aggregate")
                    {
                        aggr = info.InnerText.Trim();
                    } 
                    else if (info.Name.ToLowerInvariant() == "name_label")
                    {
                        name_label = info.InnerText.Trim();
                    } 
                    else if (info.Name.ToLowerInvariant() == "name_description")
                    {
                        name_description = info.InnerText.Trim();
                    } 
                    else if (info.Name.ToLowerInvariant() == "pool_metadata_detected")
                    {
                        bool.TryParse(info.InnerText.Trim(), out pool_metadata_detected);
                    } 
                }
                results.Add(new SRInfo(uuid, size, aggr, name_label, name_description, pool_metadata_detected));
                /*if (aggr != "")
                    results.Add(new SRInfo(uuid, size, aggr));
                else
                    results.Add(new SRInfo(uuid, size));*/
            }
            return results;
        }

        public static List<string> ParseSupportedVersionsListXML(string xml)
        {
            var supportedVersionsResult = new List<string>();

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            // If we've got this from an async task result, then it will be wrapped
            // in a <value> element.  Parse the contents instead.
            foreach (XmlNode node in doc.GetElementsByTagName("value"))
            {
                xml = node.InnerText;
                doc = new XmlDocument();
                doc.LoadXml(xml);
                break;
            }


            foreach (XmlNode node in doc.GetElementsByTagName("SupportedVersions"))
            {
                foreach (XmlNode info in node.ChildNodes)
                {
                    if (info.Name.ToLowerInvariant() == "version")
                    {
                        supportedVersionsResult.Add(info.InnerText.Trim());
                    }
                }
            }

            return supportedVersionsResult;
        }

        public String GetScsiID()
        {
            foreach (PBD pbd in Connection.ResolveAll(PBDs))
            {
                if (!pbd.device_config.ContainsKey("SCSIid"))
                    continue;

                return pbd.device_config["SCSIid"];
            }

            if (!sm_config.ContainsKey("devserial"))
                return null;

            String SCSIid = sm_config["devserial"];

            if (SCSIid.StartsWith("scsi-"))
                SCSIid = SCSIid.Remove(0, 5);

            // CA-22352: SCSI IDs on the general panel for a NetApp SR have a trailing comma
            SCSIid = SCSIid.TrimEnd(new char[] { ',' });

            return SCSIid;
        }

        /// <summary>
        /// New Lun Per VDI mode (cf. LunPerVDI method) using the hba encapsulating type
        /// </summary>
        public virtual bool HBALunPerVDI
        {
            get { return GetSRType(true) == SRTypes.rawhba; }
        }

        /// <summary>
        /// Legacy LunPerVDI mode - for old servers that this was set up on
        /// This is no longer an option (2012) for newer servers but we need to keep this
        /// </summary>
        public bool LunPerVDI
        {
            get
            {
                // Look for the mapping from scsi id -> vdi uuid 
                // in sm-config.  

                foreach (String key in sm_config.Keys)
                    if (key.Contains("LUNperVDI") || key.StartsWith("scsi-"))
                        return true;

                return false;
            }
        }

        private const String MPATH = "mpath";

        public Dictionary<VM, Dictionary<VDI, String>> GetMultiPathStatusLunPerVDI()
        {
            Dictionary<VM, Dictionary<VDI, String>> result =
                new Dictionary<VM, Dictionary<VDI, String>>();

            if (Connection == null)
                return result;

            foreach (PBD pbd in Connection.ResolveAll(PBDs))
            {
                if (!pbd.MultipathActive)
                    continue;

                foreach (KeyValuePair<String, String> kvp in pbd.other_config)
                {
                    if (!kvp.Key.StartsWith(MPATH))
                        continue;

                    int current;
                    int max;
                    if (!PBD.ParsePathCounts(kvp.Value, out current, out max))
                        continue;

                    String scsiIdKey = String.Format("scsi-{0}", kvp.Key.Substring(MPATH.Length + 1));
                    if (!sm_config.ContainsKey(scsiIdKey))
                        continue;

                    String vdiUUID = sm_config[scsiIdKey];
                    VDI vdi = null;

                    foreach (VDI candidate in Connection.ResolveAll(VDIs))
                    {
                        if (candidate.uuid != vdiUUID)
                            continue;

                        vdi = candidate;
                        break;
                    }

                    if (vdi == null)
                        continue;

                    foreach (VBD vbd in Connection.ResolveAll(vdi.VBDs))
                    {
                        VM vm = Connection.Resolve(vbd.VM);
                        if (vm == null)
                            continue;

                        if (vm.power_state != vm_power_state.Running)
                            continue;

                        if (!result.ContainsKey(vm))
                            result[vm] = new Dictionary<VDI, String>();

                        result[vm][vdi] = kvp.Value;
                    }
                }
            }

            return result;
        }

        public Dictionary<PBD, String> GetMultiPathStatusLunPerSR()
        {
            Dictionary<PBD, String> result =
                new Dictionary<PBD, String>();


            if (Connection == null)
                return result;

            foreach (PBD pbd in Connection.ResolveAll(PBDs))
            {
                if (!pbd.MultipathActive)
                    continue;

                String status = String.Empty;

                foreach (KeyValuePair<String, String> kvp in pbd.other_config)
                {
                    if (!kvp.Key.StartsWith(MPATH))
                        continue;

                    status = kvp.Value;
                    break;
                }

                int current;
                int max;
                if (!PBD.ParsePathCounts(status, out current, out max))
                    continue;

                result[pbd] = status;
            }

            return result;
        }

        public bool MultipathAOK
        {
            get
            {
                if (!MultipathCapable)
                    return true;

                if (LunPerVDI)
                {
                    Dictionary<VM, Dictionary<VDI, String>>
                        multipathStatus = GetMultiPathStatusLunPerVDI();

                    foreach (VM vm in multipathStatus.Keys)
                        foreach (VDI vdi in multipathStatus[vm].Keys)
                            if (!CheckMultipathString(multipathStatus[vm][vdi]))
                                return false;
                }
                else
                {
                    Dictionary<PBD, String> multipathStatus =
                        GetMultiPathStatusLunPerSR();

                    foreach (PBD pbd in multipathStatus.Keys)
                        if (pbd.MultipathActive && !CheckMultipathString(multipathStatus[pbd]))
                            return false;
                }

                return true;
            }
        }

        public override string NameWithLocation
        {
            get
            {
                //return only the Name for local SRs
                if (Connection != null && !shared)
                {
                    return Name;
                }

                return base.NameWithLocation;
            }
        }

        internal override string  LocationString
        {
	        get
	        { 
		         return Home != null ? Home.LocationString : base.LocationString;
	        }
        }

        private bool CheckMultipathString(String status)
        {
            int current;
            int max;
            if (!PBD.ParsePathCounts(status, out current, out max))
                return true;

            return current >= max;
        }

        public Dictionary<String, String> GetDeviceConfig(IXenConnection connection)
        {
            foreach (PBD pbd in connection.ResolveAll(PBDs))
                return pbd.device_config;

            return null;
        }

        public class SRInfo : IComparable<SRInfo>, IEquatable<SRInfo>
        {
            public readonly string UUID;
            public readonly long Size;
            public readonly string Aggr;
            public string Name;
            public string Description;
            public readonly bool PoolMetadataDetected;

            public SRInfo(string uuid)
                : this(uuid, 0, "", "", "", false)
            {
            }

            public SRInfo(string uuid, long size)
                : this(uuid, size, "", "", "", false)
            {
            }

            public SRInfo(string uuid, long size, string aggr)
                : this(uuid, size, aggr, "", "", false)
            {
            }

            public SRInfo(string uuid, long size, string aggr, string name, string description, bool poolMetadataDetected)
            {
                UUID = uuid;
                Size = size;
                Aggr = aggr;
                Name = name;
                Description = description;
                PoolMetadataDetected = poolMetadataDetected;
            }

            public int CompareTo(SRInfo other)
            {
                return (this.UUID.CompareTo(other.UUID));
            }

            public bool Equals(SRInfo other)
            {
                return this.CompareTo(other) == 0;
            }

            public override string ToString()
            {
                return UUID;
            }
        }

        //public bool TypeCIFS
        //{
        //    get
        //    {
        //        if (Connection == null || PBDs.Count == 0)
        //            return false;
        //        PBD pbd = Connection.Resolve<PBD>(PBDs[0]);
        //        if (pbd == null)
        //            return false;

        //        return (pbd.device_config.ContainsKey("options") && pbd.device_config["options"].Contains("-t cifs"))
        //            || (pbd.device_config.ContainsKey("type") && pbd.device_config["type"] == "cifs");
        //    }
        //}

        public bool MultipathCapable
        {
            get
            {
                return "true" == Get(sm_config, "multipathable");
            }
        }

        public string Target
        {
            get
            {
                SR sr = Connection.Resolve(new XenRef<SR>(this.opaque_ref));
                if (sr == null)
                    return String.Empty;

                foreach (PBD pbd in sr.Connection.ResolveAll(sr.PBDs))
                {
                    SRTypes type = sr.GetSRType(false);
                    if ((type == SR.SRTypes.netapp || type == SR.SRTypes.lvmoiscsi || type == SR.SRTypes.equal) && pbd.device_config.ContainsKey("target")) // netapp or iscsi
                    {
                        return pbd.device_config["target"];
                    }
                    else if (type == SR.SRTypes.iso && pbd.device_config.ContainsKey("location")) // cifs or nfs iso
                    {
                        String target = Helpers.HostnameFromLocation(pbd.device_config["location"]); // has form //ip_address/path
                        if (String.IsNullOrEmpty(target))
                            continue;

                        return target;
                    }
                    else if (type == SR.SRTypes.nfs && pbd.device_config.ContainsKey("server"))
                    {
                        return pbd.device_config["server"];
                    }
                }

                return String.Empty;
            }
        }

        public SrProvisioning Provisioning
        {
            get
            {
                string allocation = Get(sm_config, "allocation");
                return string.IsNullOrEmpty(allocation) || allocation == "thick"
                           ? SrProvisioning.Thick
                           : SrProvisioning.Thin;
            }
        }

        public Icons GetIcon
        {
            get
            {
                if (!HasPBDs || IsHidden)
                {
                    return Icons.StorageDisabled;
                }
                else if (IsDetached || IsBroken() || !MultipathAOK)
                {
                    return Icons.StorageBroken;
                }
                else if (NeedsUpgrading)
                {
                    return Icons.StorageNeedsUpgrading;
                }
                else if (SR.IsDefaultSr(this))
                {
                    return Icons.StorageDefault;
                }
                else
                {
                    return Icons.Storage;
                }
            }
        }

        /// <summary>
        /// The amount of memory used as compared to the available and allocated amounts as a friendly string
        /// </summary>
        public String SizeString
        {
            get
            {
                return string.Format(Messages.SR_SIZE_USED,
                    Util.DiskSizeString(physical_utilisation),
                    Util.DiskSizeString(physical_size),
                    Util.DiskSizeString(virtual_allocation));
            }
        }

        /// <summary>
        /// A friendly string indicating whether the sr is detached/broken/multipath failing/needs upgrade/ok
        /// </summary>
        public String StatusString
        {
            get
            {
                if (!HasPBDs)
                    return Messages.DETACHED;

                if (IsDetached || IsBroken())
                    return Messages.GENERAL_SR_STATE_BROKEN;

                if (!MultipathAOK)
                    return Messages.GENERAL_MULTIPATH_FAILURE;

                if (NeedsUpgrading)
                    return Messages.UPGRADE_SR_WARNING;

                return Messages.GENERAL_SR_STATE_OK;
            }
        }
        /// <summary>
        /// Returns true when there is a pbd containing adapterid else false
        /// </summary>
        public bool CanRepairAfterUpgradeFromLegacySL
        {
            get
            {
                if (type == "cslg")
                {
                    var pbds = Connection.ResolveAll(PBDs);
                    if (pbds != null)
                    {
                        return pbds.Any(pbd => pbd.device_config.ContainsKey("adapterid"));
                    }
                    return false;
                }
                return true;
            }
        }


        public StorageLinkRepository StorageLinkRepository(IEnumerable<StorageLinkConnection> connections)
        {
            if (type == "cslg")
            {
                foreach (StorageLinkConnection slCon in connections)
                {
                    foreach (StorageLinkRepository r in slCon.Cache.StorageRespositories)
                    {
                        if (r.opaque_ref == uuid)
                        {
                            return r;
                        }
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Whether SR supports database replication.
        /// </summary>
        public bool SupportsDatabaseReplication()
        {
            return SupportsDatabaseReplication(Connection, this);
        }


        /// <summary>
        /// Whether SR supports database replication.
        /// </summary>
        public static bool SupportsDatabaseReplication(IXenConnection connection, SR sr)
        {
            try
            {
                assert_supports_database_replication(connection.Session, sr.opaque_ref);
                return true;
            }
            catch (Failure)
            {
                return false;
            }
        }

        /// <summary>
        /// Is an iSL type or legacy iSl adpater type
        /// </summary>
        /// <param name="sr"></param>
        /// <returns></returns>
        public static bool IsIslOrIslLegacy(SR sr)
        {
            SRTypes currentType = sr.GetSRType(true);
            return currentType == SRTypes.cslg || currentType == SRTypes.equal || currentType == SRTypes.netapp;
        }

        /// <summary>
        /// Whether the underlying SR backend supports SR_TRIM
        /// </summary>
        /// <returns></returns>
        public bool SupportsTrim
        {
            get
            {
                System.Diagnostics.Trace.Assert(Connection != null, "Connection must not be null");

                SM sm = SM.GetByType(Connection, type);
                return sm != null && sm.features != null && sm.features.ContainsKey("SR_TRIM");
            }
        }

        public bool IsThinProvisioned
        {
            get
            {
                return this.sm_config != null && this.sm_config.ContainsKey("allocation") && this.sm_config["allocation"] == "xlvhd";
            }
        }

        #region IEquatable<SR> Members

        /// <summary>
        /// Indicates whether the current object is equal to the specified object. This calls the implementation from XenObject.
        /// This implementation is required for ToStringWrapper.
        /// </summary>
        public bool Equals(SR other)
        {
            return base.Equals(other);
        }

        #endregion
    }

    public enum SrProvisioning
    {
        Thick,
        Thin
    }
}