Microsoft Virtualization Discussions

Adding expiry time to snapshot not directly possible with PowerShell Cmdlets

florianf

I have a customer who needs to set the expiry-time for legal reasons for many snapshots via WFA. Unfortunately the PowerShell Cmdlets currently do not support setting the expiry-time for a snapshot. I would have expected Set-NcSnapshot to have a parameter for expiry time, but it is missing. I have now implemented the missing functionality using Invoke-NcSystemApi, but would be glad if it could be included in the official PowerShell Cmdlets.

 

Here's an example to get the current expiry time

 

$Template = Get-NcSnapshot -Template
$Template.ExpiryTime = ""
$Snapshot = Get-NcSnapshot -Vserver $VserverName -Volume $VolumeName -Snapshot $SnapshotName
Write-Host "Expiry time of snapshot $SnapshotName is $($Snapshot.ExpiryTimeDT)"

 

Here's the Cmdlet to set the expiry time directly using the ZAPI

 

# helper function to convert datetime to unix timestamp
function ConvertTo-UnixTimestamp {
    [CmdletBinding()]
 
    PARAM (
        [parameter(Mandatory=$True,
                    Position=0,
                    ValueFromPipeline=$True,
                    ValueFromPipelineByPropertyName=$True,
                    HelpMessage="Date to be converted.")][DateTime[]]$Date
    )
 
    BEGIN {
        $epoch = Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0
    }
 
    PROCESS {
        $Date = @($Date)
 
        foreach ($Date in $Date) {
                $Seconds = [math]::Ceiling($Date.ToUniversalTime().Subtract($epoch).TotalSeconds)
                Write-Output $Seconds
        }
    }
}
 
