Problem:

Gathering snapshot statistics is a tedious task when looking at autosupports and cli output. I needed to gather information about oldest snapshot, average number of snaps per day, total snapshots, and other various information.

Solution:

A powershell script using the Data ONTAP PS Library. Read more for the script.

.SYNOPSIS
NetApp-Gather Snapshot Information

.DESCRIPTION
This script will collect snapshot information, summary and detail
Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc

.EXAMPLE
PS S:\102 – Scripts> & ‘.\NetApp-Gather Snapshot Information v1.1.ps1’ -nodes filer1.company.biz,filer2.company.biz -username “domain\username” -IsVerbose

.EXAMPLE
PS S:\102 – Scripts> & ‘.\NetApp-Gather Snapshot Information v1.1.ps1’

cmdlet NetApp-Gather Snapshot Information v1.1.ps1 at command pipeline position 1
Supply values for the following parameters:
nodes[0]: filer1.company.biz
nodes[1]: filer2.company.biz
nodes[2]:
username: domain\username
password: *************

.PARAMETER (IsVerbose)
Use this parameter to display snap detail ( & ‘.\NetApp-Gather Snapshot Information v1.1.ps1’ -IsVerbose )

.NOTES
Written by Thomas Lasswell, TechColumnist
Version 1.1

# #############################################################################<br>
# iVision - SCRIPT - POWERSHELL - NETAPP 7-MODE COMMANDS<br>
# NAME: NetApp-Gather Snapshot Information<br>
#<br>
# AUTHOR:  Thomas Lasswell, TechColumnist<br>
# DATE:  2014/01/21<br>
# EMAIL: lasswellt at TechColumnist.com<br>
#<br>
# COMMENT:  This script will collect snapshot information, summary and detail<br>
#			Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc<br>
#<br>
# VERSION HISTORY<br>
# 1.0 2014.01.17 Initial Version.<br>
# 1.1 2014.01.21 Added CSV export, days old column fixed<br>
#<br>
# TO ADD<br>
#<br>
# #############################################################################</p>
<p>&amp;lt;#<br>
.SYNOPSIS<br>
	NetApp-Gather Snapshot Information</p>
<p>.DESCRIPTION<br>
	This script will collect snapshot information, summary and detail<br>
	Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc</p>
<p>.EXAMPLE<br>
PS S:\102 - Scripts&amp;gt; &amp;amp; '.\NetApp-Gather Snapshot Information v1.1.ps1' -nodes filer1.company.biz,filer2.company.biz -username &amp;quot;domain\username&amp;quot; -IsVerbose</p>
<p>.EXAMPLE<br>
PS S:\102 - Scripts&amp;gt; &amp;amp; '.\NetApp-Gather Snapshot Information v1.1.ps1'</p>
<p>cmdlet NetApp-Gather Snapshot Information v1.1.ps1 at command pipeline position 1<br>
Supply values for the following parameters:<br>
nodes[0]: filer1.company.biz<br>
nodes[1]: filer2.company.biz<br>
nodes[2]:<br>
username: domain\username<br>
password: *************</p>
<p>.PARAMETER (IsVerbose)<br>
	Use this parameter to display snap detail ( &amp;amp; '.\NetApp-Gather Snapshot Information v1.1.ps1' -IsVerbose )</p>
