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.
Thanks buddy. I'll rip the parts I need, improve and post back :)
ReplyDeleteI need to make this multithreaded, so I'll throw in a runfactory.