# $Id$ # # Copyright (c) 2001-2015 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @burt 946291 NACL::CS::SystemHaInterconnectInfo Initial Check-Ins ## @burt 956369 Allow_empty option does not work ## @burt 953970 NACL needs to avoid calling higher level logging statements ## @burt 946291 Added links and num_links ## @burt 946291 Fixed perlcritic issues from previous check-in ## @burt 976018 'status' output changes on Sinai ## @burt 977563 Add support for HSL and NTB for driver_type ## @burt 986821 Multiple nodes in filter only creates one object ## @burt 965571 CS throw warning for undefined requested fields ## @burt 971446 Fix multiple nodes filter to scalar from arrayref ## @burt 986865 STREAM1 channel always down on MCC ## @burt 978647 Reapply changes from change number 3487617 ## @burt 988954 'channel show-status' gone; now 'channel show' ## @burt 1013934 Warnings from MCC QPs ## @burt 1022364 Can't get just number of links ## @burt 1039985 NACL::CS::SHII - all_channels_up->expected_channels_up() ## @burt 1050699 Modify expected-channels-up to accomodate OFW QP behaviour ## @burt 1055335 Implement NACL::CS::SHIC ## @burt 1058068 Dynamic Channel Allocation for NACL::CS::SHIC ## @burt 1058722 Create NACL::CS::SHII::why_not_up() ## @burt 1067401 Implement Sinai Hack in 9.3 # ## @summary SystemHaInterconnectInfo ComponentState Module ## @author ng-interconnect-qa ## @status OPEN package NACL::CS::SystemHaInterconnectInfo; use strict; use warnings FATAL => 'all'; # General Package Imports use List::Compare; use List::MoreUtils qw(any none); use NACL::InstantComponent qw(instant_component); use NACL::Global; use NATE::Log qw(log_global); use Params::Validate qw(validate :types); # Commonly Used Exceptions use NACL::Exceptions::InvalidFilterField qw(:try); use NACL::Exceptions::NoElementsFound qw(:try); use NACL::APISet::Exceptions::InvalidParamValueException qw(:try); use base 'NACL::CS::ComponentState::ONTAP'; use Class::MethodMaker [ array => 'active_links', scalar => 'command_revision', scalar => 'debug_firmware', scalar => 'device_type', scalar => 'driver_name', scalar => 'driver_type', scalar => 'expected_channels_up', scalar => 'filer', scalar => 'firmware', scalar => 'hardware_revision', scalar => 'ic_rdma', scalar => 'initiator', scalar => 'interface', scalar => 'is_ic_up', array => 'links', scalar => 'link_monitor', scalar => 'link_status', scalar => 'link0_status', scalar => 'link1_status', scalar => 'local_sysid', scalar => 'node', scalar => 'num_active_links', scalar => 'num_links', scalar => 'partner_sysid', scalar => 'serial_number', scalar => 'slot', scalar => 'transport', scalar => 'version', ]; my ( $Allow_Empty_Copy, $Driver_Type_Flag, $Is_Ic_Up_Flag, $Log, $May_Enter, $May_Exit, @Mcc_Ignore_Qps, ); $Log = log_global(); $May_Enter = $Log->may_enter(); $May_Exit = $Log->may_exit(); @Mcc_Ignore_Qps = qw(drsom drckill); sub fetch { $Log->enter() if $May_Enter; my (%opts, $pkg, @state_objs, ); $pkg = shift; %opts = validate(@_, $pkg->_fetch_validate_spec()); $Allow_Empty_Copy = $opts{allow_empty}; @state_objs = $pkg->SUPER::fetch( @_, choices => [ { method => '_fetch_interconnect_cli', interface => 'CLI', set => 'CMode' }, ], exception_text => 'No matching elements found', ); $Log->exit() if $May_Exit; return wantarray ? @state_objs : $state_objs[0]; } # End fetch() ################################################################################ # Function : why_not_up() # Description : # Prints reason why 'is_ic_up' is returning a FALSE result. # Arguments : none # Returns : text detailing results of _determine_is_ic_up() # Exceptions : # NATE::BaseException - if called in Class context or 'is-ic-up' was not # requested on current Instance ################################################################################ sub why_not_up { my ($border, $self, ); $self = shift; if (!defined($self->{is_ic_up})) { NATE::BaseException->throw( '"is_ic_up" was not filled in for this Instance or why_not_up() ' . 'called in Class-context, which is not supported.' ); } # why_not_up log border $border = qq(\n) . q(*=)x30 . ' why_not_up ' . q(*=)x30 . qq(\n); return($border . $self->{_why_not_up} . $border); } # End why_not_up() ################################################################################ # Function : _check_for_invalid_requested_filter_fields() # Description : # Validates requested_fields and filter field # Arguments : # command_interface - object - (required) Value of command_interface # filter - hashref - (required) Value of filter # requested_fields - arrayref - (required) Value of requested_fields # Returns : # Throws exception NACL::Exceptions::InvalidFilterField for invalid field ################################################################################ sub _check_for_invalid_requested_filter_fields { $Log->enter() if $May_Enter; my ( %opts, $pkg, @underscore_warnings, ); $pkg = shift; %opts = @_; $pkg->SUPER::_check_for_invalid_requested_filter_fields(%opts); foreach my $field (@{$opts{requested_fields}}, keys %{$opts{filter}}) { if ( $field =~ m/_/ ) { push @underscore_warnings, $field; } } if (@underscore_warnings) { my $err_str = "\n Unknown requested fields: " . join(', ', @underscore_warnings) . "\n This could be because:\n" . "\t1. The field(s) is invalid\n" . "\t2. The field(s) is valid but has not been " . "implemented on the CS object '$pkg'. If " . 'this is the case, please raise a burt against ' . 'nacl (type=nacl;subtype=nacl_core) or mail ' . 'dl-nacl-dev@netapp.com regarding the issue'; NACL::Exceptions::InvalidFilterField->throw( $err_str ); } $Log->exit() if $May_Exit; return; } # End _check_for_invalid_requested_filter_fields ################################################################################ # Function : _determine_driver_type() # Description : # Calculates value of 'driver_type' field # Arguments : # transport - scalar - (required) Value of 'transport' field # Returns : scalar - value of driver_type # NATE::BaseException - if unable to determine ic driver_type ################################################################################ sub _determine_driver_type { $Log->enter() if $May_Enter; my %args = @_; my (@driver_type_list, $driver_type, $transport_val, ); @driver_type_list = qw(connectx hsl iwarp mvia ntb sinai); $transport_val = lc $args{transport}; DRIVER_CHECK: foreach my $driver_type_val (@driver_type_list) { if ($transport_val =~ /$driver_type_val/) { $driver_type = $driver_type_val; last DRIVER_CHECK; } } # End DRIVER_CHECK if (!defined($driver_type)) { $Log->exit() if $May_Exit; NATE::BaseException->throw("Unknown transport value: $transport_val"); } $Log->exit() if $May_Exit; return (uc $driver_type); } # End _determine_driver_type() ################################################################################ # Function : _determine_is_ic_up() # Description : # Calculates value of 'is_ic_up' field. Saves information about PASS/FAIL # results for each step in Instance variable _why_not_up. # Arguments : # link_status - scalar - (required) Value of 'link-status' field # link0_status - scalar - (required) Value of 'link0-status' field # link1_status - scalar - (required) Value of 'link1-status' field # ic_rdma - scalar - (required) Value of 'ic-rdma' field # expected_channels_up - scalar - (required) Value of 'expected-channels-up' # field # self - scalar - (required) Object being created # Returns : scalar - 1/0 # Exceptions : none ################################################################################ sub _determine_is_ic_up { $Log->enter() if ($May_Enter); my (%args, $is_ic_up, @why_not_up, ); %args = @_; $is_ic_up = 1; # Initialize to TRUE # Check link status. At least one should be 'up'. if ( lc($args{link_status}) eq 'up' || lc($args{link0_status}) eq 'up' || lc($args{link1_status}) eq 'up' ) { push(@why_not_up, ' PASS: Link Status'); } else { $is_ic_up = 0; push( @why_not_up, ' FAIL: Link Status', "\tlink_status : $args{link_status}", "\tlink0_status : $args{link0_status}", "\tlink1_status : $args{link1_status}", ); } # Check value of 'ic_rdma' if (lc($args{ic_rdma}) eq 'up') { push(@why_not_up, ' PASS: ic_rdma Status'); } else { $is_ic_up = 0; push(@why_not_up, " FAIL: ic_rdma Status: $args{ic_rdma}"); } # Check value of 'expected_channels_up' if ($args{expected_channels_up}) { push(@why_not_up, ' PASS: expected_channels_up'); } else { $is_ic_up = 0; push(@why_not_up, ' FAIL: expected_channels_up'); } # Save log data into _why_not_up $args{self}->{_why_not_up} .= join(qq(\n), q(), @why_not_up); $Log->exit() if ($May_Exit); return $is_ic_up; } # End _determine_is_ic_up() ################################################################################ # Function : _fetch_interconnect_cli() # Description : # Top level call: Here, we handle the 'filter' option for node(s). # Typically, the logical place to handle filters is in _apply_filters(), # however, there is a LOT of work involved with creating this CS. So, # instead of creating every possible CS for a testbed, then filtering that # set, let's only create CS objects for nodes specifically requested. Also, # if node is NOT specified as a filter, add a node filter for the node in # 'command_interface'. # Arguments : # command_interface - object - (required) Value of command_interface # apiset - object - (optional) Value of apiset # filter - hash - (optional) Value of filter # requested_fields - array - (optional) Value of requested_fields # Returns : array # Exceptions : # NACL::Exceptions::InvalidFilterField - if requested field is invalid ################################################################################ sub _fetch_interconnect_cli { $Log->enter() if $May_Enter; my (%filter, %master_opts, @node_list, @objs, $pkg, ); $pkg = shift; %master_opts = validate(@_, $pkg->_fetch_backend_validate_spec()); %filter = %{delete($master_opts{filter})}; # If no node filter is specified by user then use the local node if (!defined($filter{node})) { $Log->debug('No node filter specified - filtering on ci'); $filter{node} = $master_opts{command_interface}->name(); } # Populate the list of nodes we will be iterating over. @node_list = split(qr/[|]/, $filter{node}); $Log->debug("List of filtered nodes: @node_list"); FILTERED_NODES_LOOP: foreach my $node_name (@node_list) { $Log->debug("Creating CS object for: $node_name"); my ($obj,); $filter{node} = $node_name; $obj = _fetch_interconnect_cli_worker( $pkg, filter => \%filter, %master_opts, ); if (defined($obj)) { # Found object that matched filters push(@objs, $obj); } } # End FILTERED_NODES_LOOP $Log->exit() if $May_Exit; return @objs; } # End _fetch_interconnect_cli() ################################################################################ # Function : _fetch_interconnect_cli_worker() # Description : # Calculates multiple interconnect related fields # Arguments : # command_interface - object - (required) Value of command_interface # apiset - object - (optional) Value of apiset # filter - hash - (optional) Value of filter # requested_fields - array - (optional) Value of requested_fields # Returns : hashref - single object # Exceptions : # NACL::Exceptions::InvalidFilterField - if requested field is invalid ################################################################################ sub _fetch_interconnect_cli_worker { $Log->enter() if $May_Enter; my ( $apiset, $command_interface, $filter, %info_hash, $pkg, $requested_fields, %requested_fields, @state_objs, %opts, $obj, ); $pkg = shift; %opts = validate(@_, $pkg->_fetch_backend_validate_spec); $apiset = $opts{apiset}; $command_interface = $opts{command_interface}; $filter = $opts{filter}; $requested_fields = $opts{requested_fields}; # Check if any of the requested fields is invalid $pkg->_check_for_invalid_requested_filter_fields( command_interface => $command_interface, filter => $filter, requested_fields => $requested_fields, ); # Calculate the list of requested fields to pass to the various Component- # States that we will be calling. %requested_fields = _set_up_requested_fields( filter => $filter, package => $pkg, requested_fields => $requested_fields, ); $obj = $pkg->new(command_interface => $command_interface); if ( @{$requested_fields{channel_show}} || $pkg->_want_any_field_of( fields_filled_by_api => [ qw( active-links links num-active-links num-links) ], filter => $filter, requested_fields => $requested_fields ) ) { require NACL::CS::SystemHaInterconnectLinkInfo; my (@active_links, @links); @links = NACL::CS::SystemHaInterconnectLinkInfo->fetch( command_interface => $command_interface, filter => $filter, ); $info_hash{'links'} = \@links; $info_hash{'num-links'} = scalar @links; foreach my $link (@links) { if ($link->is_active_link() eq 'true') { push(@active_links, $link); } } $info_hash{'active-links'} = \@active_links; $info_hash{'num-active-links'} = scalar @active_links; } # Execute sub-commands to pull data for this ComponentState. Since we're # passing '%info_hash' as a REF, subroutine will update in-place. Thus, # nothing to return. _get_data_from_ontap( command_interface => $command_interface, info_hash => \%info_hash, filter => $filter, requested_fields => \%requested_fields, self => $obj, ); # Calculate 'is-ic-up' field if (!$Is_Ic_Up_Flag) { delete($info_hash{'is-ic-up'}); } else { if ( $info_hash{'link-status'} && $info_hash{'link0-status'} && $info_hash{'link1-status'} && $info_hash{'ic-rdma'} ) { $info_hash{'is-ic-up'} = _determine_is_ic_up( link_status => $info_hash{'link-status'}, link0_status => $info_hash{'link0-status'}, link1_status => $info_hash{'link1-status'}, ic_rdma => $info_hash{'ic-rdma'}, expected_channels_up => $info_hash{'expected-channels-up'}, self => $obj, ); } else { # Required field not found. Set ic-is-up to 0 $info_hash{'is-is-up'} = 0; } } # Calculate 'driver-type' field if (!$Driver_Type_Flag) { delete($info_hash{'driver-type'}); } else { if ($info_hash{'transport'}) { $info_hash{'driver-type'} = _determine_driver_type( transport => $info_hash{'transport'}, ); } } # Delete helper fields if (@{$requested_fields}) { foreach my $field (keys %info_hash) { if (none { $_ eq $field } @{$requested_fields}) { delete($info_hash{$field}); } } } else { my @helper_fields = qw(link-status link0-status link1-status); foreach my $field (@helper_fields) { delete($info_hash{$field}); } } # Add node element to object. Coming into this routine, the filter should be # set to a single node, so this should be safe. $info_hash{node} = $filter->{node}; #Populate fields $obj->_set_fields(row => \%info_hash); $Log->exit() if $May_Exit; return $obj; } # End _fetch_interconnect_cli_worker() ################################################################################ # Function : _get_data_channel_show() # Description : # Uses ComponentState object for 'system ha interconnect channel show' to # retrieve data and sets 'expected_channels_up' attribute. # Arguments : # command_interface - CI - (Required) Command Interface # info_hash - hashref - (Required) Ref to %info_hash from caller # filter - hashref - (Required) List of User-supplied filters # self - scalar - (required) Object being created # Returns : none # Exceptions : none ################################################################################ sub _get_data_channel_show { $Log->enter() if $May_Enter; require NACL::CS::SystemHaInterconnectLinkInfo; my ( %args, @channels, $control_exists, $expected_channels_up, $info_hash, $ofw, @special_needs, $traffic_exists, @up_links, @why_not_up, ); %args = @_; $info_hash = $args{info_hash}; $expected_channels_up = 1; # Assume we're good to start # First, let's get NACL::CS::SHILI objects for all 'up' links. This will # contain the channel information sorted by links. @up_links = NACL::CS::SystemHaInterconnectLinkInfo->fetch( command_interface => $args{command_interface}, filter => { 'link-status' => 'up', }, allow_empty => 1, ); # If no links are up, we wouldn't expect any channels to be up. if (!@up_links) { $Log->debug('No up links found - setting expected_channels_up to TRUE'); $info_hash->{'expected-channels-up'} = $expected_channels_up; $Log->exit() if $May_Exit; return; } # Get the list of channels that we found. Just grab from one of the link # objects @channels = @{$up_links[0]->channel_status()->channels()}; # Some notes on channels with 'special needs'. "Control" and "traffic" (if # "traffic" is enabled) should be up across all up links. "ofw" will be up # on one link, but not necessarily on the Active link. These will be checked # separately from the other channels. All other channels should be up on the # active link. @special_needs = qw(control ofw traffic); # Set flag to denote if 'control' and 'traffic' channels exist if (any {'traffic' eq $_} @channels) { $traffic_exists = 1; } else { $traffic_exists = 0; } if (any {'control' eq $_} @channels) { $control_exists = 1; } else { $control_exists = 0; } # Initialize flag for "ofw" $ofw = 0; # Loop through the links that are up and determine if anything is down that # should not be. LINK_LOOP: foreach my $link (@up_links) { # First we'll take care of the 'special needs' channels as we need to # look at both links. # "Control" should be up on any links that are up if ( $control_exists && !($link->channel_status()->control()) ) { $Log->debug('Control is down on ' . $link->port_name()); push(@why_not_up, 'FAIL: Control is down on ' . $link->port_name()); $expected_channels_up = 0; last LINK_LOOP; # No need to continue } # "traffic" should be up on any links that are up, but only if "traffic" # has been enabled. if ( $traffic_exists && ! $link->channel_status()->traffic() ) { $Log->debug('Traffic is down on ' . $link->port_name()); push(@why_not_up, 'FAIL: Traffic is down on ' . $link->port_name()); $expected_channels_up = 0; last LINK_LOOP; # No need to continue } # "ofw" can be up on any link. So, just set a flag here when we find it # and we'll check the flag after LINK_LOOP if ($link->channel_status()->ofw()) { $ofw++; } # DONE with Special Needs Channels # For all other channels, we only care about their status on the Active # link. So, continue on the current link only if it's an Active Link. if ($link->is_active_link() eq 'false') { next LINK_LOOP; } # We're on an Active Link, so check the remaining channels. CHANNEL_LOOP: foreach my $channel (@channels) { # Special Needs channels handled elsewhere if (any {$channel eq $_} @special_needs) { next CHANNEL_LOOP; } if (! $link->channel_status()->$channel()) { $Log->debug("$channel found down on " . $link->port_name()); push(@why_not_up, "FAIL: $channel found down on " . $link->port_name()); $expected_channels_up = 0; last LINK_LOOP; # No need to continue } } # End CHANNEL_LOOP # If we get here, all channels that are not "special needs" were up on # the link marked Active. $Log->debug($link->port_name() . ' - all "not special" channels found up.'); } # End LINK_LOOP # Here is where we check the flag for "ofw". if (!$ofw) { $Log->debug('\'OFW\' was not found up on any link'); push(@why_not_up, 'FAIL: \'OFW\' was not found up on any link'); $expected_channels_up = 0; } # Finally, store results $info_hash->{'expected-channels-up'} = $expected_channels_up; $args{self}->{_why_not_up} .= join(qq(\n), @why_not_up); $Log->exit() if $May_Exit; return; } # End _get_data_channel_show() ################################################################################ # Function : _get_data_from_ontap() # Description : # Uses ComponentState objects for ONTAP commands to retrieve the ONTAP-based # data that we will return. This does not create calculated data fields. # Arguments : # command_interface - CI - (Required) Command Interface # info_hash - hashref - (Required) Ref to %info_hash from caller # filter - hashref - (Required) List of User-supplied filters # requested_fields - arrayref - (Required) List of requested fields # self - scalar - (Required) Object being created # Returns : none # Exceptions : none ################################################################################ sub _get_data_from_ontap { $Log->enter() if $May_Enter; my ( %args, $command_interface, $extract_val, $info_hash, $filter, %requested_fields, ); %args = @_; $command_interface = $args{command_interface}; $info_hash = $args{info_hash}; $filter = $args{filter}; %requested_fields = %{$args{requested_fields}}; $extract_val = sub { my ($cs_obj, $field, $val, ); ($cs_obj, $field) = @_; $val = $cs_obj->$field(); if (defined $val) { $info_hash->{$field} = $val; } }; if (@{$requested_fields{port_show}}) { instant_component( component_name => 'NACL::C::SystemHaInterconnectPort', command_interface => $command_interface, ); my $port_cs = NACL::CS::SystemHaInterconnectPort->fetch( allow_empty => $Allow_Empty_Copy, command_interface => $command_interface, filter => $filter, requested_fields => $requested_fields{port_show}, ); if ($port_cs) { foreach my $port_field (@{$requested_fields{port_show}}) { $extract_val->($port_cs, $port_field); } } } # End if(@{$requested_fields{port_show}}) if (@{$requested_fields{config_show}}) { instant_component( component_name => 'NACL::C::SystemHaInterconnectConfig', command_interface => $command_interface, ); my $config_cs = NACL::CS::SystemHaInterconnectConfig->fetch( allow_empty => $Allow_Empty_Copy, command_interface => $command_interface, filter => $filter, requested_fields => $requested_fields{config_show}, ); if ($config_cs) { foreach my $config_field (@{$requested_fields{config_show}}) { $extract_val->($config_cs, $config_field); } } } # End if(@{$requested_fields{config_show}}) if (@{$requested_fields{status_show}}) { instant_component( component_name => 'NACL::C::SystemHaInterconnectStatus', command_interface => $command_interface, ); my $status_cs = NACL::CS::SystemHaInterconnectStatus->fetch( allow_empty => $Allow_Empty_Copy, command_interface => $command_interface, filter => $filter, requested_fields => $requested_fields{status_show}, ); if ($status_cs) { foreach my $status_field (@{$requested_fields{status_show}}) { $extract_val->($status_cs, $status_field); } } } # End if(@{$requested_fields{status_show}}) if (@{$requested_fields{channel_show}}) { _get_data_channel_show(self => $args{self}, %args); } # End if(@{$requested_fields{channel_show}}) $Log->exit() if $May_Exit; return; }; # End _get_data_from_ontap() ################################################################################ # Function : _set_up_requested_fields() # Description : # Sets up the lists of requested fields for each command we need to execute # in order to fill in values that have been requested of this CS. # Arguments : # filter - hashref - (Required) User-supplied filter # package - self - (Required) Link to self # requested_fields - arrayref - (Required) List of requested fields # Returns : hash - List of requested fields for each sub-command # Exceptions : none ################################################################################ sub _set_up_requested_fields { $Log->enter() if $May_Enter; my ( %args, @fields_channel_show, @fields_config_show, @fields_port_show, @fields_status_show, $filter, $pkg, @req_fields_channel_show, @req_fields_config_show, @req_fields_port_show, @req_fields_status_show, $requested_fields, ); %args = @_; $filter = $args{filter}; $pkg = $args{package}; $requested_fields = $args{requested_fields}; # Define the full list of attribute related to this CS object for each of # the four commands that will be executed. @fields_channel_show = qw(expected-channels-up); @fields_config_show = qw( initiator interface local-sysid partner-sysid transport ); @fields_port_show = qw(link-monitor); @fields_status_show = qw( firmware debug-firmware device-type driver-name hardware-revision ic-rdma version serial-number slot ); if (@{$requested_fields}) { # Set up requested fields list for each command based on user input @req_fields_port_show = List::Compare->new( \@fields_port_show, $requested_fields )->get_intersection(); @req_fields_config_show = List::Compare->new( \@fields_config_show, $requested_fields )->get_intersection(); @req_fields_status_show = List::Compare->new( \@fields_status_show, $requested_fields )->get_intersection(); @req_fields_channel_show = List::Compare->new( \@fields_channel_show, $requested_fields )->get_intersection(); } else { # Get value for all defined fields if requested_fields not specified. @req_fields_config_show = @fields_config_show; @req_fields_port_show = @fields_port_show; @req_fields_status_show = @fields_status_show; @req_fields_channel_show = @fields_channel_show; } ## The following fields needed to be calculated based on output from one or ## more of the commands we are using to collect data. Set up the required ## fields need to calculate these fields. # Calculated Field: is-ic-up if ( $pkg->_want_any_field_of( fields_filled_by_api => [qw(is-ic-up)], filter => $filter, requested_fields => $requested_fields ) ) { # We need multiple fields to calculate is-ic-up attribute my @req_fields_ic_up = qw( ic-rdma link-status link0-status link1-status ); @req_fields_status_show = List::Compare->new( \@req_fields_status_show, \@req_fields_ic_up )->get_union(); $Is_Ic_Up_Flag = 1; push(@req_fields_channel_show, 'expected-channels-up'); } else { $Is_Ic_Up_Flag = 0; } # End 'is-ic-up' # Calculated Field: driver-type if ( $pkg->_want_any_field_of( fields_filled_by_api => [qw(driver-type)], filter => $filter, requested_fields => $requested_fields ) ) { # transport field is utilized to calculate derived field driver-type my @req_fields_driver_type = qw(transport); @req_fields_config_show = List::Compare->new( \@req_fields_config_show, \@req_fields_driver_type )->get_union(); $Driver_Type_Flag = 1; } else { $Driver_Type_Flag = 0; } # End 'driver-type' # Calculated Field: num-links if ( $pkg->_want_any_field_of( fields_filled_by_api => [qw(num-links)], filter => $filter, requested_fields => $requested_fields, ) ) { my (@req_fields_num_links, ); @req_fields_num_links = qw(link-monitor); @req_fields_port_show = List::Compare->new( \@req_fields_port_show, \@req_fields_num_links, )->get_union(); } # End 'num-links' ## End Calculated Field Setup $Log->exit() if $May_Exit; return ( channel_show => \@req_fields_channel_show, config_show => \@req_fields_config_show, port_show => \@req_fields_port_show, status_show => \@req_fields_status_show, ); } # End _set_up_requested_fields() 1; ## @pod here =head1 NAME NACL::CS::SystemHaInterconnectInfo =head1 DESCRIPTION C is a derived class of L. It represents the overall state of HA Interconnect component. Unless a node is specified in the filter, the information returned is limited to the context of the HA Pair that the node 'command_interface' belongs to. This ComponentState object uses the following commands to derive it's attribute values: * I * I * I * I See also, L which represents the state of an HA Interconnect link. =head1 ATTRIBUTES The following elements are the attributes of the SystemHaInterconnectInfo ComponentState. =over =item C<< active_links >> (Array) SystemHaInterconnectLinkInfo objects for active HA Interconnect link(s). Returns an array of SystemHaInterconnectLinkInfo objects (See L) for all the active HA Interconnect links =item C<< command_revision >> High-availability interconnect device command revision. Filled in for CMode CLI. =item C<< debug_firmware >> High-availability interconnect device firmware debug capability status. Filled in for CMode CLI. =item C<< device_type >> High-availability interconnect device type name. Filled in for CMode CLI. =item C<< driver_name >> High-availability interconnect device driver name. Filled in for CMode CLI. =item C<< driver_type >> HA-Interconnect Driver in use. Returns the HAIC colloquial name for driver: I =item C<< expected_channels_up >> A summary of the status of the channels across links that are up. Returns I<'1'> if the states of all channels expected to be up are 'established'. Else, returns I<'0'>. List of channels can be looked up L =item C<< firmware >> High-availability interconnect device firmware version. Filled in for CMode CLI. =item C<< hardware_revision >> High-availability interconnect device hardware revision. Filled in for CMode CLI. =item C<< ic_rdma >> The status of failover monitor channel over the interconnect. Filled in for CMode CLI. =item C<< initiator >> The initiator of the connection request. Filled in for CMode CLI. List of possible return values can be looked up L =item C<< interface >> HA interconnect links connection interface. Filled in for CMode CLI. List of possible return values can be looked up L =item C<< is_ic_up >> The overall state of HA Interconnect for the system. Possible values: I<'0'> or I<'1'> =item C<< links >> (Array) SystemHaInterconnectLinkInfo objects for HA Interconnect link(s). Returns an array of SystemHaInterconnectLinkInfo objects (See L) for all the HA Interconnect links =item C<< link_monitor >> Link monitor detection. Filled in for CMode CLI. List of possible return values can be looked up L =item C<< local_sysid >> System id of the local node. Filled in for CMode CLI. =item C<< num_active_links >> The number of active HA Interconnect links present for the system. Returns a scalar value. =item C<< num_links >> The number of HA Interconnect links present for the system. Returns a scalar value. =item C<< partner_sysid >> System id of the partner node. Filled in for CMode CLI. =item C<< serial_number >> High-availability interconnect device serial number. Filled in for CMode CLI. =item C<< slot >> High-availability interconnect device PCI slot number. Filled in for CMode CLI. =item C<< transport >> Interconnect Type value for the system. Filled in for CMode CLI. =item C<< version >> High-availability interconnect device version number. Filled in for CMode CLI. =back =cut =head1 METHODS =head2 fetch my $HaInterconnect_info = NACL::CS::SystemHaInterconnectInfo->fetch(command_interface=>$ci,...); see L Supports CMode CLI. =over =head2 why_not_up Returns a string that can be printed in test case logs indicating the results of decisions behind the calculation of the is_ic_up attribute value. The idea is that if you expect is_ic_up to be TRUE and it is found to be FALSE, the script can print the output of $cs->why_not_up() to assist with triage. =item Exceptions =over =item C When there are no elements matching the query specified or elements of that type doesn't exist, then this exception will be thrown. =back =cut