Posts RSS Comments RSS 253 Posts and 411 Comments till now

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

switch ($a){
     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

switch -RegEx (Get-ChildItem C:\test)
{
    "^\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

Manageable HBA List

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

  TypeName: System.Management.Automation.PSCustomObject

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

Param($List,$HostName,[switch]$FullDetail,[switch]$Verbose)
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
}

18 Responses to “Using Switch -RegEx to create Custom Object (Getting HBA Info)”

  1. on 03 Jan 2008 at 9:50 amPer

    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

  2. on 05 Jan 2008 at 1:25 amRob Campbell

    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.

  3. on 20 Oct 2008 at 4:29 amBill

    What parameters do you have to have to supply in order to run the script?

  4. on 20 Oct 2008 at 7:59 amtshell

    You need either a list file $list
    .\Get-HBAInfo.ps1 bleh.txt

    or you need to specifiy the host
    .\Get-HBAInfo.ps1 -host Myhost

  5. on 14 Apr 2009 at 10:26 amJohn Middleton

    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.

  6. on 14 Apr 2009 at 10:57 amtshell

    hbacmd.exe does NOT need to be on each host, but the HBAnywhere agent needs to be installed and listening.

  7. on 25 Apr 2009 at 12:21 amtim

    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.

  8. on 27 Apr 2009 at 7:17 amtshell

    Are these host behind a firewall? Is it possible ICMP is blocked?

  9. on 28 Apr 2009 at 1:50 amtim

    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.

  10. on 28 Apr 2009 at 5:16 amtshell

    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

  11. on 28 Apr 2009 at 6:11 amtim

    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.

  12. on 28 Apr 2009 at 6:43 amtshell

    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)!”

  13. on 28 Apr 2009 at 7:04 amtim

    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

  14. on 07 May 2009 at 10:35 amDerrick

    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.

  15. on 11 May 2009 at 11:53 pmTim

    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?

  16. on 12 May 2009 at 12:17 pmDerrick

    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.

  17. on 09 Sep 2009 at 10:25 amJay

    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………….

  18. on 09 Sep 2009 at 10:47 amtshell

    Ya.. sorry it is the stupid code highlighter :/

Trackback this post | Feed on Comments to this post

Leave a Reply

You must be logged in to post a comment.