# Copyright (c) 2014-16 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary SnapmirrorPolicy Task Module ## @author bramya@netapp.com, ng-ptefit-automation-core@netapp.com ## @status shared ## @pod here package NACL::STask::SnapmirrorPolicy; use strict; use warnings; use base qw(NACL::C::SnapmirrorPolicy 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 SCALAR BOOLEAN SCALARREF); use NACL::ComponentUtils qw (_optional_scalars Dumper); use NACL::C::Exceptions::SnapmirrorPolicy::AlreadyExists qw(:try); use NACL::C::Exceptions::SnapmirrorPolicy::RuleAlreadyExists qw(:try); use NACL::Exceptions::VerifyFailure (); use NACL::C::Exceptions::SnapmirrorPolicy::DoesNotExist (); use NACL::C::Exceptions::SnapmirrorPolicy::RuleDoesNotExist qw(:try); use NATE::Exceptions::Argument qw(:try); use NACL::APISet::Exceptions::ResponseException (); =head1 NAME NACL::STask::SnapmirrorPolicy =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to SnapmirrorPolicy in ONTAP. It is created on top of C component Since it is derived class of C, we can use all the methods of C from the object of this task. =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the node to which to send commands See L. =head2 vserver (Required) The name of the vserver for the snap mirror policy. =head2 policy (Required) The SnapMirror policy name. =head1 METHODS =head2 create my $SnapmirrorPolicy = NACL::STask::SnapmirrorPolicy->create( 'command_interface' => $command_interface, 'vserver' => $vserver, 'policy' => $policy, 'nacltask_if_exists' => $action, # default die 'nacltask_verify' => $action, %other_options, ); (Class Method) This method is used to create a snapmirror policy. If policy already exists, it will perform the action based on "nacltask_if_exists" parameter. Default behavior would be "die". It returns the Task object for the created schedule. Uses CMode CLI/ZAPI APISet. =over =item Options =over =item C<< command_interface => $command_interface >> (Required) See L =item C<< 'vserver => $vserver >> (Required) The name of the vserver for the snap mirror policy. =item C<< 'policy' => $policy >> (Required) The Snapmirror policy name. =item C<< apiset_must => $ruleset >> (Optional) See L =item C<< apiset_should => $ruleset >> (Optional) See L =item C<< "nacltask_if_exists => $action" >> (Optional) What to do if the policy to be created already exists. If $action is "die", then fail with an exception. If action is "purge", then purge the policy(see the "purge" method, below) before creating a new one. If $action is "reuse", It will return the object of existing policy. =item C<< "nacltask_verify => $action" >> (Optional) The user of this library can specify to verify whether the snapmirror policy is created or not. If the action is 0, which is default, it wont verify the creation. If the user sets the action to 1, it will verify the it using the component state of this library. =item C<< "_was_created => \$scalar" >> (Optional) When this option is provided with a reference to a scalar variable, the variable gets filled in with a boolean value describing whether the policy was available (value will be 0; this scenario is possible when if_exists => "reuse") or whether the policy was created (value will be 1). This is necessary to determine whether the policy needs to be cleaned up later. my $was_created; my $SnapmirrorPolicy = NACL::STask::SnapmirrorPolicy->create( command_interface => $command_interface, vserver => $vserver, policy => $policy, $nacltask_if_exists => 'reuse', _was_created => \$was_created, nacltask_to_cleanup => $boolean, %other_opts ); # Operate on $SnapmirrorPolicy here # ... # Now determine whether to clean up the policy, since we're not sure # whether we reused an existing policy or created a new one if ($was_created) { # New snapmirror policy was created. Clean it up. $SnapmirrorPolicy->purge(); } =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is to be cleaned up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to use for registering. Default : Will use the default cleanup manager. command_interface, apiset_must, apiset_should, mode, etc. All of the other various options to L<< NACL::C::SnapmirrorPolicy->create|lib-NACL-C-SnapmirrorPolicy-pm/create >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to create an element that already exists. =item C This type of exception is thrown when verification fails for the created policy. =back =back =back =cut sub create { $Log->enter() if $may_enter; my ($pkg, @args) = @_; # $destination_ci stores AVA command_interface since same won't be available in @args post calling _check_and_setup_for_ava() # This is not stored in @args since super class call (NACL::C::SnapmirrorPolicy) doesn't accept any new parameter # and throws an error while validating input params, hence AVA ci is stored into a $destination_ci. my $destination_ci; $pkg->_check_and_setup_for_ava(\@args, \$destination_ci); my %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { nacltask_if_exists => $pkg->_if_exists_validate_spec(), nacltask_verify => {type => BOOLEAN, default => 0}, _was_created => {type => SCALARREF, optional => 1}, $pkg->_cleanup_validate_spec(), }, allow_extra => 1 ); # Transform %opts from the options we received into the options to # pass to the base class method. my ($self, %common_opts, %nacltask_opts); $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); $pkg->_move_nacltask_options( source => \%opts, target => \%nacltask_opts ); my $was_created = delete $opts{_was_created}; $$was_created = 0; my $if_exists = $nacltask_opts{'nacltask_if_exists'}; my $verify = $nacltask_opts{'nacltask_verify'}; CREATE: { use warnings; try { $self = $pkg->SUPER::create(%opts); $$was_created = 1; } catch NACL::C::Exceptions::SnapmirrorPolicy::AlreadyExists with { my $exception = shift; $Log->comment("The Snap Mirror Policy already Exists!"); $self = $pkg->_element_exists_handler( create_opts => \%opts, nacltask_if_exists => $if_exists, exception => $exception, ); if (!$self) { no warnings qw(exiting); redo CREATE; } }; } # add destination_ci to created stask object $self->{destination_ci} = $destination_ci if ($destination_ci); if ($verify) { $pkg->verify_state(%opts); } # Register 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 purge $SnapmirrorPolicy->purge(); (or) NACL::STask::SnapmirrorPolicy->purge( 'command_interface' => $command_interface, 'vserver' => $vserver, 'policy' => $policy, 'nacltask_verify' => 1, 'nacltask_if_purged => $action, ); (Class or instance method) This method is used to delete the snapmirror policy. Uses a CMode CLI/ZAPI APISet. =over =item Options =over =item C<< command_interface => $command_interface >> (Required for class method, Not Applicable for instance method) A component object that represents the host which to send commands. See NACL::C::Component::command_interface =item C<< 'vserver' => $vserver >> (Required for class method, Not Applicable for instance method) The name of the vserver for the snap mirror policy. =item C<< 'policy' => $policy >> (Optional for class method, Not Applicable for instance method) The Snapmirror policy name. =item C<< nacltask_verify => $nacltask_verify_boolean >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the deletion did happen successfully. =item C<< _was_deleted => \$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 policy did not exist (value will be 0; this scenario is possible when nacltask_if_purged => "pass") or whether the policy was deleted (value will be 1). =item C<< nacltask_if_purged => $nacltask_if_purged >> (Optional) If 'pass' (default), It will pass if the policy is already deleted. If 'fail', It will fail if the policy is already deleted. All of the other various options to L<< NACL::C::SnapmirrorPolicy->delete|lib-NACL-C-SnapmirrorPolicy-pm/delete >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete an element that does not exists. =item C This type of exception is thrown when verification fails for the deleted snapmirror policy. =back =cut sub purge { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; # $destination_ci stores AVA command_interface since same won't be available in @args post calling _check_and_setup_for_ava() # This is not stored in @args since super class call (NACL::C::SnapmirrorPolicy) doesn't accept any new parameter # and throws an error while validating input params, hence AVA ci is stored into a $destination_ci. my $destination_ci; $pkg_or_obj->_check_and_setup_for_ava(\@args, \$destination_ci); my $additional_spec = { nacltask_verify => {type => BOOLEAN, default => 0}, nacltask_if_purged => $pkg_or_obj->_if_action_completed_already_validate_spec(), _was_deleted => {type => SCALARREF, optional => 1}, }; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => $additional_spec, allow_extra => 1, ); my %nacltask_opts; $pkg_or_obj->_move_nacltask_options( source => \%opts, target => \%nacltask_opts, ); my $if_purged = $nacltask_opts{nacltask_if_purged}; my %common_opts; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $was_deleted = delete $opts{_was_deleted}; $$was_deleted = 0; use warnings qw(exiting); try { $pkg_or_obj->SUPER::delete(%common_opts, %opts); $$was_deleted = 1; } catch NACL::C::Exceptions::SnapmirrorPolicy::DoesNotExist with { my $exception = shift; $Log->comment("Entry Does Not Exists!"); if ($if_purged !~ /pass/i) { $Log->exit() if $may_exit; $exception->throw(); } }; if ($opts{nacltask_verify}) { $pkg_or_obj->_generic_purge_verify(%opts); } $Log->exit() if $may_exit; } ## end sub purge =head2 add_rule NACL::STask::SnapmirrorPolicy->add_rule( 'command_interface' => $command_interface, 'vserver' => $vserver, 'policy' => $policy, 'snapmirror-label' => $label, 'keep' => $keep, 'nacltask_if_exists' => $action, # default die 'nacltask_verify' => $action, %other_options, ); (Class Method) This method is used to create a snapmirror policy rule. If rule already exists, it will perform the action based on "nacltask_if_exists" parameter. Default behavior would be "die". It returns the Task object for the created schedule. Uses CMode CLI/ZAPI APISet. =over =item Options =over =item C<< command_interface => $command_interface >> (Required) See L =item C<< 'vserver => $vserver >> (Required) The name of the vserver for the snap mirror policy. =item C<< 'policy' => $policy >> (Required) The Snapmirror policy name. =item C<< 'snapmirror-label => $label >> (Required) This is used for Snapshot copy selection as well as for accounting of Snapshot copies at the SnapMirror vault destination. =item C<< 'keep' => $keep >> (Required) Specifies the maximum number of Snapshot copies that are retained on the SnapMirror vault destination volume for a rule. =item C<< apiset_must => $ruleset >> (Optional) See L =item C<< apiset_should => $ruleset >> (Optional) See L =item C<< "nacltask_if_exists => $action" >> (Optional) What to do if the rule to be created already exists. If $action is "die", then fail with an exception. If action is "purge", then purge the policy rule before creating a new one. The new one will have the new attribute values passed in. If $action is "reuse", the policy rule with same snapmirror-label will be reused. Other attributes like keep will not be updated and continue using the values of the old policy rule which already exists. =item C<< "_was_created => \$scalar" >> (Optional) When this option is provided with a reference to a scalar variable, the variable gets filled in with a boolean value describing whether the rule was available (value will be 0; this scenario is possible when if_exists => "reuse") or whether the policy was created (value will be 1). This is necessary to determine whether the rule needs to be cleaned up later. my $was_created; NACL::STask::SnapmirrorPolicy->add_rule( command_interface => $command_interface, vserver => $vserver, policy => $policy, snapmirror-label => $label, keep => $keep, $nacltask_if_exists => 'reuse', _was_created => \$was_created, nacltask_to_cleanup => $boolean, %other_opts ); # Operate on $SnapmirrorPolicyRule here # ... # Now determine whether to clean up the policy, since we're not sure # whether we reused an existing policy or created a new one if ($was_created) { # New snapmirror policy rule was created. Clean it up. $SnapmirrorPolicyRule->remove_rule(); } =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is to be cleaned up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to use for registering. Default : Will use the default cleanup manager. command_interface, apiset_must, apiset_should, mode, etc. All of the other various options to L<< NACL::C::SnapmirrorPolicy->add_rule|lib-NACL-C-SnapmirrorPolicy-pm/add_rule >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to create an element that already exists. =item C This type of exception is thrown when verification fails for the created rule. =back =back =back =cut sub add_rule { $Log->enter() if $may_enter; my ($pkg, @args) = @_; # $destination_ci stores AVA command_interface since same won't be available in @args post calling _check_and_setup_for_ava() # This is not stored in @args since super class call (NACL::C::SnapmirrorPolicy) doesn't accept any new parameter # and throws an error while validating input params, hence AVA ci is stored into a $destination_ci. my $destination_ci; $pkg->_check_and_setup_for_ava(\@args, \$destination_ci); my %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { nacltask_if_exists => $pkg->_if_exists_validate_spec(), _was_created => {type => SCALARREF, optional => 1}, $pkg->_cleanup_validate_spec(), }, allow_extra => 1 ); # Transform %opts from the options we received into the options to # pass to the base class method. my ($self, %common_opts, %nacltask_opts); $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); $pkg->_move_nacltask_options( source => \%opts, target => \%nacltask_opts ); my $was_created = delete $opts{_was_created}; $$was_created = 0; my $if_exists = $nacltask_opts{'nacltask_if_exists'}; ADD: { use warnings; try { $pkg->SUPER::add_rule(%opts); $$was_created = 1; } catch NACL::C::Exceptions::SnapmirrorPolicy::RuleAlreadyExists with { my $exception = shift; $Log->comment("The Snap Mirror Policy Rule already Exists!"); if ($if_exists eq 'die') { $Log->exit() if $may_exit; $exception->throw(); } elsif ($if_exists eq 'reuse') { $Log->debug("Exception suppressed because 'if_exists' " . "was sent as '$if_exists'"); } else { $pkg->remove_rule( 'command_interface' => $opts{command_interface}, 'vserver' => $opts{vserver}, 'policy' => $opts{policy}, 'snapmirror-label' => $opts{'snapmirror-label'}, ); no warnings qw(exiting); redo ADD; } }; } # Register 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' => 'remove_rule' ); if ($nacltask_to_cleanup && $$was_created) { $pkg->_register_for_cleanup( %opts_for_register, other_opts => { 'snapmirror-label' => $opts{'snapmirror-label'}, }, ); } $Log->exit() if $may_exit; } ## end sub add_rule =head2 remove_rule NACL::STask::SnapmirrorPolicyRule->remove_rule( 'command_interface' => $command_interface, 'vserver' => $vserver, 'policy' => $policy, 'snapmirror-label' => $label, 'nacltask_if_purged => $action, ); (Class or instance method) This method is used to delete the snapmirror policy rule. Uses a CMode CLI/ZAPI APISet. =over =item Options =over =item C<< command_interface => $command_interface >> (Required for class method, Not Applicable for instance method) A component object that represents the host which to send commands. See NACL::C::Component::command_interface =item C<< 'vserver' => $vserver >> (Required for class method, Not Applicable for instance method) The name of the vserver for the snap mirror policy. =item C<< 'policy' => $policy >> (Optional for class method, Not Applicable for instance method) The Snapmirror policy name. =item C<< 'snapmirror-label => $label >> (Required) This is used for Snapshot copy selection as well as for accounting of Snapshot copies at the SnapMirror vault destination. =item C<< _was_deleted => \$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 rule did not exist (value will be 0; this scenario is possible when nacltask_if_purged => "pass") or whether the rule was deleted (value will be 1). =item C<< nacltask_if_purged => $nacltask_if_purged >> (Optional) If 'pass' (default), It will pass if the rule is already deleted. If 'fail', It will fail if the rule is already deleted. All of the other various options to L<< NACL::C::SnapmirrorPolicy->remove_rule|lib-NACL-C-SnapmirrorPolicy-pm/remove_rule >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete an element that does not exists. =item C This type of exception is thrown when verification fails for the deleted snapmirror policy rule. =back =cut sub remove_rule { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; # $destination_ci stores AVA command_interface since same won't be available in @args post calling _check_and_setup_for_ava() # This is not stored in @args since super class call (NACL::C::SnapmirrorPolicy) doesn't accept any new parameter # and throws an error while validating input params, hence AVA ci is stored into a $destination_ci. my $destination_ci; $pkg_or_obj->_check_and_setup_for_ava(\@args, \$destination_ci); my $additional_spec = { nacltask_if_purged => $pkg_or_obj->_if_action_completed_already_validate_spec(), _was_deleted => {type => SCALARREF, optional => 1}, }; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => $additional_spec, allow_extra => 1, ); my %nacltask_opts; $pkg_or_obj->_move_nacltask_options( source => \%opts, target => \%nacltask_opts, ); my $if_purged = $nacltask_opts{nacltask_if_purged}; my %common_opts; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $was_deleted = delete $opts{_was_deleted}; $$was_deleted = 0; use warnings qw(exiting); try { $pkg_or_obj->SUPER::remove_rule(%common_opts, %opts); $$was_deleted = 1; } catch NACL::C::Exceptions::SnapmirrorPolicy::RuleDoesNotExist with { my $exception = shift; $Log->comment("Entry Does Not Exists!"); if ($if_purged !~ /pass/i) { $Log->exit() if $may_exit; $exception->throw(); } }; $Log->exit() if $may_exit; } ## end sub remove_rule # Validate the parameters in case the # destination is an AVA appliance. sub _validate_ava_args { $Log->enter() if $may_enter; my ($self, %args) = @_; # We must have a source_ci: if (!$args{source_ci}) { $Log->exit(); NATE::Exceptions::Argument->throw( 'When the destination is an AltaVault appliance, "source_ci" ' . 'must be present in the call.'); } $Log->exit() if $may_exit; return; } ## end sub _validate_ava_args # Update $args->{command_interface} with ONTAP ci value as all snapmirror policy # related commands needs to be executed on ONTAP ci only. This is so that later NACL calls that # default to using command_interface get the CI that is an ONTAP node. Note # that we do NOT do this to the object properties, only to $args. sub _swap_command_interface { $Log->enter() if $may_enter; my ($self, $args) = @_; # (since an earlier check should have verified that source_ci is present). # simply update $args->{command_interface} with ONTAP ci value as all snapmirror # related commands needs to be executed on ONTAP ci only. $args->{command_interface} = $args->{source_ci}; delete $args->{source_ci}; $Log->exit() if $may_exit; return; } ## end sub _swap_command_interface # Encapsulate the two previous methods into a single call that validates # and massages the parameters. sub _check_and_setup_for_ava { $Log->enter() if $may_enter; my ($pkg, $args, $destination_ci) = @_; my %tmp_opts = @{$args}; my $command_interface = $pkg->command_interface() || $tmp_opts{command_interface}; if ($command_interface->is_ava()) { # store destination command_interface for future use # since same won't be available post calling _swap_command_interface() $$destination_ci = $pkg->command_interface() || $tmp_opts{command_interface}; # Perform few basic validations for ONTAP-AVA snapmirror policy related functonalities $pkg->_validate_ava_args(%tmp_opts); # Update command_interface field to contain ONTAP command_interface, since all # commands will be executed on ONTAP rather than AVA $pkg->_swap_command_interface(\%tmp_opts); @$args = %tmp_opts; } $Log->exit() if $may_exit; return; } ## end sub _check_and_setup_for_ava 1;