We have a collection of NetApp devices hosting a large number of volumes, and utilise SnapMirrors extensively for backup and DR, as well as moving information between departments
As the storage has grown, and different people have added new volumes, new SnapMirrors have been created, with a wide variety of naming formats to match the mood of the creator on that particular day. This makes managing volumes, when you are not exactly sure what they do, a little challenging.
A standard for the naming of these volumes was decided on, and all new SnapMirror destinations must be in the format of the original volume name with _SnapMirror appended to the end. This is similar to what the OnCommand System centre uses, just without the date.
Whilst this was all good for new volumes, the 200 existing volumes and mirrors would likely not be changed for a long time. So, I set about looking for a way to update all of our SnapMirror destinations. With there being a lot of volumes, scripting was the only way to be looking at this.
So, here is a module containing two functions. The first, Get-NonStandardSnapMirrorNames looks through all of the specified NetApp Filers for SnapMirrors do not fall into the format specified. The second function, Repair-NonStandardSnapMirrorNames, runs through a procedure which can correct them.
I have been good and even included PowerShell help inside the function, so once you have imported the module you can run Get-Help with the relevant function in order to find out the exact syntax.
If you use this and have some suggestions on how it could be improved then please let me know. I am relatively new to NetApp storage devices and still working out some of the peculiarities. There are probably a number of ways that this could be improved, but it worked well for me in the environment that I have,
You will need to have the DataOnTap PowerShell kit available from NetApp to use this script, as everything is performed using the PowerShell commands rather than using any SSH sessions.
So, here it is. Copy into your favourite editor, save, import to PowerShell and off you go.
# ---------------------------------------------------------------------------------------------------------- # PURPOSE: Check all SnapMirrors on Filers to ensure that they are named correctly. # # VERSION DATE USER DETAILS # 1 09/07/2014 Craig Tolley First version # 1.1 15/07/2014 Craig Tolley Moved Disables/Enables Snapmirrors into a batch to stop failed transfers # Made Common Credentials a parameter that then asks for details. # # ---------------------------------------------------------------------------------------------------------- <# .Synopsis Find all SnapMirrors where the destination volume name does not match the specified convention. Returns a collection of SnapMirrors. .EXAMPLE Get-NonStandardSnapMirrorNames -FilersToCheck "filer1", "filer2" Provide a list of the filers to check using the default suffix of _SnapMirror. e.g This would be considered a valid destination. Source: vol1 Destination: vol1_SnapMirror e.g This would be considered invalid. Source: vol1 Destination: vol1_SM .EXAMPLE Get-NonStandardSnapMirrorNames -FilersToCheck "filer1", "filer2" -SnapMirrorSuffix "_Snap" Provide a list of the filers to check. Also specify what the SnapMirror suffix should be. In this case it would check to see if the destination was the name of the source with _Snap appended to the end. .EXAMPLE Get-NonStandardSnapMirrorNames -FilersToCheck "filer1", "filer2" -CommonCredentials Provide a list of the filers to check and that all filers use the same credentials. You will only be prompted once for credentials #> function Get-NonStandardSnapMirrorNames { [CmdletBinding()] Param ( # A collection of the filers that we want to check. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] [string[]]$FilersToCheck, # The suffix that you would expect to have found in a properly named SnapMirror relationship. By default it expects _SnapMirror, this can be overridden. # e.g If the source volume is called 'MyVol', would mean that we expect the destination volume to be 'MyVol_SnapMirror' [string]$SnapMirrorSuffix = "_SnapMirror", #Credentials to be used for every filer that you are connecting to. If each filer requires different credentials then leave this option out. [Switch]$CommonCredentials ) Begin { #Check that that DataOnTap Module is available, check it is loaded. Write-Host "Checking for DataONTAP Module..." if ((Get-Module -ListAvailable | Where {$_.Name -eq "DataONTAP"}).Count -ne 1) { Write-Error "DataONTAP not installed on targeted machine" Exit 1 } if ((Get-Module | Where {$_.Name -eq "DataONTAP"}).Count -ne 1) { Write-Host "Importing DataONTAP Module..." Import-Module -Name "DataONTAP" | Out-Null } #Create a new collection of Snapmirrors $SnapmirrorsToFix = @() #Get credentials for all of the filers - to save constantly asking for them later if ($CommonCredentials) {$Credentials = Get-Credential} foreach($Filer in $FilersToCheck) { if ($CommonCredentials) {Add-NaCredential -Controller $Filer -Credential $Credentials | Out-Host} else {Add-NaCredential -Controller $Filer -Credential (Get-Credential -Message "Please supply credentials for $filer") | Out-Host} } } Process { Write-Host "Getting all SnapMirrors that do not match the specified convention" Write-Host "Set SnapMirror Suffix is: $SnapMirrorSuffix" Write-Host " " #Go through each of the filers in turn and look at all of the volumes and all of the SnapMirrors foreach($Filer in $FilersToCheck) { #Connect to the filer and get all of the volumes Write-Host " " Write-Host "Connecting to $Filer..." Connect-NaController $Filer | Out-Host $VolCount = 0 $Volumes = Get-NaVol foreach($Volume in $Volumes) { #Get the snapmirrors for the volume where it is the source of the mirror $SnapMirrors = Get-NaSnapmirror -Location $Volume | Where {$_.State -eq "source"} foreach($SnapMirror in $SnapMirrors) { [string]$DestinationFilerString = $SnapMirror.Destination.Split(":")[0] [string]$DestinationVolumeString = $SnapMirror.Destination.Split(":")[1] [string]$SourceVolumeName = -Join ("$($Volume.Name)","$SnapMirrorSuffix") If($DestinationVolumeString -ne $SourceVolumeName) { $SnapmirrorsToFix += $SnapMirror $VolCount += 1 } } } Write-Host " " Write-Host "$VolCount volumes found on $Filer that do not match the specified convention" } } End { Write-Output $SnapmirrorsToFix Write-Host " " Write-Host " -- Completed finding SnapMirrors that do not match the convention -- " } } <# .Synopsis Re-configures all of the provided Snapmirrors to match the conventions. Pass in a Snapmirror object. Updates the volume name, the SnapMirror schedule on the destination filer, the SnapMirror relationship on the source filer Forces a re-sync to ensure that everything is working. Requires an initial baseline to exist before the operation will complete. Also the SnapMirror must be Idle. .EXAMPLE Repair-NonStandardSnapMirrorNames -SnapMirrorsToFix $SnapMirror .EXAMPLE Repair-NonStandardSnapMirrorNames -SnapMirrorsToFix $SnapMirror -SnapMirrorSuffix "_Snap" .EXAMPLE Repair-NonStandardSnapMirrorNames -SnapMirrorsToFix $SnapMirror -CommonCredentials Supply one set of credentials for use with all the filers that will be connected to. If the filers require different credentials, then this parameter should be omitted. If omitted you will be prompted for credentials for each of the filers that are required to be connected to. #> function Repair-NonStandardSnapMirrorNames { [CmdletBinding()] Param ( # A collection of the filers that we want to check. [Parameter(Mandatory=$true, ValueFromPipeline=$false, Position=0)] [DataONTAP.Types.Snapmirror.SnapmirrorStatusInfo[]]$SnapMirrorsToFix, # The suffix that you would expect to have found in a properly named SnapMirror relationship. By default it expects _SnapMirror, this can be overridden. # e.g If the source volume is called 'MyVol', would mean that we expect the destination volume to be 'MyVol_SnapMirror' [string]$SnapMirrorSuffix = "_SnapMirror", #Credentials to be used for every filer that you are connecting to. If each filer requires different credentials then leave this option out. [Switch]$CommonCredentials ) Begin { #Check that that DataOnTap Module is available, check it is loaded. Write-Host "Checking for DataONTAP Module..." if ((Get-Module -ListAvailable | Where {$_.Name -eq "DataONTAP"}).Count -ne 1) { Write-Error "DataONTAP not installed on targeted machine" Exit 1 } if ((Get-Module | Where {$_.Name -eq "DataONTAP"}).Count -ne 1) { Write-Host "Importing DataONTAP Module..." Import-Module -Name "DataONTAP" | Out-Null } [DataONTAP.Types.Snapmirror.SnapmirrorStatusInfo[]]$Snapmirror #Connect to all of the necessary filers in advance. Controllers are then explicitly defined for each operation. $Filers = @() foreach ($Snapmirror in $SnapMirrorsToFix) { $Filers += $SnapMirror.Source.Split(":")[0] $Filers += $Snapmirror.Destination.Split(":")[0] } $Filers = $Filers | Select -Unique #Get credentials for all of the filers - to save constantly asking for them later if ($CommonCredentials) {$Credentials = Get-Credential} foreach($Filer in $Filers) { if ($CommonCredentials) {Add-NaCredential -Controller $Filer -Credential $Credentials | Out-Host} else {Add-NaCredential -Controller $Filer -Credential (Get-Credential -Message "Please supply credentials for $filer") | Out-Host} } } Process { #Disable SnapMirrors on all Destination Filers $Filers = @() foreach ($Snapmirror in $SnapMirrorsToFix) {$Filers += $Snapmirror.Destination.Split(":")[0]} $Filers = $Filers | Select -Unique Write-Host "Disabling SnapMirrors on the destination so that they are not updated during the rename" foreach($Filer in $Filers) { Write-Host $Filer Connect-NaController $Filer | Out-Null Disable-NASnapMirror | Out-Null } #Perform the actual repair operation. foreach($Snapmirror in $SnapmirrorsToFix) { Write-Host "Fixing SnapMirror destination name" Write-Host $Snapmirror #Set all of the names for the sources and the destinations. Easier than keeping calculating them on the fly. $SourceFiler = $Snapmirror.Source.Split(":")[0] $SourceVolName = $Snapmirror.Source.Split(":")[1] $DestinationFiler = $Snapmirror.Destination.Split(":")[0] $OriginalDestinationVolName = $Snapmirror.Destination.Split(":")[1] $NewDestinationVolName = -Join ($Snapmirror.Source.Split(":")[1],"$SnapMirrorSuffix") #Connect to the Source Write-Host "Connecting to the source filer" Connect-NaController $SourceFiler $Snapmirror = Get-NaSnapmirror $Snapmirror.Destination.Split(":")[1] #Check that the source volume is attached to vfiler0. If not, editing becomes a lot harder and not implemented here! :-) If ((Get-NAVol $SourceVolName).OwningVfiler -ne "vfiler0") { Write-Error "The source volume is not owned by the default vFiler. Cannot edit SnapMirrors that are owned by non-default vFilers. Stopping change for this SnapMirror" Break } #Check that the snapmirror is not actually running. If it is, then break. If ($Snapmirror.Status -ne "idle") { Write-Error "The snapmirror is not idle. Cancelling operation" Break } #We must have at least one base snapshot, else the release and the update will fail as no common baseline exists. If (($Snapmirror.BaseSnapshot).ToString -eq "") { Write-Error "The snapmirror relationship does not have a common base snapshot. Operation would break the current relationship" Break } #Check whether the destination is already in the correct format. If ($NewDestinationVolName -eq $OriginalDestinationVolName) { Write-Host "Snapmirror Destination is already in the correct format. Not going to change anything" Break } #Release the Snapmirror - which removes it from the Source Filer Write-Host "Releasing the SnapMirror source connection" Connect-NaController $SourceFiler | Out-Null Get-NASnapMirror $OriginalDestinationVolName | Invoke-NaSnapmirrorRelease -Confirm:$false | Out-Null Try { Connect-NaController $DestinationFiler | Out-Null #Rename the destination Vol Write-Host "Renaming the destination volume" Get-NaVol -Name $OriginalDestinationVolName | Rename-NaVol -NewName $NewDestinationVolName | Out-Null #Update the Destination of the Snapmirror on the Destination Filer Write-Host "Updating the SnapMirror Schedule with the new destination volume name" $SMSched = Get-NaSnapmirrorSchedule $OriginalDestinationVolName $SMSched.Destination = "$DestinationFiler`:$NewDestinationVolName" $SMSched | Set-NaSnapmirrorSchedule #Remove the Old SnapMirror Schedule Write-Host "Removing the SnapMirror schedule for the old destination" Get-NaSnapmirrorSchedule $OriginalDestinationVolName | Remove-NaSnapmirrorSchedule | Out-Null } Catch { Write-Error "Failed to update the volume" Break } } #Re-enable SnapMirrors on all Destination Filers Write-Host "Re-enabling SnapMirrors on the destination..." foreach($Filer in $Filers) { Write-Host $Filer Connect-NaController $Filer | Out-Null Enable-NASnapMirror | Out-Null } #Update all Snapmirrors so that they re-register on the Source Filer foreach($Snapmirror in $SnapmirrorsToFix) { Write-Host "Registering the SnapMirror on the Source filer and reconnecting the SnapMirror" $NewDestinationVolName = -Join ($Snapmirror.Source.Split(":")[1],"$SnapMirrorSuffix") $DestinationFiler = $Snapmirror.Destination.Split(":")[0] Connect-NaController $DestinationFiler | Out-Null Do {Write-Host "."} Until ((Get-NASnapMirror $NewDestinationVolName).Count) Get-NASnapMirror $NewDestinationVolName | Invoke-NaSnapmirrorBreak -Confirm:$false | Out-Null Get-NASnapMirror $NewDestinationVolName | Invoke-NaSnapmirrorResync -Confirm:$false | Out-Host } #Update any Snapmirrors that might be in a failed State. The Disable above does not check for any that were already running. Write-Host "Resuming any SnapMirrors that might have failed due to SnapMirror being Disabled. " foreach($Filer in $Filers) { Connect-NaController $Filer | Out-Null Get-NaSnapmirror | Where {$_.CurrentTransferError -ne $null -and $_.Status -eq "idle"} | Invoke-NaSnapmirrorUpdate } } End { Write-Host " -- Completed repairing SnapMirrors that do not match the convention -- " } }