# # Copyright (c) 2014 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary VserverServicesLdapClient Task Module ## @author rahula@netapp.com, dl-nacl-dev ## @status shared ## @pod here package NACL::STask::VserverServicesLdapClient; use strict; use warnings; use base qw(NACL::C::VserverServicesLdapClient NACL::STask::STask); use Params::Validate qw(:all); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::STask::_VserverServices qw(:all); use NACL::C::DiagSecdConfiguration; use NACL::Exceptions::ReplayNotComplete; use NACL::APISet::Exceptions::CommandFailedException qw(:try); =head1 NAME NACL::STask::VserverServicesLdapClient =head1 DESCRIPTION C provide methods to create & purge vserver services ldap client in ONTAP. It is created on top of C component Since it is derived class of C, we can use all the methods of C from the object of this task. =head1 CLEANUP METHODS Cleanup can be registered for the following methods. Cleanup methods are, VserverServicesLdapClient Method Cleanup Method --------------------------------------------------------- create purge =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the node to which to send commands. See L. =head2 vserver (Required) The name of the Vserver to be used. =head2 client_config This parameter specifies the name of the LDAP client configuration, used to identify it uniquely. =head1 METHODS =head2 create my $ldapclient_obj = NACL::STask::VserverServicesLdapClient->create( command_interface => $ci, 'client-config' => $Client_config, servers => [$Ldap_server_ip], schema => $Schema, vserver => $TestVserver, 'nacltask_if_exists' => $action # default die 'nacltask_to_cleanup' => 1, #default 0 'nacltask_cleanup_manager' => $CleanupObj, %other_options, ); (Class Method) This method is used to configure LDAP client on the particular vserver. If the LDAP client already exists, it will perform the action based on "nacltask_if_exists" parameter. Default behavior would be "die". =over =item Options =over =item C<< command_interface => $command_interface >> (Required) See L =item C<< 'client-config' => $config >> (Required) The client configuration name which is required for creating LDAP client. =item C<< secd_replay_check => 0|1 >> (Optional) If '1', To verify whether secd replay is completed or not If '0' (default), not to verify, whether secd replay is completed or not =item C<< 'nacltask_replay_timeout' => $nacltask_retry_timeout >> (Optional) The retry timeout for secd replay The default value of nacltask_replay_timeout is 30 =item C<< 'nacltask_replay_interval' => $nacltask_retry_interval >> (Optional) The retry interval for secd replay The default value of nacltask_replay_interval is 5 =item C<< servers => [ $server1 $server2 ..] >> (Required,Arrayref) The servers which is supported for the LDAP configuration. =item C<< 'vserver' => $vserver >> (Required) Name of the vserver to be used. =item C<< "nacltask_if_exists => $action" >> (Optional) What to do if the LDAP client to be configured already exists. If $action is "die", then fail with NACL::C::Exceptions::VserverServicesLdapClient::AlreadyExists. If $action is "purge", then purge the existing LDAP client configuration(see the "purge" method, below) before creating a new one. If $action is "reuse", It will return the object of existing LDAP client configuration for the vserver. =item C<< "nacltask_verify => $action" >> (Optional) The user of this library can specify to verify whether the LDAP client is configured or not. If the action is 0, which is default, verification is not done. If the user sets the action to 1, it will verify it using the component state of this library. =item C<< "_was_created => \$scalar" >> (Optional) When this option is provided with a reference to a scalar variable, the variable gets filled in with a boolean value describing whether the LDAP client was available (value will be 0; this scenario is possible when if_exists => "reuse") or whether the LDAP client was created (value will be 1). This is necessary to determine whether the LDAP client configuration needs to be cleaned up later. my $was_created; my $ldap_client_obj = NACL::STask::VserverServicesLdapClient->create( nacltask_if_exists => 'reuse', _was_created => \$was_created, %other_opts ); # Operate on $ldap_client_obj here # Now determine whether to clean up the ldap, since we're not sure # whether we reused an existing ldap or created a new one if ($was_created) { # New LDAP configuration. Clean it up. $ldap_client_obj->purge(); } =item C<< nacltask_to_cleanup => 0|1 >> (Optional, default to 0(no to cleanup) Flag indicating if this operation needs to be registered for clean up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to be used for registering. Default : Will use the default cleanup manager. =item C The options accepted for MCC configuration replication verification is documented at L. =back command_interface, apiset_must, apiset_should, etc. All of the other various options, See L<< NACL::C::VserverServicesLdapClient::create|lib-NACL-C-VserverServicesLdapClient-pm/create >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to configure LDAP client on a given vserver when it already exists. =item C This type of exception is thrown when verification fails for the LDAP client configured on a given vserver. =item C The options accepted for MCC configuration replication verification is documented at L. =back =back =cut sub create { $Log->enter() if $may_enter; my ( $pkg, %args) = @_; my $secd_replay_check = delete $args{secd_replay_check} || 0; my @params = %args; my $self = $pkg->_create_helper(@params); $pkg->_query(%args) if($secd_replay_check == 1); $Log->exit() if $may_exit; return $self; } ## end sub create =head2 modify NACL::STask::VserverServicesLdapClient->modify( command_interface => $ci, vserver => $TestVserver, nacltask_to_cleanup => 1, %other_options ); or $ldapClient_obj->modify(%opts); (Class or Instance method) Modify ldap client options. This method will modify the options present for the specified ldap client. =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. =item other options See Lmodify|lib-NACL-C-VserverServicesLdapClient-pm/modify> for all the other options accepted by this method. =back =back =cut sub modify { $Log->enter() if $may_enter; my ( $pkg_or_obj, %args ) = @_; my $secd_replay_check = delete $args{secd_replay_check} || 0; my @params = %args; my %opts = $pkg_or_obj->_common_validate_with( params => \@params, 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); $pkg_or_obj->_query(%args) if($secd_replay_check == 1); # 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; } ## end sub modify =head2 purge (Class or instance method) This method is used to remove LDAP configured for a particular vserver. =over =item Options =over =item C<< command_interface => $ci >> (Required for class method, Not Applicable for instance method) A component object that represents the host which to send commands. See L. =item C<< 'vserver' => $vserver >> (Required for class method, Not Applicable for instance method) Name of the vserver used for LDAP client. =item C<< 'client-config' => $config >> (Required for class method, Not Applicable for instance method) Name of the LDAP client configuration used. =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<< nacltask_if_purged => $nacltask_if_purged >> (Optional) If 'pass', It will pass if the LDAP client configuration is already deleted. If 'fail' (default), NACL::C::Exceptions::VserverServicesLdapClient::DoesNotExist type of exception is raised. =item C The options accepted for MCC configuration replication verification is documented at L. =back All of the other various options, See L<< NACL::C::VserverServicesLdapClient::delete|lib-NACL-C-VserverServicesLdapClient-pm/delete >> =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete LDAP client configuration on a given vserver whch does not exists. =item C This type of exception is thrown when verification fails for the deleted LDAP client configuration. =back =back =cut sub purge { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $pkg_or_obj->_purge_helper( @_ ); $Log->exit() if $may_exit; } ## end sub purge sub _query { my($obj, %opts)= @_; my $vserver = $opts{'vserver'}; my %validate_spec = ( %{NACL::C::Component->_common_validate_spec()}, nacltask_replay_timeout => {type => SCALAR , default => 30}, nacltask_replay_interval => {type => SCALAR , default => 5}, ); my %params = validate_with( params => \%opts, spec => \%validate_spec, allow_extra => 1 ); my $nacltask_options = {}; $obj->_move_nacltask_options( source => \%params, target => $nacltask_options ); # get rid of extra options (if any) my %new_opts; $obj->_hash_move( source => \%params, 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(); # fetch the values for timeout and interval my $timeout = $nacltask_options->{nacltask_replay_timeout}; my $interval = $nacltask_options->{nacltask_replay_interval}; #If the Command interface is 7 Mode, the replay check is not needed if ($mode ne 'CMode' || $timeout <= 0) { $Log->exit() if $may_exit; return; } # The Configuration replay needs to be verified on all the nodes and the command diag secd configuration query # takes node as a parameter and hence we need all the node names in the cluster. my @nodes = (); # The system node and diag secd configuration query commands will not be available in vserver context hence we need to fetch it from the # NACL::C::Node if ($command_interface->isa('NACL::C::Vserver')) { @nodes = NACL::C::Node->find(); $command_interface = $nodes[0]; } else { @nodes = NACL::C::SystemNode->find( command_interface => $command_interface); } my @node_names = map { $_->node() } @nodes; my $elapsed_time = 0; my ($start_time, $end_time); # In order to avoid more iterations we will start with wait for a minimum of nacltask_replay_interval seconds my $replay_status = 0; my $response = $command_interface->apiset()->debug_smdb_table_show( table => "vserver_id_to_name", name => $vserver , ); my $id = $response->get_parsed_output()->[0]->{id}; my $query = "vserver=".$id; my @query_string= ($query); Tharn::snooze($interval); foreach my $node_name (@node_names) { $replay_status = 0; $elapsed_time = $interval; ## The total time taken for all configuration replay on all the nodes should not exceed nacltask_replay_timeout value while ($elapsed_time <= $timeout) { $start_time = time(); my $output; my $retry_count = 6; SecD_config: { try { $output = NACL::C::DiagSecdConfiguration->query( command_interface => $command_interface, node => $node_name, 'source-name' => "ldap-client-config", 'query-string' => \@query_string, ); } catch NACL::APISet::Exceptions::CommandFailedException with { my $ex = shift; if ( $ex->text() =~ /SecD Error: Internal marshaling error/i && $retry_count-- > 0 ) { if ( ! defined $opts{'bind-password'} ) { NATE::BaseException->throw ( "In order to resolve \"Internal marshaling error\" you need to pass \"bind-password\" while invoking NACL::STask::VserverServicesLdapClient->create/modify call." ); } NACL::C::VserverServicesLdapClient->modify( command_interface => $command_interface, 'vserver' => $vserver, 'client-config' => $opts{'client-config'}, 'bind-password' => $opts{'bind-password'}, ); Tharn::snooze(3); redo SecD_config; ##retrying 6 times. } $ex->throw(); }; } ##end SecD_config: if($output->{'client_config'} eq $opts{'client-config'}) { $replay_status = 1; last; } Tharn::snooze($interval); $end_time = time(); $elapsed_time += ($end_time - $start_time); } $Log->exit() if $may_exit; NACL::Exceptions::ReplayNotComplete->throw( "Replay did not complete within $timeout seconds on all the nodes in the cluster" ) if (!$replay_status); } $Log->exit() if $may_exit; } 1;