Using Switch -RegEx to create Custom Object (Getting HBA Info)
The other day I had a need to collect HBA (Host Base Fiber Adapters) from all my Servers. So the first place I looked was at WMI, but unfortunately… no dice. It didnt have the information I needed. The only way I knew to get the info I needed was to use was HBACmd.exe (utility to collect HBA information remotely.) So I went to writing a wrapper script in powershell to call the exe and then grep the text for what I was looking for, but I thought… HEY! Thats not the powershell thing to do! We do objects not text. So I went to parsing the text and making an object out of it. The script below is the result and while I dont believe many of you will find it particularly useful as it has a VERY specific use, I wanted to share with you how I went about objectizing the output.
Here is a Little Q and A on the script
Q: Purpose?
A: To get all the HBA information include Type, Firmware, Bios, Target Lun, WWN and a slew of other stuff.
Q: Why Did I objectize my text?
A: Because I know can simply use properties to filter and output data. Like what Machines have what Bios and what type of HBAs they have. Before I would have the parse the text for every different senario… now I just use where-object and filter away.
I few things that I wanted to point out here are the use of Switch to create the Custom Objects. IMO, Switch is one of the most powerful commands in the Powershell Language. It is INSANELY Powerfull. To be honest, It is pretty much the only one you need.
To compare it “Select Case” in vbscript would be insulting, but it can peform a similar function Like
Value1 {"It was Value 1"}
Value2 {"It was Value 2"}
Value3 {"It was Value 3"}
Value4 {"It was Value 4"}
}
It would take a whole series of post to completely cover switch, but for this one I only want to go over -regex use. For complete use read the help located:
PS> Get-help About_Switch # Read it, Learn it, Love it
Some Quick Notes about Switch
- Can use RegEx, WildCard, Exact, CaseSensitive, or File options.
- It takes input via Pipeline {expression} or File. The cool thing is the pipeline can be any expression that results in piped output.
- For each match it can perform any ScritpBlock use $_ as the current Item
- It performs EVERY match on each element unless you use Continue after a match to stop processing that record
Like I said, Switch is insanely powerful. Just one of those powers is using RegEx for comparison.
Here is an example of using the -RegEx option
{
"^\d" {"Starts with number: " + $_.FullName}
"\d" {"Has a number in it: " + $_.FullName}
"[^A-Za-z]" {"Does NOT start with Number: " + $_.FullName}
"tmp" {"Has ‘tmp’ in it: " + $_.FullName}
# Notice that you can even use and expression to match
{$_.Mode -match "-a"} {"Has Archive Bit Set: " + $_.FullName}
}
Now.. lets look at the script below. You will noticed I used RegEx to decide what value gets put in to what property of the object.
The script converts the output of three commands into two different objects. Lets look at one of them
It takes text like This
Port WWN : 10:00:00:00:11:11:11:11
Node WWN : 20:00:00:00:11:11:11:11
Fabric Name: 00:00:00:00:00:00:00:00
Flags : 0000f0a5
Host Name : Server1
Mfg : Emulex Corporation
Port WWN : 10:00:00:00:22:22:22:22
Node WWN : 20:00:00:00:22:22:22:22
Fabric Name: 00:00:00:00:00:00:00:00
Flags : 0000f0a5
Host Name : Server1
Mfg : Emulex Corporation
And converts Into an object like This
Name MemberType Definition
—- ———- ———-
Equals Method System.Boolean Equals(Object obj)
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
ToString Method System.String ToString()
Fabric NoteProperty System.String Fabric=00:00:00:00:00:00:00:00
Flags NoteProperty System.String Flags=0000f0a5
HBADetail NoteProperty System.Object[] HBADetail=System.Object[]
Host NoteProperty System.String Host=Server1
LUN NoteProperty System.String LUN=01
MFG NoteProperty System.String MFG=Emulex Corporation
NodeWWN NoteProperty System.String NodeWWN=20:00:00:00:11:11:11:11
PortWWN NoteProperty System.String PortWWN=10:00:00:00:11:11:11:11
Here is the Script
Begin{
$erroractionpreference = "SilentlyContinue"
$HBACMDPath = "<path To HbaCmd.exe>"
function CreateHBAListObj{
Param($srv)
$objCol = @()
$result = &"$HBACMDPath" "h=$srv" ListHBAs
foreach($item in $result)
{
$parsd = $item.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)
switch -regex ($parsd[0])
{
"^Port" {
$myobj = "" | Select-Object Host,PortWWN,NodeWWN,Fabric,Flags,LUN,MFG
$myobj.PortWWN = $parsd[1]
$myobj.Lun = GetTargetLun $srv $myobj.PortWWN
}
"^Node" {$myobj.NodeWWN = $parsd[1]}
"^Fabr" {$myobj.Fabric = $parsd[1]}
"^Flag" {$myobj.Flags = $parsd[1]}
"^Host" {$myobj.Host = $parsd[1]}
"^MFG " {
$myobj.MFG = $parsd[1]
$objCol += $myObj
}
}
}
$objCol
}
function CreateHBAInfoObj{
Param($srv,$wwn)
$objCol = @()
$result = &"$HBACMDPath" "h=$srv" HBAAttrib $wwn
$myobj = "" |Select-Object Host,MFG,SN,Model,ModelDesc,NodeWWN,NodeSymname,
HWVersion,ROM,FW,VenderID,Ports,DriverName,DeviceID,HBAType,
OpFW,SLT1FW,SLT2FW,IEEEAddress,BootBios,DriverVer,KernelVer
foreach($item in $result)
{
$parsd = $item.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)
switch -regex ($parsd[0])
{
"^Host" {$myobj.Host = $parsd[1]}
"^Manufacturer" {$myobj.MFG = $parsd[1]}
"^Serial" {$myobj.Sn = $parsd[1]}
"^Model " {$myobj.Model = $parsd[1]}
"^Model Desc" {$myobj.ModelDesc = $parsd[1]}
"^Node WWN " {$myobj.NodeWWN = $parsd[1]}
"^Node Symname" {$myobj.NodeSymname = $parsd[1]}
"^HW" {$myobj.HWVersion = $parsd[1]}
"^Opt" {$myobj.ROM = $parsd[1]}
"^FW" {$myobj.FW = $parsd[1]}
"^Vender" {$myobj.VenderID = $parsd[1]}
"^Number" {$myobj.Ports = $parsd[1]}
"^Driver Name" {$myobj.DriverName = $parsd[1]}
"^Device" {$myobj.DeviceID = $parsd[1]}
"^HBA Type" {$myobj.HBAType = $parsd[1]}
"^Operational" {$myobj.OpFW = $parsd[1]}
"^SLI1 FW" {$myobj.SLT1FW = $parsd[1]}
"^SLI2 FW" {$myobj.SLT2FW = $parsd[1]}
"^IEEE" {$myobj.IEEEAddress = $parsd[1]}
"^Boot " {$myobj.BootBios = $parsd[1]}
"^Driver Ver" {$myobj.DriverVer = $parsd[1]}
"^Kernel " {$myobj.KernelVer = $parsd[1]
$objCol += $myObj}
}
}
$objCol
}
function GetTargetLun{
Param($srv,$wwn)
$objCol = @()
$result = &"$HBACMDPath" "h=$srv" TargetMapping $wwn
[int]$lun = 0
switch -regex ($result)
{
"^SCSI OS Lun" {$lun = $_.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)[1].trim()}
}
"{0:x}" -f $lun
}
function Ping-Server {
Param([string]$srv)
if($srv -eq ""){return $false}
$pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
if($pingresult.statuscode -eq 0) {$true} else {$false}
}
Write-Host
if($verbose){$VerbosePreference = "Continue"}
}
Process{
if($_)
{
Write-Host "Getting HBA Info from $_"
if($FullDetail)
{
$MyObject = CreateHBAListObj $_
$HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
$MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
$MyObject
}
else
{
$MyObject = CreateHBAListObj $_
$MyObject
}
}
}
End{
if($list)
{
$servers = Get-Content $list
Write-Host "Running HBA Check against Servers in $list"
foreach($server in $servers)
{
if($server -ne "")
{
if(ping-server $server)
{
Write-Host "Getting HBA Info from $server"
if($FullDetail)
{
$MyObject = CreateHBAListObj $server
$HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
$MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
$MyObject
}
else
{
$MyObject = CreateHBAListObj $server
$MyObject
}
}
else
{
Write-Host "$Server not Pingable `n" -foregroundcolor RED
}
}
}
}
if($HostName)
{
Write-Host "Running HBA Check against Servers in $HostName"
if(ping-server $HostName)
{
Write-Host "Getting HBA Info from $HostName"
if($FullDetail)
{
$MyObject = CreateHBAListObj $HostName
$HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
$MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
$MyObject
}
else
{
$MyObject = CreateHBAListObj $HostName
$MyObject
}
}
else
{
Write-Host "$HostName not Pingable `n" -foregroundcolor RED
}
}
Write-Host
}
tshell :: Nov.08.2007 :: Custom Objects, functions, HowTo, Powershell, Scripting :: 18 Comments »
18 Responses to “Using Switch -RegEx to create Custom Object (Getting HBA Info)”
Leave a Reply
You must be logged in to post a comment.


