Microsoft Virtualization Discussions
Microsoft Virtualization Discussions
Hello,
OnTap 9.12.1P11
FAS2720 & AFF-0300
PSTK : 9.14.1
Prior to our update of OnTAP our scripts to report on snapshot usage ran by simply executing the Get-NcSnapshot cmdlet.
After the upgrade, running our script our script returned errors and research shows that the PSTK checks the version of OnTap and defaults to REST-API rather than ZAPI.
Our script was quickly fixed by forcing ZAPI usage with the -ZAPICALL argument on the Connect-NcController cmdlet.
I'd prefer to use the REST-API as I can see ZAPI is now being deprecated.
Using the REST API the Get-NcSnapshot cmdlet usage changes and seems to mandate the use of the -volume argument.
I re-wrote the line in the script to loop through the list of volumes and push the results into a variable.
This is working, but it is exceptionally slow.
One volume (on SSD storage) took over 7 minutes to get a list of snapshots on the volume (there were 32 snapshots).
Another volume took over 22 minutes (again 32 snapshots and again on SSD storage). These timings were obtained by writing out to a log file.
Is there a quicker way to get this information?
Old code using ZAPI :
$snapshots = Get-NcSnapshot -controller $controller
New code using REST API:
$volumes = Get-NcVol -controller $controller
$snapshots = ForEach ($volume in $volumes){
Get-NcSnapshot -volume $volume -WarningAction SilentlyContinue
}
There is a similar article that references the changes where someone was trying to use the Get-NcSnapshot command and had issues getting the list of snapshots for their volumes (https://community.netapp.com/t5/Microsoft-Virtualization-Discussions/Powershell-Pipe-issues/td-p/445340) and I based the new code used above on the information in the article.
Thank you!
Hi,
I'm not sure what the issue is but have you tried testing how responsive the native REST API is if the Get-NcSnapshot CmdLet is taking an excessively long time to return results? Here's an example:
Param(
[Parameter(Mandatory = $True, HelpMessage = "The Cluster name or IP Address")]
[String]$Cluster,
[Parameter(Mandatory = $False, HelpMessage = "The Vserver name")]
[String]$Vserver,
[Parameter(Mandatory = $False, HelpMessage = "The Volume name")]
[String]$Volume,
[Parameter(Mandatory = $True, HelpMessage = "The Credential to authenticate to ONTAP")]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]$Credential
)
#'------------------------------------------------------------------------------
Function Get-OntapAuthorization{
[Alias("Get-OntapAuth")]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, HelpMessage = "The Credential to authenticate to ONTAP")]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]$Credential
)
#'---------------------------------------------------------------------------
#'Set the authentication header to connect to AIQUM.
#'---------------------------------------------------------------------------
$auth = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Credential.UserName + ':' + $Credential.GetNetworkCredential().Password))
$headers = @{
"Authorization" = "Basic $auth"
"Accept" = "application/json"
"Content-Type" = "application/json"
}
Return $headers;
}#'End Function Get-UMAuthorization.
#'------------------------------------------------------------------------------
Function Get-OntapVolume{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, HelpMessage = "The Cluster name or IP Address")]
[String]$Cluster,
[Parameter(Mandatory = $False, HelpMessage = "The Vserver name")]
[String]$Vserver,
[Parameter(Mandatory = $False, HelpMessage = "The Volume name")]
[String]$Volume,
[Parameter(Mandatory = $True, HelpMessage = "The Credential to authenticate to ONTAP")]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]$Credential
)
#'---------------------------------------------------------------------------
#'Set the headers to authenticate to ONTAP.
#'---------------------------------------------------------------------------
$headers = Get-OntapAuthorization -Credential $Credential
#'---------------------------------------------------------------------------
#'Set the URL.
#'---------------------------------------------------------------------------
[String]$uri = "https://$Cluster/api/storage/volumes?"
[Bool]$query = $False;
[String]$message = $Null
If($Volume){
[String]$uri += "&name=$Volume"
[Bool]$query = $True;
}
If($Vserver){
[String]$uri += "&svm.name=$Vserver"
[Bool]$query = $True;
}
If(-Not($query)){
[String]$uri = $uri.SubString(0, ($uri.Length -1))
}Else{
[String]$uri = $uri.Replace("?&", "?")
}
#'---------------------------------------------------------------------------
#'Get the volumes.
#'---------------------------------------------------------------------------
Try{
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers -ErrorAction Stop
Write-Host $("Enumerated volumes on cluster ""$Cluster"" using URI ""$uri""")
}Catch{
Write-Warning -Message $("Failed enumerating volumes on cluster ""$Cluster"" using URI ""$uri"". Error " + $_.Exception.Message + ". Status Code " + $_.Exception.Response.StatusCode.value__)
}
Return $response;
}#'End Function Get-OntapVolumes.
#'------------------------------------------------------------------------------
Function Get-OntapSnapshot{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, HelpMessage = "The Cluster name or IP Address")]
[String]$Cluster,
[Parameter(Mandatory = $True, HelpMessage = "The Volume UUID")]
[String]$VolumeID,
[Parameter(Mandatory = $True, HelpMessage = "The Credential to authenticate to ONTAP")]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]$Credential
)
#'---------------------------------------------------------------------------
#'Set the headers to authenticate to ONTAP.
#'---------------------------------------------------------------------------
$headers = Get-OntapAuthorization -Credential $Credential
#'---------------------------------------------------------------------------
#'Set the URL.
#'---------------------------------------------------------------------------
[String]$uri = "https://$Cluster/api/storage/volumes/$VolumeID/snapshots?fields=volume.name"
#'---------------------------------------------------------------------------
#'Get the Snapshots.
#'---------------------------------------------------------------------------
Try{
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers -ErrorAction Stop
Write-Host $("Enumerated snapshots on cluster ""$Cluster"" using URI ""$uri""")
}Catch{
Write-Warning -Message $("Failed enumerating snapshots on cluster ""$Cluster"" using URI ""$uri"". Error " + $_.Exception.Message + ". Status Code " + $_.Exception.Response.StatusCode.value__)
}
Return $response;
}#'End Function Get-OntapSnapshots.
$processTimer = [System.Diagnostics.Stopwatch]::StartNew()
#'------------------------------------------------------------------------------
#'Set the certificate policy and TLS version.
#'------------------------------------------------------------------------------
Add-Type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls12'
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
#'------------------------------------------------------------------------------
#'Enumerate the Volumes.
#'------------------------------------------------------------------------------
[String]$command = "Get-OntapVolume -Cluster $Cluster "
If($Vserver){
[String]$command += "-Vserver $Vserver "
}
If($Volume){
[String]$command += "-Volume $Volume "
}
[String]$command += "-Credential `$Credential"
$volumes = Invoke-Expression -Command $command
#'------------------------------------------------------------------------------
#'Enumerate the snapshots on the volumes.
#'------------------------------------------------------------------------------
ForEach($volumeID In $volumes.records.uuid){
$snapshots = Get-OntapSnapshot -Cluster $Cluster -VolumeID $volumeID -Credential $Credential
$snapshots.records | Format-List
}
#'------------------------------------------------------------------------------
#'Display the duration.
#'------------------------------------------------------------------------------
$processTimer.Stop()
$ts = $processTimer.Elapsed
$elapsedTime = "{0:00}:{1:00}:{2:00}.{3:00}" -f $ts.Hours, $ts.Minutes, $ts.Seconds, ($ts.Milliseconds / 10)
Write-Host $("Enumerated Snapshots - Elapsed Time $elapsedTime")
#'------------------------------------------------------------------------------
Usage:
# Get all snapshots on a volume:
PS C:\Scripts\PowerShell\Projects\ONTAP\GetSnapshots> .\GetSnapshots.ps1 -Cluster cluster1 -Vserver svm1_cluster1 -Volume volume_001 -Credential $credential
# Get all snapshots on all volumes on a vserver:
PS C:\Scripts\PowerShell\Projects\ONTAP\GetSnapshots> .\GetSnapshots.ps1 -Cluster cluster1 -Vserver svm1_cluster1 -Credential $credential
# Get all snapshots on all volumes on all vservers on a cluster:
PS C:\Scripts\PowerShell\Projects\ONTAP\GetSnapshots> .\GetSnapshots.ps1 -Cluster cluster1 -Credential $credential
I'll look into it further as I've got some snapshot automation (currently using ZAPI) that will break when migrating to REST by the sounds of it.
Hope this gives you some ideas in the interim.
/Matt