# Hey emacs, this is basically -*- perl -*- # Copyright (c) 2001-2017 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary VolumeSnapshot Task Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::STask::VolumeSnapshot; use v5.14; use strict; use warnings; use base qw(NACL::C::VolumeSnapshot NACL::STask::STask); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Params::Validate qw(validate validate_with :types); use NATE::BaseException qw(:try); use NACL::APISet::Exceptions::NoMatchingEntriesException; use NACL::Exceptions::Timeout; use NATE::Time qw(timeout2time); use NACL::GeneralUtils qw( nacl_method_retry); use NACL::C::VolumeClone; use NACL::STask::Snapmirror; use NACL::C::VolumeSnapshotPolicy; use NACL::C::JobScheduleCron; use NACL::C::VolumeSnapshotPolicy; use constant DEFAULT_TIMEOUT => 300; use constant POLL_DELTA => 10; use NACL::ComponentUtils qw(_optional_scalars Dumper); use NACL::Exceptions::VerifyFailure (); use NACL::C::Exceptions::VolumeSnapshotPolicy::DoesNotExist; use NACL::C::Exceptions::VolumeSnapshot::SnapshotHasOwner; use NACL::C::Exceptions::VolumeSnapshot::RetryCmd; use NACL::C::Exceptions::VolumeSnapshot::CurrentlyBusy; use NACL::C::Exceptions::VolumeSnapshot::DisableSnapshotPolicies; use Class::MethodMaker [ scalar => [ { -type => 'NACL::C::Job' }, 'job_component', ], ]; =head1 NAME NACL::STask::VolumeSnapshot =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to Volume Snapshots in ONTAP. It builds on top of, and is a derived class of C, and so it also provides methods that are more in the scope of individual volume Snapshot-related commands. See C for details. This also means that a C object may generally be used in place of a component object. =head1 CLEANUP METHODS Cleanup can be registered for the following methods, 'create', 'modify', 'rename' Cleanup methods are, Volume Method Cleanup Method -------------------------------------- create purge modify modify rename rename =head1 ATTRIBUTES =head2 command_interface (Required) As in NACL::C::VolumeSnapshot A component object that represents the host to which to send commands. =head2 snapshot (Required) As in NACL::C::VolumeSnapshot The name of the volume snapshot. =head2 volume (Required) As in NACL::C::VolumeSnapshot The name of the volume . =head2 vserver (Required) As in NACL::C::VolumeSnapshot The name of the vserver containing volume . =head2 job_component (Optional) A job component (NACL::C::Job) representing the last job that operated on this snapshot. =head1 METHODS =head2 create my $snapshot = NACL::STask::VolumeSnapshot->create( command_interface => $command_interface, volume => $volume, vserver => $vserver, snapshot => $snapshot, nacltask_if_exists => $action, # default 'die' nacltask_wait => $boolean, # default '1' nacltask_to_cleanup => 1, # default 0, nacltask_cleanup_manager => $CleanupObj, %other_options ); (Class method) Create a snapshot of the given volume. This method provides additional services beyond what's inherent to the product's snapshot creation commands, as controlled and described by the new C and C options. Note that the arguments C optional for this method. Refer to the section "Arguments defaulted/auto-determined" below for a description of the value auto-assigned to this argument if they are not supplied by the caller. With these default, it is now possible to create a snapshot without passing a snapshot name. =over =item Options =over =item C<< nacltask_if_exists=>$action >> (Optional) What to do if the snapshot to be created already exists. If $action is "die" (the default), then fail with an exception (in the same way that the snapshot component would have: by trying the snapshot create and letting the product complain about the snapshot already existing). If action is "reuse", then do nothing (return a task object referring to the existing snapshot). If action is "purge", then delete the snapshot (see the C method, below) before creating a new one. =item C<< nacltask_wait => 0|1 >> (Optional) If 1 (the default), wait for this snapshot to be created (see "wait_for_creation" method, below). If 0, do not wait any longer than necessary (any longer than the snapshot creation command waits). =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) Verify that the created volume snapshot has all of the attributes set to the values specified in the call. Note that if set to 1, this will automatically set nacltask_wait to 1. Note that it is also possible to perform the verification later by calling L. This can be used for cases where ONTAP chooses to not set all attributes to the exact values specified. In the call we can specify those attributes we want verified. =item C<< nacltask_rng_args => { args_for_rng_here } >> (Optional, used only if a random volume name is generated) If no snapshot name is supplied, a random name is generated using L with two random words in it and with the suffix being "_snapshot". This argument can be used to customize the arguments to that method. For example, if we'd like to specify that the random generated snapshot name should be 255 characters length, then this could be: NACL::STask::Snapshot->create( %other_opts, nacltask_rng_args => { length => '255' } ); See L for a description of the arguments it accepts. =item C<< "method-timeout" => $ti >> (Optional) As component method-timeout, controls how long the command will wait before completing. =item C<< polling_interval => $interval >> (Optional) If nacltask_wait is set to 1, this is the interval at which to show the progress of the job (if we're watching the progress of the job) or the interval at which to poll the filer for the value of the "state" field. =item C<< _was_created => \$scalar >> (Optional) When this option is provided a reference to a scalar variable, the variable gets filled in with a boolean value describing whether the snapshot was found (value will be 0; this scenario is possible when nacltask_if_exists => "reuse") or whether the snapshot was created (value will be 1). This is necessary to determine whether the snapshot needs to be cleaned up later. my $was_created; my $snapshot_obj = NACL::STask::Snapshot->create( nacltask_if_exists => 'reuse', _was_created => \$was_created, %other_opts ); # Operate on $snapshot_obj here # ... # Now determine whether to clean up the snapshot, since we're not sure # whether we reused an existing snapshot or created a new one if ($was_created) { # New snapshot was created. Clean it up. $snapshot_obj->purge(); } =item C<< command_interface=>$command_interface >> (Required ) See L =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =item C<< volume=>$vol_name >> (Required ) Name of volume. =item C<< vserver=>$vserver_name >> (Required for C-mode for class method, ignored for 7-mode) Name of vserver containing volume =item C<< snapshot=>$snapshot_name >> (Required ) Name of snapshot to be created. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, dafaults to 0(no to cleanup) Flag indicating if this operation needs to be registered for clean up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to be used for registering Default : will use the default cleanup manager. All of the other various options to L<< NACL::C::VolumeSnapshot->create|lib-NACL-C-VolumeSnapshot-pm/create >> =item C<< do_not_retry_on_volume_busy => 0|1 >> (Optional, defaults to 0(retry on volume busy) Flag indicating if this create operation needs to be retried on volume busy error. =back =back =over =item Arguments defaulted/auto-determined =over =item C Note that if this is not specified, a random name is generated using L. (See C for how to customize arguments to random_name_generator()) =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to create a snapshot that already exists. =item C This type of exception is thrown when verification fails for the created volume snapshot. =back =cut sub create { $Log->enter() if $may_enter; my ( $pkg, @args ) = @_; my %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { do_not_retry_on_volume_busy => { type => BOOLEAN, default => 0 }, nacltask_if_exists => $pkg->_if_exists_validate_spec(), nacltask_wait => { type => SCALAR, default => 1 }, nacltask_verify => { type => BOOLEAN, default => 0 }, _was_created => { type => SCALARREF, optional => 1 }, nacltask_rng_args => $pkg->_rng_args_validate_spec(), nacltask_retry_tries_count => { type => SCALAR, default => 5 }, nacltask_retry_sleep_time => { type => SCALAR, default => 30 }, nacltask_retry_num_times_code_ran => { type => SCALARREF, optional => 1 }, _optional_scalars(qw(snapshot)), }, allow_extra => 1, ); my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); my %nacltask_options; $pkg->_move_nacltask_options( source => \%opts, target => \%nacltask_options, ); my $volume_busy_retry_count = $nacltask_options{nacltask_retry_tries_count}; my $if_exists = $nacltask_options{nacltask_if_exists}; my $wait = $nacltask_options{nacltask_wait}; my $verify = $nacltask_options{nacltask_verify}; my $rng_args = delete $nacltask_options{nacltask_rng_args}; # Override wait to 1 if verification was requested for $wait = 1 if $verify; my $command_interface = $opts{command_interface}; my $polling_interval = delete $opts{polling_interval}; my $was_created = delete $opts{_was_created}; my $do_not_retry_on_volume_busy = delete $opts{do_not_retry_on_volume_busy}; $$was_created = 0; my $random_name_generated; if ( !defined $opts{snapshot} ) { $opts{snapshot} = $pkg->random_name_generator(%$rng_args); $random_name_generated = 1; } my $self; # We want to know the job-ID even if the user doesn't { my $job_ref; $opts{job_component} ||= \$job_ref; my $is_infinivol_ref; $opts{is_infinivol_ref} ||= \$is_infinivol_ref; } my %retry_opts; $pkg->_copy_retry_params( source => \%nacltask_options, target => \%retry_opts ); CREATE: { use warnings; try { nacl_method_retry( code => sub { $self = $pkg->SUPER::create(%opts); }, exceptions => [ qw(NACL::C::Exceptions::VolumeSnapshot::ClonesBackedBySnapshots) ], %retry_opts, ); my $is_infinivol = $self->_read_infinivol_val(%opts); if ($is_infinivol) { $wait = 1; } else { $$was_created = 1; } $self->job_component(${$opts{job_component}}); if ($wait) { my %common_options = (); $self->_copy_common_component_params( source => \%opts, target => \%common_options ); $common_options{polling_interval} = $polling_interval; $self->wait_for_creation(%common_options); $$was_created = 1; } if ($verify) { delete $opts{job_component}; delete $opts{foreground}; delete $opts{is_infinivol_ref}; $self->taskverify_create(%opts); } } catch NACL::C::Exceptions::VolumeSnapshot::AlreadyExists with { my $exception = shift; $Log->debug("Snapshot already Exists!"); my $is_infinivol = $pkg->_read_infinivol_val(%opts); $opts{is_infinivol} = $is_infinivol if (defined $is_infinivol); if ($random_name_generated) { $Log->trace( 'Random name collision, regenerate a ' . 'new name and retry' ); $opts{snapshot} = $pkg->random_name_generator(%$rng_args); no warnings qw(exiting); redo CREATE; } else { my %purge_opts; if (defined $is_infinivol) { $purge_opts{is_infinivol} = $is_infinivol; } $self = $pkg->_element_exists_handler( create_opts => \%opts, nacltask_if_exists => $if_exists, exception => $exception, purge_opts => \%purge_opts ); if ($self) { $self->_update_infinivol_val_in_self(%opts); } else { no warnings qw(exiting); redo CREATE; } } } catch NACL::C::Exceptions::VolumeSnapshot::CurrentlyBusy with { my $exception = shift; $Log->debug("Volume is currently busy!"); if (!$do_not_retry_on_volume_busy && $volume_busy_retry_count-- > 0) { no warnings qw(exiting); redo CREATE; } $exception->throw(); }; } #end CREATE # Regigster this resource with the cleanup manager for cleanup. $pkg->_copy_common_opts_for_cleanup( 'source' => {%opts, %opts_for_cleanup}, 'target' => \%opts_for_register, 'nacltask_to_cleanup'=> \$nacltask_to_cleanup, 'to_cleanup' => 'purge', ); $pkg->_register_for_cleanup(%opts_for_register) if ($nacltask_to_cleanup && $$was_created); $Log->exit() if $may_exit; return $self; } ## end sub create =head2 modify NACL::STask::VolumeSnapshot->modify( command_interface => $command_interface, 'volume' => $volume, 'vserver' => $vserver, 'snapshot' => $snapshot, 'cpcount-sort' => $cpcount_sort, 'nacltask_verify' => 1, #default '0' 'nacltask_to_cleanup' => 1, #default '0' 'nacltask_cleanup_manager => $CleanupObj, %other_options ); or NACL::STask::VolumeSnapshot->modify(%opts); (Class or Instace method) modify the snapshot attributs. This method will modify the snapshot attributes of a given volume. And also it provides a verification for all the attributes got set to the specified values. The modify method is also provides an cleanup for the objects get created by the modify method. Modification of snapshot attributes get controlled by the new C option. =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) verifies that the requested attributes are changed to specified value. Note that it is also possible to perform the verification later by calling L. This can used for cases where ONTAP chooses to not set all attributes to the exact values specified. In the call we can specify those attributes we want to verify. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(not to cleanup)) Flag indicating if this operation needs to be registered for clean up or not. =item C<< nacltask_cleaup_manager >> Cleanup manager to use for registering Default : will use the default cleanup manager. See Lmodify|lib-NACL-C-VolumeSnapshot-pm/modify> for all the other options accepted by this method. =back =cut sub modify { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => {$pkg_or_obj->_cleanup_validate_spec(),}, allow_extra => 1, ); my (%opts_for_cleanup, $opts_for_register, $register_for_cleanup); $pkg_or_obj->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, nacltask_to_cleanup => \$register_for_cleanup, ); if ($register_for_cleanup) { $opts_for_register = $pkg_or_obj->_common_modify_for_cleanup(%opts_for_cleanup, %opts); } $pkg_or_obj->_call_method_then_verify(%opts); #Register for cleanup if $nacltask_to_cleanup is set $pkg_or_obj->_register_for_cleanup(%{$opts_for_register}) if ($register_for_cleanup); $Log->exit() if $may_exit; } # end sub modify. =head2 rename NACL::STask::VolumeSnapshot->rename( command_interface => $command_interface, volume => $volume, vserver => $vserver, snapshot => $snapshot, nacltask_verify => 1, #default '0' nacltask_to_cleanup =>1, # default '0' polling_interval => $polling_interval, nacltask_cleanup_manager => $CleanupObj, %other_options ); or $VolumeSnapshot_obj->rename(%opts); (Class or Instance method) This method performs renaming of the specified volume snapshot.And also, provide an cleanup for the objects get created by this method. Renaming snapshot attributes are get controlled by the new C option. =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) verifies that the requested attributes are changed to specified value. Note that it is also possible to perform the verification later by calling L. This can used for cases where ONTAP chooses to not set all attributes to the exact values specified. In the call we can specify those attributes we want to verify. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(not to cleanup)) Flag indicating if this operation needs to be registered for clean up or not. =item C<< nacltask_cleaup_manager >> Cleanup manager to use for registering Default : will use the default cleanup manager. =item C<< polling_interval => $interval >> (Optional) This is the interval (in seconds) after which the value of the attributes will be polled. If not specified it defaults to 20 seconds. =item C<< nacltask_retry_tries_count => $tries_count >> (Optional, defaults to 5) The number of times to retry the resync command if it fails because the snapshot is temporarily busy or stale catch entry. Note that it is possible to make it not retry the rename command by setting the value of this argument to 1. =item C<< "method-timeout" => $ti >> (Optional) As component method-timeout, controls how long the command will wait before completing. However, the default timeout has been raised to 100 seconds. =item C<< nacltask_retry_sleep_time => $sleep_time >> (Optional, defaults to 10 seconds) The time to sleep between retries of the resync command. See Lrename|lib-NACL-C-VolumeSnapshot-pm/rename> for all the other options accepted by this method. =back =cut sub rename { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { polling_interval => { type => SCALAR, default => 20 }, "method-timeout" => { type => SCALAR, default => 100 }, $pkg_or_obj->_cleanup_validate_spec(), 'new-name' => { type => SCALAR }, nacltask_verify => { type => SCALAR, default => 0 }, nacltask_retry_tries_count => { type => SCALAR, default => 5 }, nacltask_retry_sleep_time => { type => SCALAR, default => 10 }, }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; my $newname = $opts{'new-name'}; my $nacltask_verify = delete $opts{nacltask_verify}; my %common_opts; my $timeout = $opts{"method-timeout"}; my $polling_interval = delete $opts{polling_interval}; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts, ); my ( %nacltask_options, $nacltask_to_cleanup, %opts_for_cleanup ); $pkg_or_obj->_copy_common_opts_for_cleanup( 'source' => \%opts, 'target' => \%opts_for_cleanup, 'nacltask_to_cleanup' => \$nacltask_to_cleanup, 'to_cleanup' => 'rename', ); $pkg_or_obj->_move_nacltask_options( source => \%opts, target => \%nacltask_options ); my %retry_opts; $pkg_or_obj->_copy_retry_params( source => \%opts, target => \%retry_opts ); nacl_method_retry( code => sub { $pkg_or_obj->SUPER::rename(%opts); }, exceptions => [ qw(NACL::C::Exceptions::VolumeSnapshot::CurrentlyBusy) ], %retry_opts, ); #verifying the new-name my $end_time = time() + $timeout; FIND: { use warnings; try { my $vs_obj = $pkg_or_obj->find( command_interface => $command_interface, filter => { volume => $opts{volume}, vserver => $opts{vserver}, snapshot => $newname, }, ); } catch NACL::Exceptions::NoElementsFound with { if ( time() > $end_time ) { $Log->exit() if $may_exit; NACL::APISet::Exceptions::TimeoutException->throw( "The rename has not happened within the TIMEOUT value = $end_time" ); } else { no warnings; Tharn::snooze($polling_interval); goto FIND; } } } if ($nacltask_to_cleanup) { $opts_for_cleanup{'new_opts'} = { 'new-name' => $opts{'new-name'} }; $opts_for_cleanup{'orig_opts'} = { 'new-name' => $opts{'snapshot'} }; $opts_for_cleanup{'snapshot'} = $opts{'new-name'}; $pkg_or_obj->_register_for_cleanup(%opts_for_cleanup); } $Log->exit() if $may_exit; } =head2 purge NACL::STask::VolumeSnapshot->purge( command_interface => $command_interface, snapshot => $snapshot_name, volume => $volume_name, vserver => $vserver_name, nacltask_wait => 1, #default 1 nacltask_if_purged => $action, #default "die" %other_options, ); (or) $snapshotshot_obj->purge(); (Class or instance method) This method deletes a snapshot on the given volume and vserver. Note that it is possible that the delete command can fail if the snapshot is temporarily in use. For this reason, there is a provision to retry the delete command. See the various C options listed below. =over =item Options =over =item C<< nacltask_wait=>0|1 >> (Optional) If 1 (the default), wait for this snapshot to be deleted (calls wait_for_deletion method). If 0, do not wait any longer than necessary (any longer than the Snapshot deletion command waits). =item C<< nacltask_if_purged=>die|pass >> (Optional) If "die", exception is thrown when snapshot to be deleted does not exist If "pass", exits quietly when snapshot to be deleted does not exist =item C<< polling_interval => $interval >> (Optional) If nacltask_wait is set to 1, this is the interval at which to show the progress of the job (if we're watching the progress of the job) or the interval at which to poll the filer for the value of the "state" field. =item C<< command_interface=>$command_interface >> (Required for class method, Not Applicable for instance method) See L =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =item C<< "method-timeout" => 300 >> (Optional) As component method-timeout, controls how long the command will wait before completing. However, the default timeout has been raised to 300 seconds. =item C<< volume=>$vol_name >> (Required for class method, Not Applicable for instance method) Name of volume. =item C<< vserver=>$vserver_name >> (Required for C-mode for class method, ignored for 7-mode, Not Applicable for instance method) Name of vserver containing volume =item C<< snapshot=>$sshot_name >> (Required for class method, Not Applicable for instance method) Name of snapshot to be deleted. =item C<< nacltask_retry_tries_count => $tries_count >> (Optional, defaults to 3) The number of times to retry the delete command if it fails because the snapshot is temporarily busy. Note that it is possible to make it not retry the delete command by setting the value of this argument to 1. =item C<< nacltask_retry_sleep_time => $sleep_time >> (Optional, defaults to 10 seconds) The time to sleep between retries of the delete command. =item C<< nacltask_retry_num_times_code_ran => \$variable >> (Optional) A reference to a scalar variable which gets filled in with the number of times the delete command was invoked. All of the other various options to L<< NACL::C::Snapshot->delete|lib-NACL-C-Snapshot-pm/delete >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete a volume snapshot that does not exists. =back =cut sub purge { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { nacltask_wait => { type => BOOLEAN, default => 1 }, nacltask_if_purged => { type => SCALAR, default => 'die', callbacks => { "Value of 'nacltask_if_purged' should be 'die' or 'pass'" => sub { $_[0] =~ /^(?:die|pass)$/ } } }, nacltask_retry_tries_count => { type => SCALAR, default => 3 }, nacltask_retry_sleep_time => { type => SCALAR, default => 10 }, nacltask_retry_num_times_code_ran => { type => SCALARREF, optional => 1 }, }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; my %nacltask_options; $pkg_or_obj->_move_nacltask_options( source => \%opts, target => \%nacltask_options ); my %retry_opts; $pkg_or_obj->_copy_retry_params( source => \%nacltask_options, target => \%retry_opts ); my $wait = $nacltask_options{nacltask_wait}; my $if_purged = $nacltask_options{nacltask_if_purged}; # Verify if the user has passed a scalar reference as an argument for # storing the job component that gets populated by the # NACL::C::VolumeSnapshot->delete() through the C_SUPER call made below. # If not create a scalar reference for $job_ref and add it to the %opts # parameter hash with key 'job_component', which is being passed # to the delete() method { my $job_ref; $opts{job_component} ||= \$job_ref; my $is_infinivol_ref; $opts{is_infinivol_ref} ||= \$is_infinivol_ref; } try { nacl_method_retry( code => sub { $pkg_or_obj->delete(%opts) }, exceptions => 'NACL::C::Exceptions::VolumeSnapshot::InUse', %retry_opts ); my $job = ${$opts{job_component}}; $pkg_or_obj->job_component($job) if (defined ($job) && ref $pkg_or_obj); my $is_infinivol = ${$opts{is_infinivol_ref}}; if ($wait || $is_infinivol) { my %wait_opts; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%wait_opts ); unless (ref $pkg_or_obj) { $pkg_or_obj->_copy_primary_keys( source => \%opts, target => \%wait_opts, ); } $wait_opts{job_component} = $job if (defined $job); $pkg_or_obj->wait_for_deletion(%wait_opts); } } catch NACL::C::Exceptions::VolumeSnapshot::DoesNotExist with { my $exception = shift; $Log->debug("Entry Does Not Exist!"); if ( $if_purged !~ /pass/i ) { $Log->exit() if $may_exit; $exception->throw(); } }; $Log->exit() if $may_exit; } ## end sub purge =head2 wait_for_deletion $snapshot_obj->wait_for_deletion() or NACL::STask::VolumeSnapshot->wait_for_deletion( "method-timeout" => $timeout, polling_interval => $poll_intr, command_interface => $command_interface, job => $job_component, snapshot => $snapshot_name, volume => $volume_name, vserver => $vserver_name, %other_options ); Wait for the snapshot deletion to complete. If a job for deleting this snapshot is known (because the mode is C-mode and NACL::STask::VolumeSnapshot->purge was used to delete this object, or because it was passed in using the "job" option) then this waits for the job. =over =item Options =over =item C<< "command_interface=>$command_interface" >> (Required for class method, Not Applicable for instance method) See L =item C<< "method-timeout=>$timeout" >> (Optional) How long in seconds to wait for the job to finish Defaults to 300 (5 minutes). =item C<< "polling_interval => $polling_value" >> (Optional) This is the interval (in seconds) after which the value of the attributes will be polled. If not specified it defaults to 10 seconds. =item B<< job=>$job_component >> Optional for class method, Not Applicable for instance method) A NACL::C::Job object representing the C-mode Job used to delete this Snapshot. =item C<< volume=>$vol_name >> (Required ) Name of volume. =item C<< vserver=>$vserver_name >> (Required for C-mode for class method, ignored for 7-mode) Name of vserver containing volume =item C<< snapshot=>$snapshot_name >> (Required ) Name of snapshot to be deleted. =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =back =back =cut sub wait_for_deletion { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; $Log->debug(sub { "Parameters to the wait_for_deletion method:\n " . NACL::ComponentUtils::Dumper({@args}) }); my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { job => { isa => 'NACL::C::Job', optional => 1 }, job_component => { isa => 'NACL::C::Job', optional => 1 }, polling_interval => { type => SCALAR, default => POLL_DELTA }, }, allow_extra => 1, ); $opts{job_component} ||= $opts{job} || $pkg_or_obj->job_component(); delete $opts{job}; $opts{'method-timeout'} ||= DEFAULT_TIMEOUT; try { $pkg_or_obj->SUPER::wait_for_deletion(%opts); } catch NACL::Exceptions::JobFailed with { $pkg_or_obj->_delete_exception_handler(@_); }; $Log->exit() if $may_exit; } ## end sub wait_for_deletion =head2 wait_for_creation $snapshot->wait_for_creation(); (or) NACL::STask::VolumeSnapshot->wait_for_creation( command_interface => $command_interface, 'method-timeout' => $timeout, job => $job_component, polling_interval => $interval , snapshot => $snapshot_name, volume => $volume_name, vserver => $vserver_name, %other_options ); Wait for the snapshot creation to complete. This will wait for the job (if there is a job, such as may occur in C-mode) . =over =item Options =over =item C<< "command_interface=>$command_interface" >> (Required for class method, Not Applicable for instance method) See L =item C<< "method-timeout=>$timeout" >> (Optional) How long in seconds to wait for the job to finish Defaults to 300 (5 minutes). =item C<< "polling_interval => $polling_value" >> (Optional) This is the interval (in seconds) after which the value of the attributes will be polled. If not specified it defaults to 10 seconds. =item B<< job=>$job_component >> Optional for class method, Not Applicable for instance method) A NACL::C::Job object representing the C-mode Job used to create this Snapshot. =item C<< volume=>$vol_name >> (Required ) Name of volume. =item C<< vserver=>$vserver_name >> (Required for C-mode for class method, ignored for 7-mode) Name of vserver containing volume =item C<< snapshot=>$snapshot_name >> (Required ) Name of snapshot to be created. =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =back =back =cut sub wait_for_creation { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { polling_interval => { type => SCALAR | UNDEF, optional => 1 }, job => { isa => 'NACL::C::Job', optional => 1 }, job_component => { isa => 'NACL::C::Job', optional => 1 } }, ); $Log->debug(sub { 'Opts to ' . "NACL::STask::VolumeSnapshot::wait_for_creation are:\n" . Dumper (\%opts) } ); $opts{polling_interval} ||= POLL_DELTA; $opts{'method-timeout'} ||= DEFAULT_TIMEOUT; $opts{job_component} ||= $opts{job} || $pkg_or_obj->job_component(); delete $opts{job}; if ($opts{job_component}) { try { $pkg_or_obj->wait_on_job(%opts); } catch NACL::Exceptions::JobFailed with { $pkg_or_obj->_create_exception_handler(@_); }; } $Log->exit() if $may_exit; } ## end sub wait_for_creation =head2 schedule NACL::STask::VolumeSnapshot->schedule( command_interface => $command_interface, snapshot => $snapshot_name, volume => $volume_name, %schedule_opts, ); (or) $snapshot_obj->schedule(%schedule_opts); where %schedule_opts contains "weeks","days","hours" and the "list" (Class or instance method) For 7Mode cli, this method sets the schedule for automatic snapshot creation on the given volume. For Cmode cli, this method creates the job schedule using "job schedule crone create" and then the created schedule needs to be added to the policy for automatic snapshot creation on the given volume using "volume snapshot policy add-schedule". If the Snapshot Policy doesn't exists then it will throw the exception thrwon by API layer. In both the cases, on invalid schedule it will throw the exception thrown from APIset. Supports CMode CLI/ZAPI and 7Mode CLI =over =item Options =over =item C<< "command_interface=>$command_interface" >> (Required for class method, Not Applicable for instance method) See L =item C<< vserver=>$vserver_name >> (Required for C-mode for class method, ignored for 7-mode) Name of vserver containing volume =item C<< snapshot=>$snapshot_name >> (Required ) Name of snapshot to be created. =item C<< policy=>$policy_name >> (Required for CMode and Ignored for 7Mode) Name of policy to which the schedule needs to be added. =item C<< count=>$count >> (Required for CMode and Ignored for 7Mode) Number of snapshots needs to be created in the specified schedule. =item C<< job_schedule_cron=>\$job_schedule_cron >> (Optional for CMode and Ignored for 7Mode) A Scalar refernce to the job schedule cron which needs to be added to the snapshot policy. This option will get populated by creating the schedule using NACL::C::JobScheduleCron->create(). =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =back =back =cut sub schedule { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my $pkg = $pkg_or_obj->get_package_name(); my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { hour => { type => SCALAR, optional => 1 }, minute => { type => SCALAR, optional => 1 }, day => { type => SCALAR, optional => 1 }, count => { type => SCALAR, optional => 1 }, policy => { type => SCALAR, optional => 1 }, job_schedule_cron => { type => SCALARREF, optional => 1 } }, allow_extra => 1, ); my %common_opts; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $command_interface = $opts{command_interface}; my $schedule_name = $pkg->random_name_generator( prefix => 'sched' ); my %job_sched_opts; if ( $command_interface->is_cmode() ) { $job_sched_opts{hour} = [ $opts{hours} ] if ( $opts{hours} ); $job_sched_opts{minute} = [ $opts{minutes} ] if ( $opts{minutes} ); $job_sched_opts{day} = [ $opts{day} ] if ( $opts{day} ); #Creating a Job Schedule my $job_obj = NACL::C::JobScheduleCron->create( name => $schedule_name, %job_sched_opts, %common_opts ); ${ $opts{job_schedule_cron} } = $job_obj if ( $opts{job_schedule_cron} ); # Adding the Created Schedule to the Policy NACL::C::VolumeSnapshotPolicy->add_schedule( %common_opts, policy => $opts{policy}, schedule => $job_obj->name(), count => $opts{count}, vserver => $opts{vserver} ); } else { my $sched_opts = $pkg->_hash_copy( source => \%opts, copy => [qw(weeks days hours minutes volume aggregate hour-list)], ); $pkg->sched( %{$sched_opts}, %common_opts ); } $Log->exit() if $may_exit; } ## end sub schedule =head2 restore # Class method NACL::STask::VolumeSnapshot->restore( command_interface => $ci, volume => $vol_name, vserver => $vs_name, # Required for CMode snapshot => $snap_name, nacltask_wait => 0 | 1 # Defaults to 1 ); or # Instance method $snapshot_obj->restore(nacltask_wait => 0 | 1,nacltask_retry => 3); (Class or instance method) This method is used to restore the volume to a snapshot. In addition to the facilities provided by the component, this method waits till completion of the operation (configurable by the C argument) and installs the SnapRestore license if it's not already present. =over =item Options =over =item C<< nacltask_wait => 0 | 1 >> (Optional, defaults to 1) If set to 1 (the default), wait till the restore operation is complete. =item C<< nacltask_retry => 3 >> (Optional, defaults to 3) Retry restore operation as number of times as it set in parameter nacltask_retry (default is 3) =item Other options All the other options accepted by L<< NACL::C::VolumeSnapshot->restore|lib-NACL-C-VolumeSnapshot-pm/restore >> are also accepted by this method. =back =back =cut sub restore { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { nacltask_wait => { type => SCALAR, default => 1 }, nacltask_retry => { type => SCALAR, default => 3 }, }, allow_extra => 1 ); my $wait = delete $opts{nacltask_wait}; my $retry = delete $opts{nacltask_retry}; { my $job_ref; $opts{job_component} ||= \$job_ref; my $is_infinivol_ref; $opts{is_infinivol_ref} ||= \$is_infinivol_ref; } my %common_opts; $pkg_or_obj->_copy_common_component_params( source => \%opts, target => \%common_opts ); RESTORE: { use warnings qw(exiting); try { $pkg_or_obj->SUPER::restore(%opts); my $job = ${$opts{job_component}}; $pkg_or_obj->job_component($job) if (defined ($job) && ref $pkg_or_obj); my $is_infinivol = ${$opts{is_infinivol_ref}}; if (($wait || $is_infinivol) && defined $job) { $job->wait_on_job(%common_opts); } } catch NACL::C::Exceptions::VolumeSnapshot::SnapRestoreNotLicensed with { my $exception = $_[0]; $exception->license_feature(%common_opts); my $is_infinivol = $pkg_or_obj->_read_infinivol_val(%opts); $opts{is_infinivol} = $is_infinivol if (defined $is_infinivol); no warnings qw(exiting); redo RESTORE; } catch NACL::APISet::Exceptions::CommandFailedException with { my $exception = shift; if (($exception->text() =~ /Volume is busy/i) && $retry) { my $vol_obj = NACL::STask::Volume->new( command_interface => $opts{command_interface}, vserver => $opts{vserver}, volume => $opts{volume}, ); my $is_flexgroup = $vol_obj->state()->is_flexgroup(); # retry if volume is FlexGroup if ($is_flexgroup =~ /true/i) { $retry--; no warnings qw(exiting); redo RESTORE; } else { $exception->throw(); } } else { $exception->throw(); } }; } $Log->exit() if $may_exit; } =head2 restore_file # Class method NACL::STask::VolumeSnapshot->restore_file( command_interface => $ci, path => $path, vserver => $vs_name, volume => $volume, snapshot => $snap_name, 'restore-path' => $restore_path, nacltask_wait => 1, # defaults to 1 ); (or) # Instance method $snapshot_obj->restore_file(nacltask_wait => 1); (Class or instance method) This method is used to restore the file present in the volume to a snapshot. In addition to the facilities provided by the component, this method will verify whether the lock is released on the snapshot by checking the owners field of that particular snapshot. The method also installs the SnapRestore license if it's not already present. =over =item Options =over =item C<< nacltask_wait => 0 | 1 >> (Optional, defaults to 1) If set to 1 (the default), the method will wait for the lock on the snapshot to be released. =item C<< "method-timeout" => $timeout >> (Optional, defaults to 120 secs) This parameter contains the time in seconds to wait for the lock to be released for the file on which restore is done. =item C<< nacltask_polling_interval => $time >> (Optional, defaults to 5 seconds) This parameter contains the time to wait between each check for the lock state of the file. =item C<< nacltask_retry_tries_count => $tries_count >> (Optional, defaults to 5) The number of times to retry the resync command if it fails because the snapshot is temporarily busy or stale catch entry. Note that it is possible to make it not retry the restore command by setting the value of this argument to 1. =item C<< nacltask_retry_sleep_time => $sleep_time >> (Optional, defaults to 20 seconds) The time to sleep between retries of the restore command. =item Other options All the other options accepted by L<< NACL::C::VolumeSnapshot->restore_file|lib-NACL-C-VolumeSnapshot-pm/restore_file >> are also accepted by this method. =over =item Exceptions =over =item C This exception is thrown if the lock is not released on the file within the method-timeout value. =back =back =cut sub restore_file { $Log->enter() if $may_enter; my ($pkg_or_obj, %args) = @_; $pkg_or_obj->_partial_complete_restore_file( %args, _method => "restore_file", ); $Log->exit() if $may_exit; } =head2 partial_restore_file # Class method NACL::STask::VolumeSnapshot->partial_restore_file( command_interface => $ci, path => $path, volume => $volume, vserver => $vs_name, snapshot => $snap_name, 'start-byte' => $bytes, 'byte-count' => $count, nacltask_wait => 1, # defaults to 1 ); (or) # Instance method $snapshot_obj->partial_restore_file(nacltask_wait => 1); (Class or instance method) This method is used to partial-restore the file present in the volume to a snapshot. In addition to the facilities provided by the component, this method will verify whether the lock is released on the snapshot by checking the owners field of that particular snapshot. The method also installs the SnapRestore license if it's not already present. =over =item Options =over =item C<< nacltask_wait => 0 | 1 >> (Optional, defaults to 1) If set to 1 (the default), the method will wait for the lock on the snapshot to be released. =item C<< "method-timeout" => $timeout >> (Optional, defaults to 120 secs) This parameter contains the time in seconds to wait for the lock to be released for the file on which restore is done. =item C<< nacltask_polling_interval => $time >> (Optional, defaults to 5 seconds) This parameter contains the time to wait between each check for the lock state of the file. =item Other options All the other options accepted by L<< NACL::C::VolumeSnapshot->partial_restore_file|lib-NACL-C-VolumeSnapshot-pm/partial_restore_file >> are also accepted by this method. =over =item Exceptions =over =item C This exception is thrown if the lock is not released on the file within the method-timeout value. =back =back =cut sub partial_restore_file { $Log->enter() if $may_enter; my ($pkg_or_obj, %args) = @_; $pkg_or_obj->_partial_complete_restore_file( %args, _method => "partial_restore_file" ); $Log->exit() if $may_exit; } sub _partial_complete_restore_file { my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { nacltask_wait => { type => SCALAR, default => 1 }, "method-timeout" => { type => SCALAR, default => 120 }, nacltask_polling_interval => { type => SCALAR, default => 5 }, _method => { type => SCALAR }, nacltask_retry_tries_count => { type => SCALAR, default => 5 }, nacltask_retry_sleep_time => { type => SCALAR, default => 20 }, }, allow_extra => 1, ); my $wait = delete $opts{nacltask_wait}; my $timeout = delete $opts{"method-timeout"}; my $poll_time = delete $opts{nacltask_polling_interval}; my $method = delete $opts{_method}; my $super_method = "SUPER::$method"; my %nacltask_options; $pkg_or_obj->_move_nacltask_options( source => \%opts, target => \%nacltask_options, ); my %common_opts; $pkg_or_obj->_copy_common_component_params( source => \%opts, target => \%common_opts ); my %retry_opts; $pkg_or_obj->_copy_retry_params( source => \%nacltask_options, target => \%retry_opts ); RESTORE: { use warnings qw(exiting); try { if($method eq 'restore_file') { nacl_method_retry( code => sub {$pkg_or_obj->$super_method(%opts);}, exceptions => [ qw(NACL::C::Exceptions::VolumeSnapshot::CurrentlyBusy) ], %retry_opts, ); }else { $pkg_or_obj->$super_method(%opts); } } catch NACL::C::Exceptions::VolumeSnapshot::SnapRestoreNotLicensed with { my $exception = $_[0]; $exception->license_feature(%common_opts); no warnings qw(exiting); redo RESTORE; }; } if($wait) { # Need to have this subroutine as the attribute which we needs to be # checked "owners" is of array type. # wait_on_attribute does not support "array" type. $pkg_or_obj->_wait_for_release( command_interface => $opts{command_interface}, vserver => $opts{vserver}, volume => $opts{volume}, snapshot => $opts{snapshot}, "method-timeout" => $timeout, nacltask_polling_interval => $poll_time, %common_opts, ); } $Log->exit() if $may_exit; } sub _wait_for_release { $Log->enter() if $may_enter; my ($pkg_or_obj,@opts) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@opts, additional_spec => { "method-timeout" => { type => SCALAR}, nacltask_polling_interval => { type => SCALAR}, }, allow_extra => 1, ); my %common_opts; my $poll_time = delete $opts{nacltask_polling_interval}; $pkg_or_obj->_copy_common_component_params( source => \%opts, target => \%common_opts ); my $counter = 0; my $end_time = timeout2time($opts{'method-timeout'}); while(1) { my $snapshot = NACL::CS::VolumeSnapshot->fetch( command_interface => $opts{command_interface}, filter => { vserver => $opts{vserver}, volume => $opts{volume}, snapshot => $opts{snapshot}, }, requested_fields => ['owners'], %common_opts, ); my $owners = $snapshot->owners(); if(time() < $end_time ) { if( $owners->[0] eq "-" ) { last; } else { Tharn::snooze($poll_time); } } else { $Log->exit() if $may_exit; NACL::Exceptions::Timeout ->throw("The lock on the restored file has not been released"); } } $Log->debug("Lock has been released successfully"); $Log->exit() if $may_exit; } =head2 parents @parents = $vol_snapshot_obj->parents(); ( Class or Instance method ) This method returns the list containing the names of the parent objects of this resource. Used for finding dependencies between resources. =cut sub parents { $Log->enter() if $may_enter; $Log->exit() if $may_exit; return qw(NACL::STask::Volume); } =head2 prepare_for_revert NACL::STask::VolumeSnapshot->prepare_for_revert( command_interface => $command_interface, node => $node); or $vol_snapshot_obj->prepare_for_revert(); ( Class or Instance method ) This method deletes all Snapshot copies that have the format used by the current version of Data ONTAP. =item options All the other options accepted by L<< NACL::C::VolumeSnapshot->prepare_for_revert|lib-NACL-C-VolumeSnapshot-pm/prepare_for_revert >> are also accepted by this method. =over =item Exceptions =over =item C This exception is thrown if this method doesn't remove ownership of snapshot. =back =back =cut sub prepare_for_revert { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { "method-timeout" => { type => SCALAR, default => 120 }, }, ignore_primary_keys => 1, allow_extra => 1, ); my $node = $opts{"node"}; my %common_opts; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $obj; my $retry = 3; RETRY: { try { $obj = $pkg_or_obj->SUPER::prepare_for_revert(%opts); } catch NACL::C::Exceptions::VolumeSnapshot::SnapshotHasOwner with { my $exception = shift; if ($retry--) { try { NACL::C::VolumeSnapshotPolicy->modify( %common_opts, extended_query => { enabled => 'true' }, enabled => 'false' ); } catch NACL::C::Exceptions::VolumeSnapshotPolicy::DoesNotExist with { $Log->comment( "All Snapshot policies are already disabled."); }; my @snapshots = NACL::CS::VolumeSnapshot->fetch( %common_opts, filter => { owners => ['!-'], node => $node }, requested_fields => ['owners'], allow_empty => 1 ); foreach my $ss (@snapshots) { # Need to release the snampmirror my @sm_source = NACL::STask::Snapmirror->find( %common_opts, filter => { 'source-path' => $ss->vserver . ':' . $ss->volume }, allow_empty => 1 ); my @sm_dest = NACL::STask::Snapmirror->find( %common_opts, filter => { 'destination-path' => $ss->vserver . ':' . $ss->volume }, allow_empty => 1 ); foreach my $sm_obj ( @sm_source,@sm_dest ) { $sm_obj->break( nacltask_if_broken => 'pass' ); $sm_obj->release(); } my $owners = join( ',', $ss->owners() ); for ($owners) { _split_volume_clone( %common_opts, snapshot_obj => $ss ) when /volume clone/i; default { $Log->exit() if $may_exit; NATE::BaseException->throw( "Unable to remove ownership for $owners. Raise BURT, type=NACL subtype=nacl_core." ); } } } goto RETRY; } else { $Log->exit() if $may_exit; $exception->throw(); } } catch NACL::C::Exceptions::VolumeSnapshot::DisableSnapshotPolicies with { my $exception = shift; if ($retry--) { # disable snapshot policies NACL::C::VolumeSnapshotPolicy->modify ( %common_opts, vserver => '*', enabled => 'false' ); # retry prepare for revert goto RETRY; } else { $Log->exit() if $may_exit; $exception->throw(); } } catch NACL::C::Exceptions::VolumeSnapshot::RetryCmd with { my $exception = shift; if ($retry--) { my @sm = NACL::STask::Snapmirror->find( %common_opts, filter => { type => 'LS' }, allow_empty => 1 ); map { $_->delete() } @sm; @sm = NACL::STask::Snapmirror->find( %common_opts, filter => { state => '!Broken-off' }, allow_empty => 1 ); map { $_->break( nacltask_if_broken => 'pass' ) } @sm; map { $_->purge( nacltask_if_purged => 'pass' ) } @sm; map { $_->release() } @sm; goto RETRY; } else { $Log->exit() if $may_exit; $exception->throw(); } }; } $Log->exit() if $may_exit; return $obj; } # it splits the volume clone of given snapshot sub _split_volume_clone { $Log->enter() if $may_enter; my %opts = @_; my $timeout = $opts{"method-timeout"}; my $snapshot_obj = delete $opts{'snapshot_obj'}; my @volume_clone = NACL::STask::VolumeClone->find( %opts, filter => { 'parent-volume' => $snapshot_obj->volume(), vserver => $snapshot_obj->vserver() } ); map {$_->split(nacltask_wait => 0)} @volume_clone; map {$_->wait_for_split()} @volume_clone; $Log->exit() if $may_exit; } 1;