<p>.NOTES<br>
Written by Thomas Lasswell, iVision<br>
Version 1.1</p>
<p>.LINK</p>
<p>#&amp;gt;<br>
#Get Parameters -- nodes is each node, when finished, hit enter and it will continue<br>
Param (<br>
    [Parameter(Mandatory=$True)]<br>
    [Array]$nodes,<br>
    [Parameter(Mandatory=$True)]<br>
    [String]$username,<br>
    [Parameter(Mandatory=$True,ParameterSetName = 'Secret')]<br>
    [Security.SecureString]$password,<br>
	[switch]$IsVerbose<br>
)<br>
#create outfile information<br>
$exedir= Split-Path -Path $MyInvocation.MyCommand.Definition -Parent<br>
$currentDate= (get-date -format yyyymmdd.Hm.s)<br>
$outsummary= ($exedir + &amp;quot;\&amp;quot; + &amp;quot;GatherSnapshotInformation_&amp;quot; + $currentDate + &amp;quot;_Summary.csv&amp;quot;)<br>
$outdetails= ($exedir + &amp;quot;\&amp;quot; + &amp;quot;GatherSnapshotInformation_&amp;quot; + $currentDate + &amp;quot;_Detail.csv&amp;quot;)</p>
<p>#Load ONTAP PowerShell Toolkit<br>
$module = Get-Module DataONTAP<br>
if ($module -EQ $NULL)<br>
{<br>
	Import-Module DataONTAP<br>
}</p>
<p>try<br>
{<br>
    $requiredVersion = New-Object System.Version(1.2)<br>
    if ((Get-NaToolkitVersion).CompareTo($requiredVersion) -LT 0) { throw }<br>
}<br>
catch [Exception]<br>
{<br>
    Write-Host &amp;quot;`nThis script requires Data ONTAP PowerShell Toolkit 1.2 or higher`n&amp;quot; -ForegroundColor Red<br>
    return<br>
}<br>
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password</p>
<p>#function to calculate difference in dates<br>
function Get-DateDiff {<br>
	param (<br>
		[CmdletBinding()]<br>
		[parameter(Mandatory=$true)]<br>
		[datetime]$date1,<br>
		[parameter(Mandatory=$true)]<br>
		[datetime]$date2<br>
	)<br>
		if ($date2 -gt $date1){$diff = $date2 - $date1}<br>
		else {$diff = $date1 - $date2}<br>
		$diff<br>
}<br>
#declare object arrays<br>
$objDetail = @()<br>
$objSummary = @()</p>
<p>#connect to each node individually<br>
foreach ($node in $nodes){<br>
	Write-Host &amp;quot;connecting to node $node...&amp;quot;<br>
	$conn = Connect-NaController -name $node -HTTPS -Credential $cred<br>
	$snapinfoS = @()<br>
	$snapinfoD = @()</p>
<p>	if ( $conn -ne $null ) {<br>
		Write-host &amp;quot;node connected, continuing on to snapshot calculations...&amp;quot;<br>
		Write-host &amp;quot;gathering node volumes...&amp;quot;<br>
		$vols =  Get-NaVol | ? {$_.state -eq &amp;quot;online&amp;quot; -and $_.raidstatus -notmatch &amp;quot;read-only&amp;quot;}<br>
		if ( $vols -ne $null ) {<br>
			foreach ($vol in $vols) {<br>
				Write-host &amp;quot;gathering snapshots for volume $vol...&amp;quot;<br>
				#get snapshots<br>
				$snaps= Get-NaSnapshot -targetname $vol</p>
<p>				#group to count snapshots per day<br>
				$snapsdatecount= $snaps | Group { ((Get-date) - $_.Created).Days } -NoElement | Sort Name -Descending<br>
				#measure to get count and average, average is average snapshots per day<br>
				$totalavgdays= $snapsdatecount | Measure-Object Count -ave | Select Count,Average<br>
				$avgdays= $totalavgdays.Average<br>
				$totaldays= $totalavgdays.Count</p>
<p>				#calculate sum of snapshots for total snapshot consumption<br>
				$totalsize= $snaps | Measure-Object Total -Sum | Select Count,Sum<br>
				$ftotalsize= ConvertTo-FormattedNumber $totalsize.sum DataSize &amp;quot;0.0&amp;quot;</p>
<p>				#get oldest snapshot<br>
				$foldestsnap= $snaps | Sort Created -Descending | Select-Object -Last 1</p>
<p>				#build array for summary<br>
				if ($avgdays -ne $null){<br>
					$summaryprop = @{'Node'=$node;<br>
									'Volume'=$vol;<br>
									'TotalSnaps'=$snaps.length;<br>
									'TotalDays'=$totaldays;<br>
									'AvgPerDay'=$avgdays;<br>
									'TotalSize'=$ftotalsize;<br>
									'Oldest'=$foldestsnap.created}<br>
					$objectS = New-Object -TypeName PSObject -Prop $summaryprop<br>
					$objSummary += $objectS<br>
					$objectS</p>
<p>					foreach ($snap in $snaps){<br>
						$daysold= Get-DateDiff (get-date) $snap.created;<br>
						$ftotal= ConvertTo-FormattedNumber $snap.total DataSize &amp;quot;0.0&amp;quot;;<br>
						$fcumulative= ConvertTo-FormattedNumber $snap.CumulativeTotal DataSize &amp;quot;0.0&amp;quot;;</p>
<p>						#build array for details<br>
						$detailprop = @{'Node'=$node;<br>
										'Volume'=$vol;<br>
										'Name'=$snap.name;<br>
										'Created'=$snap.created;<br>
										'DaysOld'=$daysold.days;<br>
										'TotalSize'=$ftotal;<br>
										'CumulativeTotal'=$fcumulative}<br>
						$objectD = New-Object -TypeName PSObject -Prop $detailprop<br>
						$objDetail += $objectD<br>
					}<br>
					if ($IsVerbose){<br>
						$snaps | ft `<br>
							@{Expression={$node};Label=&amp;quot;Node name&amp;quot;;Width=20},`<br>
							@{Expression={$vol};Label=&amp;quot;Volume&amp;quot;;Width=40},`<br>
							@{Expression={$_.Name};Label=&amp;quot;Name&amp;quot;;Width=150},`<br>
							@{Expression={$_.Created.ToShortDateString()};Label=&amp;quot;Created&amp;quot;;Width=12},`<br>
							@{E = {'{0} Days' -f (Get-DateDiff (get-date) $_.created).days};L=&amp;quot;Days Old&amp;quot;;W=20},`<br>
							@{Expression={ConvertTo-FormattedNumber $_.Total DataSize &amp;quot;0.0&amp;quot;};Label=&amp;quot;Total&amp;quot;;Width=15},`<br>
							@{Expression={ConvertTo-FormattedNumber $_.CumulativeTotal DataSize &amp;quot;0.0&amp;quot;};Label=&amp;quot;Cumulative&amp;quot;;Width=10} -autosize `<br>
							|  Out-String -Width 1000 | Write-Host<br>
					}<br>
				}<br>
				if ($avgdays -eq $null){ Write-Host &amp;quot;`tNo Snapshots Exist on $vol...&amp;quot;}<br>
			}<br>
		}<br>
	}<br>
}</p>
<p>#save files<br>
$objSummary | Select Node, Volume, TotalSnaps, TotalDays, AvgPerDay, TotalSize, Oldest | Export-CSV -Path $outsummary -NoTypeInformation<br>
$objDetail | Select Node, Volume, Name, Created, DaysOld, TotalSize, CumulativeTotal | Export-CSV -Path $outdetails -NoTypeInformation<br>

I was curious to see what ChatGPT code interpreter would do with this code and this is it’s output.

<#
.SYNOPSIS
NetApp-Gather Snapshot Information

.DESCRIPTION
This script collects snapshot information, summary, and detail.
Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc.

.NOTES
Written by Thomas Lasswell, TechColumnist
Version 1.1
#>

Param (
    [Parameter(Mandatory=$True)]
    [Array]$nodes,
    [Parameter(Mandatory=$True)]
    [String]$username,
    [Parameter(Mandatory=$True,ParameterSetName = 'Secret')]
    [Security.SecureString]$password,
    [switch]$IsVerbose
)

$exedir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$currentDate = (Get-Date -format "yyyyMMdd.Hms")
$outsummary = -join ($exedir, "\GatherSnapshotInformation_", $currentDate, "_Summary.csv")
$outdetails = -join ($exedir, "\GatherSnapshotInformation_", $currentDate, "_Detail.csv")

Import-Module DataONTAP -ErrorAction SilentlyContinue

try {
    $requiredVersion = New-Object System.Version(1.2)
    if ((Get-NaToolkitVersion).CompareTo($requiredVersion) -LT 0) { throw "This script requires Data ONTAP PowerShell Toolkit 1.2 or higher." }
} catch {
    Write-Host "`nThis script requires Data ONTAP PowerShell Toolkit 1.2 or higher`n" -ForegroundColor Red
    return
}

$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password

function Get-DateDiff {
    param (
        [parameter(Mandatory=$true)]
        [datetime]$date1,
        [parameter(Mandatory=$true)]
        [datetime]$date2
    )
    return [Math]::Abs(($date2 - $date1).Days)
}

$objDetail = @()
$objSummary = @()

foreach ($node in $nodes) {
    Write-Host "Connecting to node $node..."
    $conn = Connect-NaController -name $node -HTTPS -Credential $cred

    if ($conn) {
        Write-host "Node connected, continuing on to snapshot calculations..."
        $vols = Get-NaVol | Where-Object {$_.state -eq "online" -and $_.raidstatus -notmatch "read-only"}

        foreach ($vol in $vols) {
            $snaps = Get-NaSnapshot -targetname $vol
            $snapsdatecount = $snaps | Group-Object { ($_.Created).Days } -NoElement | Sort-Object Name -Descending
            $totalavgdays = $snapsdatecount | Measure-Object Count -Average | Select-Object Count,Average
            $totalsize = $snaps | Measure-Object Total -Sum | Select-Object Count,Sum
            $foldestsnap = $snaps | Sort-Object Created -Descending | Select-Object -Last 1

            if ($totalavgdays.Average) {
                $summaryprop = @{
                    'Node' = $node;
                    'Volume' = $vol;
                    'TotalSnaps' = $snaps.length;
                    'TotalDays' = $totalavgdays.Count;
                    'AvgPerDay' = $totalavgdays.Average;
                    'TotalSize' = [System.Math]::Round($totalsize.Sum, 2);
                    'Oldest' = $foldestsnap.created
                }
                $objSummary += New-Object -TypeName PSObject -Property $summaryprop

                foreach ($snap in $snaps) {
                    $daysold = Get-DateDiff (Get-Date) $snap.created
                    $detailprop = @{
                        'Node' = $node;
                        'Volume' = $vol;
                        'Name' = $snap.name;
                        'Created' = $snap.created;
                        'DaysOld' = $daysold;
                        'TotalSize' = [System.Math]::Round($snap.total, 2);
                        'CumulativeTotal' = [System.Math]::Round($snap.CumulativeTotal, 2)
                    }
                    $objDetail += New-Object -TypeName PSObject -Property $detailprop
                }
            } else {
                Write-Host "`tNo Snapshots Exist on $vol..."
            }
        }
    }
}

$objSummary | Export-CSV -Path $outsummary -NoTypeInformation
$objDetail | Export-CSV -Path $outdetails -NoTypeInformation