<#
.SYNOPSIS
Modifies expiry-time of a snapshot
.DESCRIPTION
This operation modifies the expiry time of a snapshot
.EXAMPLE
$ExpiryTime = (Get-Date).AddYear(1)
Set-NcSnapshotExpiryTime -Controller $Controller -Vserver $VserverName -Volume $VolumeName -Snapshot $SnapshotName -ExpiryTime $ExpiryTime
.PARAMETER Controller
A clustered Data ONTAP controller to receive this cmdlet, as embodied by an NcController object.  This parameter is returned by the Connect-NcController cmdlet.  If not specified, the value in the global variable CurrentNcController is used.  In the latter case, if CurrentNcController contains a collection of NcController objects, this cmdlet is invoked on each controller in succession.
.PARAMETER Vserver
Name of the vserver to which this volume belongs.
.PARAMETER Volume
Volume name.
.PARAMETER Snapshot
Snapshot name.
.PARAMETER ExpiryTime
Expiry time as DateTime object.
.LINK
Get-NcSnapshot
#>
function global:Set-NcSnapshotExpiryTime {
    [CmdletBinding(DefaultParameterSetName="none")]
    PARAM (
        [parameter(Mandatory=$False,
                    Position=0,
                    HelpMessage="A clustered Data ONTAP controller to receive this cmdlet, as embodied by an NcController object. This parameter is returned by the Connect-NcController cmdlet.  If not specified, the value in the global variable CurrentNcController is used.  In the latter case, if CurrentNcController contains a collection of NcController objects, this cmdlet is invoked on each controller in succession.")][NetApp.Ontapi.Filer.C.NcController]$Controller,
        [parameter(Mandatory=$True,
                    Position=1,
                    ValueFromPipeline=$True,
                    ValueFromPipelineByPropertyName=$True,
                    HelpMessage="Vserver to which to direct this command. If the currently connected controller (as embodied by an NcController object) represents a cluster, there are multiple means to address commands to one of its vservers.  Either set the Vserver field on the NcController object to direct all subsequent commands to that vserver, or supply the VserverContext parameter to an individual cmdlet.  Only cmdlets which may be invoked on vservers offer the VserverContext parameter.")][String]$Vserver,
        [parameter(Mandatory=$True,
                    Position=2,
                    HelpMessage="Names of the volumes across which the snapshot is to be created.",
                    ValueFromPipeline=$True,
                    ValueFromPipelineByPropertyName=$True)][String]$Volume,
        [parameter(Mandatory=$True,
                    Position=3,
                    HelpMessage="Name of the snapshot to be modified.",
                    ValueFromPipeline=$True,
                    ValueFromPipelineByPropertyName=$True)][Alias("Name")][String]$Snapshot,
        [parameter(Mandatory=$True,
                    Position=4,
                    HelpMessage="Expiry time as DateTime object..")][DateTime]$ExpiryTime
    )
 
    PROCESS {
        Write-Verbose "Setting expiry time $ExpiryTime for snapshot $Snapshot of Volume $Volume in SVM $Vserver"
 
        $xmlDoc = New-Object system.Xml.XmlDocument
        $xmlDoc.loadXml(@"
<snapshot-modify-iter>
    <attributes>
        <snapshot-info>
            <expiry-time></expiry-time>
        </snapshot-info>
    </attributes>
    <query>
        <snapshot-info>
            <name></name>
            <volume></volume>
            <vserver></vserver>
        </snapshot-info>
    </query>
</snapshot-modify-iter>
"@)
 
        $xmlDoc."snapshot-modify-iter".query.'snapshot-info'.vserver = $Vserver
        $xmlDoc."snapshot-modify-iter".query.'snapshot-info'.volume = $Volume
        $xmlDoc."snapshot-modify-iter".query.'snapshot-info'.name = $Snapshot
        $xmlDoc."snapshot-modify-iter".attributes.'snapshot-info'.'expiry-time' = ($ExpiryTime | ConvertTo-UnixTimestamp).ToString()
 
        $result = Invoke-NcSystemApi -RequestXML $xmlDoc -VserverContext $Vserver -ErrorAction Stop
 
        if ($result.results.'num-failed' -ge 1) {
            throw "Setting Expiry Time failed with error " + $result.results.'failure-list'.'snapshot-modify-iter-info'.'error-message'
        }
    }
} 

 

7 REPLIES 7

randys

For those that find this... 

This... 

<#
.SYNOPSIS
Modifies expiry-time of a snapshot
.DESCRIPTION
This operation modifies the expiry time of a snapshot
.EXAMPLE
$ExpiryTime = (Get-Date).AddYear(1)
Set-NcSnapshotExpiryTime -Controller $Controller -Vserver $VserverName -Volume $VolumeName -Snapshot $SnapshotName -ExpiryTime $ExpiryTime
.PARAMETER Controller
A clustered Data ONTAP controller to receive this cmdlet, as embodied by an NcController object.  This parameter is returned by the Connect-NcController cmdlet.  If not specified, the value in the global variable CurrentNcController is used.  In the latter case, if CurrentNcController contains a collection of NcController objects, this cmdlet is invoked on each controller in succession.
.PARAMETER Vserver
Name of the vserver to which this volume belongs.
.PARAMETER Volume
Volume name.
.PARAMETER Snapshot
Snapshot name.
.PARAMETER ExpiryTime
Expiry time as DateTime object.
.LINK
Get-NcSnapshot
#>

Should follow: 

function global:Set-NcSnapshotExpiryTime {

not precede it.  Makes it more portable without creating weird errors....

 

randys

get-ncsnapshot is not capable of returning the ExpiryTime property.  Tested it on cdot 9.7 with the 9.8.0 toolkit.  So trying to set just the snapshots that have expired or just seeing the ExpiryTime on snapshots from powershell with the get-ncsnapshot in the example above does not work.  My script just tries to set the Expiry every time I run my script because it is blind to the current setting.  Any thoughts on that?

That said the Zapi hack does work but it is a little slow.

Frome CLI:
…
TESTnetappcluster02::> snapshot show -vserver TESTFILE01 -volume test -fields vserver ,volume ,snapshot ,Expiry-time  -Snapshot daily.2021-06-25_0010
vserver  volume snapshot              expiry-time
-------- ------ --------------------- ------------------
TESTFILE01 test   daily.2021-06-25_0010 6/26/2021 00:00:00

TESTnetappcluster02::>
…
From Powershell Toolkit:
…

PS C:\windows\system32> get-ncsnapshot -vserver TESTFILE01 -volume test -Snapshot daily.2021-06-25_0010 | Select-Object -Property Vserver,Volume,Name,ExpiryTime,ExpiryTimeDT,AccessTime
Vserver      : TESTFILE01
Volume       : test
Name         : daily.2021-06-25_0010
ExpiryTime   : 
ExpiryTimeDT : 
AccessTime   : 1624605000
PS C:\windows\system32>
…

 

parisi

Sounds like maybe PowerShell toolkit isn't asking for the proper attributes from ONTAP for those, or that the attributes changed in a release. That would need to be addressed by the PSTK maintainers. I'll point them to this thread.

 

Another workaround is to use CLI passthrough via invoke-ncssh with PowerShell.

 

For example:

 

PS C:\> $SnapshotExpiryTime = "snapshot show -vserver DEMO -volume files -snapshot base -fields expiry-time"

PS C:\> Invoke-NcSsh -Command $SnapshotExpiryTime -Name $clusterIP
Keyboard-interactive authentication prompts from server:
End of keyboard-interactive prompts from server


NcController : x.x.x.x
Value :

Last login time: 6/25/2021 19:11:33

vserver volume snapshot expiry-time
------- ------ -------- -----------------
DEMO files base 9/1/2040 00:00:00

 

randys

So ncssh will return the data, but if I recal it returns it as a string that would then have to be parsed to get a usable array or hash table.  While a solution, not a great one as invariably someone will use non convention names or the results will change format slightly between versions and the parsing function would be rendered unusable or worse return fields in the wrong columns...  Scraping a ncssh string response just isn't a good long-term solution.

Thanks though.

R

 

parisi

The PSTK developers are aware of this issue and are investigating to see if a bug fix is needed (which it probably is).

randys

get-member of get-ncsnapshot calls out the ExpiryTime property as  get;set; but it does not seem to do either.

PS C:\windows\system32> Get-NcSnapshot |gm


   TypeName: DataONTAP.C.Types.Snapshot.SnapshotInfo

Name                                       MemberType    Definition                                                              
----                                       ----------    ----------                                                              
Created                                    AliasProperty Created = AccessTimeDT                                                  
Cumulative                                 AliasProperty Cumulative = CumulativeTotal                                            
Equals                                     Method        bool Equals(System.Object obj)                                          
GetHashCode                                Method        int GetHashCode()                                                       
GetType                                    Method        type GetType()                                                          
ToString                                   Method        string ToString()                                                       
Validate                                   Method        void Validate()                                                         
AccessTime                                 Property      System.Object AccessTime {get;set;}                                     
AccessTimeDT                               Property      System.Nullable[datetime] AccessTimeDT {get;}                           
AccessTimeSpecified                        Property      bool AccessTimeSpecified {get;set;}                                     
AfsUsed                                    Property      System.Object AfsUsed {get;set;}                                        
AfsUsedSpecified                           Property      bool AfsUsedSpecified {get;set;}                                        
Busy                                       Property      System.Nullable[bool] Busy {get;set;}                                   
BusySpecified                              Property      bool BusySpecified {get;set;}                                           
Comment                                    Property      System.Object Comment {get;set;}                                        
CompressionType                            Property      System.Object CompressionType {get;set;}                                
CompressSavings                            Property      System.Object CompressSavings {get;set;}                                
CompressSavingsSpecified                   Property      bool CompressSavingsSpecified {get;set;}                                
ContainsLunClones                          Property      System.Nullable[bool] ContainsLunClones {get;set;}                      
ContainsLunClonesSpecified                 Property      bool ContainsLunClonesSpecified {get;set;}                              
CumulativePercentageOfTotalBlocks          Property      System.Object CumulativePercentageOfTotalBlocks {get;set;}              
CumulativePercentageOfTotalBlocksSpecified Property      bool CumulativePercentageOfTotalBlocksSpecified {get;set;}              
CumulativePercentageOfUsedBlocks           Property      System.Object CumulativePercentageOfUsedBlocks {get;set;}               
CumulativePercentageOfUsedBlocksSpecified  Property      bool CumulativePercentageOfUsedBlocksSpecified {get;set;}               
CumulativeTotal                            Property      System.Nullable[long] CumulativeTotal {get;}                            
CumulativeTotalBlocks                      Property      System.Object CumulativeTotalBlocks {get;set;}                          
CumulativeTotalSpecified                   Property      bool CumulativeTotalSpecified {get;set;}                                
DedupSavings                               Property      System.Object DedupSavings {get;set;}                                   
DedupSavingsSpecified                      Property      bool DedupSavingsSpecified {get;set;}                                   
Dependency                                 Property      System.Object Dependency {get;set;}                                     
ExpiryTime                                 Property      System.Object ExpiryTime {get;set;}                                     
ExpiryTimeDT                               Property      System.Nullable[datetime] ExpiryTimeDT {get;set;}                       
ExpiryTimeSpecified                        Property      bool ExpiryTimeSpecified {get;set;}                                     
InfiniteSnaplockExpiryTime                 Property      System.Nullable[bool] InfiniteSnaplockExpiryTime {get;set;}             
InfiniteSnaplockExpiryTimeSpecified        Property      bool InfiniteSnaplockExpiryTimeSpecified {get;set;}                     
InofileVersion                             Property      System.Object InofileVersion {get;set;}                                 
InofileVersionSpecified                    Property      bool InofileVersionSpecified {get;set;}                                 
Is7ModeSnapshot                            Property      System.Nullable[bool] Is7ModeSnapshot {get;set;}                        
Is7ModeSnapshotSpecified                   Property      bool Is7ModeSnapshotSpecified {get;set;}                                
IsConstituentSnapshot                      Property      System.Nullable[bool] IsConstituentSnapshot {get;set;}                  
IsConstituentSnapshotSpecified             Property      bool IsConstituentSnapshotSpecified {get;set;}                          
Name                                       Property      System.Object Name {get;set;}                                           
NcController                               Property      NetApp.Ontapi.Filer.C.NcController NcController {get;set;}              
PercentageOfTotalBlocks                    Property      System.Object PercentageOfTotalBlocks {get;set;}                        
PercentageOfTotalBlocksSpecified           Property      bool PercentageOfTotalBlocksSpecified {get;set;}                        
PercentageOfUsedBlocks                     Property      System.Object PercentageOfUsedBlocks {get;set;}                         
PercentageOfUsedBlocksSpecified            Property      bool PercentageOfUsedBlocksSpecified {get;set;}                         
SnaplockExpiryTime                         Property      System.Object SnaplockExpiryTime {get;set;}                             
SnaplockExpiryTimeDT                       Property      System.Nullable[datetime] SnaplockExpiryTimeDT {get;set;}               
SnaplockExpiryTimeSpecified                Property      bool SnaplockExpiryTimeSpecified {get;set;}                             
SnapmirrorLabel                            Property      System.Object SnapmirrorLabel {get;set;}                                
SnapshotInstanceUuid                       Property      System.Object SnapshotInstanceUuid {get;set;}                           
SnapshotOwnersList                         Property      DataONTAP.C.Types.Snapshot.SnapshotOwner[] SnapshotOwnersList {get;set;}
SnapshotVersionUuid                        Property      System.Object SnapshotVersionUuid {get;set;}                            
State                                      Property      System.Object State {get;set;}                                          
Total                                      Property      System.Nullable[long] Total {get;}                                      
TotalBlocks                                Property      System.Object TotalBlocks {get;set;}                                    
TotalSpecified                             Property      bool TotalSpecified {get;set;}                                          
Vbn0Savings                                Property      System.Object Vbn0Savings {get;set;}                                    
Vbn0SavingsSpecified                       Property      bool Vbn0SavingsSpecified {get;set;}                                    
Volume                                     Property      System.Object Volume {get;set;}                                         
VolumeProvenanceUuid                       Property      System.Object VolumeProvenanceUuid {get;set;}                           
Vserver                                    Property      System.Object Vserver {get;set;}                                        



PS C:\windows\system32> 

randys

So tested this out.  while the ZAPI call works, it is a bit slower than I would hope. BTW get-ncsnapshot in the Toolkit v9.8.0 is not capable of reporting the ExpiryTime property once it is set, so my script runs and attempts to set all the expirytime properties whether they are already set or not. 

 

Frome CLI:
…
TESTnetappcluster02::> snapshot show -vserver TESTFILE01 -volume test -fields vserver ,volume ,snapshot ,Expiry-time  -Snapshot daily.2021-06-25_0010
vserver  volume snapshot              expiry-time
-------- ------ --------------------- ------------------
TESTFILE01 test   daily.2021-06-25_0010 6/26/2021 00:00:00

TESTnetappcluster02::>
…
From Powershell Toolkit:
…

PS C:\windows\system32> get-ncsnapshot -vserver TESTFILE01 -volume test -Snapshot daily.2021-06-25_0010 | Select-Object -Property Vserver,Volume,Name,ExpiryTime,ExpiryTimeDT,AccessTime
Vserver      : TESTFILE01
Volume       : test
Name         : daily.2021-06-25_0010
ExpiryTime   : 
ExpiryTimeDT : 
AccessTime   : 1624605000
PS C:\windows\system32>
…

 

 

 

Announcements
NetApp on Discord Image

We're on Discord, are you?

Live Chat, Watch Parties, and More!

Explore Banner

Meet Explore, NetApp’s digital sales platform

Engage digitally throughout the sales process, from product discovery to configuration, and handle all your post-purchase needs.

NetApp Insights to Action
I2A Banner
Public