Friday, 17 February 2017

Treesize for Clustered ONTAP (Get-NcTreesize.ps1)

A colleague asked me whether I knew of anything like “Treesize for NetApp”, I didn’t, so I thought I’d knock up my own version, and here it is.

The recursive function - at the core of the PowerShell script - has been used previously in a fair few posts. The usual ‘Caveat Utilitor’ applies - don’t use this on your production cluster (at least not without serious testing). Check out this post for more background and links "How to reclaim a lot of space and save money" (that wasn’t the original title - it should have been.)

Copy and paste the script to a text document and save as Get-NcTreesize.ps1. The various parameters are detailed at the top of the script. Usage examples:


$P = read-host -AsSecureString

.\Get-NcTreesize.ps1 -Cluster 10.3.5.50 -UserName admin -Password $P -DataSVM SVM1 -ScanStartPath /vol/TEST0001 -OutputFile "TEST0001.CSV"

.\Get-NcTreesize.ps1 -Cluster 10.3.5.50 -UserName admin -Password $P -DataSVM SVM1 -ScanStartPath /vol/TEST0002 -GetFileDirSecInfo -OutputFile "TEST0002.CSV"


Note: -GetFileDirSecInfo substantially slows down Get-NcTreesize. If you don’t want to get file and directory security info, I highly recommend skipping this.

Image: Example of the raw, unformatted CSV output
The Script


####################
## Get-NcTreesize ##
####################

Param(
  [Parameter(Mandatory=$True)][String]$Cluster,
  [Parameter(Mandatory=$True)][String]$UserName,
  [Parameter(Mandatory=$True)][Security.SecureString]$Password,
  # TIP: PS> $P = Read-Host -AsSecureString
  # ... you can use $P after -Password
  [Parameter(Mandatory=$True)][String]$DataSVM,
  [Parameter(Mandatory=$True,HelpMessage="/vol/VOLNAME(/...)")]
  [String]$ScanStartPath,
  [String]$OutputFile = "NcTreesizeReport.CSV",
  [String]$DateFormat = "yyyy.MM.dd",
  [Switch]$GetFileDirSecInfo
);""

## GENERIC: Loading PSTK ##
"INFO: DataONTAP PSTK loading ..."
[Void](Import-Module DataONTAP)
If(!(Get-Module DataONTAP)){"ERROR: No DataONTAP PSTK!";EXIT}
"INFO: DataONTAP PSTK loaded.`n"

## GENERIC: Connecting to $Cluster ##
"INFO: $Cluster connecting ..."
$Global:CurrentNcController = $NULL
$Cred = New-Object System.Management.Automation.PsCredential($UserName,$Password)
[Void](Connect-NcController -Name $Cluster -Credential $Cred)
If(!$Global:CurrentNcController){"ERROR: Cannot connect to $Cluster!";EXIT}
"INFO: $Cluster connected.`n"

## GENERIC: Verifying $DataSVM ##
"INFO: $DataSVM verifying ..."
$vsAttrs = Get-NcVserver -Template
$vsQuery = Get-NcVserver -Template
$vsQuery.VserverType = "data"
[System.Array]$DataSVMs = (Get-NcVserver -Attributes $vsAttrs -Query $vsQuery).VserverName
If($DataSVMs -notcontains $DataSVM){"ERROR: No $DataSVM!";EXIT}
"INFO: $DataSVM verified.`n"
$Global:CurrentNcController.Vserver = $DataSVM

## VERIFY $ScanStartPath ##
"INFO: $ScanStartPath verifying ..."
If(!(Read-NcDirectory $ScanStartPath)){"ERROR: $ScanStartPath is incorrect!";EXIT}
"INFO: $ScanStartPath verified.`n"

## OPTIMIZE READ-NCDIRECTORY ##
# To optimize the process, we only collect the attributes we're interested in.
[System.Array]$L = ,
"AccessedTimestamp",
"ChangedTimestamp",
"CreationTimestamp",
"FileSize",
"FileType",
"IsEmpty",
"Name"
$Attrs = Read-NcDirectory -Template
$L | Foreach{$Attrs.$_ = ""}

## (OPTIONAL) GET JUNCTION PATH FOR FILE DIRECTORY SECURITY ##
If($GetFileDirSecInfo){
  $vAttrs = Get-NcVol -Template
  Initialize-NcObjectProperty -Object $vAttrs -Name VolumeIdAttributes
  $vAttrs.VolumeIdAttributes.JunctionPath = ""
  [String]$Volume = $ScanStartPath.Split("/")[2]
  [String]$JPath = (Get-NcVol -Name $Volume -Attributes $vAttrs).VolumeIdAttributes.JunctionPath
  If(!$JPath){
    "ERROR: Cannot find junction path for $Volume, GetFileDirSecInfo is disabled!`n"
    $GetFileDirSecInfo = $FALSE
  }else{
    $JPath = $JPath.Substring(0,($JPath.Length - 1 - $Volume.Length))
  }
}

