# $Id$ # # # Copyright (c) 2001-2015 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary SystemHaInterconnectLink Task Module ## @author sturgis@netapp.com, ng-interconnect-qa@netapp.com ## @burt 962756 Create STask NACL lib for SystemHaInterconnect*.pm ## @burt 988954 'channel show-status' gone; now 'channel show' ## @burt 1055335 Implement NACL::CS::SHIC ## @burt 1059512 Pass Nate Params to downstream thplets ## @burt 1108532 Failures on MCC-IP configuration # ## @summary SystemHaInterconnect STask Module ## @author ng-interconnect-qa ## @status OPEN package NACL::STask::SystemHaInterconnect; use strict; use warnings; use base qw(NACL::C::Component NACL::STask::STask); use List::Compare; use List::MoreUtils qw(any); use NACL::C::SystemNode; use NACL::Exceptions::InvalidParam qw(:try); use NACL::Exceptions::Timeout qw(:try); use NACL::Exceptions::VerifyFailure; use NACL::Expectation; use NATE::BaseException qw(:try); use NATE::Log qw(log_global); use Params::Validate qw(validate OBJECT SCALAR ARRAYREF UNDEF); use Readonly; use Class::MethodMaker [ scalar => [ { -type => 'NACL::C::CommandInterface::ONTAP' }, 'command_interface' ], array => 'this', scalar => 'method_timeout', scalar => 'node', scalar => 'pollwait_interval', ]; Readonly my $method_timeout => 120; Readonly my $pollwait_interval => 10; my $Log = log_global(); ################################################################################ # METHODS ################################################################################ ################################################################################ # Function : enable # Description : # The enable method, used to enable parts of or the entire HA Interconnect # Arguments : # this - Arrayref - (Required) What to operate on # Returns : none # Exceptions : none ################################################################################ sub enable { $Log->enter(); my ($pkg, %opts, ) = @_; # If this isn't supplied, assign it 'all' as an ARRAYREF obj if (!defined $opts{this}) { $opts{this} = ['all']; } # Assign the action, verify the options are valid, then go into the main # subroutine $opts{action} = 'enable'; $pkg->_verify_options(%opts); $pkg->_main_enable_disable(%opts); $Log->exit(); return; } # end enable() ################################################################################ # Function : disable # Description : # The disable method, used to disable parts of or the entire HA Interconnect # Arguments : # this - Arrayref - (Required) What to operate on # Returns : none # Exceptions : none ################################################################################ sub disable { $Log->enter(); my ($pkg, %opts, ) = @_; # If this isn't supplied, assign it 'links' as an ARRAYREF obj if (!defined $opts{this}) { $opts{this} = ['links']; } # Assign the action, verify the options are valid, then go into the main # subroutine $opts{action} = 'disable'; $pkg->_verify_options(%opts); $pkg->_main_enable_disable(%opts); $Log->exit(); return; } # end _disable_this() ################################################################################ # LOCAL ROUTINES ################################################################################ ################################################################################ # Function : _main_enable_disable # Description : # Based on the the method and parameters supplied, enable or disable # parts of, or the entire, HA Interconnect. # Arguments : # action - Scalar - (Required) What action to take (enable/disable) # command_interface - Scalar - (Required) NACL::C::SystemNode object # this - Arrayref - (Optional) What to operate on # method_timeout - Scalar - (Optional) Time in seconds before timeout # node - Scalar or ArrayRef - (Required) List of nodes # pollwait_interval - Scalar - (Optional) Time in seconds to wait before polling # Returns : none # Exceptions : # NACL::Exceptions::InvalidChoice - If an option is not valid in the list loop ################################################################################ sub _main_enable_disable { $Log->enter(); my(@all_options, @args, $list, %opts, $pkg, ); $pkg = shift; @args = @_; %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { action => {optional => 0, type => SCALAR}, this => {optional => 1, type => ARRAYREF}, method_timeout => {optional => 1, default => $method_timeout}, node => {optional => 1, type => SCALAR|ARRAYREF|UNDEF}, nacltask_verify => {optional => 1, default => 1 }, pollwait_interval => {optional => 1, default => $pollwait_interval}, }, allow_extra => 1 ); # This array will be used if 'all' is passed @all_options = qw(links channel_ofw channel_kstat nvram_mirroring); # If node is not defined, assign local then convert to an ArrayRef if needed if (!defined($opts{node})) { $opts{node} = ($opts{command_interface}->name()); } $opts{node} = ["$opts{node}"] if (ref($opts{node}) ne 'ARRAY'); # Loop through the list and call the associated subroutine. # If 'all' was called then assign the 'all_options' array. $list = $opts{this}; LOOP: foreach my $item (@{$list}) { if ($item eq 'all') { push($list, @all_options); } elsif ($item eq 'links') { $pkg->_links(%opts); } elsif ($item eq 'channel_ofw') { $pkg->_channel_ofw(%opts); } elsif ($item eq 'channel_kstat') { $pkg->_channel_kstat(%opts); } elsif ($item eq 'nvram_mirroring') { $pkg->_nvram_mirroring(%opts); } else { $Log->exit(); NACL::Exceptions::InvalidChoice->throw("$item is invalid."); } }; # end LOOP $Log->exit(); return; } # end _main_enable_disable() ################################################################################ # Function : _verify_options() # Description : # Checks to make sure only one method was called and that all options passed # are valid. # Arguments : # action - Scalar - (Required) What action to take (enable/disable) # this - Arrayref - (Optional) What to operate on if disable method used # Returns : none # Exceptions : # NACL::Exceptions::InvalidParam - If invalid options are supplied or both # methods are used ################################################################################ sub _verify_options { $Log->enter(); my ( $action, @invalid_items, $invalid_output, $lc, $list, %opts, $pkg, @valid_list, ); ($pkg, %opts, ) = @_; $action = $opts{action}; $list = $opts{this}; @valid_list = qw(all links channel_ofw channel_kstat nvram_mirroring); # Compare the provided list to what we have set as valid entries # If invalid entries exist, throw exception $lc = List::Compare->new(\@valid_list, $list); @invalid_items = $lc->get_Ronly; if (@invalid_items) { $invalid_output = join(', ', @invalid_items); $Log->exit(); NACL::Exceptions::InvalidParam->throw( "Invalid options supplied: $invalid_output" ); } $Log->exit(); return; } # end _verify_options() ################################################################################ # Function : _links() # Description : # Calls NACL::STask::SystemHaInterconnectLinks to toggle on or off all links # and verify state. # Arguments : # action - Scalar - (Required) What action to take (enable/disable) # command_interface - Scalar - (Required) NACL::C::SystemNode object # method_timeout - Scalar - (Optional) Time in seconds before timeout # node - Scalar or ArrayRef (Required) List of nodes # pollwait_interval - Scalar - (Optional) Time in seconds to wait before polling # Returns : none # Exceptions : none ################################################################################ sub _links { require NACL::STask::SystemHaInterconnectLink; $Log->enter(); my( $action, $ci, $list, $method_timeout, $nacltask_verify, $node, %opts, $pkg, $pollwait_interval, $sub_name, ); ($pkg, %opts, ) = @_; $action = $opts{action}; $ci = $opts{command_interface}; $method_timeout = $opts{method_timeout}; $node = $opts{node}; $nacltask_verify = $opts{nacltask_verify}; $pollwait_interval = $opts{pollwait_interval}; $sub_name = (caller(0))[3] . '(): '; $Log->debug( "$sub_name Performing the following action on all links: $action" ); # Convert ENABLE/DISABLE to ON/OFF for STASK::SHIL $action = ($action eq 'enable') ? 'on' : 'off'; LOOP_LINKS: foreach my $node_name ($node) { NACL::STask::SystemHaInterconnectLink->$action( command_interface => $ci, node => $node_name, nacltask_verify => $nacltask_verify, pollwait_interval => $pollwait_interval, method_timeout => $method_timeout, ); }; # End LOOP_LINKS $Log->exit(); return; } # end _links() ################################################################################ # Function : _channel_ofw() # Description : Toggles OFW QP on or off and verifies the state. # Arguments : # action - Scalar - (Required) What action to take (enable/disable) # command_interface - Scalar - (Required) NACL::C::SystemNode object # method_timeout - Scalar - (Optional) Time in seconds before timeout # node - Scalar or ArrayRef - (Required) List of nodes # pollwait_interval - Scalar - (Optional) Time in seconds to wait before polling # Returns : none # Exceptions : none ################################################################################ sub _channel_ofw { $Log->enter(); my( $apiset, $action, $ci, $method_timeout, $nacltask_verify, $node, %opts, $pkg, $platform, $pollwait_interval, $req_state, $sub_name, ); ($pkg, %opts, ) = @_; $action = $opts{action}; $ci = $opts{command_interface}; $method_timeout = $opts{method_timeout}; $node = $opts{node}; $nacltask_verify = $opts{nacltask_verify}; $pollwait_interval = $opts{pollwait_interval}; $sub_name = (caller(0))[3] . '(): '; $Log->debug( "$sub_name Performing the following action on the OFW Channel: $action" ); # Some platforms accept the command however running 'channel show' will # show as UP, per DEV this is expected behavior. So if nacltask_verify is # set to 1 check if it's one of these platforms. If it is, then set to # nacltask_verify to 0 to bypass the check. if ($nacltask_verify == 1) { $nacltask_verify = _is_nonconforming_platform( command_interface => $ci, node => $node, ); } # Use nodescope 'ic ofw' command LOOP_OFW: foreach my $node_name (@{$node}) { $ci->get_7m_or_nodescope_apiset()->execute_command( 'nodescope-node-name' => $node_name, command => "ic $action ofw", ); # If NACLTASK_VERIFY is set, ensure the channel is in the appropriate state if ($nacltask_verify == 1) { $Log->debug("$sub_name Checking state of OFW Channel"); if ($action eq 'enable') { $req_state = 'established'; } elsif ($action eq 'disable') { $req_state = 'disconnected'; } this_block { require NACL::CS::SystemHaInterconnectChannel; my (@channels, ); @channels = NACL::CS::SystemHaInterconnectChannel->fetch( command_interface => $ci, filter => { node => $node_name, }, requested_fields => [qw(ofw)], ); foreach my $channel (@channels) { if ($channel->ofw()) { return 'established'; } } # End foreach (@channels) # We get here if no OFW channels were found up. return 'disconnected'; } should(eventually( Be->lexically_equal_to($req_state) )->every($pollwait_interval)->within($method_timeout)->seconds()); }; }; #End LOOP_OFW $Log->exit(); return; } # end _channel_ofw() ################################################################################ # Function : _channel_kstat() # Description : Toggles KSTAT QP on or off and verifies the state. # Arguments : # action - Scalar - (Required) What action to take (enable/disable) # command_interface - Scalar - (Required) NACL::C::SystemNode object # method_timeout - Scalar - (Optional) Time in seconds before timeout # node - Scalar or ArrayRef - (Required) List of nodes # pollwait_interval - Scalar - (Optional) Time in seconds to wait before polling # Returns : none # Exceptions : none ################################################################################ sub _channel_kstat { $Log->enter(); my( $apiset, $action, $ci, $method_timeout, $nacltask_verify, $node, %opts, $pkg, $platform, $pollwait_interval, $req_state, $sub_name, ); ($pkg, %opts, ) = @_; $action = $opts{action}; $ci = $opts{command_interface}; $method_timeout = $opts{method_timeout}; $node = $opts{node}; $nacltask_verify = $opts{nacltask_verify}; $pollwait_interval = $opts{pollwait_interval}; $sub_name = (caller(0))[3] . '(): '; $Log->debug( "$sub_name Performing the following on KSTAT Channel: $action" ); # Some platforms accept the command however running 'channel show' will # show as UP, per DEV this is expected behavior. So if nacltask_verify is # set to 1 check if it's one of these platforms. If it is, then set to # nacltask_verify to 0 to bypass the check. if ($nacltask_verify == 1) { $nacltask_verify = _is_nonconforming_platform( command_interface => $ci, node => $node, ); } # Use nodescope 'ic kstat' command LOOP_KSTAT: foreach my $node_name (@{$node}) { $ci->get_7m_or_nodescope_apiset()->execute_command( 'nodescope-node-name' => $node_name, command => "ic $action kstat", ); # If NACLTASK_VERIFY is set, ensure the channel is in the appropriate state if ($nacltask_verify == 1) { $Log->debug("$sub_name Checking state of KSTAT Channel"); if ($action eq 'enable') { $req_state = 'established'; } elsif ($action eq 'disable') { $req_state = 'disconnected'; } this_block { require NACL::CS::SystemHaInterconnectLinkInfo; my (@links, ); @links = NACL::CS::SystemHaInterconnectLinkInfo->fetch( command_interface => $ci, filter => { 'node' => $node_name, 'is-active-link' => 'true', }, allow_empty => 1, ); foreach my $link (@links) { if (! $link->channel_status()->kstat()) { return 'disconnected'; } } # End foreach (@links) return 'established'; } should(eventually( Be->lexically_equal_to($req_state) )->every($pollwait_interval)->within($method_timeout)->seconds()); }; }; # End LOOP_KSTAT $Log->exit(); return; } # end _channel_kstat() ################################################################################ # Function : _nvram_mirroring() # Description : Toggles NVRAM_Mirroring on or off and verifies the state. # Arguments : # action - Scalar - (Required) What action to take (enable/disable) # command_interface - Scalar - (Required) NACL::C::SystemNode object # node - Scalar or ArrayRef - (Required) List of nodes # Returns : none # Exceptions : NATE::BaseException if nvramMirror status and passed action # do not match ################################################################################ sub _nvram_mirroring { $Log->enter(); my( $action, $ci, $method_timeout, $nacltask_verify, $node, %opts, $pkg, $pollwait_interval, $status, $sub_name, ); ($pkg, %opts, ) = @_; $action = $opts{action}; $ci = $opts{command_interface}; $node = $opts{node}; $sub_name = (caller(0))[3] . '(): '; # Use nodescope 'cf nvramMirror' command $Log->debug( "$sub_name Performing the following action on NVRAM Mirror: $action" ); LOOP_NVRAM: foreach my $node_name (@{$node}) { $ci->get_7m_or_nodescope_apiset()->execute_command( 'nodescope-node-name' => $node_name, command => "cf $action nvramMirror", ); # Check status of nvramMirror, returns 'true' or 'false' $status = NACL::CS::StorageFailover->fetch( command_interface => $ci, filter => { 'node-name' => $node_name, }, requested_fields => [ 'is-mirror-enabled', ], apiset_must => { interface => 'CLI' } )->is_mirror_enabled(); # Compare the new status to the passed action if ( ($status eq 'true' && $action eq 'enable') || ($status eq 'false' && $action eq 'disable') ) { $Log->log('NVRAM Mirror is in expected state: ' . $status); } else { $Log->exit(); NATE::BaseException->throw( 'NVRAM mirror state is NOT in the expected state: ' . $status ); } }; # End LOOP_NVRAM $Log->exit(); return; } # end _nvram_mirroring() ################################################################################ # Function : _is_nonconforming_platform() # Description : Checks to see if the driver type is one of HAIC drivers that # do not behave consistently with others. # Arguments : # command_interface - Scalar - (Required) NACL::C::SystemNode object # Returns : 0|1 - Based on driver type # Exceptions : none ################################################################################ sub _is_nonconforming_platform { $Log->enter(); require NACL::CS::SystemHaInterconnectInfo; my ( $ci, $haic_driver, $node, %args, $nacltask_verify, ); %args = @_; $ci = $args{command_interface}; $haic_driver = NACL::CS::SystemHaInterconnectInfo->fetch( command_interface => $ci, requested_fields => ['driver-type'], )->driver_type(); if (any {$haic_driver eq $_} qw(IWARP SINAI)) { $Log->log('Platform is a non-conforming platform.'); $Log->exit(); return 0; } else { $Log->exit(); return 1; } } # End _is_nonconforming_platform() ## @pod here =head1 NAME NACL::STask::SystemHaInterconnect =head1 DESCRIPTION C provides a well-defined set of methods to enable and disable the entire, or parts of, the HA Interconnect on a testbed configured as a HA Pair. The STask uses C and C to derive information and provides methods that are more in the scope of individual 'system ha interconnect' commands. =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the host to which to send commands. See L =head1 METHODS $stask_obj = NACL::STask::SystemHaInterconnect->new( command_interface => $command_interface, ); (Class method) Creates a SystemHaInterconnect Object. =over =item Options =over =item C<< node => $node_name >> (Optional) This indicates the node which will be operated on. If not supplied, the method will operate on both the nodes in the HA pair for the I<'command_interface'> specified. =item C<< method_timeout => $timeout >> (Optional) Time in seconds to wait for the verification of individual methods. Default value is 600 seconds (10 minutes) if not specified. =item C<< nacltask_polling_interval => $interval >> (Optional) Time in seconds after which the value of the state information will be polled. Default value is 10 seconds if not specified. =back =back =head2 enable NACL::STask::SystemHaInterconnect->enable( command_interface => $ci, this => $this, # default 'all' %other_options ); (Class method) This method can be used to enable parts of or the entire HA Interconnect. For further details on what actions can be done, please review the Options. The default action is 'all'. Which will enable the entire HA Interconnect. The method will not complain if the HA Interconnect is already enabled. The method will throw an exception if there is a failure to enable the HA Interconnect. =over =item Options =over =item C<< command_interface => $ci >> (Required) See L =item C<< this => $this >> (Optional) One or more of the following options: 'links' - HA Interconnect Link(s) and verify they are in a 'up' state 'nvram_mirroring' - Enable NVRAM Mirroring 'channel_ofw' - The OFW QP Channel and verify it is in a 'Connected' state 'channel_kstat' - The KSTAT QP Channel and verify it is in a 'Connected' state 'all' - (Default) Will enable link(s) and verify that all QPs are in a 'Connected' state If not supplied the default will be 'all'. =item C<< node => $node_name >> (Optional) Indicates the Node. If not supplied the method will operate on both the nodes in the HA pair for the I<'command_interface'> specified. =item C<< method_timeout => $timeout >> (Optional) Time in seconds to wait for the verification of individual methods. Default value is 600 seconds (10 minutes) if not specified. =item C<< nacltask_polling_interval => $interval >> (Optional) Time in seconds after which the value of the state information will be polled. Default value is 10 seconds if not specified. =back =back =over =item Exceptions =over =item C This type of exception is thrown when an invalid choice is passed for the options 'enable_this'. =item C This type of exception is thrown when verification of the HA Interconnect or its options fail. =back =back =head2 disable NACL::STask::SystemHaInterconnect->disable( command_interface => $ci, this => $disable # default 'links' %other_options ); (Class method) This method can be used to disable parts of or the entire HA Interconnect. For further details on what actions can be done, please review the Options. The method will not complain if the HA Interconnect is already disabled. The method will throw an exception if there is a failure to disable the HA Interconnect. =over =item Options =over =item C<< command_interface => $ci >> (Required) See L =item C<< disable_this => $disable_this >> (Optional) One or more of the following options: 'links' - (Default) Disable HA Interconnect links, verifying that the links are in a 'down' state and the QPs are in a 'Disconnected' state. 'nvram_mirroring' - Disable the NVRAM Mirroring 'channel_ofw' - The OFW QP Channel and verify it is in a 'Disconnected' state 'channel_kstat' - The KSTAT QP Channel and verify it is in a 'Disconnected' state =item C<< node => $node_name >> (Optional) Indicates the Node. If not supplied the method will operate on both the nodes in the HA pair for the I<'command_interface'> specified. =item C<< method_timeout => $timeout >> (Optional) Time in seconds to wait for the verification of individual methods. Default value is 600 seconds (10 minutes) if not specified. =item C<< nacltask_polling_interval => $interval >> (Optional) Time in seconds after which the value of the state information will be polled. Default value is 10 seconds if not specified. =back =back =over =item Exceptions =over =item C This type of exception is thrown when an invalid choice is passed for the option 'disable_this'. =item C This type of exception is thrown when verification of the HA Interconnect or its options fail. =back =back =cut 1;