# # Copyright (c) 2001-2013 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Volume Copy Task Module ## @author dl-nacl-dev@netapp.com ## @status Public ## @pod here package NACL::STask::VolumeCopy; use strict; use warnings; use base qw(NACL::C::VolumeCopy 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); use NACL::APISet::Exceptions::ResponseException (); use NACL::ComponentUtils qw( _optional_scalars Dumper); use constant DEFAULT_TIMEOUT => 500; use constant POLL_DELTA => 5; use NATE::BaseException qw(:try); use NATE::Exceptions::Argument (); use NATE::Time qw(timeout2time); use Class::MethodMaker [ 'scalar' => [ { -type => 'NACL::C::Job' }, 'last_job' ] ]; =head1 NAME NACL::STask::VolumeCopy =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to Volume Copy task on 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 VolumeCopy-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, 'start' Cleanup methods are, Volume Method Cleanup Method -------------------------------------- create _purge_for_cleanup =head1 ATTRIBUTES =head2 command_interface (Required) As C. A component object that represents the host to which to send commands. =head2 destination-volume (Required) As NACL::C::VolumeCopy>. The name of the Destination volume. =head2 vserver (Required for C-mode, ignored for 7-mode) As NACL::C::VolumeCopy>. The name of the vserver containing the source volume and destination volume. =head2 last_job (Optional) A job component (NACL::C::Job) representing the job used to start this volume copy task. =head1 METHODS =head2 start my $volume = NACL::STask::VolumeCopy->start( command_interface => $command_interface, 'destination-volume' => $volume, vserver => $vserver, nacltask_wait => $boolean, #default '1' nacltask_if_exists => $action, # default 'die' nacltask_to_cleanup => 1, # default 0, nacltask_cleanup_manager => $CleanupObj, %other_options ); (Class method) start a volume Copy Operation. This method provides additional services beyond what's inherent to the product's volume creation commands, as controlled and described by the new C options. =over =item Options =over =item C<< nacltask_wait => 0|1 >> (Optional) If true (the default), wait for volume copy job completion (see "wait_for_copy" method, below). If false and 7Mode, do not wait. =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) Verify that the volume copy job 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_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. =item C<< "method-timeout"=> $scalar >> (Optional) The timeout value for waiting job completion. default 500 sec =back =item command_interface, apiset_must, apiset_should, method-timeout, volume, vserver,destination-volume,destination-stripe-format,foreground,destination-aggregate . All of the other various options to L<< NACL::C::VolumeCopy->start | lib-NACL-C-VolumeCopy-pm/start >> =back =cut sub start { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { nacltask_wait => { type => SCALAR, default => 1 }, 'method-timeout' => { type => SCALAR, 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 $wait = delete $opts{nacltask_wait}; # get the 'nacltask_wait' value my $timeout = $opts{'method-timeout'} || DEFAULT_TIMEOUT; my %wait_for_copy_opts = ( 'method-timeout' => $timeout ); my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); { # We want to know the job ID of the job performed by start my $job_ref; $opts{job_component} ||= \$job_ref; } my $self; if ($opts{command_interface}->is_cmode()){ $self = $pkg->SUPER::start(%opts); $self->last_job( ${ $opts{job_component} } ); # Create an object for the job component created $self->wait_for_copy(%wait_for_copy_opts) if ($wait); } else { $self = $pkg->_start_7mode(%opts); $self->wait_for_copy(%wait_for_copy_opts) if ($wait); } # 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_for_cleanup', ); $pkg->_register_for_cleanup(%opts_for_register) if ($nacltask_to_cleanup); $Log->exit() if $may_exit; return $self; } ## end sub start sub _start_7mode { $Log->enter() if $may_enter; my ($pkg,%opts) = @_; my ($self,$vol_obj,$Was_Vol_Created,%common_options); $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_options ); my $volume; if(ref $opts{'volume'}) { $volume = $opts{'volume'}->volume(); } else { $volume = $opts{'volume'}; } AGAIN: { use warnings; try { $self = $pkg->SUPER::start(%common_options,%opts); } catch NACL::C::Exceptions::VolumeCopy::DestinationVolumeDoesNotExist with { # finding the size of parent volume # to create a destination volume greater # than or equal to the size of the poarent volume. my $obj = NACL::CS::Volume->fetch( %common_options, filter => { vserver => $opts{'vserver'}, volume => $opts{'volume'} }, requested_fields => ['size'], ); my $size = $obj->size(); $vol_obj = NACL::STask::Volume->create( %common_options, volume => $opts{'destination-volume'}, size => $size, state => 'restricted' ); no warnings; redo AGAIN; } catch NACL::C::Exceptions::VolumeCopy::RestrictBeforeCopying with { NACL::STask::Volume->modify( %common_options, vserver => $opts{vserver}, volume => $opts{'destination-volume'}, state => 'restricted' ); no warnings; redo AGAIN; }; } NACL::STask::Volume->modify( %common_options, vserver => $opts{vserver}, volume => $opts{'destination-volume'}, state => 'online' ); $Log->exit() if $may_exit; return $self; } ## end sub _start_7mode =head2 wait_for_copy $volcopy_task_obj->wait_for_copy( "method-timeout" => $timeout, last_job => $last_job, %wait_options ); This function takes job component as input and waits for the job "to get completed" -> "to be completed" or -> "to complete" =over =item Options =over =item B<<"method-timeout"=> $scalar>> (Optional) The timeout value for waiting job completion. default 500 sec =item B<<"polling_interval"=> $scalar>> (Optional) The polling time interval value for waiting job completion. default 5 sec =item B<< last_job => $last_job >> (Required) This is a reference to job component of the copy job from NACL::STask::VolumeCopy->start(..., nacltask_wait => 0, last_job => \$last_job); =back =over =item Exceptions =over =item C This type of exception is thrown when volume copy operations has not completed with in specified $timeout seconds. =item C This type of exception is thrown when no job component was passed to wait_for_copy method call. =back =cut sub wait_for_copy { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { _optional_scalars(qw(polling_interval last_job)) }, ); my $last_job = delete $opts{last_job}; my $timeout = delete $opts{"method-timeout"} || DEFAULT_TIMEOUT; $timeout = timeout2time($timeout); my $polling_interval = delete $opts{polling_interval} || POLL_DELTA; # Get hold of the job component if ( ref($pkg_or_obj) ) { $last_job ||= $pkg_or_obj->last_job(); } if ($last_job) { $Log->trace("Wait for completion"); $pkg_or_obj->wait_for_completion( job_component => $last_job, 'method-timeout' => $timeout, polling_interval => $polling_interval, 'check_job_and_attribute' => 1 # Needs to be included as mandatory if using wait_for_completion to wait on job ); } elsif ( $opts{command_interface}->is_7mode() ) { my $found = 1; my $end_time = time() + $timeout; while ($found) { # Calling NACL::CS::VolumeCopy until it gives the # status of volume copy operation as no operation is in progress. try { $pkg_or_obj->wait_on_attribute( command_interface => $opts{command_interface}, 'method-timeout' => $timeout, attribute_to_check => "status", till_value => ["100% done"], polling_interval => $polling_interval ); Tharn::snooze(POLL_DELTA); } ## end try catch NACL::Exceptions::NoElementsFound with { # When volume copy operation is complete # then the volume entry is removed from # "volume copy status" $found = 0; }; if ( time() > $end_time ) { $Log->exit() if $may_exit; NACL::Exceptions::Timeout->throw( "wait_for_move timed out after" . " waiting for $timeout seconds" ); } } ## end while ($found) } else { NACL::APISet::Exceptions::ResponseException->throw ( text => "Error, No Job Component was passed\nplease make sure you pass last_job for wait_for_copy method"); } $Log->exit() if $may_exit; } ## end sub wait_for_copy =head2 _purge_to_cleanup NACL::STask::VolumeCopy->_purge_to_cleanup( command_interface => $command_interface, vserver => $vserver, volume => $volume ); OR $VolumeCopy->_purge_to_cleanup(); (Class or Instance method) Deletes the destination volume for the particular vserver. =over =item Options =over =item C<< command_interface => $ci >> (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_name >> (Required for C-mode for class method, ignored for 7-mode, Not Applicable for instance method) Name of vserver containing volume =item C<< destination-volume=>$destination_volume >> (Required for C-mode for class method, Not applicable for instance method) Name of destination volume to be deleted =item command_interface, apiset_must, apiset_should, etc. All of the other options refer to L<< NACL::STask::Volume->purge|lib-NACL-STask-Volume-pm/purge >> =over =back =cut sub _purge_for_cleanup { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %orig_opts = $pkg_or_obj->_common_validate_with( params => \@args, allow_extra => 1, ); $orig_opts{'volume'} = delete $orig_opts{'destination-volume'}; NACL::STask::Volume->purge(%orig_opts); $Log->exit() if $may_exit; } ## end sub _purge_for_cleanup 1;