## (OPTIONAL) FILE DIRECTORY SECURITY FUNCTION ##
Function Get-NcFileDirSecInfo {
  Param([String]$Path)
  $G = Get-NcFileDirectorySecurity -Path ($JPath + $Path)
  $ACLs = ""
  If($G.Acls){$G.Acls.Split("`n") | Foreach{$ACLs += ($_.Trim(" ") + ";")}}
  $SecInfo = [PSCustomObject]@{
    "DosAttrs"       = $G.DosAttributes
    "DosAttrsText"   = $G.DosAttributesText
    "SecurityStyle"  = $G.SecurityStyle
    "EffectiveStyle" = $G.EffectiveStyle
    "UnixGroupId"    = $G.UnixGroupId
    "UnixModeBits"   = $G.UnixModeBIts
    "UnixUserId"     = $G.UnixUserId
    "ACLs"           = $ACLs
  }
  Return $SecInfo
}

## GLOBAL VARIABLES ##
[System.Array]$Global:CSV = @() # Output CSV
[Int64]$Global:Order = 0 # For output ordering

## RECURSIVE SCANNING FUNCTION ##
Function GetDirInfoRecursive{
  Param([String]$Path)
  Write-Host "." -NoNewLine # Indicates the program is running.
  $GetDirInfo = Read-NcDirectory $Path -Attributes $Attrs
  If(!$GetDirInfo){Return 0} # Return 0 size if failed to read $Path
  $Global:Order ++
  $Local:Order = $Global:Order
  $Local:DirSize = 0
  [Int]$level = $Path.Split("/").count - 2
 
  ## CYCLE THROUGH $GetDirInfo ##
  Foreach ($l in $GetDirInfo){          
    If($l.FileType -eq "directory"){
      If($l.Name -eq "."){ $CurrentDir = $l }
      If(($l.Name -ne ".") -and ($l.Name -ne "..") -and ($l.Name -ne ".snapshot")) {
        If ($l.IsEmpty -ne "False"){
          $Local:DirSize += (GetDirInfoRecursive ($Path + "/" + $l.Name))
        }
      }
    }elseif($l.FileType -eq "file"){           
      $Global:Order ++
      $Local:DirSize += $l.FileSize
     
      ## (OPTIONAL) FILE DIRECTORY SECURITY (FDS) INFO ##
      [System.Object]$FDS = @{}
      If($GetFileDirSecInfo){$FDS = Get-NcFileDirSecInfo ($Path.SubString(4) + "/" + $l.Name)}
     
      ## ADD FILE INFORMATION TO CSV ##
      $Global:CSV += [PSCustomObject]@{
        "Order"           = $Global:Order
        "Directory Level" = $Level
        "File Type"       = "file"
        "Name"            = $l.Name
        "Path"            = $Path
        "File Size"       = $l.FileSize
        "Directory Size"  = ""
        "Accessed Time"   = $l.AccessedTimeStampDT.ToString($DateFormat)
        "Changed Time"    = $l.ChangedTimeStampDT.ToString($DateFormat)
        "Creation Time"   = $l.CreationTimeStampDT.ToString($DateFormat)
        "DosAttrs"        = $FDS."DosAttrs"
        "DosAttrsText"    = $FDS."DosAttrsText"
        "SecurityStyle"   = $FDS."SecurityStyle"
        "EffectiveStyle"  = $FDS."EffectiveStyle"
        "UnixGroupId"     = $FDS."UnixGroupId"
        "UnixModeBits"    = $FDS."UnixModeBits"
        "UnixUserId"      = $FDS."UnixUserId"
        "ACLs"            = $FDS."ACLs"
      }
    }
  }   
 
  ## (OPTIONAL) FILE DIRECTORY SECURITY (FDS) INFO ##
  [System.Object]$FDS = @{}
  If($GetFileDirSecInfo){$FDS = Get-NcFileDirSecInfo $Path.SubString(4)}
 
  ## ADD DIRECTORY INFORMATION TO CSV ##
  $Global:CSV += [PSCustomObject]@{
    "Order"           = $Local:Order
    "Directory Level" = $Level
    "File Type"       = "directory"
    "Name"            = $Path.Split("/")[$level + 1]
    "Path"            = $Path
    "File Size"       = ""
    "Directory Size"  = $Local:DirSize
    "Accessed Time"   = $CurrentDir.AccessedTimeStampDT.ToString($DateFormat)
    "Changed Time"    = $CurrentDir.ChangedTimeStampDT.ToString($DateFormat)
    "Creation Time"   = $CurrentDir.CreationTimeStampDT.ToString($DateFormat)
    "DosAttrs"        = $FDS."DosAttrs"
    "DosAttrsText"    = $FDS."DosAttrsText"
    "SecurityStyle"   = $FDS."SecurityStyle"
    "EffectiveStyle"  = $FDS."EffectiveStyle"
    "UnixGroupId"     = $FDS."UnixGroupId"
    "UnixModeBits"    = $FDS."UnixModeBits"
    "UnixUserId"      = $FDS."UnixUserId"
    "ACLs"            = $FDS."ACLs"
  }
  Return $Local:DirSize
}

##################
## MAIN PROGRAM ##
##################

("START: " + [String](Get-Date))
[Void](GetDirInfoRecursive $ScanStartPath);""
("END: " + [String](Get-Date) + "`n")
$Global:CSV | Sort "Order" | Export-CSV "$OutputFile" -NoTypeInformation


Note: This was tested against Clustered Data ONTAP 8.3.2.

No comments:

Post a Comment