Sunday, 19 January 2014

Clustered ONTAP Aggregate Auditor Powershell Script

This script generates an Aggregate Analysis spreadsheet, giving you the % aggregate used, total size of the aggregate, max size of the aggregate (works from Clustered Data ONTAP 8.2 onwards), raid group size, number of disks, number of disks to complete all raid groups, disk type, min and max size of disks in the aggregates, spares, and more … Apologies in advance for the formatting!

### THE START ###

######################################################################
## TITLE: Aggregate Auditor - rev 1.3                               ##
######################################################################

# FIRST: RECOMMEND reducing no. of disk as required to speed up the script
# $diskTypes = @("ATA","BSAS","FCAL","FSAS","MSATA","SAS","SATA","SSD")
$diskTypes = @("ATA","BSAS","FCAL","FSAS","MSATA","SAS","SATA","SSD")
$aggPercWarn = 79
$units = "g"

# User Prompts and Connect
Import-module DataOnTap; ""
$controller = Read-host "Enter IP of cluster-mgmt LIF"
$user = Read-host "Enter username"; ""; "connecting ..."
Connect-NcController $controller -credential $user; ""
$conversion = 1; $unit = "B"
if ($units -eq "k"){ $conversion = 1024; $unit = "KB" }
if ($units -eq "m"){ $conversion = 1048576; $unit = "MB" }
if ($units -eq "g"){ $conversion = 1073741824; $unit = "GB" }; ""

# Initiate Excel Workbook
$xl=New-Object -ComObject "Excel.Application"
$wb=$xl.Workbooks.Add()
$xl.Worksheets.Item("Sheet2").Delete()
$xl.Worksheets.Item("Sheet3").Delete()
$ws=$wb.ActiveSheet; $worksheets=1; $row=1
$cells=$ws.Cells

# The Heading
$cluster = Get-NcCluster
$clusterName = $cluster.ClusterName
$ws.name = "$clusterName"
$cells.item($row,1)="Cluster";
$cells.item($row,2)="$clusterName"
$cells.item($row,3)="Aggregate"
$cells.item($row,4)="Analysis"
$cells.item($row,1).EntireRow.font.bold = $true
$cells.item($row,1).EntireRow.font.size = 16
$cells.item($row,1).EntireRow.Interior.ColorIndex = 37
$row++;$row++

# Checking for unassigned disks
$q = get-ncdisk -template
Initialize-NcObjectProperty -object $q -name DiskRaidInfo
$q.DiskRaidInfo.ContainerType = "unassigned"
"Getting unassigned disks ..."
$unassigned = Get-NcDisk -Query $q
$count = $unassigned.count
if (!$unassigned){$count = 0} # Fixes no 0 count returned
if ($unassigned -and !$count){$count = 1} # Fixes 1 disk returns no value
$cells.item($row,1) = "No. of unassigned disks"
$cells.item($row,1).font.bold = $True
$cells.item($row,2) = $count
$cells.item($row,2).HorizontalAlignment = -4131
if ($unassigned){$cells.item($row,2).Interior.ColorIndex = 6}
$row++;$q=$null

# Checking for broken disks
$q = get-ncdisk -template
Initialize-NcObjectProperty -object $q -name DiskRaidInfo
$q.DiskRaidInfo.ContainerType = "broken"
"Getting broken disks ..."
$broken = Get-NcDisk -Query $q
$count = $broken.count
if (!$broken){$count = 0} # Fixes no 0 count returned
if ($broken -and !$count){$count = 1} # Fixes 1 disk returns no value
$cells.item($row,1) = "No. of broken disks"
$cells.item($row,1).font.bold = $True
$cells.item($row,2) = $count
$cells.item($row,2).HorizontalAlignment = -4131
if ($broken){$cells.item($row,2).font.bold = $True}
if ($broken){$cells.item($row,2).font.ColorIndex = 2}
if ($broken){$cells.item($row,2).Interior.ColorIndex = 3}
$row++;$row++;$q=$null

