Hi all,
I wrote a script that fetches storage information pertaining to a particular cluster, including path state as below.
# Define default timestamp format
$timestampFormat = 'yyyy-MM-dd HH:mm:ss'
# Initialize timestamp for output
filter timestamp {"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): $_"} # format according to mysql TIMESTAMP format
foreach ($CurrentServerIP in $ServerIPs) {
Write-Host "Connecting to vCenter server IP: $CurrentServerIP ... "
try {
Connect-ViServer -server $CurrentServerIP -User $User -password $UnsecurePassword -ErrorAction Stop
} catch {
Throw $error[0]
}
# Fetch clusters according to user input if present, if not defaults to fetching all clusters allocated to current vCenter IP
if ($ClusterInput) {
Write-Host "Reading cluster input: $ClusterInput"
$Clusters = Get-Cluster -Name $ClusterInput
} else {
Write-Host "No input for cluster, fetching information for all clusters ... "
$Clusters = Get-Cluster | Sort-Object Name
}
foreach ($Cluster in $Clusters) {
$ClusterName = $Cluster.Name
$DatacenterName = (Get-Datacenter -cluster $Cluster).Name
$VMHosts = $Cluster | Get-VMHost | Sort-Object Name
foreach ($VMHost in $VMHosts) {
$VMHostName = $VMHost.Name
$VMHostConnectionState = $VMHost.ConnectionState
Write-Host "Getting for VMHost: $VMHostName with Connection State: $VMHostConnectionState..."
# Ignore disconnected hosts since Get-View cmdlet will fail
if ($VMHost.ConnectionState -ne "Disconnected" ) {
$objViewESX = Get-View -id $VMHost.id
$objViewESXstorageSys = Get-View -id $objViewESX.ConfigManager.StorageSystem
$objEsxCli = (Get-ESXCli -VMHost $VMHost -v2)
$objScsidisks = $objViewESXstorageSys.StorageDeviceInfo.ScsiLun | Where-Object {
($_.LunType -eq 'disk') -and ($_.LocalDisk -eq $false)
}
foreach ($objScsidisk in $objScsidisks) {
$HbaSet = @{}
$objAdapters = $objViewESXstorageSys.StorageDeviceInfo.HostBusAdapter | Where-Object {$_.Key -like "*FibreChannelHba*"}
foreach ($objAdapter in $objAdapters) {
$HbaSet[$objAdapter.Device] = $objAdapter
}
$objScsilun = $objViewESXstorageSys.StorageDeviceInfo.MultiPathInfo.Lun | Where-Object {
($_.Id -eq $objScsidisk.Uuid)
}
$objDatastore = $objViewESXstorageSys.FileSystemVolumeInfo.MountInfo | Where-Object {
($_.Volume.Extent.DiskName -eq $objScsidisk.CanonicalName)
}
if ($objDatastore) {
$Datastore = Get-Datastore -name $objDatastore.Volume.Name
}
$IoOperationLimit = ""
$LimitType = ""
if ($objScsilun.Policy.Policy -match 'RR') {
$IoOperationLimit = $objEsxCli.storage.nmp.psp.roundrobin.deviceconfig.get.Invoke(@{'device'=$objScsidisk.CanonicalName}).IOOperationLimit
$LimitType = $objEsxCli.storage.nmp.psp.roundrobin.deviceconfig.get.Invoke(@{'device'=$objScsidisk.CanonicalName}).LimitType
}
$objPaths = $objScsilun.Path
foreach($objPath in $objPaths) {
$objAdapter = $objAdapters | Where-Object {
($_.Key -eq $objPath.Adapter)
}
$AdapterName = $objAdapter.Device
if ($AdapterName) {
$HbaSet.Remove($AdapterName)
$result = [PSCustomObject]@{
'CountryCode' = $CountryCode
'vCenter_IP' = $CurrentServerIP
'Datacenter' = $DatacenterName
'Cluster' = $ClusterName
'VMHost' = $VMHost.Name
'VMHost_ConnectionState' = $VMHost.ConnectionState
'SCSILun_DeviceName' = $objScsidisk.CanonicalName
'SCSILun_Type' = $objScsidisk.LunType
'SCSILun_Vendor' = $objScsidisk.Vendor
'SCSILun_IsLocal' = $objScsidisk.LocalDisk
'SCSILun_IsSSD' = $objScsidisk.Ssd
'SCSILun_CapacityGB' = [math]::round($objScsidisk.capacity.BlockSize * $objScsidisk.capacity.Block / 1GB,1)
'SCSILun_Datastore_Name' = $objDatastore.Volume.Name
'SCSILun_Datastore_Type' = $objDatastore.Volume.Type
'SCSILun_Datastore_VMFS_Version' = $objDatastore.Volume.Version
'SCSILun_Datastore_CapacityGB' = $Datastore.CapacityGB
'SCSILun_Datastore_FreeSpaceGB' = $Datastore.FreeSpaceGB
'SCSILun_Datastore_StorageIOControlEnabled'= $Datastore.StorageIOControlEnabled
'SCSILun_Datastore_CongestionThreshold_ms'= $Datastore.CongestionThresholdMillisecond
'SCSILun_Datastore_CongestionThreshold_Mode'= $Datastore.ExtensionData.IORMConfiguration.CongestionThresholdMode
'SCSILun_Datastore_CongestionThreshold_PrcntofPeakThrput'= $Datastore.ExtensionData.IORMConfiguration.PercentOfPeakThroughput
'SCSILun_PathSelectionPolicy' = $objScsilun.Policy.Policy
'SCSILun_IoOperationLimit' = $IoOperationLimit
'SCSILun_LimitType' = $LimitType
'SCSILun_StorageArrayType' = $objScsilun.StorageArrayTypePolicy.Policy
'SCSILunPath_Name' = $objPath.Name
'SCSILunPath_Preferred' = $objPath.IsWorkingPath
'SCSILunPath_State' = $objPath.State
'SCSILunPath_PortWWN' = "{0:x}" -f $objPath.Transport.PortWorldWideName -replace '(..(?!$))','$1:'
'SCSILunPath_NodeWWN' = "{0:x}" -f $objPath.Transport.NodeWorldWideName -replace '(..(?!$))','$1:'
'HBA_Name' = $objAdapter.Device
'HBA_Type' = "FibreChannel"
'HBA_PCI' = $objAdapter.Pci
'HBA_Driver' = $objAdapter.Driver
'HBA_Model' = $objAdapter.Model
'HBA_Status' = $objAdapter.Status
'HBA_PortWWN' = "{0:x}" -f $objAdapter.PortWorldWideName -replace '(..(?!$))','$1:'
'HBA_NodeWWN' = "{0:x}" -f $objAdapter.NodeWorldWideName -replace '(..(?!$))','$1:'
'Timestamp' = Get-Date -Format $timestampFormat
}
# Write out at every line
$result | Export-CSV -path $FilePath -NoTypeInformation -Append -ErrorAction Stop
}
}
# HBAs that have missing paths for particular Lun, path fields as blank
if ($HbaSet.Count -ne 0) {
$HbaSet.Values | % {
$AdapterStatus = "missing"
if ($_.Status) {
$AdapterStatus = $_.Status
}
$result = [PSCustomObject]@{
'CountryCode' = $CountryCode
'vCenter_IP' = $CurrentServerIP
'Datacenter' = $DatacenterName
'Cluster' = $ClusterName
'VMHost' = $VMHost.Name
'VMHost_ConnectionState' = $VMHost.ConnectionState
'SCSILun_DeviceName' = $objScsidisk.CanonicalName
'SCSILun_Type' = $objScsidisk.LunType
'SCSILun_Vendor' = $objScsidisk.Vendor
'SCSILun_IsLocal' = $objScsidisk.LocalDisk
'SCSILun_IsSSD' = $objScsidisk.Ssd
'SCSILun_CapacityGB' = [math]::round($objScsidisk.capacity.BlockSize * $objScsidisk.capacity.Block / 1GB,1)
'SCSILun_Datastore_Name' = $objDatastore.Volume.Name
'SCSILun_Datastore_Type' = $objDatastore.Volume.Type
'SCSILun_Datastore_VMFS_Version' = $objDatastore.Volume.Version
'SCSILun_Datastore_CapacityGB' = $Datastore.CapacityGB
'SCSILun_Datastore_FreeSpaceGB' = $Datastore.FreeSpaceGB
'SCSILun_Datastore_StorageIOControlEnabled'= $Datastore.StorageIOControlEnabled
'SCSILun_Datastore_CongestionThreshold_ms'= $Datastore.CongestionThresholdMillisecond
'SCSILun_Datastore_CongestionThreshold_Mode'= $Datastore.ExtensionData.IORMConfiguration.CongestionThresholdMode
'SCSILun_Datastore_CongestionThreshold_PrcntofPeakThrput'= $Datastore.ExtensionData.IORMConfiguration.PercentOfPeakThroughput
'SCSILun_PathSelectionPolicy' = $objScsilun.Policy.Policy
'SCSILun_IoOperationLimit' = $IoOperationLimit
'SCSILun_LimitType' = $LimitType
'SCSILun_StorageArrayType' = $objScsilun.StorageArrayTypePolicy.Policy
'SCSILunPath_Name' = "N.A."
'SCSILunPath_Preferred' = $null
'SCSILunPath_State' = "N.A."
'SCSILunPath_PortWWN' = "N.A."
'SCSILunPath_NodeWWN' = "N.A."
'HBA_Name' = $_.Device
'HBA_Type' = "FibreChannel"
'HBA_PCI' = $_.Pci
'HBA_Driver' = $_.Driver
'HBA_Model' = $_.Model
'HBA_Status' = $AdapterStatus
'HBA_PortWWN' = $_.PortWorldWideName
'HBA_NodeWWN' = $_.NodeWorldWideName
'Timestamp' = Get-Date -Format $timestampFormat
}
# Write out at every line
$result | Export-CSV -path $FilePath -NoTypeInformation -Append -ErrorAction Stop
}
}
}
}
}
}
Disconnect-VIServer -server $CurrentServerIP -Confirm:$false
}
Upon running the script, I noticed that the path objects returned from LUNs configured with VMware's PSP and SATP only had 'active' as path state, regardless of whether the state shown in vCenter was Active or Active (I/O)
Contrasted with LUNs configured with Hitachi Dynamic Link Manager (HDLM), which shows path state as Active and Standby below. Output from PowerCLI also shows 'active' or 'standby'.
Is there any way to distinguish between Active and Active I/O paths using PowerCLI, such as using different PowerCLI modules as the ones I am currently using in my script?
Thanks in advance.
The IO indication is not present in the State property of the HostMultipathInfoPath object.
To find the IO indication you have to look at the IsWorkingPath property. As is explained in the API Reference "Paths slated for I/O can be found using isWorkingPath."
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference