# $Id$ # # 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 Move Task Module ## @author dl-nacl-dev@netapp.com ## @status Public ## @pod here package NACL::STask::VolumeMove; use strict; use warnings; use base qw(NACL::C::VolumeMove 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(UNDEF BOOLEAN SCALAR OBJECT HASHREF ARRAYREF CODEREF); use NATE::Exceptions::Argument qw(:try); use NACL::Exceptions::NoElementsFound qw(:try); use NACL::Exceptions::VerifyFailure qw(:try); use NACL::ComponentUtils qw(Dumper _optional_scalars); use NACL::STask::Volume; use NACL::CS::VolumeMoveTargetAggr; use NACL::C::StorageAggregate; use NACL::C::VolumeSnapshot; use NACL::C::DebugSmdbTrace; use NATE::BaseException qw(:try); use NACL::Exceptions::Timeout; use NACL::STask::VolumeClone; use constant DEFAULT_TIMEOUT => 500; use constant POLL_DELTA => 10; use constant KSMF_TABLE_NAMES => (#destination side ksmf tables # Following Interfaces are removed from FS, hence removing from here, "dstVolMoveObject_commit,srcVolMoveObject_commit,srcVolMoveObject_hardDeferred" "dstVolMoveObject", "dstVolMoveObject_abort", "dstVolMoveObject_commitCutover", "dstVolMoveObject_setupCutover", "dstVolMoveObject_updatePhase", #source side ksmf tables "srcVolMoveObject", "srcVolMoveObject_abort", "srcVolMoveObject_conclude", "srcVolMoveObject_dbladeCommit", "srcVolMoveObject_lockdown", "srcVolMoveObject_prepare", "srcVolMoveObject_setup", "srcVolMoveObject_setupTransfer", "srcVolMoveObject_transferFailed", "srcVolMoveObject_validate"); use Class::MethodMaker [ 'scalar' => [ { -type => 'NACL::C::Job' }, 'job_component' ], scalar => 'destination_aggregate', scalar => 'destination_volume_name' ]; =head1 NAME NACL::STask::VolumeMove =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to Volume Move 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 VolumeMove-related commands. See C for details. This also means that a C object may generally be used in place of a component object. =head1 ATTRIBUTES =head2 command_interface (Required) As C. A component object that represents the host to which to send commands. =head2 volume (Required) As NACL::C::VolumeMove>. The name of the volume to be moved. =head2 vserver (Required for C-mode, ignored for 7-mode) As NACL::C::VolumeMove>. The name of the vserver containing the volume. =head2 job_component (Optional) A job component (NACL::C::Job) representing the job used to start this volume move task. =head2 destination_aggregate (Optional) Name of the destination aggregate. This is not a primary key and is valid only for CMode Volume Move Operation. This primary key need not be provided in the call to new(). This attribute is populated by the task itself to store the information about destination-aggregate and use it later when wait_for_move is invoked as an instance method call. This is more useful for 7M runs in which case 'destination-aggregate' is a mandatory option for wait_for_move =head2 destination_volume_name It represents the name of the destination volume created by the move operation. The attribute is populated by the start method after starting the move operation. This field is valid only for CMode Volume Move operation. =head1 Linkage from Volume STask All of the methods in this package are callable with a Volume STask object, by using the "move_" prefix. For example, if we want to invoke the C method of C, we can do: my $vol_move_stask_obj = $vol_stask_obj->move_start(%opts); The volume name, vserver name (for CMode) and command_interface are extracted from the volume object, so they do not need to be provided as options in the method call. So for example if we'd like to start a volume move in the foreground, we could simply do: my $vol_move_stask_obj = $vol_obj->move_start(foreground => 'true'); =head1 METHODS =head2 start my $volume = NACL::STask::VolumeMove->start( command_interface => $command_interface, 'volume' => $volume, vserver => $vserver, nacltask_wait => $boolean, #default '1' nacltask_verify => $boolean, #default '0' %other_options ); (Class method) start a Volume Move Operation. This method also waits for completion of the move (controlled by the C argument), optionally verifies that the volume was moved to the destination aggregate and is online (controlled by the C argument) and automatically picks a suitable C if not provided. For 7Mode CLI volume is unmounted (entry for that volume would be removed from /etc/exports file and note that no client side unmounting will happen) prior to move and remounted (entry for the volume would be added again to /etc/exports file, if it was present earlier) after move operation. =over =item Options =over =item C<< nacltask_wait => 0|1 >> (Optional) If 1 (the default), wait for volume move operation to complete. (see "wait_for_move" method, below). If 0, do not wait. Additional step handled for 7Mode: For 7Mode CLI, if the volume was unmounted by the task to perform 'volume move' operation, it will be remounted again (entry for that volume will be added to /etc/exportfs) only if nacltask_wait option is set to 1. =item C<< nacltask_verify => 0|1 >> (Optional) If 1 (the default is 0), verify the volume state on completion of volume move operation. If 0, do not verify. This option is only valid for CMode Volume Move Operation If nacltask_wait is set to 0 and nacltask_verify is set to 1, then the STask will not wait for the job to complete and hence not call the verify also. The caller can call the verify() separately after the job has its operation. =item command_interface, apiset_must, apiset_should, method-timeout, volume, vserver, foreground, destination-aggregate, destination-stripe-format, cutover-window, cutover-attempts, cutover-action, perform-validation-only. All of the other various options to L<< NACL::C::VolumeMove->start | lib-NACL-C-VolumeMove-pm/start >> =back =back =over =item Arguments defaulted/auto-determined =over =item C If a value is not provided, then one is automatically picked by using C. =back =over =item Exceptions =over =item C This type of exception is thrown when verification fails for the moved volume. =item C This type of exception is thrown when volume move operation is occurring in the foreground, but no output has been displayed for $max_idle seconds. =back =cut sub start { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { nacltask_wait => { type => SCALAR, default => 1 }, nacltask_verify => { type => SCALAR, default => 0 }, }, allow_extra => 1, ); my $wait = delete $opts{nacltask_wait}; my $verify = delete $opts{nacltask_verify}; my $timeout = $opts{'method-timeout'} || DEFAULT_TIMEOUT; my %common_params_with_ci; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_params_with_ci, ); my %primary_keys; $pkg->_copy_primary_keys( source => \%opts, target => \%primary_keys ); if (!defined $opts{'destination-aggregate'}) { $opts{'destination-aggregate'} = NACL::CS::VolumeMoveTargetAggr->fetch( %common_params_with_ci, filter => {%primary_keys}, requested_fields => ['aggregate'] )->aggregate(); $Log->trace('Using volume move target-aggr, Destination Aggregate: ' . $opts{'destination-aggregate'}); } # CMode - volume can be moved without unmounting it. # 7Mode - volume has to be unmounted prior to move. my %mount_unmount_opts; my $was_unmounted; if ($opts{command_interface}->is_7mode()) { %mount_unmount_opts = (%common_params_with_ci, %primary_keys); try { NACL::STask::Volume->unmount(%mount_unmount_opts); $was_unmounted = 1; } ## end try catch NACL::C::Exceptions::Volume::NotExported with { $Log->debug("Volume $primary_keys{volume} is not currently " . 'exported'); }; ## end with } ## end if ( $opts{command_interface...}) my $self; # We want to know the job ID of the job performed by start my $job_ref; $opts{job_component} ||= \$job_ref; # If foreground => 'true' set, then (for CMode-CLI) starts up a # foreground job. The approach is to not use a method-timeout value # here (which is why it's set to -1, which is infinity) but to instead # track whether new output is being displayed. No new output for 2 # minutes would most likely mean that the job has stalled, so timeout. # (Typically, new output is displayed after 5-10s, so a 2 minute # limit is rather kind and will almost definitely mean something # has gone wrong) my %foreground_opts; my $idled_out = 0; my $max_idle = 120; if ($opts{command_interface}->is_cmode() && defined $opts{foreground} && $opts{foreground} eq 'true') { if (defined $opts{'method-timeout'}) { $Log->warn("'method-timeout' was passed in the call to " . 'to NACL::STask::VolumeMove->create while foreground => ' . 'true was also specified. Note that the STask library is ' . 'capable of picking a more robust timeout value, so ' . "the 'method-timeout' argument can be removed from the " . 'call'); } else { $foreground_opts{'method-timeout'} = -1; } $foreground_opts{'connectrec-max_idle'} = $max_idle; # Ideally, we'd simply be able to specify to throw a custom # exception on idle, but the current implementation in the # APISet makes that tricky, so we need to workaround it by # setting a flag on idle and then throw the exception we want. # See burt 668983 for a request to fix the APISet behavior. $foreground_opts{'connectrec-on_idle'} = sub { $idled_out = 1 }; } AGAIN: { use warnings; try { $self = $pkg->SUPER::start(%foreground_opts, %opts); } catch NACL::C::Exceptions::VolumeMove::IsAFlexClone with { my $exception = shift; NACL::STask::VolumeClone->split( %common_params_with_ci, flexclone => $opts{volume}, vserver => $opts{vserver}, ); no warnings; redo AGAIN; } catch NACL::APISet::Exceptions::TimeoutException with { my $exception = $_[0]; if ($idled_out) { $Log->exit() if $may_exit; NACL::Exceptions::Timeout->throw('The volume move operation ' . 'is occurring in the foreground, but no output has ' . "been displayed for $max_idle seconds, so the job has " . 'probably stalled', timeout => $max_idle ); } else { $Log->exit() if $may_exit; $exception->throw(); } }; } $self->destination_aggregate($opts{'destination-aggregate'}); # Create an object for the job component created $self->job_component(${$opts{job_component}}); if (defined $self->job_component) { my $job_id = $self->job_component->id; my $dsid = NACL::CS::Volume->fetch( %common_params_with_ci, filter => {%primary_keys}, requested_fields => ['dsid'] )->dsid(); $self->destination_volume_name( 'temp__' . $dsid . '__' . $job_id . '__' . $opts{volume}); } my $state; my $wait_time = $timeout; if ($wait) { STATE: { try { my $vol_mov = NACL::CS::VolumeMove->fetch( command_interface => $opts{command_interface}, filter => { vserver => $opts{vserver}, volume => $opts{volume}}, requested_fields => [qw(state)], ); $state = $vol_mov->state(); if($state !~ /done/i) { if($wait_time > 0){ sleep(5); $wait_time -= 5; goto STATE; } else { NACL::Exceptions::Timeout->throw("After waiting for $timeout " . "seconds the job did not complete") if(!$wait_time); } } } catch NACL::Exceptions::NoElementsFound with { my $ex = shift; if($ex->text() =~ /No matching volume move/i) { $Log->comment("No volume move status to fetch,continue.."); } else { $ex->throw(); } }; } $pkg->check_moved_volume_online( command_interface => $opts{command_interface}, 'method-timeout' => $timeout, volume => $opts{volume}, vserver => $opts{vserver}, polling_interval => POLL_DELTA ); if ($verify) { $self->verify(); } if ($was_unmounted) { # It seems this sleep is needed to perform mount operation on the # volume after it came online Tharn::snooze(1); NACL::STask::Volume->mount(%mount_unmount_opts); } } ## end if ($wait) $Log->exit() if $may_exit; return $self; } ## end sub start =head2 wait_for_move $volmove_task_obj->wait_for_move( "method-timeout" => $timeout, job_component => $last_job, %wait_options ); or NACL::STask::VolumeMove->wait_for_move( command_interface => $node, volume => $vol_name, vserver => $vserver_name, "method-timeout" => $timeout, job_component => $last_job, %wait_options ); (Class or Instance method) This function takes job component as input and waits for the job until the move operation is completed.If job component is not provided then it polls C for CMode and C for 7Mode until the move operation is complete. =over =item Options =over =item B<<"method-timeout"=> $scalar>> (Optional) The timeout value for volume move operation to complete and the volume to reach online state in the destination aggregate. default 500 sec =item B<<"polling_interval"=> $scalar>> (Optional) The polling time interval value for waiting job completion. default 10 sec =item B<< job_component => $last_job >> (Required) This is a reference to job component of the move job from NACL::STask::VolumeMove->start(..., nacltask_wait => 0, job_component => \$last_job); =item B<< destination-aggregate => $aggregate_name >> (Not applicable for instance method calls. For class method calls, it is optional for CMode and mandatory for 7Mode) Name of the destination aggregate. Specifying this option enables the task to ensure that the moved volume is present in the destination aggregate and in the online state apart from waiting on the job or querying on the status of volume move operation. =back =over =item Exceptions =over =item C This type of exception is thrown when either of destination-aggregate is not specified or volume move operation has not been completed with in $timeout seconds. =back =cut sub wait_for_move { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { job_component => { isa => 'NACL::C::Job', optional => 1 }, _optional_scalars(qw(polling_interval destination-aggregate )) }, ); my $last_job = delete $opts{job_component} || $pkg_or_obj->job_component(); my $timeout = delete $opts{"method-timeout"} || DEFAULT_TIMEOUT; my $polling_interval = delete $opts{polling_interval} || POLL_DELTA; my $volume = delete $opts{volume}; my $vserver = delete $opts{vserver}; my $command_interface = delete $opts{command_interface}; my $aggregate = delete $opts{'destination-aggregate'}; if ( !$aggregate && ref($pkg_or_obj) && $pkg_or_obj->destination_aggregate_isset ) { $aggregate = $pkg_or_obj->destination_aggregate; } my $pkg; $pkg = ref($pkg_or_obj) ? ref($pkg_or_obj) : $pkg_or_obj; if ( !$aggregate and !$last_job ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "destination-aggregate has to" . " be specified if job_component not provided." ); } my %aggr_args = (); if ( defined $aggregate ) { $aggr_args{aggregate} = $aggregate; } if ($last_job) { $Log->debug("Wait for completion"); $pkg_or_obj->wait_on_job( job_component => $last_job, 'method-timeout' => $timeout, polling_interval => $polling_interval, ); } elsif ( $command_interface->mode() eq '7Mode' ) { my $found = 1; my $end_time = time() + $timeout; while ($found) { # Querying NACL::CS::VolumeMove will give the # status of volume move operation. # Assuming the final state for vol move is "cutover" try { $pkg->wait_on_attribute( volume => $volume, command_interface => $command_interface, 'method-timeout' => $timeout, attribute_to_check => "state", till_value => ["cutover"], valid_intermediate_states => [ "setup", "move" ], polling_interval => $polling_interval ); Tharn::snooze(POLL_DELTA); } ## end try catch NACL::Exceptions::NoElementsFound with { # When volume move operation is complete # then the volume entry is removed from # "volume move status" $found = 0; }; if ( time() > $end_time ) { NATE::BaseException->throw( "wait_for_move timed out after" . " waiting for $timeout seconds" ); } } ## end while ($found) } $pkg->check_moved_volume_online( command_interface => $command_interface, 'method-timeout' => $timeout, volume => $volume, vserver => $vserver, polling_interval => $polling_interval, %aggr_args ); $Log->exit() if $may_exit; } ## end sub wait_for_move =head2 check_moved_volume_online NACL::STask::VolumeMove->check_moved_volume_online( command_interface => $command_interface, aggregate => $aggregate, 'method-timeout' => $timeout, volume => $volume, vserver => $vserver, polling_interval => $polling_interval ); or $volmov_obj->check_moved_volume_online( aggregate => "test1", 'method-timeout' => 200, polling_interval=>10 ); (Class or Instance method) CMode/7Mode CLI/ZAPI This method can be used wait until the specified volume on a particular aggregate comes online. =over =item Options =over =item C<< apiset_must => $ruleset >> (Optional) A hashref constraining which API (mode, interface, etc.) on the given command_interface will be used. See L =item C<< apiset_should => $ruleset >> (Optional) A hashref suggesting which API (mode, interface, etc.) on the given command_interface will be used. See L =item C<< 'method-timeout' => $timeout_in_seconds >> (Required) We can use this option to specify a timeout value, until which the volume is polled for online status. if the time exceeds timout value then a timeout exception is thrown =item C<< 'polling_interval' => $polling_time_interval >> (Required) Polling time to check volume status. =item C<< 'aggregate' => $aggregate_name >> (optional) Name of the aggregate on which the volume resides. =item C<< 'volume' => $volume_name >> (Required for Class method call) Name of the volume. =item C<< 'vserver' => $vserver_name >> (Required for Class method call) Name of the vserver =back =over =item Exceptions =over =item C This type of exception is thrown when timeout occured while waiting for the specified volume on a particular aggregate to come online. =back =cut sub check_moved_volume_online { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { aggregate => { type => SCALAR, optional => 1 }, 'method-timeout' => { type => SCALAR }, polling_interval => { type => SCALAR }, }, ); my $volume = delete $opts{volume}; my $vserver = delete $opts{vserver}; my $aggregate = delete $opts{aggregate}; my $timeout = delete $opts{'method-timeout'}; my $polling_interval = delete $opts{polling_interval}; # if vol move failed to start a job. # query NACL::CS::Volume to see status. my $found = 1; my $end_time = time() + $timeout; my $vol_cs_obj; while ($found) { try { $vol_cs_obj = NACL::CS::Volume->fetch( %opts, requested_fields => ['state'], filter => { volume => $volume, vserver => $vserver, aggregate => $aggregate } ); if ( $vol_cs_obj->state ne 'online' ) { my $vol_obj = $vol_cs_obj->get_component_instance(); $vol_obj->wait_on_attribute( 'method-timeout' => $timeout, attribute_to_check => "state", till_value => ["online"], polling_interval => $polling_interval ); } $found = 0; } ## end try catch NACL::Exceptions::NoElementsFound with { # When volume move operation is complete # then the volume entry is removed from # "volume move status" Tharn::snooze(POLL_DELTA); }; if ( time() > $end_time ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "check_moved_volume_online timed out after" . " waiting for $timeout seconds" ); } } ## end while ($found) $Log->exit() if $may_exit; } ## end sub check_moved_volume_online =head2 verify $volmove_obj->verify(); (Instance method)CMode This method can be used to perform sanity checks at the end of a volume move operation. =over =item C<< apiset_must => $ruleset >> (Optional) A hashref constraining which API (mode, interface, etc.) on the given command_interface will be used. See L =item C<< apiset_should => $ruleset >> (Optional) A hashref suggesting which API (mode, interface, etc.) on the given command_interface will be used. See L =item C<< 'method-timeout' => $timeout_in_seconds >> (Required) We can use this option to specify a timeout value, until which the volume is polled for online status. if the time exceeds timout value then a timeout exception is thrown =item It checks for the following conditions: =over =item The source volume should not be on-disk quiesced =item The source volume should not be in-memory quiesce =item The 'is-moving' flag should be set to false =item The 'move-target' flag should be set to false =item The destination volume should have been deleted =item There should not be any move generated snapshots on the source volume =item There should not be any snaptags by move on any snapshots =back =back =over =item Exceptions =over =item C In case of any of the below mentioned conditions being true, an exception of this type will be thrown. * The source volume should not be on-disk quiesced * The source volume should not be in-memory quiesce * The 'is-moving' flag should be set to false * The 'move-target' flag should be set to false * The destination volume should have been deleted * There should not be any move generated snapshots on the source volume * There should not be any snaptags by move on any snapshots =back =cut sub verify { $Log->enter() if $may_enter; my $self = shift; my %opts = $self->_common_validate_with( params => \@_, additional_spec => { 'destination-aggregate' => { type => SCALAR, optional => 1 }, polling_interval => { type => SCALAR, optional => 1 }, job_component => { type => SCALAR, optional => 1 }, }, ); my $last_job = delete $opts{job_component}; my $command_interface = delete $opts{command_interface}; my $polling_interval = delete $opts{polling_interval} || POLL_DELTA; my $volume = delete $opts{volume}; my $vserver = delete $opts{vserver}; my $aggregate = delete $opts{'destination-aggregate'}; # make sure that the source volume is unquiesced, and is not marked as # a move destination volume my $state = NACL::CS::Volume->fetch( %opts, command_interface => $self->command_interface, filter => { vserver => $self->vserver, volume => $self->volume }, requested_fields => ['is-quiesced-on-disk', 'is-quiesced-in-memory', 'is-moving', 'move-target' ], ); if ($state->{'is_quiesced_on_disk'} eq 'true') { $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw( "The source volume . " . $self->volume . " is still" . " quiesced on disk"); } if ($state->{'is_quiesced_in_memory'} eq 'true') { $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw( "The source volume . " . $self->volume . " is still" . " quiesced in memory"); } if ($state->{'move_target'} eq 'true') { $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw( "The source volume . " . $self->volume . " is marked" . "as a move destination volume "); } #destination volume should be deleted try { my $dest_vol_name = $self->destination_volume_name(); if (defined $dest_vol_name) { my $dest_vol = NACL::C::Volume->find( %opts, command_interface => $self->command_interface, filter => { vserver => $self->vserver, volume => $dest_vol_name }, ); $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw("Temporary volume " . $dest_vol_name . " left around"); } } catch NACL::Exceptions::NoElementsFound with { $Log->debug("Destination volume is deleted"); }; if (defined $self->job_component) { #check for snapshots my @snapshots = undef; @snapshots = NACL::CS::VolumeSnapshot->fetch( %opts, command_interface => $self->command_interface, filter => { vserver => $self->vserver, volume => $self->volume }, requested_fields => ['snapshot', 'tags'], allow_empty => 1 ); my $job_id = $self->job_component->id; foreach my $snap (@snapshots) { if ($snap->snapshot =~ m/(ref_ss_volmove_initial_$job_id|ref_ss_volmove_$job_id)/) { $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw("Move generated snapshot " . $snap->snapshot . " is still left around"); } #check for tags my @tags = $snap->tags; foreach my $tag (@tags) { if ($tag =~ m/(VOPL_owner_$job_id)/) { $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw("tags for snapshot" . $snap->snapshot . " are left around."); } } } } $Log->exit() if $may_exit; } ## end verify =head2 enable_tracing NACL::STask::VolumeMove->enable_tracing() (Class method)CMode This method can be used to enable tracing for volume move operations. =over =item C (Optional) A list of node objects. If specified, will enable tracing only on those nodes. If not specified, it will enable tracing on all the nodes in the cluster. =over This method currently enables: =over =item Volume Move KSMF table tracing on: "dstVolMoveObject" "dstVolMoveObject_abort" "dstVolMoveObject_commitCutover" "dstVolMoveObject_setupCutover" "dstVolMoveObject_updatePhase" "srcVolMoveObject" "srcVolMoveObject_abort" "srcVolMoveObject_conclude" "srcVolMoveObject_dbladeCommit" "srcVolMoveObject_lockdown" "srcVolMoveObject_prepare" "srcVolMoveObject_setup" "srcVolMoveObject_setupTransfer" "srcVolMoveObject_transferFailed" "srcVolMoveObject_validate" =item Volume Move SK-Tracing =item BRE Transfer Engine SK-Tracing =back =back This is a node level command and hence it will switch on the tracing for all the volume move operations on that particular node. =back =cut sub enable_tracing { $Log->enter() if $may_enter; my $pkg = shift; my %opts = Params::Validate::validate( @_, { command_interface => { isa => 'NACL::C::CommandInterface::ONTAP' }, nodes => { type => ARRAYREF | UNDEF, optional => 1, callbacks => { 'undef or NACL::C::SystemNode' => sub { my $nodes = $_; foreach my $node (@$nodes) { if (!UNIVERSAL::isa($node, 'NACL::C::SystemNode')) { return 0; } } return 1; }, }, }, } ); my $command_interface = $opts{command_interface}; my @nodes = undef; if (!defined $opts{nodes}) { @nodes = NACL::C::SystemNode->find(); } else { @nodes = @{$opts{nodes}}; delete $opts{nodes}; } #1. enable ksmf table smdb tracing foreach my $node (@nodes) { foreach my $table (KSMF_TABLE_NAMES) { my $table_obj = undef; try { $table_obj = NACL::C::DebugSmdbTrace->modify( command_interface => $command_interface, table => $table, node => $node, 'process' => 'mgwd', 'action' => 'both' ); } catch NACL::APISet::Exceptions::NoMatchingEntriesException with { $table_obj = NACL::C::DebugSmdbTrace->create( command_interface => $command_interface, 'table' => $table, 'node' => $node, 'process' => 'mgwd', 'action' => 'both' ); }; } } #2. Enable sk-tracing on both the nodes foreach my $node (@nodes) { my $shell = $node->apiset( interface => 'CLI', set => 'Systemshell' ); #MOVE_AGENT sktrace, captures every trace from MOVEAGENT in the dblade $shell->execute_command( command => "\"/sbin/sysctl sysvar.sktrace.MOVEAGENT_enable=255\"", ); #Transfer Engine sktrace, this is capturing repl tracing at PRI_HIGH_MED #level $shell->execute_command( command => "\"/sbin/sysctl sysvar.sktrace.REPL_enable=2\"", ); } $Log->exit() if $may_exit; } ## end enable_tracing =head2 disable_tracing NACL::STask::VolumeMove->disable_tracing() (Class method)CMode This method can be used to disable tracing for volume move operations. =over =item C (Optional) A list of node objects. If specified, will enable tracing only on those nodes. If not specified, it will enable tracing on all the nodes in the cluster. =over This will disable: =over =item Volume Move KSMF table tracing =item Volume Move SK-Tracing =item BRE Transfer Engine SK-Tracing =back =back This is a node level command, and hence it will disable tracing for all the volume moves for that particular node. =back =cut sub disable_tracing { $Log->enter() if $may_enter; my $pkg = shift; my %opts = Params::Validate::validate( @_, { command_interface => { isa => 'NACL::C::CommandInterface::ONTAP' }, nodes => { type => ARRAYREF | UNDEF, optional => 1, callbacks => { 'undef or NACL::C::SystemNode' => sub { my $nodes = $_; foreach my $node (@$nodes) { if (!UNIVERSAL::isa($node, 'NACL::C::SystemNode')) { return 0; } } return 1; }, }, }, } ); my $command_interface = $opts{command_interface}; my @nodes = undef; if (!defined $opts{nodes}) { @nodes = NACL::C::SystemNode->find(); } else { @nodes = @{$opts{nodes}}; } #1. disable ksmf table smdb tracing foreach my $node (@nodes) { foreach my $table (KSMF_TABLE_NAMES) { my $prev_action = undef; my $table_obj = undef; try { $table_obj = NACL::C::DebugSmdbTrace->delete( command_interface => $command_interface, table => $table, node => $node, process => 'mgwd' ); } catch NACL::APISet::Exceptions::NoMatchingEntriesException with { $Log->debug("Tracing was not enabled for table " . $table); }; } } #2. Disable sk-tracing on both the nodes foreach my $node (@nodes) { my $shell = $node->apiset( interface => 'CLI', set => 'Systemshell' ); #MOVE_AGENT sktrace, captures every trace from MOVEAGENT in the dblade $shell->execute_command( command => "\"/sbin/sysctl sysvar.sktrace.MOVEAGENT_enable=0\"", ); #Transfer Engine sktrace, this is capturing repl tracing at PRI_HIGH_MED #level $shell->execute_command( command => "\"/sbin/sysctl sysvar.sktrace.REPL_enable=0\"", ); } $Log->exit() if $may_exit; } ## end disable_tracing =head2 get_destination_metafile_path my $file_path = $volmove_obj->get_destination_metafile_path(); (Instance method) CMode This method can be used to fetch the path for the destination metafile created during the volume move operation. =back =over =item Exceptions =over =item C This type of exception is thrown when the destination volume is not set in the object. =back =cut sub get_destination_metafile_path { $Log->enter() if $may_enter; my $obj = shift; $obj->_verify_invocation( style => 'instance_only' ); my %opts = $obj->_common_validate_with(params => \@_); my $volume = delete $opts{volume}; my $vserver = delete $opts{vserver}; my $dest_vol_name = $obj->destination_volume_name(); if (!defined $dest_vol_name) { $Log->exit() if $may_exit; NATE::BaseException->throw("The destination volume is not set in the object"); } my $info = NACL::CS::Volume->fetch( %opts, command_interface => $opts{command_interface}, filter => { volume => $dest_vol_name, vserver => $obj->{vserver} }, requested_fields => ['aggregate', 'uuid'], ); $Log->debug("The destination volume path is " . $info->aggregate() . "/metadir/vvols/" . $info->uuid() . "/volops_status"); $Log->exit() if $may_exit; return $info->aggregate() . "/metadir/vvols/" . $info->uuid() . "/volops_status"; } ## end get_destination_metafile_path =head2 wait_until_hard_deferred $volmove_task_obj->wait_until_hard_deferred( "method-timeout" => $timeout, %wait_options ); or NACL::STask::VolumeMove->wait_until_hard_deferred( command_interface => $node, volume => $vol_name, vserver => $vserver_name, "method-timeout" => $timeout, %wait_options ); (Class or Instance method) This method can be used to wait until the phase of the volume move operation becomes cutover_hard_deferred. =over =item Options =over =item B<<"method-timeout"=> $scalar>> (Optional) The timeout value for volume move operation phase to reach cutover_hard_deferred state. =item B<<"polling_interval"=> $scalar>> (Optional) The polling time interval value for waiting move operation phase to reach cutover_hard_deferred state. =back =over =item Exceptions =over =item C This type of exception is thrown when volume move operation has not been completed within $timeout seconds. =back =cut sub wait_until_hard_deferred { $Log->enter() if $may_enter; my ($pkg_or_obj, %opts) = @_; $pkg_or_obj->wait_on_attribute( %opts, attribute_to_check => 'phase', till_value => qr/cutover_hard_deferred/i, ); $Log->exit() if $may_exit; } ## end sub wait_until_hard_deferred 1;