# Display Aggr Warning Threshold
$cells.item($row,3) = "Warn % >"
$cells.item($row,3).font.bold = $True
$cells.item($row,1).EntireRow.Interior.ColorIndex = 15
$row++; $cells.item($row,3) = "$aggPercWarn"

# Column Headers
$row++; $col = 1
$cells.item($row,1).EntireRow.Interior.ColorIndex = 15
"Node","Aggregate","% Used","Total Size ($unit)","Max Size ($unit)","RG Size","# Disks","*TCARG","Disk Type","Min Disk Size ($unit)","Max Disk Size ($unit)" | foreach {
$cells.item($row,$col)=$_
$cells.item($row,$col).font.bold=$True
$col++}; $row++; $row++

# Checking for spare disks
$q = get-ncdisk -template
Initialize-NcObjectProperty -object $q -name DiskRaidInfo
$q.DiskRaidInfo.ContainerType = "spare"
"Getting spare disks ..."
$spares = Get-NcDisk -Query $q

# Cycle Through the Nodes
$nodes = Get-NcNode
foreach ($node in $nodes){

# Node Name Output
$nodeName = $node.Node
$cells.item($row,1) = $nodeName; "Scanning node = $nodeName"
$cells.item($row,1).font.bold = $True
$cells.item($row,1).font.size = 14
$cells.item(($row+1),1) = $node.NodeModel
$cells.item(($row+1),1).Font.Italic = $True
$row++

# Get Aggregates for the Node
$aggrQuery = Get-NcAggr -Template
Initialize-NcObjectProperty -object $aggrQuery -name AggrOwnershipAttributes
$aggrQuery.AggrOwnershipAttributes.HomeName = $nodeName
$aggs = Get-NcAggr -Query $aggrQuery

# Ouptut Aggregate, Size, % Used, RaidSize, # Disks, RightSize
foreach ($agg in $aggs){
$aggName = $agg.Name; "Scanning aggregate = $aggName"
$cells.item($row,2)=$aggName
$aggUsed = $agg.used
$cells.item($row,3)=$aggUsed
if ($aggUsed -gt $aggPercWarn){
$cells.item($row,3).font.bold = $True
$cells.item($row,3).font.ColorIndex = 2
$cells.item($row,3).Interior.ColorIndex = 3}
$aggTotal = $agg.TotalSize
$cells.item($row,4)=[int]($aggTotal/$conversion)
$aggMax = $node.MaximumAggregateSize
$cells.item($row,5)=[int]($aggMax/$conversion)
$aggRaidSize = $agg.RaidSize
$cells.item($row,6)=$aggRaidSize
$aggDisks = $agg.Disks
$cells.item($row,7)=$aggDisks
if (!($agg.AggrRaidAttributes.HasLocalRoot)){
$mod = $aggDisks
$mod %= $aggRaidSize
if ($mod -eq 0){$mod = $aggRaidSize} # 0 TCARG for ful RGs
$cells.item($row,8) = ($aggRaidSize - $mod)
if ($mod -ne $aggRaidSize){$cells.item($row,8).Interior.ColorIndex = 6}}
if ($agg.AggrRaidAttributes.HasLocalRoot){$cells.item($row,8)="root"}
$cells.item($row,8).HorizontalAlignment = -4108
# Here we get all the aggregates disks
$queryDisk = Get-NcDisk -Template
$queryDisk.Aggregate = $aggName
$aggrsDisks = Get-NcDisk -Query $queryDisk
$aDisk = $aggrsDisks[0]
# Only the first disk in the aggregate is checked for type (assume all the rest are the same)
$aggDiskType = $aDisk.DiskInventoryInfo.DiskType      
$cells.item($row,9)=$aggDiskType
# Scan through the aggr's disks checking for capacity
$capMin = 0
$capMax = 0
foreach ($disk in $aggrsDisks) {
$diskSize = $disk.DiskInventoryInfo.Capacity
if (!$capMin){$capMin = $diskSize}
if (!$capMax){$capMax = $diskSize}
if ($spareDiskSize -lt $capMin){$capMin = $diskSize}
if ($spareDiskSize -gt $capMax){$capMax = $diskSize}
} # foreach ($disk in $aggrsDisks)
$cells.item($row,10)=[int]($capMin/$conversion)
$cells.item($row,11)=[int]($capMax/$conversion)
$row++
} # foreach ($agg in $aggs)

# Get spares info (reset counters first)
$diskRecord = @(0)*($diskTypes.count)
$capMinRecord = @(0)*($diskTypes.count)
$capMaxRecord = @(0)*($diskTypes.count)
foreach ($spare in $spares){
if (($spare.DiskOwnershipInfo.OwnerNodeName) -eq $nodeName){
$i = 0
foreach ($diskType in $diskTypes){
if (($spare.DiskInventoryInfo.DiskType) -eq $diskType){
$diskRecord[$i]++
$spareDiskSize = $spare.DiskInventoryInfo.Capacity
if (!$capMinRecord[$i]){$capMinRecord[$i] = $spareDiskSize}
if (!$capMaxRecord[$i]){$capMaxRecord[$i] = $spareDiskSize}
if ($spareDiskSize -lt $capMinRecord[$i]){$capMinRecord[$i] = $spareDiskSize}
if ($spareDiskSize -gt $capMaxRecord[$i]){$capMaxRecord[$i] = $spareDiskSize}
}; $i++}}}

# Display spares info
$i = 0
foreach ($diskType in $diskTypes){
if ($diskRecord[$i]){
$cells.item($row,6) = "SPARES:"
$cells.item($row,6).font.bold = $True
$cells.item($row,7) = $diskRecord[$i]
$cells.item($row,9) = $diskType
$cells.item($row,10) = [int]($capMinRecord[$i]/$conversion)
$cells.item($row,11) = [int]($capMaxRecord[$i]/$conversion)
$row++}; $i++};      $row++

} # foreach ($node in $nodes)

