Thursday, April 30, 2015

Building a Hard Drive Report for Controller 0 in Dell OpenManage Server Administrator


I was recently asked a question on if there was a way to capture a couple pieces of data from the hard drives attached to the internal PERC RAID controller.  The first thought was that maybe we could pull this from one of the classes in the root/cimv2/dell WMI namespace available with the OpenManage Server Administrator (OMSA) installation.

We soon found out that the data available from those classes did not provide the necessary data (we wanted to capture the vendor and model of the drives), but we could get that information from the OMSA CLI using "omreport storage pdisk controller=0".


This came with it's own set of challenges:  We don't display the actual vendor since these are Dell validated drives, and you're spending a whole lot of time looking for the information you need.

Being that I'm one of the world's worst "lazy" people and I'll work hard to automate things, I decided to do some experimenting to see if I could quickly pull the key information we need using PowerShell.

First up, I really don't want to spend time parsing text if I can help it, and I know that "omreport" will take an argument to export the data to XML.  PowerShell likes XML... so I set off to work looking at the output of the "omreport storage pdisk controller=0 -fmt xml" command.


As luck would have it, the XML actually presents the disk vendor as DiskProductVendor.  That makes getting just the data we need a whole lot easier.  After casting the xml output into a PowerShell object and working out where it was addressed, I put together a command to pull that data and submitted it for review:

$test = [xml] $(omreport storage pdisk controller=0 -fmt xml) ; $test.OMA.ArrayDisks.DCStorageObject.DiskProductVendor ; $test.OMA.ArrayDisks.DCStorageObject.ProductID

type                                                        #text
----                                                        -----
astring                                                     SEAGATE
astring                                                     SEAGATE
astring                                                     ST9500620SS
astring                                                     ST9500620SS

Of course, the next question was how we could run it against a bunch of systems.  Since one of the assumptions I was given to work with is that OMSA would be installed, and the systems are in the same domain, we can use the "Invoke-Command" cmdlet with the "-ComputerName" and "-ScriptBlock" switches to invoke omreport remotely.  With that, getting the data becomes as simple as creating a foreach statement and piping the hostnames into it.

$servers = "R620-1","R620-2"
$servers | foreach { $test = $(Invoke-Command -Computer $_ -ScriptBlock { omreport storage pdisk controller=0 -fmt xml }) ; $($test.OMA.ArrayDisks.DCStorageObject.DiskProductVendor) ; $($test.OMA.ArrayDisks.DCStorageObject.ProductID) }

type                                                        #text
----                                                        -----
astring                                                     SEAGATE
astring                                                     SEAGATE
astring                                                     ST9500620SS
astring                                                     ST9500620SS
astring                                                     SEAGATE
astring                                                     SEAGATE
astring                                                     ST9500620SS
astring                                                     ST9500620SS

Success!  The data we wanted was actually pulled.  To increase the scale you could easily create a text file with a list of servers, and then just populate $servers using Get-Content ($servers = Get-Content .\servers.txt).

Now, I'm a bit picky about how data is presented.  This is still very ugly to look at, and you don't even know which drives belong to which server.  What if you wanted to know the physical drive position?  With these thoughts in mind, I set out to make it better, though it did take a little experimentation.  Cramming it together like a one-liner wasn't going to cut it.

The next step for me was to get this data sorted out and in an order I could create a PSObject and display the data in a table.  While I was at it, I grabbed the DeviceID (which correlates to drive bay):

$test.OMA.ArrayDisks.DCStorageObject | foreach {echo "Disk $($_.DeviceID.'#text')";$_.DiskProductVendor.'#text'
; $_.ProductID.'#text'}
Disk 0
SEAGATE
ST9500620SS
Disk 1
SEAGATE
ST9500620SS


Now this is definitely something we can work with!  I decided to go ahead and grab the output of hostname from the system we're executing against, though I probably could have just assigned $_ to $hostname in the outer foreach loop.  It wasn't necessary to define variables for $diskID, $vendor, and $model, it was just my way to keep myself from chasing down parsing errors because I missed a single or double quote somewhere.

Here's what I ended up with:

$report = ($servers | foreach {
$hostname = $(Invoke-Command -Computer $_ -ScriptBlock { hostname })
$pdisks = [xml] $(Invoke-Command -Computer $_ -ScriptBlock { omreport storage pdisk controller=0 -fmt xml })
@($pdisks.OMA.ArrayDisks.DCStorageObject | foreach {
$diskID = "Disk $($_.DeviceID.'#text')"
$vendor = $($_.DiskProductVendor.'#text')
$model = $($_.ProductID.'#text')
[pscustomobject]@{"Hostname"=$hostname;
  "Disk ID"=$diskID;
  "Vendor"=$vendor;
  "Model"=$model}
})
})
$report


Hostname Disk ID Vendor  Model
-------- ------- ------  -----
R620-1   Disk 0  SEAGATE ST9500620SS
R620-1   Disk 1  SEAGATE ST9500620SS
R620-2   Disk 0  SEAGATE ST9500620SS
R620-2   Disk 1  SEAGATE ST9500620SS



I think we can all agree that's a lot easier on the eyes!  And since this is a PSObject, we can export that to a CSV with "$report | Export-Csv <file>" like so:

$report | Export-Csv .\export.csv
type .\export.csv
#TYPE System.Management.Automation.PSCustomObject
"Hostname","Disk ID","Vendor","Model"
"R620-1","Disk 0","SEAGATE","ST9500620SS"
"R620-1","Disk 1","SEAGATE","ST9500620SS"
"R620-2","Disk 0","SEAGATE","ST9500620SS"
"R620-2","Disk 1","SEAGATE","ST9500620SS"


And with that, my tinkering on this subject is complete.