# # Copyright (c) 2001-2011 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Utility methods for Tasks ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::TaskUtils; =head1 NAME NACL::TaskUtils =head1 DESCRIPTION C is a collection of utility methods for tasks. This currently consists of extracting common component parameters so they can be sent to the underlying component call(s). =cut use strict; use warnings; use feature qw(state); use Params::Validate qw(validate validate_with HASHREF BOOLEAN UNDEF OBJECT SCALAR ARRAYREF); use NACL::CS::ComponentState; use NACL::ComponentUtils qw(_hash_move _hash_copy); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::APISet::Exceptions::CommandNotFoundException qw/:try/; use NACL::APISet::Exceptions::MethodNotFoundException qw/:try/; use Scalar::Util qw/blessed/; use NACL::Exceptions::ReplayNotComplete (); use NACL::CS::DiagNbladeReplay; use NACL::CS::StorageFailover; BEGIN { use Exporter qw(import); our @EXPORT_OK = qw( _copy_nacltask_options _move_nacltask_options wait_for_replay _if_action_completed_already_validate_callback _if_action_completed_already_validate_spec _get_client_path_to_file ); } use NACL::C::Component; =head2 _copy_common_component_params L<_copy_common_component_params|lib-NACL-GeneralUtils-pm/_copy_common_component_params> =head2 _move_common_component_params L<_move_common_component_params|lib-NACL-GeneralUtils-pm/_move_common_component_params> =head2 _copy_common_component_params_with_ci L<_copy_common_component_params_with_ci|lib-NACL-GeneralUtils-pm/_copy_common_component_params_with_ci> =head2 _move_common_component_params_with_ci L<_move_common_component_params_with_ci|lib-NACL-GeneralUtils-pm/_move_common_component_params_with_ci> =head2 _copy_or_move_common_params L<_copy_or_move_common_params|lib-NACL-GeneralUtils-pm/_copy_or_move_common_params> =head2 _copy_nacltask_options $pkg_or_obj->_copy_nacltask_options( source => \%opts, target => \%nacltask_opts ); or my $nacltask_opts = $pkg_or_obj->_copy_nacltask_options( source => \%opts ); This method is used to copy task-specific options from a source hash-reference into a target hash-reference. (Those keys beginning with a "nacltask_" are task-specific) ("copy" denotes that these parameters will not be removed from the source hash-reference) =over =item Options =over =item C<< source => \%source >> (Mandatory, HASHREF) The source hash-reference from which to copy/move the common parameters. =item C<< target => \%target >> (Optional, HASHREF) The hash-reference into which to copy/move the common parameters. If not provided, then this method will create the target hash-reference. =back =back =cut sub _copy_nacltask_options { $Log->enter() if $may_enter; my $pkg_or_obj = shift; _copy_or_move_nacltask_options( @_, _copy => 1 ); $Log->exit() if $may_exit; } =head2 _move_nacltask_options Similar to L<_copy_nacltask_options|lib-NACL-TaskUtils-pm/_copy_nacltask_options>, however the task parameters are moved out of the source hash-reference and into the target hash-reference. (i.e. will be deleted from the source hash-reference). =cut sub _move_nacltask_options { $Log->enter() if $may_enter; my $pkg_or_obj = shift; _copy_or_move_nacltask_options( @_, _move => 1 ); $Log->exit() if $may_exit; } =head2 _copy_or_move_nacltask_options This method provides the implementation for the methods L<_copy_nacltask_options|lib-NACL-TaskUtils-pm/_copy_nacltask_options>, L<_move_nacltask_options|lib-NACL-TaskUtils-pm/_move_nacltask_options>, Task methods should not invoke this directly, but should instead go through any of the methods listed above. =over =item Options =over =item C<< source => \%source >> (Mandatory, HASHREF) The source hash-reference from which to copy/move the common parameters. =item C<< target => \%target >> (Optional, HASHREF) The hash-reference into which to copy/move the common parameters. If not provided, then this method will create the target hash-reference. =item C<< _copy => 0|1 >> (Optional, BOOLEAN) Either this parameter or '_move' should be provided. This specifies that the parameters should be copied. =item C<< _move => 0|1 >> (Optional, BOOLEAN) Either this parameter or '_copy' should be provided. This specifies that the parameters should be move. =back =back =cut # Internal helper, don't export this sub _copy_or_move_nacltask_options { $Log->enter() if $may_enter; my %opts = validate( @_, { source => { type => HASHREF }, target => { type => HASHREF, default => {} }, _copy => { type => BOOLEAN, optional => 1 }, _move => { type => BOOLEAN, optional => 1 }, } ); if ( !$opts{_copy} && !$opts{_move} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( "Internal error: Either of '_copy' " . "or '_move' should have been set to 1 in the call to " . "'_copy_or_move_nacltask_options()' but neither were" ); } if ( $opts{_copy} && $opts{_move} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( "Internal error: Either of '_copy' " . "or '_move' should have been set to 1 in the call to " . "_copy_or_move_nacltask_options()' but both were" ); } foreach my $key ( keys %{ $opts{source} } ) { if ( $key =~ /^nacltask_/ ) { $opts{target}->{$key} = $opts{source}->{$key}; delete $opts{source}->{$key} if $opts{_move}; } } $Log->exit() if $may_exit; } =head2 wait_for_replay This method can be used to wait for configuration changes to replayed to the nblade. More info here: http://wikid.netapp.com/w/User:Ernest/MP_N-Blade_Configuration_Status This method can be called from any STask or MTask (more specifically from any place that ends up using this package). This can either be called as an instance method or invoked as a package call. Keys not specific to the component like apiset_must are allowed. Usage: (with object) $task_object->wait_for_replay(); $task_object->wait_for_replay( nacltask_replay_interval => 15, nacltask_replay_timeout => 95 ); (with package name) NACL::STask::VserverCifsShare->wait_for_replay( command_interface => $c, nacltask_replay_timeout => 95, apiset_must => {interface => "CLI" } ); =over =item Options =over =item C<< command_interface => $cmd_intr_obj >> (Required for class method, Not Applicable for instance method, Command interface object) See L =item C<< nacltask_replay_timeout => $val >> (Optional, SCALAR) The total timeout before complaining that the replay didn't succeed. In order for this wait not to happen, timeout can be specified as 0. The default value is 30 seconds =item C<< nacltask_replay_interval => $val2 >> (Optional, SCALAR) The interval between two invocations of diag nblade replay show. The default value is 5 seconds =back =back =cut sub wait_for_replay { $Log->enter() if $may_enter; my $obj = shift; my %opts = @_; # only for command_interface, we extract the value in the object if not # already present in the list of arguments passed to this method # this is when this method is invoked on an Task object (STask OR MTask) if ( !$opts{command_interface} && blessed $obj && $obj->isa('NACL::C::Component') ) { $opts{command_interface} = $obj->command_interface(); @_ = %opts; } my %validate_spec = ( %{ NACL::C::Component->_common_validate_spec() }, nacltask_replay_timeout => { type => SCALAR, default => 30 }, nacltask_replay_interval => { type => SCALAR, default => 5 } ); %opts = validate_with( params => \@_, spec => \%validate_spec, allow_extra => 1 ); my $nacltask_options = {}; $obj->_move_nacltask_options( source => \%opts, target => $nacltask_options ); # get rid of extra options (if any) my %new_opts; $obj->_hash_move( source => \%opts, target => \%new_opts, move => [ keys %validate_spec ] ); # now %opts should not have any of the options specific to the component # involved (the one whose object / package name is used to invoke this # method) my $command_interface = $new_opts{command_interface}; my $mode = $command_interface->mode(); # default values for timeout and interval my $timeout = $nacltask_options->{nacltask_replay_timeout}; my $interval = $nacltask_options->{nacltask_replay_interval}; my $elapsed_time = 0; my ( $start_time, $end_time ); # Do nothing for 7Mode if ( $mode eq 'CMode' && $timeout > 0 ) { while ( $elapsed_time <= $timeout ) { # wait for a minimum of $interval seconds Tharn::snooze($interval); $start_time = time(); my @replays; try { @replays = NACL::CS::DiagNbladeReplay->fetch( %new_opts, allow_empty => 1, ); } catch NACL::APISet::Exceptions::CommandNotFoundException with { # incase the build doesn't have this command, just wait for the # duration ($timeout) before returning Tharn::snooze( $timeout - $interval ); goto LAST; } catch NACL::APISet::Exceptions::MethodNotFoundException with { # incase the cdef directory doesn't have this command do the # same as above Tharn::snooze( $timeout - $interval ); goto LAST; }; my $status = 1; if (@replays) { foreach my $r (@replays) { if ( $r->result() ne 'succeeded' ) { $status = 0; last; } } # if $status is still true, then the replay has finished # so return if ($status) { $Log->exit() if $may_exit; return; } } $end_time = time(); $elapsed_time += ( $end_time - $start_time + $interval ); } if ( $command_interface->can('node') ) { my $node = $command_interface->node(); my $sfo_state = NACL::CS::StorageFailover->fetch( 'command_interface' => $command_interface, 'filter' => { 'node' => $node } )->state_description(); if ( $sfo_state =~ /In takeover|Waiting for partner/i ) { goto LAST; } } $Log->exit() if $may_exit; NACL::Exceptions::ReplayNotComplete->throw( "Replay did not complete within $timeout"); } LAST: $Log->exit() if $may_exit; } =head2 _if_action_completed_already_validate_callback sub purge { my $pkg = shift; my %opts = validate(@_, { nacltask_if_purged => { %other_validation, callbacks => $pkg->_if_action_completed_already_validate_callback( name => 'nacltask_if_modified' ), }, ... } ); ... This method provides the Params::Validate callback for options such as C, C where the user specifies what action is to be performed if the operation is already completed. The callback validates that the value of the given option is 'die' or 'pass'. (See http://search.cpan.org/~drolsky/Params-Validate-0.95/lib/Params/Validate.pm#Callback_Validation for an explanation of callback validation) =over =item Options =over =item C<< name => $option >> (Optional, defaults to 'nacltask_if_purged') This argument is to be used to specify the option which is being validated. This is used in the callback error message. =back =back =cut sub _if_action_completed_already_validate_callback { my ( $pkg_or_obj, %opts ) = @_; # Assign 'nacltask_if_purged' as the default $opts{name} ||= 'nacltask_if_purged'; return { "Value of '$opts{name}' should be either 'die' or 'pass'" => sub { $_[0] =~ /^(die)|(pass)$/ } }; } =head2 _if_action_completed_already_validate_spec sub modify { my $pkg_or_obj = shift; $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { nacltask_if_modified => $pkg_or_obj->_if_action_completed_already_validate_spec( name => 'nacltask_if_modified' ), ... } ); ... } The validation spec for C, C sort of options where the user specifies what action to perform if the operation is already completed. This is a scalar, which defaults to 'die' and validates whether the specified value is either of 'die' or 'pass'. This method can only be used for validating options which takes either 'die' or 'pass' as the possible values. =over =item Options =over =item C<< name => $option >> (Optional, defaults to 'nacltask_if_purged') This argument is to be used to specify the option which is being validated. This is used in the callback error message. =back =back =cut sub _if_action_completed_already_validate_spec { my ( $pkg_or_obj, %opts ) = @_; return { type => SCALAR, default => 'die', callbacks => $pkg_or_obj->_if_action_completed_already_validate_callback( name => $opts{name} ) }; } sub _get_client_path_to_file { my (@args) = @_; state $spec = { mount_obj => {type => OBJECT, isa => 'NACL::C::Client::Mount'}, file_obj => {type => OBJECT, isa => 'NACL::C::VolumeFile'}, }; my %opts = validate_with( params => \@args, spec => $spec, ); my $mount_obj = $opts{mount_obj}; my $file_obj = $opts{file_obj}; return $mount_obj->mount_point() . "/" . $file_obj->get_file_or_dir_name_from_path(path => $file_obj->path()); } 1;