Great article. I really, really like the simple way how you create custom object. See my blog entry regarding this http://msgoodies.blogspot.com/2008/01/simple-and-clever-way-to-create-custom.html
Nice. It seems like the ability to create custom objects is essential if PS is going to meet the objective of providing that “last mile” of a management solution. Unforunatly creating them seems to be a rather inelegant proposition. I’ve turned to PSCX’s new-hashobject cmdlet for the task on occasion even though I dislike having to rely on non-native solutions because it complicates distribution. Hopefully V2 will have some better options.
What parameters do you have to have to supply in order to run the script?
You need either a list file $list
.\Get-HBAInfo.ps1 bleh.txt
or you need to specifiy the host
.\Get-HBAInfo.ps1 -host Myhost
This script is exactly what is needed but being new to Powershell I could really use some help…
Does HbaCmd.exe need to be on each server checked.. or can it be run from my laptop to access remote servers?
And on the path statement.. does it need to have HbaCmd.exe at the end..?
$HBACMDPath = “c:\Program Files\Emulex\Util\HBAnyware\hbacmd.exe’
I would really like to run even just the first section to insure that HbaCmd will work in my environment… and extra help is appreciated.
Thanks, – John M.
hbacmd.exe does NOT need to be on each host, but the HBAnywhere agent needs to be installed and listening.
When I run this script against a list of our 80 SAN connected servers, all I get in return is:
not pingable.
not pingable.
not pingable.
not pingable.
I have even tried running this script against a local server and got the same result. Is there something I am missing? How do I check to make sure the HBAnyware agent is installed and listening? I have been trying to get this information for about 3 days. Any help would be greatly appreciated.
Are these host behind a firewall? Is it possible ICMP is blocked?
I can ping the servers just fine from command line. Yes they are behind a firewall, but I am running this command from the same side of the firewall as the hosts.
Odd… perhaps it is a issue with the ServerName being passed to ping-server.
Change this line
Write-Host “Running HBA Check against Servers in $HostName”
to
Write-Host “Running HBA Check against Servers in $($HostName)!”
This should show you if the $hostname has spaces or odd characters
I’m sorry my previous post didn’t show up correctly, the error I receive is:
servername not pingable
servername not pingable
It is actually pulling the host names from the text file I am running the script against.
I understood what the error was. My point was that if they are indeed pingable then the perhaps the “HostName” is not in the proper format (i.e. trailing spaces or control characters.)
By changing this line we can get a visual identication
Write-Host “Running HBA Check against Servers in $HostName”
to
Write-Host “Running HBA Check against Servers in $($HostName)!”
Ok here is my output now. I have even tried to type in the IP Address manually.
Running HBA Check against Servers in 172.17.17.64!
172.17.17.64 not Pingable
Here is what I did to just make sure the IP part was working. I was getting the same errors above. knowing localhost has to work. I tried the above script with localhost with no luck so just wanted to get the ping part working.
Just using the below:
(filename ip.ps1)
param($hostname)
$pingresult = get-WmiObject Win32_PingStatus -filter “Address=’$hostname’”
$address = $pingresult.address
if($pingresult.statuscode -eq 0)
{ Write-Host “$address Pingable `n” -foregroundcolor GREEN}
else
{Write-Host “$address Not Pingable `n” -foregroundcolor RED}
results:
PS C:\Program Files (x86)\Emulex\Util\HBAnyware> .\ip.ps1 localhost
localhost Pingable
PS C:\Program Files (x86)\Emulex\Util\HBAnyware> .\ip.ps1 WIN-BFZWO4Q9WRX
WIN-BFZWO4Q9WRX Pingable
PS C:\Program Files (x86)\Emulex\Util\HBAnyware> .\ip.ps1 blah
blah Not Pingable
PS C:\Program Files (x86)\Emulex\Util\HBAnyware> .\ip.ps1 10.13.172.82
10.13.172.82 Pingable
Taking this now, I plan to integrate it into the code above and get it working.
Derrick, were you able to get this script working? If so, will you please either post the script or send it to me via email?
working on it today. first opening I have had to get back to it. had to update some vbscripts for testing first.
this looks promising. a few tweaks and I am sure I will have it.
If you go to the following piece of code:
“address=’$srv’”
and remove the single-quotes around $srv
Then – go back and re-add them….it will work?
The single-quotes used here (assuming you did a C&P) are being displayed properly, but apparently they are not actually the correct character code for a single-quote………….
Ya.. sorry it is the stupid code highlighter :/