Getting More Information About You Cluster LUN’s (Part 1 of 3)

So here’s the scenario you need or want to figure out which LUN is which on your cluster… 

Well if you just use the Get-ClusterSharedVolume cmdlet it won’t get you very far…  However there’s another cmdlet Get-ClusterParameter that will give you enough data that when combined with some other PowerShell/.Net magic will.  This function will return the Cluster Object Private Properties for a given object you can look on MSDN to see all of them – in this case we are interested specifically in the Physical Disk Private Properties.

For this first example I am going to utilize the DiskUniqueIds property to extract information about the specific LUN.  Looking at the documentation for this property it returns a STORAGE_DEVICE_ID_DESCRIPTOR structure which has a set of identifiers – after some more digging around it turns out these identifiers are actually STORAGE_IDENTIFIER structures which have some interesting values.  For most of the storage I have in the lab at the very least the vendor ID (StorageIdTypeEUI64) which is actually a bit mask of the IEEE vendor ID along with some vendor specific data (the bit map is documented here and you can search for vendor ID’s here) as well as a VendorID string and some LUN information although some will tell you what port is being used etc…

In order to format all of this data I’m using some inline C# to define a class with the reprehensive structures and enumerations – just copied from MSDN and made pretty for .Net interop.  I also wrote a function in the C# class named DiskUniqueIdstoStorageId, this function takes the byte array returned from calling the Get-ClusterParameter cmdlet and marshel’s it or formats it into the C# classes thus allowing me to consume the now managed object’s in C#.  All in all it might be overkill so look for a shorter version coming soon.

Script

$StorageDevice= @" using System; using System.Runtime.InteropServices; public static class StorageDevice { public enum StorageIdCodeSet { Reserved = 0, Binary = 1, Ascii = 2, Utf8 = 3 } public enum StorageIdType { VendorSpecific = 0, VendorId = 1, EUI64 = 2, FCPHName = 3, PortRelative = 4, TargetPortGroup = 5, LogicalUnitGroup = 6, MD5LogicalUnitIdentifier = 7, ScsiNameString = 8 } public enum StorageIdAssoc { Device = 0, Port = 1, Target = 2 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct STORAGE_IDENTIFIER { public StorageDevice.StorageIdCodeSet CodeSet; public StorageDevice.StorageIdType Type; public UInt16 IdentifierSize; public UInt16 NextOffset; public StorageDevice.StorageIdAssoc Association; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] public string Identifiers; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct STORAGE_DEVICE_ID_DESCRIPTOR { public UInt32 Version; public UInt32 Size; public UInt32 NumberOfIdentifiers; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] public string Identifiers; } public static STORAGE_IDENTIFIER[] DiskUniqueIdstoStorageId(byte[] DiskUniqueIds) { IntPtr ArrayAddress = Marshal.UnsafeAddrOfPinnedArrayElement(DiskUniqueIds, 0); STORAGE_DEVICE_ID_DESCRIPTOR devId = (STORAGE_DEVICE_ID_DESCRIPTOR)Marshal.PtrToStructure( ArrayAddress, typeof(STORAGE_DEVICE_ID_DESCRIPTOR)); IntPtr AddressOfIdentifiers = (IntPtr) ((long)ArrayAddress + (long)(Marshal.OffsetOf( typeof(STORAGE_DEVICE_ID_DESCRIPTOR), "Identifiers"))); IntPtr OffsetOfIdentifiersStrings = Marshal.OffsetOf( typeof(STORAGE_IDENTIFIER), "Identifiers"); STORAGE_IDENTIFIER[] ArrayofSTORAGE_IDENTIFIER = new STORAGE_IDENTIFIER[devId.NumberOfIdentifiers]; IntPtr CurrentAddressOfIdentifiers=AddressOfIdentifiers; for (int i = 0; i < devId.NumberOfIdentifiers; i++) { ArrayofSTORAGE_IDENTIFIER[i] = (STORAGE_IDENTIFIER)Marshal.PtrToStructure( CurrentAddressOfIdentifiers, typeof(STORAGE_IDENTIFIER)); IntPtr AddressOfIdentifiersString = (IntPtr)((long)CurrentAddressOfIdentifiers + (long)OffsetOfIdentifiersStrings); switch (ArrayofSTORAGE_IDENTIFIER[i].CodeSet) { case StorageIdCodeSet.Ascii: ArrayofSTORAGE_IDENTIFIER[i].Identifiers = Marshal.PtrToStringAnsi(AddressOfIdentifiersString, (Int32)ArrayofSTORAGE_IDENTIFIER[i].IdentifierSize); break; case StorageIdCodeSet.Utf8: ArrayofSTORAGE_IDENTIFIER[i].Identifiers = Marshal.PtrToStringUni(AddressOfIdentifiersString, (Int32)ArrayofSTORAGE_IDENTIFIER[i].IdentifierSize); break; default: for (int x=0; x < ArrayofSTORAGE_IDENTIFIER[i].IdentifierSize; x++) { ArrayofSTORAGE_IDENTIFIER[i].Identifiers = String.Format("{0} {1:X}", ArrayofSTORAGE_IDENTIFIER[i].Identifiers, Marshal.ReadByte(AddressOfIdentifiersString, x)); } break; } CurrentAddressOfIdentifiers = (IntPtr) ((long)CurrentAddressOfIdentifiers + (long)ArrayofSTORAGE_IDENTIFIER[i].NextOffset); } return ArrayofSTORAGE_IDENTIFIER; } } "@ add-type -TypeDefinition $StorageDevice Import-Module FailoverClusters $AllCSVLuns = Get-ClusterSharedVolume foreach ($CSV in $AllCSVLuns) {     $CSVParams = Get-ClusterParameter -InputObject $CSV     $DiskUniqueId = ($CSVParams | Where-object -FilterScript {$_.Name -eq "DiskUniqueIds"}).Value     $StorageId = [StorageDevice]::DiskUniqueIdstoStorageId($DiskUniqueId)     Write-Host "CSV ClusterResourceName: " $CSV.Name     Write-Host "Identifiers: "     $StorageId     Write-Host     Write-Host }

 

