Multiple Volumes to Aggregates Automatic Placement

I’ve been working on a fairly major tool - in-between doing lots of other stuff - over the last half a year or so (I won’t divulge here what it does here, maybe later ...) One of the features I wanted to add to the tool was automatic placement of a list of volumes to data aggregates in the NetApp Clustered ONTAP cluster (to save time manually working this out.) So I thought I’d write a standalone function to do this, with the view to adding this code to my major tool at a later date.

The code is designed to be used in other tools, but can be run standalone also. In the below example I connect to a simple test one node cluster, which has just two aggregates, and ask it to tell me which aggregates to put 5 new volumes on.

Image: VolsToAggrsAutoPlacer.ps1 in action
The Script

Note: As always, formatted for blogger (replacing tabs with two spaces)

## GENERIC INPUT DETAILS ##
$Cluster    = Read-Host "Cluster  "
$UserName   = Read-Host "Username "
$Password   = Read-Host "Password " -AsSecureString
$Credential = New-Object System.Management.Automation.PsCredential($UserName,$Password)
[VOID](Connect-NcController -Name $Cluster -Credential $Credential); Write-Host

## FUNCTION TO FIND AGGREGATE WITH LARGEST FREE SPACE ##
FUNCTION Place-OnLargestAggregate {
  $JustStarted = $TRUE
  $NodeList | Foreach {
    Foreach ($Aggr in $Aggrs.$_.DataAggrsList){
      If($JustStarted){
        $NodeWithLargestAggr = $_
        $LargestAggr         = $Aggr
        $LargestAvailSize    = $Aggrs.$_.AggrInfo.$Aggr.AvailSize
        $JustStarted         = $FALSE
      }
      elseif ($Aggrs.$_.AggrInfo.$Aggr.AvailSize -gt $LargestAvailSize) {
        $NodeWithLargestAggr = $_
        $LargestAggr         = $Aggr
        $LargestAvailSize    = $Aggrs.$_.AggrInfo.$Aggr.AvailSize
      }
    }
  }
  RETURN @{"node" = $NodeWithLargestAggr; "aggr" = $LargestAggr}
}

## FUNCTION TO FIND THE LARGEST VOLUME FROM THE SUPPLIED LIST (RECURSIVE) ##
FUNCTION Find-LargestVolRecursive {
  $JustStarted = $TRUE
  [System.Array]$NewUnPlacedVols = @()
  $UnPlacedVols | Foreach {
    If($JustStarted){
      $currentLargest   = $_
      $largestSize      = $VolSizes[$_]
      $JustStarted      = $FALSE
    } elseif ($VolSizes[$_] -gt $largestSize){
      $NewUnPlacedVols += $currentLargest
      $currentLargest   = $_
      $largestSize      = $VolSizes[$_]
    } else {
      $NewUnPlacedVols += $_
    }
  }
 
  ## GET PLACEMENT AGGR INFO ##
  $LargestFreeAggr = Place-OnLargestAggregate
  $Node = $LargestFreeAggr.Node
  $Aggr = $LargestFreeAggr.Aggr
  $Aggrs.$Node.AggrInfo.$Aggr.AvailSize -= $VolSizes[$CurrentLargest]
  $ReturnAggrsObject.$CurrentLargest = $Aggr
 
  ## SCREEN OUTPUT SECTION ##
  $ScreenOutputString = ("Place " + $VolsToPlace[$CurrentLargest] + " on " + $Node + " and " + $Aggr + ". Remaining free space = " + $Aggrs.$Node.AggrInfo.$Aggr.AvailSize)
  If ($Aggrs.$Node.AggrInfo.$Aggr.AvailSize -ge 0){ Write-Host $ScreenOutputString -ForegroundColor GREEN }
  else { Write-Host $ScreenOutputString -ForegroundColor RED   }
 
  ## UPDATE $UnPlacedVols AND RECURSE (if not empty) ##
  $UnPlacedVols = $NewUnPlacedVols
  If($UnPlacedVols.count -ne 0){ [VOID](Find-LargestVolRecursive) }
}