# Autofit Excel Columns and Name
$range = $ws.UsedRange
$range.EntireColumn.Autofit() | out-null

# Explain TCARG
$cells.item($row,1) = "*TCARG is: number of disks 'To Complete All RAID Groups'"
$cells.item($row,1).Font.Italic = $True

# Display the Excel Workbook
$xl.Visible=$True

### THE END ###

Image: An Example of Aggregate Auditor in action! (Note: “The Max Size (GB)” - max size of aggregate column - works from Clustered Data ONTAP 8.2 onwards)

5 comments:

  1. Nice job. However to clarify for newer admins. When you say "To complete all raid groups" you mean the raid group is not balanced and some of the disks in the aggr may run into a hot disk scenario? So if 9 disks were added from you screen shots then the raid groups would be balanced as it would show a value of zero.

    ReplyDelete
    Replies
    1. Hello Unknown,
      Yes that's correct. The TCARG or "To complete all raid groups" column just exists really to flag up where you've not got complete RAID groups. For newer admins, if you're adding to aggregates ideally you want to add in complete RAID group sized chunks so as to avoid disk hot spots created by say adding say just one disk to an existing RAID group (that disk will get most of the new writes), or say having a tiny 5 disk RAID group bolted on the end of a 20 disk RAID group (the performance of the 5 disk RAID group will suck.)
      Cheers,
      vCosonok

      Delete
    2. you posted this Clustered ONTAP Aggregate Auditor Powershell Script i tested it and it worked can you modify the script to save the excel file instead of displaying it as seen in the script

      Delete
  2. Indeed! And then you will also have to perform a reallocate after the fact as the current data in the aggr doesnt really "know" about the new disk. Just the new data that is added afterwards will.

    ReplyDelete

  3. you posted this Clustered ONTAP Aggregate Auditor Powershell Script i tested it and it worked can you modify the script to save the excel file instead of displaying it as seen in the script

    ReplyDelete