Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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 |