Sample Output

Windows PowerShell Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\taylorb>.\GetCSVDevInfo1.ps1

CSV ClusterResourceName: Cluster_CSV1_IBMXIV Identifiers: CodeSet : Binary Type : EUI64 IdentifierSize : 8 NextOffset : 28 Association : Device Identifiers : 0 17 38 0 1 13 0 1C

CodeSet : Ascii Type : VendorId IdentifierSize : 36 NextOffset : 56 Association : Device Identifiers : IBM 2810XIV 1300113001C

CodeSet : Ascii Type : VendorSpecific IdentifierSize : 13 NextOffset : 32 Association : Device Identifiers : vol=CSV_1

CodeSet : Ascii Type : VendorSpecific IdentifierSize : 22 NextOffset : 40 Association : Device Identifiers : host=37-4611K2713K

CodeSet : Binary Type : TargetPortGroup IdentifierSize : 4 NextOffset : 24 Association : Port Identifiers : 0 0 0 0

CodeSet : Binary Type : PortRelative IdentifierSize : 4 NextOffset : 24 Association : Port Identifiers : 0 0 7 1

CSV ClusterResourceName: Cluster_CSV2_IBMXIV Identifiers: CodeSet : Binary Type : EUI64 IdentifierSize : 8 NextOffset : 28 Association : Device Identifiers : 0 17 38 0 1 13 0 1D

CodeSet : Ascii Type : VendorId IdentifierSize : 36 NextOffset : 56 Association : Device Identifiers : IBM 2810XIV 1300113001D

CodeSet : Ascii Type : VendorSpecific IdentifierSize : 13 NextOffset : 32 Association : Device Identifiers : vol=CSV_2

CodeSet : Ascii Type : VendorSpecific IdentifierSize : 22 NextOffset : 40 Association : Device Identifiers : host=37-4611K2713F

CodeSet : Binary Type : TargetPortGroup IdentifierSize : 4 NextOffset : 24 Association : Port Identifiers : 0 0 0 0

CodeSet : Binary Type : PortRelative IdentifierSize : 4 NextOffset : 24 Association : Port Identifiers : 0 0 7 3

Taylor Brown Hyper-V Enterprise Deployment Team taylorb@microsoft.com https://blogs.msdn.com/taylorb

WS08R2-HyperV_v_rgb