# # Copyright (c) 2001-2014 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Lun STask Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::STask::Lun; use strict; use warnings; use base qw(NACL::C::Lun 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 BOOLEAN SCALAR ARRAYREF HASHREF SCALARREF OBJECT validate_with ); use NATE::Exceptions::Argument qw(:try); use NACL::ComponentUtils qw(Dumper); use constant LUN_TIMEOUT => 60; use NATE::BaseException; use NACL::Exceptions::CompareLunFailure; use Time::Local; use NACL::STask::Volume; use NACL::C::Client; =head1 NAME NACL::STask::Lun =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to Lun's 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 Lun-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 path (Required) As C. The path of the lun. =head1 METHODS =head2 create my $lun = NACL::STask::Lun->create( command_interface => $command_interface, lun => $lun_name, volume => $volume_name, vserver => $vserver_name, if_exists => $action, # default 'die' nacltask_verify => 1, # default 0 %other_options ); my $lun = NACL::STask::Lun->create( command_interface => $command_interface, path => $lun_path, if_exists => $action, # default 'die' nacltask_verify => 1, # default 0 %other_options ); (Class method) Create a Lun. This method provides additional services beyond what's inherent to the product's lun creation commands, as controlled and described by the new C and C options. Note that the arguments C, C are optional for this method. Refer to the section "Arguments defaulted/auto-determined" below for for a description of the values auto-assigned to these arguments if they are not supplied by the caller. =over =item Options =over =item C<< if_exists=>$action >> B, use C instead. (This has been retained only for backwards compatibility). =item C<< nacltask_if_exists=>$action >> (Optional) What to do if the lun to be created already exists. If $action is "die" (the default), then fail with an exception (in the same way that the lun component would have: by trying the lun create and letting the product complain about the lun already existing). If action is "reuse", then do nothing (return a task object referring to the existing lun). If action is "purge", then delete the lun (see the C method, below) before creating a new one. =item C<< nacltask_verify=>$boolean >> (Optional) Verify the created lun attributes from C with the inputs provided to the task.Throw a exception when there is a difference in input and C state attributes. =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 volume was found (value will be 0; this scenario is possible when if_exists => "reuse") or whether the lun was created (value will be 1). This is necessary to determine whether the lun needs to be cleaned up later. my $was_created; my $lun_obj = NACL::STask::Lun->create( if_exists => 'reuse', _was_created => \$was_created, %other_opts ); # Operate on $lun_obj here # ... # Now determine whether to clean up the volume, since we're not sure # whether we reused an existing lun or created a new one if ($was_created) { # New volume was created. Clean it up. $lun_obj->purge(); } =item C<< lun=>$lun_name >> (Required) Name of the Lun. =item C<< client_command_interface => $client >> (Optional) Represents NACL::C::Client from which ostype of lun is determined. Note that this argument is mutually exclusive with C. =item C<< "method-timeout" => $timeout >> (Optional) As component method-timeout, controls how long the command will wait before completing. However, the default timeout has been raised to 60 seconds. =item command_interface, apiset_must, apiset_should, lun_name, volume, size, qtree, etc. All of the other various options to C<< NACL::C::Lun->create|lib-NACL-C-Lun-pm/create >> =back =item Arguments defaulted/auto-determined =over =item C If volume is not specified, then it will attempt to find a suitable volume which is online , not a vserver root volume , also having suitable space to create the lun. After determining this , it use _construct_path to construct the value for path. Note that , this will happen only if user did not pass 'path' parameter. =item C Note that if this is not specified, then it will attempt to find a suitable volume which is online , not a vserver root volume , also having suitable space to create the lun. After determining this , it use _construct_path to construct the value for path. =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. =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to create a lun that already exists. =item C This type of exception is thrown when verification fails for the created lun. =back =back =cut sub create { $Log->enter() if $may_enter; my $pkg = shift; # We cannot directly use _if_exists_validate_spec since it defaults # the value to "die". This would mean that we would not know whether # if_exists or nacltask_if_exists was provided. my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { # Kept for backwards compatibility if_exists => { type => SCALAR, callbacks => $pkg->_if_exists_validate_callback(), optional => 1 }, nacltask_if_exists => { type => SCALAR, callbacks => $pkg->_if_exists_validate_callback(), optional => 1 }, nacltask_verify => { type => SCALAR, default => 0 }, _was_created => { type => SCALARREF, optional => 1 }, volume => { type => SCALAR, optional => 1 }, client_command_interface => { type => OBJECT, isa => "NACL::C::Client", optional => 1 }, $pkg->_cleanup_validate_spec(), }, allow_extra => 1, ignore_primary_keys => 1, ); # move the cleanup related options to separate hash my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); my $if_exists = delete $opts{nacltask_if_exists} || delete $opts{if_exists}; # Having the "delete $opts{if_exists}" on the previous line would not # necessarily delete it, because it would not get evaluated if # the "delete $opts{nacltask_if_exists}" returned some value. Hence, # we're explicitly deleting it here separately. delete $opts{if_exists}; # Assign the default of "die" if neither of nacltask_if_exists or if_exists # were provided. $if_exists ||= 'die'; my $nacltask_verify = delete $opts{nacltask_verify}; my $dummy; my $was_created = delete $opts{_was_created} || \$dummy; $$was_created = 0; my $client = delete $opts{client_command_interface}; if ( defined $client ) { $opts{ostype} = $client->get_lun_ostype(); } my %common_opts; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); if ( ! defined $opts{path} ) { if ( ! defined $opts{volume} ) { my $size = $opts{size} || '20MB' ; my $volume_obj = NACL::STask::Volume->find( %common_opts, filter => { vserver => $opts{vserver}, vsroot => 'false' , available => ">=$size", state => 'online' }, ); $opts{volume} = $volume_obj->volume(); } $pkg->_construct_path(params => \%opts); } $opts{'method-timeout'} ||= LUN_TIMEOUT; my $self; CREATE: { use warnings qw(exiting); try { $self = $pkg->SUPER::create(%opts); $$was_created = 1; if ($nacltask_verify) { # TODO : Handle input UnitNormalization for size "mb,gb,etc" # as output gives bytes. $self->taskverify_create(); } ## end if ($nacltask_verify) } ## end try catch NACL::C::Exceptions::Lun::AlreadyExists with { my $exception = shift; # covers CMode CLI and ZAPI, 7M CLI and ZAPI $Log->trace('The lun 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; } }; } ## end CREATE: # 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 $Lun->purge(); (or) NACL::STask::Lun->purge( command_interface => $command_interface, lun => $lun_name, volume => $volume_name, vserver => $vserver_name, nacltask_if_purged => $if_purged, nacltask_verify => 1, # default 0 %other_options ); NACL::STask::Lun->purge( command_interface => $command_interface, path => $lun_path, nacltask_verify => 1, # default 0 nacltask_if_purged => $if_purged, %other_options ); (Class or Instance method) Deletes the lun Uses a CMode CLI/ZAPI and 7Mode CLI. =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 class method, Not Applicable for instance method) The name of the vserver. =item C<< lun=>$lun_name >> (Required) Name of the Lun. =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 lun did not exist (value will be 0; this scenario is possible when nacltask_if_purged => "pass") or whether the lun was deleted (value will be 1). =item C<< nacltask_if_purged=>die|pass >> (Optional) If "die", exception is thrown when lun to be deleted does not exist If "pass", exits quietly when lun to be deleted does not exist command_interface, apiset_must, apiset_should, etc. All of the other various options to NACL::C::Lun->delete. =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete lun that does not exists. =item C This type of exception is thrown when verification fails for the deleted lun. =back =back =cut sub purge { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %orig_opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { nacltask_verify => { type => BOOLEAN, default => 0 }, nacltask_if_purged => { type => BOOLEAN, default => 0 }, _was_deleted => { type => SCALARREF, optional => 1 }, }, allow_extra => 1, ); my %nacltask_opts; $pkg_or_obj->_move_nacltask_options( source => \%orig_opts, target => \%nacltask_opts ); my $verify = $nacltask_opts{'nacltask_verify'}; my $if_purged = $nacltask_opts{nacltask_if_purged}; my $was_deleted = delete $orig_opts{_was_deleted}; $$was_deleted = 0; try { $pkg_or_obj->delete( force => 1, %orig_opts); $$was_deleted = 1; } catch NACL::C::Exceptions::Lun::DoesNotExist with { my $e = shift; # Check if the command failed because the lun # does not exist if ( $if_purged !~ /pass/i ) { $Log->exit() if $may_exit; $e->throw(); } }; if ( $verify ) { $pkg_or_obj->_generic_purge_verify(%orig_opts); } $Log->exit() if $may_exit; } ## end sub purge =head2 map my $lun = NACL::STask::Lun->map( command_interface => $command_interface, lun => $lun_name, volume => $volume_name, vserver => $vserver_name, igroup => $igroup, nacltask_if_mapped_igroup => $action, # default 'die' %other_options ); $lun->map(igroup => $igroup, nacltask_if_mapped_igroup => $action, # default 'die' %other_options ); (Class and Instance method) Map a Lun to the given igroup. This method provides additional services beyond what's inherent to the product's lun map command, as controlled and described by the new C and C options. =over =item Options =over =item C<< nacltask_if_mapped_igroup=>$action >> (Optional) What to do if the lun to be mapped is already mapped to this igroup. If $action is "die" (the default), then fail with an exception (in the same way that the lun component would have: by trying the lun map and letting the product complain about the lun already mapped to this igroup). If action is "reuse", then do nothing (return a task object referring to the existing mapped lun). If action is "remap", then unmap the lun (see the C method, below) before mapping it again to the igroup . =item C<< nacltask_verify=>$boolean >> (Optional) Verify the lun attribute to make sure that it is mapped Default value is 0. If the value is set to 1 , then verify. =item C<< _was_mapped => \$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 lun was already mapped (value will be 0; this scenario is possible when nacltask_if_mapped_igroup => "reuse") or whether the lun was mapped (value will be 1). =item C<< lun=>$lun_name >> (Required) Name of the Lun. =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. =item C<< "method-timeout" => $timeout >> (Optional) As component method-timeout, controls how long the command will wait before completing. However, the default timeout has been raised to 60 seconds. =item command_interface, apiset_must, apiset_should,volume, lun_id,lun etc. All of the other various options to C<< NACL::C::Lun->map|lib-NACL-C-Lun-pm/map >> =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to map lun that is already mapped. =item C This type of exception is thrown when verification fails for the mapped lun. =back =back =cut sub map { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { nacltask_if_mapped_igroup => $pkg_or_obj->_if_already_mapped_igroup_spec(), nacltask_verify => { type => SCALAR, default => 0 }, _was_mapped => { type => SCALARREF, optional => 1 }, }, allow_extra => 1, ); my $pkg = ref $pkg_or_obj || $pkg_or_obj; # move the cleanup related options to separate hash my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); $opts{'method-timeout'} ||= LUN_TIMEOUT; my $if_mapped_igroup = delete $opts{'nacltask_if_mapped_igroup'}; my $verify = delete $opts{'nacltask_verify'}; my $was_mapped = delete $opts{_was_mapped}; $$was_mapped = 0; try { $pkg->SUPER::map(%opts); $$was_mapped = 1; } catch NACL::C::Exceptions::Lun::AlreadyMapped with { my $exception = shift; if ( $if_mapped_igroup =~ /^die$/i ) { $Log->exit() if $may_exit; $exception->throw(); } elsif ( $if_mapped_igroup =~ /^remap$/i ) { my %unmap_opts; $pkg->_hash_copy( source => \%opts, target => \%unmap_opts, copy => [ keys %{ $pkg->_common_validate_spec() }, $pkg->get_primary_keys(), 'igroup' ] ); $pkg->unmap(%unmap_opts); $pkg->SUPER::map(%opts); } ## end elsif ( $if_mapped_igroup...) }; if ($verify) { $pkg->verify_map(%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' => 'unmap' ); if ($nacltask_to_cleanup) { my $other_opts = {'igroup' => $opts{'igroup'}}; $pkg->_register_for_cleanup(%opts_for_register, 'other_opts' => $other_opts); } $Log->exit() if $may_exit; } ## end sub map # method verify_map called by map() method of this package. # It verifies the attribute of the lun to make sure that it is mapped. sub verify_map { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, allow_extra => 1 ); my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my ( %common_opts, %primary_keys ); $pkg->_hash_move( source => \%opts, target => \%common_opts, move => [ keys %{ $pkg->_common_validate_spec_without_ci() } ] ); my $out = { $pkg->_primary_keys_validate_spec() }; my @output = keys %$out; $pkg->_hash_move( source => \%opts, target => \%primary_keys, move => \@output ); $Log->debug( sub { "Opts to find " . NACL::ComponentUtils::Dumper( \%opts ) } ); $Log->debug( sub { "Primary Key to find" . NACL::ComponentUtils::Dumper( \%primary_keys ); } ); $Log->debug( sub { "common_opts to find" . NACL::ComponentUtils::Dumper( \%common_opts ); } ); my $lunobj = $pkg->find( command_interface => $opts{'command_interface'}, filter => \%primary_keys, %common_opts ); $lunobj->verify_state( mapped => 'mapped' ); $Log->exit() if $may_exit; } ## end sub verify_map =head2 unmap my $lun = NACL::STask::Lun->unmap( command_interface => $command_interface, lun => $lun_name, volume => $volume_name, vserver => $vserver_name, igroup => $igroup, %other_options ); $lun->unmap( igroup => $igroup, %other_options ); (Class and Instance method) Unmap a Lun from the given igroup. This method provides additional services beyond what's inherent to the product's lun map command, as controlled and described by the new C options. =over =item Options =over =item C<< nacltask_if_unmapped_igroup=>$action >> (Optional) What to do if the lun to be unmapped is already unmapped from this igroup. If $action is "die" (the default), then fail with an exception (in the same way that the lun component would have: by trying the lun unmap and letting the product complain about the lun already unmapped from this igroup). If $action is pass , then just exit quietly if the lun is already unmapped from this igroup =item C<< _was_unmapped => \$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 lun was already unmapped (value will be 0; this scenario is possible when nacltask_if_unmapped_igroup => "pass") or whether the lun was unmapped (value will be 1). =item C<< lun=>$lun_name >> (Required) Name of the Lun. =item C<< "method-timeout" => $timeout >> (Optional) As component method-timeout, controls how long the command will wait before completing. However, the default timeout has been raised to 60 seconds. =item command_interface, apiset_must, apiset_should, volume,lun All of the other various options to C<< NACL::C::Lun->unmap|lib-NACL-C-Lun-pm/unmap >> =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to unmap lun which is not mapped. =back =back =cut sub unmap { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { nacltask_if_unmapped_igroup => $pkg_or_obj->_if_action_completed_already_validate_spec( name => 'nacltask_if_unmapped_igroup' ), _was_unmapped => { type => SCALARREF, optional => 1 }, }, allow_extra => 1, ); my $pkg = ref $pkg_or_obj || $pkg_or_obj; $opts{'method-timeout'} ||= LUN_TIMEOUT; my $if_unmapped_igroup = delete $opts{'nacltask_if_unmapped_igroup'}; my $was_unmapped = delete $opts{_was_unmapped}; $$was_unmapped = 0; try { $pkg->SUPER::unmap(%opts); $$was_unmapped = 1; } catch NACL::C::Exceptions::Lun::NotMapped with { my $exception = shift; if ( $if_unmapped_igroup =~ /^die$/i ) { $Log->exit() if $may_exit; $exception->throw(); } }; $Log->exit() if $may_exit; } ## end sub unmap # A validate spec to be used by map() method. It indicates the action # that needs to be taken if the lun is already mapped to the igroup given # The values that could be taken by the spec are 'die', 'reuse','remap'. sub _if_already_mapped_igroup_spec { return { type => SCALAR, default => 'die', callbacks => { "The value of nacltask_if_mapped_igroup should be one of 'die','reuse', 'remap'" => sub { $_[0] =~ /^(die)|(reuse)|(remap)$/ } } }; } ## end sub _if_already_mapped_igroup_spec =head2 verify_lun_operation_attributes NACL::STask::Lun->verify_lun_operation_attributes ( pre_operation_object => $pre_op_obj, post_operation_object => $post_op_obj, operation => $lon_operation ); (Class method) Validate the LUN configuration againest pre_operation_object and post_operation_object based upon operation performed. If the post_operation_object does not matched according to operation performed, the method will raise a exception(NACL::Exceptions::VerifyFailure). =over =item Options =over =item C<< pre_operation_object => $pre_operation_object >> (Required) A state object of LUN. This object should be taken before performing the operation. =item C<< post_operation_object => $post_operation_object >> (Required) A state object of LUN. This object should be taken after performing the operation. =item C<< operation => $operation >> (Required) Name of the operation which are performed in pre_operation_object. The supported operation are: 1. volume_copy 2. volume_clone 3. LUN_clone_create_noreserve 4. SFSR_out_of_place 5. SFSR_in_place 6. volume_snapshot_restore 7. volume_move 8. PFSR 9. LUN_clone_create 10. restore_from_tape 11. snapmirror_update 12. sub_LUN_clone_create 13. default This Link describes the changes in LUN configuration across various data management operations. This is especially important in the case of operations that change the contents of a LUN - restore of a single LUN, volume snaprestore, etc. The intent is get the data in the LUN, of course, from the source/snapshot - what happens to its configuration needs more attention. http://wikid.netapp.com/w/Smokejumper/Bcom/Documents/VDOM_DS#LUN_Configuration_and_Data_Management_Operations =item C<< validation_hash => $validation_hash >> (Optional) A User defined hashref for validation. Values of validation_hash will take a priority over Task defined validation hash. See C<< operation >> for detail. =item C<< nacltask_verify_uniqueness => 0|1 >> (Optional) Default to '0' This will verify the uniqueness of the lun fields that are related to ids like. device_text_id,device_binary_id,uuid,device_legacy_id,vdiskId,serial. If '1', This will verify the uniqueness of the fields . If '0', This will skip the verification of the uniqueness. =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to call 'verify_lun_operation_attributes' method as an instance method or When operation is not supported for Lun validation or When number of keys in pre operation & post operation objects are not same. =back =back =cut sub verify_lun_operation_attributes { $Log->enter() if $may_enter; my $pkg = shift; $Log->trace( "Opts to 'NACL::STask::Lun::verify_lun_operation_attributes:' front end are:\n" . Dumper( \@_ ) ); if ( ref($pkg) ) { NATE::BaseException->throw( "NACL::STask::Lun::verify_lun_operation_attributes: This method is not an instance " . "method" ); } my %opts = NACL::C::Component->_common_validate_with( params => \@_, additional_spec => { pre_operation_object => { type => OBJECT, callbacks => { "object provided must be of type 'NACL::(C/CS/STask)::Lun'" => sub { return 0 if ( ref( $_[0] ) !~ /^NACL::(C|CS|STask)::Lun/ ); return 1; } } }, post_operation_object => { type => OBJECT, callbacks => { "object provided must be of type 'NACL::(C/CS/STask)::Lun'" => sub { return 0 if ( ref( $_[0] ) !~ /^NACL::(C|CS|STask)::Lun/ ); return 1; } } }, operation => { type => SCALAR }, validation_hash => { type => HASHREF, optional => 1 }, nacltask_verify_uniqueness => { type => BOOLEAN, default => 1 } }, allow_extra => 1, ignore_primary_keys => 1, no_command_interface => 1 ); my $operation = delete $opts{operation}; my $base_obj = delete $opts{pre_operation_object}; my $cmp_obj = delete $opts{post_operation_object}; my $user_validation_spec = delete $opts{validation_hash} if ( defined $opts{validation_hash} ); my $verify_unique = delete $opts{nacltask_verify_uniqueness}; my @luns; my $command_interface = $cmp_obj->command_interface(); # converting to state object if ( ref($base_obj) !~ /NACL::CS::/ ) { $Log->debug( 'pre_operation_object is not a NACL::CS object, converting it into CS' ); $base_obj = $base_obj->state(); } ## end if ( ref($base_obj) !~...) if ( ref($cmp_obj) !~ /NACL::CS::/ ) { $Log->debug( 'post_operation_object is not a NACL::CS object, converting it into CS' ); $cmp_obj = $cmp_obj->state(); } ## end if ( ref($cmp_obj) !~ ...) my $validation_for_new = sub { my ( $key, $base_obj_value, $cmp_obj_value ) = @_; # if nacltask_verify_uniqueness is '0', then skipping this check return 1 if ( $verify_unique == 0 ); @luns = NACL::CS::Lun->fetch( command_interface => $command_interface ) if ( $#luns <= 0 ); my $count = 0; foreach (@luns) { $count++ if ( $_->$key() eq $cmp_obj_value ); } if ( $count > 1 ) { $Log->debug( "FAIL: Found $count duplicates of $key field $cmp_obj_value"); return 0; } return 1; }; my $validation_for_new_time_diff = sub { my ( $key, $base_obj_value, $cmp_obj_value ) = @_; $base_obj_value =~ s/^"|"$//g; $cmp_obj_value =~ s/^"|"$//g; my $diff = _calculate_time_diff( time_1 => $cmp_obj_value, time_2 => $base_obj_value ); if ( $diff <= 0 ) { $Log->debug( "FAIL: time difference for $key field is less than 0" . ", Field = new" . ", Pre operation time = " . $base_obj_value . ", Post operation time = " . $cmp_obj_value ); return 0; } ## end if ( $diff <= 0 ) return 1; }; # data structure for validating both the objects # new: got the new/unique value and should not match with others in some cases # unassigned: NULL or '-' value # no-change: value same as pre-operation-object # not-requried: validation is not requried # undef: same as unassigned # others value: should match with respective ds value my $config = { 'device_text_id' => { new_code_validation_spec => $validation_for_new, 'default' => 'no-change', 'volume_copy' => 'new', 'volume_clone' => 'new', 'LUN_clone_create_noreserve' => 'unassigned', 'SFSR_out_of_place' => 'unassigned', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'unassigned', 'restore_from_tape' => 'unassigned', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'block_size' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'no-change', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'undef', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'undef', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'undef' }, 'creation_timestamp' => { new_code_validation_spec => $validation_for_new_time_diff, 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'new', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'new', 'restore_from_tape' => 'new', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'device_binary_id' => { new_code_validation_spec => $validation_for_new, 'default' => 'no-change', 'volume_copy' => 'new', 'volume_clone' => 'new', 'LUN_clone_create_noreserve' => 'unassigned', 'SFSR_out_of_place' => 'unassigned', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'unassigned', 'restore_from_tape' => 'unassigned', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'inconsistent_blocks' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'not-requried', 'SFSR_out_of_place' => 'not-requried', 'SFSR_in_place' => 'not-requried', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'not-requried', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'uuid' => { new_code_validation_spec => $validation_for_new, 'default' => 'no-change', 'volume_copy' => 'new', 'volume_clone' => 'new', 'LUN_clone_create_noreserve' => 'new', 'SFSR_out_of_place' => 'new', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'new', 'restore_from_tape' => 'new', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'state' => { 'default' => 'no-change', 'volume_copy' => 'online', 'volume_clone' => 'online', 'LUN_clone_create_noreserve' => 'online', 'SFSR_out_of_place' => 'online', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'online', 'restore_from_tape' => 'offline', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'size' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'no-change', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'device_legacy_id' => { new_code_validation_spec => $validation_for_new, 'default' => 'no-change', 'volume_copy' => 'new', 'volume_clone' => 'new', 'LUN_clone_create_noreserve' => 'unassigned', 'SFSR_out_of_place' => 'unassigned', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'unassigned', 'restore_from_tape' => 'unassigned', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'ostype' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'no-change', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'vdiskId' => { new_code_validation_spec => $validation_for_new, 'default' => 'no-change', 'volume_copy' => 'new', 'volume_clone' => 'new', 'LUN_clone_create_noreserve' => 'new', 'SFSR_out_of_place' => 'new', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'new', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'size_used' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'not-requried', 'SFSR_out_of_place' => 'not-requried', 'SFSR_in_place' => 'not-requried', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'not-requried', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'inconsistent_filesystem' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'not-requried', 'SFSR_out_of_place' => 'not-requried', 'SFSR_in_place' => 'not-requried', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'not-requried', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'suffix_size' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'no-change', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'serial' => { new_code_validation_spec => $validation_for_new, 'default' => 'no-change', 'volume_copy' => 'new', 'volume_clone' => 'new', 'LUN_clone_create_noreserve' => 'new', 'SFSR_out_of_place' => 'new', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'new', 'restore_from_tape' => 'new', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'prefix_size' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'no-change', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'space_alloc' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'disabled', 'SFSR_out_of_place' => 'disabled', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'disabled', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'space_reserve' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'new', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'debug' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'no-change', 'SFSR_out_of_place' => 'no-change', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'no-change', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'nvfail' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'false', 'LUN_clone_create_noreserve' => 'false', 'SFSR_out_of_place' => 'false', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'false', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'false', 'sub_LUN_clone_create' => 'not-requried' }, 'mapped' => { 'default' => 'no-change', 'volume_copy' => 'unmapped', 'volume_clone' => 'unmapped', 'LUN_clone_create_noreserve' => 'unmapped', 'SFSR_out_of_place' => 'unmapped', 'SFSR_in_place' => 'no-change', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'unmapped', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' }, 'read_only' => { 'default' => 'no-change', 'volume_copy' => 'no-change', 'volume_clone' => 'no-change', 'LUN_clone_create_noreserve' => 'not-requried', 'SFSR_out_of_place' => 'not-requried', 'SFSR_in_place' => 'not-requried', 'volume_snapshot_restore' => 'no-change', 'volume_move' => 'no-change', 'PFSR' => 'not-requried', 'LUN_clone_create' => 'not-requried', 'restore_from_tape' => 'no-change', 'snapmirror_update' => 'no-change', 'sub_LUN_clone_create' => 'not-requried' } }; # Over-writing the user define hash to standerd config if ( defined $user_validation_spec ) { foreach my $key (%$user_validation_spec) { $config->{$operation}->{$key} = $user_validation_spec->{$key}; } } # Validation for requested operation is supported by the task if ( !exists( $config->{'read_only'}->{$operation} ) ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "$operation operation is not supported for Lun validation by the task" ); } ## end if ( !exists( $config->...)) # Validate for numbers of keys should be same if ( keys %$base_obj ne keys %$cmp_obj ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "number of keys in pre operation & post operation objects are not same" ); } ## end if ( keys %$base_obj ne...) my @keys = keys %$base_obj; my $unexpected_value = {}; foreach my $key (@keys) { $Log->debug("Verifying $key field for $operation"); if ( $key eq 'command_interface' ) { next; } elsif ( !exists $config->{$key} ) { $Log->debug( "IGNORE: $key is not defined under $operation validation specification. So ignoring it.." ); next; } elsif ( $config->{$key}->{$operation} eq 'no-change' ) { if ( $base_obj->{$key} ne $cmp_obj->{$key} ) { $Log->debug( "FAIL: $key should be equal." . " Field = no-change" . ", Expected Value = " . $base_obj->{$key} . ", Post ops Value = " . $cmp_obj->{$key} ); $unexpected_value->{$key} = { pre_value => $base_obj->{$key}, post_value => $cmp_obj->{$key}, rule => $config->{$key}->{$operation} }; } ## end if ( $base_obj->{$key}...) } elsif ( $config->{$key}->{$operation} eq 'not-requried' ) { $Log->debug("IGNORE: $key Validation skip Field = not-requried"); next; } elsif ( $config->{$key}->{$operation} eq 'unassigned' or $config->{$key}->{$operation} eq 'undef' ) { if ( !( $cmp_obj->{$key} eq '-' || $cmp_obj->{$key} eq '' ) ) { $Log->debug( "FAIL: $key value should be unassgined." . "Field = unassgined/undef" . ", Expected Value = '-' or ''" . ", Post ops Value = " . $cmp_obj->{$key} ); $unexpected_value->{$key} = { pre_value => $base_obj->{$key}, post_value => $cmp_obj->{$key}, rule => $config->{$key}->{$operation} }; } ## end if ( !( $cmp_obj->{$key...})) } elsif ( $config->{$key}->{$operation} eq 'new' ) { if ( $base_obj->{$key} eq $cmp_obj->{$key} ) { $Log->debug( "FAIL: $key value should be new." . "Field = new" . ", Pre ops Value = " . $base_obj->{$key} . ", Post ops Value = " . $cmp_obj->{$key} ); $unexpected_value->{$key} = { pre_value => $base_obj->{$key}, post_value => $cmp_obj->{$key}, rule => $config->{$key}->{$operation} }; #} elsif ($verify_unique && exists($config->{$key}->{new_code_validation_spec})) { } else { $unexpected_value->{$key} = { pre_value => $base_obj->{$key}, post_value => $cmp_obj->{$key}, rule => $config->{$key}->{$operation} } if ( $config->{$key} ->{new_code_validation_spec}( $key, $base_obj->{$key}, $cmp_obj->{$key} ) == 0 ); } ## end else [ if ( $base_obj->{$key}...)] } else { if ( $cmp_obj->{$key} ne $config->{$key}->{$operation} ) { $Log->debug( "FAIL: $key should be same." . " Field = REST" . ", Actual Value = " . $cmp_obj->{$key} . ", Expected Value = " . $config->{$key}->{$operation} ); $unexpected_value->{$key} = { pre_value => $base_obj->{$key}, post_value => $cmp_obj->{$key}, rule => $config->{$key}->{$operation} }; } ## end if ( $cmp_obj->{$key} ...) } ## end else [ if ( $key eq 'command_interface')] } ## end foreach my $key (@keys) if ( keys( %{$unexpected_value} ) > 0 ) { NACL::Exceptions::VerifyFailure->throw( "verify_lun_operation failed.", unexpected_values => $unexpected_value ); } ## end if ( keys( %{$unexpected_value...})) $Log->exit() if $may_exit; } ## end sub verify_lun_operation_attributes sub _calculate_time_diff { $Log->enter() if $may_enter; my %opts = validate_with( params => \@_, spec => { 'time_1' => { regex => qr/^\d{1,2}\/\d{1,2}\/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}$/ }, 'time_2' => { regex => qr/^\d{1,2}\/\d{1,2}\/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}$/ }, }, ); my @time1 = split( /\/|\s|:/, $opts{time_1} ); my @time2 = split( /\/|\s|:/, $opts{time_2} ); $time1[0] = $time1[0] - 1; $time2[0] = $time2[0] - 1; my $epoch_time_1 = timelocal( ( map { s/^0*//; $_ } ( @time1[ 5, 4, 3, 1, 0, 2 ] ) ) ); my $epoch_time_2 = timelocal( ( map { s/^0*//; $_ } ( @time2[ 5, 4, 3, 1, 0, 2 ] ) ) ); $Log->exit() if $may_exit; return $epoch_time_1 - $epoch_time_2; } ## end sub _calculate_time_diff =head2 find_luns_with_duplicate_ids NACL::STask::Lun->find_luns_with_duplicate_ids ( command_interface => $ci, fields_to_verify => $field, filter => \%filter, duplicates=> \@duplicates ); (Class method) Verifies that all the existing LUN requested fields are unique. If request field is not unique in a cluster , method will raise a exception(NATE::BaseException). =over =item Options =over =item C<< command_interface => $command_interface >> (Required) As C. A component object that represents the node to which to send commands. =item C<< fields_to_verify => $field >> (Optional) default to ['serial', 'uuid', 'vdiskId'] A list of lun field(ARRAYREF) to check for uniqueness. It should be single/combination of 'serial|vdiskId|uuid' field. =item C<< filter => \%filter >> (Optional) A HASHREF filters that are valid for the lun show command. =item C<< duplicates => \@duplicates >> (Optional) A REFERENCE to an array, where all the duplicated serial numbers will be stored. =back =item Exceptions =over =item C This type of exception is thrown when find_luns_with_duplicate_ids is called as an instance method. OR This type of exception is thrown when lun is not unique. =back =back =cut sub find_luns_with_duplicate_ids { $Log->enter() if $may_enter; my $pkg = shift; $Log->trace( "Opts to 'NACL::STask::Lun::find_luns_with_duplicate_ids:' front end are:\n" . Dumper( \@_ ) ); if ( ref($pkg) ) { NATE::BaseException->throw( "NACL::STask::Lun::find_luns_with_duplicate_ids: This method is not an instance " . "method" ); } my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { fields_to_verify => { type => ARRAYREF, callbacks => { "fields_to_verify should be combination of 'serial|vdiskId|uuid' field" => sub { my @arr = @{ $_[0] }; foreach (@arr) { return 0 if ( $_ !~ /(serial)|(vdiskId)|(uuid)/ ); } return 1; } }, default => [ 'serial', 'uuid', 'vdiskId' ], }, filter => { type => HASHREF, optional => 1 }, duplicates => { type => HASHREF, optional => 1 } }, allow_extra => 1, ignore_primary_keys => 1 ); my $duplicates = delete $opts{duplicates} || {}; my $field_to_verify = delete $opts{fields_to_verify}; my $lun_details = {}; my $result = 1; my @luns; # As part of burt fix 796311 we are making the call to go through CLI until # burt 796361 get fixed. if ( grep( /vdiskId/, @$field_to_verify ) ) { @luns = NACL::CS::Lun->fetch(%opts,apiset_must =>{ interface => "CLI" }); } else { @luns = NACL::CS::Lun->fetch(%opts); } foreach my $field (@$field_to_verify) { foreach my $lun_obj (@luns) { if ( !defined $lun_details->{$field}->{ $lun_obj->$field() } ) { $lun_details->{$field} = {} if ( !exists( $lun_details->{$field} ) ); $lun_details->{$field}->{ $lun_obj->$field() } = 1; } else { $lun_details->{$field}->{ $lun_obj->$field() }++; } } ## end foreach my $lun_obj (@luns) while ( my ( $value, $count ) = each( %{ $lun_details->{$field} } ) ) { if ( $count > 1 ) { if ( !exists( $duplicates->{$field} ) ) { $duplicates->{$field} = [$value]; } else { push @{ $duplicates->{$field} }, $value; } } ## end if ( $count > 1 ) } ## end while ( my ( $value, $count...)) } ## end foreach my $field (@$field_to_verify) $Log->debug( 'Details of LUN. ' . Dumper($lun_details) ); $Log->debug( 'Details of LUN duplicates. ' . Dumper($duplicates) ); if ( keys( %{$duplicates} ) > 0 ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Lun is not unique." . Dumper($duplicates) ); } $Log->exit() if $may_exit; } ## end sub find_luns_with_duplicate_ids =head2 compare_luns NACL::STask::Lun->compare_luns( command_interface => $command_interface, lun1 => $spec1, lun2 => $spec2, skip => @skip_fields, must_mismatch => @mismatch_fields, %other_options ); (Only Class method) Compare two luns . 1. CS objects would be obtained for the two luns which need to be compared. (when the options lun1 and lun2 are not given CS objects by users) 2. Using the obtained objects it will get all the attributes which needs to be compared. 3. All attributes other than those specified in 'skip parameter would be compared and mismatch would be ensured for the attributes specified in 'must_mismatch'. If there are any other extra or less mismatches than what is specified in 'must_mismatch' exception of type NACL::Exceptions::CompareLunFailure would be thrown. =over =item Examples NACL::STask::Lun->compare_luns( skip => ('ostype', 'state'), must_mismatch => ('serial', 'uuid', 'volume', 'lun-path'), lun1 => $path1, lun2 => $path2 ); Supports Cmode CLI =item Options =over =item C<< skip => @skip_fields >> (Required) Arrayref of fields to ignore during compare. For example you may skip lun state if it's not important. =item C<< must_mismatch => @mismatch_fields >> (Required) Arrayref containing list of attributes which must mismatch. For example lun's serial number must be different for same lun on source and destination. =item C<< lun1 =>$spec1 >> (Required) Path of the first Lun or Object of type NACL::C::Lun or NACL::STask::Lun or NACL::CS::Lun. =item C<< lun2 =>$spec2 >> (Required) Path of the second Lun or Object of type NACL::C::Lun or NACL::STask::Lun or NACL::CS::Lun. =item command_interface, apiset_must, apiset_should, method-timeout. =back =item Exceptions =over =item C This type of exception is thrown when comparison of two lun's fail. =back =back =cut sub compare_luns { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { lun1 => { type => SCALAR | OBJECT, callbacks => { "Value of 'lun1' must be a string or a Object of type" . " NACL::STask::Lun or NACL::C::Lun or NACL::CS::Lun" => sub { return ( ( ( $_[0] ) =~ /\S+/i ) || $_[0]->isa('NACL::C::Lun') || $_[0]->isa('NACL::CS::Lun') ); } } ## end }, lun2 => { type => SCALAR | OBJECT, callbacks => { "Value of 'lun2' must be a string or a Object of type" . " NACL::STask::Lun or NACL::C::Lun or NACL::CS::Lun" => sub { return ( ( ( $_[0] ) =~ /\S+/i ) || $_[0]->isa('NACL::C::Lun') || $_[0]->isa('NACL::CS::Lun') ); } } ## end }, skip => { type => ARRAYREF }, must_mismatch => { type => ARRAYREF, }, command_interface => { isa => 'NACL::C::CommandInterface::ONTAP', optional => 1 }, }, ignore_primary_keys => 1, only_static_method => 1 ); if ( !defined $opts{command_interface} ) { unless ( ref $opts{'lun1'} || ref $opts{'lun2'} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( " The command_interface needs to be provided in the input " . " as both 'lun1' and 'lun2' are not objects of type CS/C/STask " ); } } my ( $sec_lun_obj, $frst_lun_state_obj, @lun_obj, $val ); my %common_opts; my $skip_fields = delete $opts{'skip'}; my $must_mismatch_fields = delete $opts{'must_mismatch'}; $pkg->_copy_common_component_params( source => \%opts, target => \%common_opts ); # Anonymus sub routine to fetch the CS objects # for the luns which needs to be compared my $var = sub { my ( $option_name, $val , $other_val ) = @_; if ( ref $val ) { if ( !( $val->isa('NACL::CS::ComponentState') ) ) { push @lun_obj, $val->state(); } else { push @lun_obj, $val; } } else { ## Hitting this loop means that $val is not an object ## So set the value of command-interface: ## either from the input opts or from the other object my $command_interface = $opts{command_interface} || $other_val->command_interface(); my $obj = NACL::CS::Lun->fetch( %common_opts, command_interface => $command_interface, filter => { 'path' => $val }, ); push @lun_obj, $obj; } }; $var->( "lun1", $opts{'lun1'}, $opts{'lun2'} ); $var->( "lun2", $opts{'lun2'}, $opts{'lun1'} ); my %hash; $frst_lun_state_obj = $lun_obj[0]->get_attributes(); $sec_lun_obj = $lun_obj[1]; # Delete the fields of "skip_fields" from the # state object of first lun foreach (@$skip_fields) { delete $$frst_lun_state_obj{$_}; } # constructing a hash from the state object # of first lun foreach ( keys(%$frst_lun_state_obj) ) { $hash{$_} = $lun_obj[0]->$_(); } try { $sec_lun_obj->verify_fields(%hash); } catch NACL::Exceptions::VerifyFailure with { my $e = shift; my $unexpected_values = $e->unexpected_values(); my $mismatched = 0; my $must_mismatch_str = join( '&&', sort(@$must_mismatch_fields) ); my $unexpected_values_str = join( '&&', sort( keys %{$unexpected_values} ) ); my ( $missing_expected_mismatches, $unexpected_lun_values ); if ( $must_mismatch_str ne $unexpected_values_str ) { my %must_mismatch_hash = map { $_ => 1 } @$must_mismatch_fields; my $i = -1; foreach my $field ( keys %{$unexpected_values} ) { unless ( exists $must_mismatch_hash{$field} ) { # # Extra mismatch $i++; $unexpected_lun_values->[$i]->{'attribute'} = $field; $unexpected_lun_values->[$i]->{'lun1_value'} = $hash{$field}; $unexpected_lun_values->[$i]->{'lun2_value'} = $sec_lun_obj->$field(); $mismatched = 1; } else { delete $must_mismatch_hash{$field}; } } if ( scalar( keys %must_mismatch_hash ) ) { my @keys = keys %must_mismatch_hash; for ( my $j = 0; $j < @keys; $j++ ) { # These are expected mismatches that didn't happen $missing_expected_mismatches->[$j]->{'attribute'} = $keys[$j]; $missing_expected_mismatches->[$j]->{'value'} = $hash{ $keys[$j] }; } } } if ($mismatched) { # Calling the format_output() to format the exception object # values in a nicer way my $msg = $pkg->format_output( unexpected_values => $unexpected_lun_values, missing_expected_mismatches => $missing_expected_mismatches, frst_lun_obj => $lun_obj[0], sec_lun_obj => $lun_obj[1] ); $Log->exit() if $may_exit; NACL::Exceptions::CompareLunFailure->throw( $msg, unexpected_values => $unexpected_lun_values, missing_expected_mismatches => $missing_expected_mismatches ); } }; $Log->exit() if $may_exit; } ## end sub compare_luns sub format_output { my ( $self, %opts ) = @_; my $frst_lun_obj = delete $opts{'frst_lun_obj'}; my $sec_lun_obj = delete $opts{'sec_lun_obj'}; my $msg = "\n\nComparison failed between first lun " . $frst_lun_obj->path() . "\nand second lun " . $sec_lun_obj->path() . "\n"; $msg .= sprintf( "\n%-20s%-20s%-30s\n", 'Fields', 'First Lun Values ', 'Second Lun Values' ); my @unexpected_lun_values_arr = @{ $opts{'unexpected_values'} }; foreach (@unexpected_lun_values_arr) { $msg .= sprintf( "\n%-20s%-20s%-30s\n", $_->{'attribute'}, $_->{'lun1_value'}, $_->{'lun2_value'} ); } if ( defined( @{ $opts{'missing_expected_mismatches'} } ) ) { my @missing_expected_mismatches_arr = @{ $opts{'missing_expected_mismatches'} }; foreach (@missing_expected_mismatches_arr) { my $lun1_value = "!" . $frst_lun_obj->{ $_->{attribute} }; $msg .= sprintf( "\n%-20s%-20s%-30s\n", $_->{'attribute'}, $lun1_value, $sec_lun_obj->{ $_->{attribute} } ); } } return $msg; } ## end sub format_output =head2 modify NACL::STask::Lun->modify( command_interface => $node, path => '/vol/' . $Volume . '/' . $Lun, vserver => $TestVserver, state => 'offline' ); or $lun_object->modify( "state" => "offline", ... ); (Class/Instance method) Modifies attributes of a Lun. Also verify that all attributes got set to the specified values if C is set to 1. =over =item Options =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) Verifies whether all of the attributes got set to the specified value. 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, 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. =back =item Other options =over See NACL::C::Lun->modify for all the other options accepted by this method. =back =item Exceptions =over =item C This type of exception is thrown when verification for lun modify has failed. =back =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_register, $register_for_cleanup, %opts_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; } =head2 parents @parents = $lun_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 iofence_operations NACL::STask::Lun->iofence_operations( command_interface => $command_interface, vserver => $vserver, path => $path, nacltask_iofence_action => $action, nacltask_verify => $boolean, # default 1 ); (or) NACL::STask::Lun->iofence_operations( command_interface => $command_interface, lun => $lun, volume => $volume, vserver => $vserver nacltask_iofence_action => $action, nacltask_verify => $boolean, # default 1 ); (or) $Lun_Object->iofence_operations( nacltask_iofence_action => $action, nacltask_verify => $boolean, # default 1 ); (Class method or Instance method) This method performs iofence operations on the specified lun. It will perform iofence set or iofence get or iofence clear based on the value of 'nacltask_iofence_action'. This method will perform the followning steps : 1 . Before performing any iofence operation on a lun, it would find the owning node of the lun. It would use the containing aggregate of the volume on which the lun is created for finding the owning node 2 . Using the DSID of a volume ,it would find the inode number and base generation number by executing "vdom dump" 3 . This method will take volume and lun or path as the input and perform iofence set or clear or get operation on it. Supports only Nodescope. =over =item Options =over =item C<< nacltask_iofence_action => $action >> (Required)Based on this value , appropriate iofence operation will get called. Possible values are 'set' or 'get' or 'clear' =item C<< nacltask_verify=>$boolean >> (Optional)Verify whether iofence operations have been configured on the specified lun. Verify whether iofence operations have been configured on the specified lun. =item C<< path => $path >> (Required only for Class call) The path to the LUN. =item C<< lun => $lun >> (Required only for Class call) Lun name . This can be either a scalar value or an object of type "NACL::STask::Lun" or "NACL::C::Lun". =item C<< volume => $volume >> (Required only for Class call, if path is not provided) Name of the volume on which the LUN is present. command_interface, apiset_must, apiset_should, etc. All of the other various options supported by NACL::C::Lun->iofence_get NACL::C::Lun->iofence_set NACL::C::Lun->iofence_clear NACL::CS::Volume->fetch NACL::CS::StorageAggregate->fetch =back =item Exceptions =over =item C This type of exception is thrown if parameter 'lun' is not defined for a class call. =item C This type of exception is thrown when verification for iofence operations on a particular lun has failed. =back =back =cut sub iofence_operations { my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { nacltask_verify => { type => SCALAR, default => 1 }, nacltask_iofence_action => { type => SCALAR }, }, allow_extra => 1, ); my $action = $opts{'nacltask_iofence_action'}; my $verify = $opts{'nacltask_verify'}; my $command_interface = $opts{command_interface}; my $vserver = $opts{vserver}; my ($result_obj, $volume, $msg, %args); if (! $opts{volume}) { $volume = $1 if ( $opts{path} =~ /\/vol\/(.*)\/.*/); } else { $volume = $opts{volume}; } my $vol = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => {"volume" => $volume , vserver => $vserver}, requested_fields => [qw( fsid dsid aggregate)], ); my $aggr = NACL::CS::Aggregate->fetch( command_interface => $command_interface, filter => {"aggregate" => $vol->aggregate}, requested_fields => [qw( owner-name )], ); my $node_command_interface = NACL::C::Node->find( command_interface => $command_interface, filter => { node => $aggr->owner_name() } ); my $response = $node_command_interface->get_7m_or_nodescope_apiset()->vdom_dump( "metafile" => "vtoc", "dsid" => $vol->dsid(), ); my $result = $response->get_parsed_output(); $args{command_interface} = $node_command_interface; $args{'inode-num'} = $result->[0]->{'base_fileid'}; $args{'generation-num'} = $result->[0]->{'record_contents'}->[0]->{'base_generation'}; $args{fsid} = $vol->fsid(); my $method = "iofence_$action"; $result_obj = NACL::C::Lun->$method(%args); if ($verify) { try { NACL::C::Lun->iofence_get(%args); } catch NACL::APISet::Exceptions::CommandFailedException with { $msg = 'Verification failed !! iofenece ' . $action . " operation failed!!\n"; $Log->exit() if $may_exit; NACL::Exceptions::VerifyFailure->throw($msg) if ( $action !~ /clear/); }; } $Log->exit() if $may_exit; ($action =~ /get/) ? return $result_obj : return ; } ## end sub iofence_operations 1;