## [MAIN FUNCTION] VOLUMES TO AGGREGATES AUTO PLACER ##
FUNCTION VolsToAggrsAutoPlacer {
  Param(
    [Parameter(Mandatory=$TRUE,HelpMessage="Enter comma separated list of volumes to place")][String]$VolsToPlaceCSL,
    [Parameter(Mandatory=$TRUE,HelpMessage="Enter comma separated list of sizes of the vols")][String]$VolumeSizesCSL
  )

  ## PROCESS VOL SIZE UNITS ##
  [System.Array]$VolsToPlace = $VolsToPlaceCSL.Split(",")
  [System.Array]$VolSizes    = @()
  $VolumeSizesCSL.Split(",") | Foreach{
    If($_ -match("k")){ $_ = ([Int64]($_.Replace("k",""))) * 1024 }
    If($_ -match("m")){ $_ = ([Int64]($_.Replace("m",""))) * 1024 * 1024 }
    If($_ -match("g")){ $_ = ([Int64]($_.Replace("g",""))) * 1024 * 1024 * 1024 }
    $VolSizes += [Int64]$_
  }
  If($VolsToPlace.Count -ne $VolSizes.Count){ Write-Host "`n'Volumes To Place List' and 'Volume Sizes List' have mistmatched length - exiting! `n" -ForegroundColor RED; EXIT}
 
  ## GET CLUSTER NODES ##
  $AttrsNode = Get-NcNode -Template
  $AttrsNode.Node = ""
  [System.Array]$NodeList = (Get-NcNode -Attributes $AttrsNode).Node
 
  ## GET ROOT AGGREGATES AND BUILD $Aggrs OBJECT ##
  $AttrsAggr = Get-NcAggr -Template
  $QueryAggr = Get-NcAggr -Template
  Initialize-NcObjectProperty -Object $AttrsAggr -Name AggrOwnershipAttributes
  Initialize-NcObjectProperty -Object $QueryAggr -Name AggrRaidAttributes
  $AttrsAggr.AggrOwnershipAttributes.OwnerName = ""
  $QueryAggr.AggrRaidAttributes.HasLocalRoot   = "True"
  $RootAggrs = Get-NcAggr -Query $QueryAggr -Attributes $AttrsAggr
  [System.Object]$Aggrs = @{}
  $RootAggrs | Foreach {
    [String       ]$Owner                      = $_.AggrOwnershipAttributes.OwnerName
    [System.Object]$Aggrs.$Owner               = @{}  
    [String       ]$Aggrs.$Owner.Root          = $_.Name
    [System.Array ]$Aggrs.$Owner.DataAggrsList = @()
    [System.Object]$Aggrs.$Owner.AggrInfo      = @{}
  }

  ## GET DATA AGGRS INFO ##
  Initialize-NcObjectProperty -Object $AttrsAggr -Name AggrSpaceAttributes
  $AttrsAggr.AggrSpaceAttributes.SizeAvailable = ""
  $AggrsInfo = Get-NcAggr -Attributes $AttrsAggr
  $AggrsInfo | Foreach {
    [String]$Owner         = $_.AggrOwnershipAttributes.OwnerName
    [Int64 ]$AvailableSize = $_.AggrSpaceAttributes.SizeAvailable
    If($_.Name -ne $Aggrs.$Owner.Root){
      $Aggrs.$Owner.DataAggrsList               += $_.Name
      $Aggrs.$Owner.AggrInfo.($_.Name)           = @{}
      $Aggrs.$Owner.AggrInfo.($_.Name).AvailSize = $AvailableSize
    }
  }
      
  ## CYCLE THROUGH VOLUMES PLACING IN ORDER FROM LARGEST TO SMALLEST ##   
  [System.Array]$UnPlacedVols = @(); $i = 0
  $VolsToPlace | Foreach { $UnPlacedVols += $i; $i ++ }
  [System.Object]$ReturnAggrsObject = @{}
  Write-Host; [VOID](Find-LargestVolRecursive); Write-Host
 
  ## RETURN AGGREGATE PLACEMENTS (SAME ORDER AS INPUT VOLS) ##
  [System.Array]$ReturnArray = @(); $i = 0
  $VolsToPlace | Foreach { $ReturnArray += $ReturnAggrsObject.$i; $i ++ }
  , $ReturnArray
}

VolsToAggrsAutoPlacer

Comments