# # Copyright (c) 2001-2015 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @burt 965196 NACL::CS::SHILI contains port and port_number ## @burt 965212 NACL::CS::SHILI loads all instant_cs even if not required ## @burt 967344 'status show' query is not filtered - can break on XNC ## @burt 971446 Add 'node' field and support for multiple nodes filter ## @burt 993664 Throw exception for requested_fields with underscores ## @burt 1043909 port-name filter is not working ## @burt 1055335 Implement NACL::CS::SHIC ## @burt 1059512 Pass Nate Params to downstream thplets # ## @summary SystemHaInterconnectLinkInfo ComponentState Module ## @author dl-interconnect-qa ## @status OPEN package NACL::CS::SystemHaInterconnectLinkInfo; use strict; use warnings FATAL => 'all'; use base 'NACL::CS::ComponentState::ONTAP'; use List::MoreUtils qw(any none); use NACL::Exceptions::NoElementsFound qw(:try); use NACL::InstantComponent qw(instant_component); use NATE::Log qw(log_global); use Params::Validate qw(validate :types); use Class::MethodMaker [ scalar => 'channel_status', scalar => 'filer', scalar => 'flags', scalar => 'initiator', scalar => 'interface', scalar => 'ipaddress', scalar => 'is_active_link', scalar => 'link_monitor', scalar => 'link_layer_state', scalar => 'link_status', scalar => 'local_sysid', scalar => 'node', scalar => 'partner_sysid', scalar => 'phy_layer_state', scalar => 'phy_link_down_count', scalar => 'phy_link_up_count', scalar => 'port_base_lid', scalar => 'port_data_rate', scalar => 'port_gid', scalar => 'port_link_info', scalar => 'port_mtu', scalar => 'port_name', scalar => 'port_number', scalar => 'port_qsfp_part_number', scalar => 'port_qsfp_serial_number', scalar => 'port_qsfp_type', scalar => 'port_qsfp_vendor', scalar => 'port_rm_lid', scalar => 'transport', ]; my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); ################################################################################ # Function : fetch() # Description : # Our 'constructor' of sorts. This method is called to create the CS # object(s) and returns them to the caller. # Arguments : None consumed explicitly. # Returns : NACL::CS::SystemHaInterconnectLinkInfo object(s) ################################################################################ sub fetch { my ($pkg, @state_objs); $Log->enter() if $may_enter; $pkg = shift; @state_objs = $pkg->SUPER::fetch( @_, choices => [ { method => '_fetch_link_cli', interface => 'CLI', set => 'CMode', }, ], exception_text => 'No matching SystemHaInterconnectLinkInfo element found', ); $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } ## end sub fetch ################################################################################ # 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_fields, ); $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_fields, $field; } } if (@underscore_fields) { NACL::Exceptions::InvalidFilterField->throw( join( qq(\n), 'Unknown requested/filter fields: ' . join(', ', @underscore_fields), 'This could be because:', ' 1. The field(s) is invalid', ' 2. 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', ) ); } $Log->exit() if $may_exit; return; } # End _check_for_invalid_requested_filter_fields ################################################################################ # Function : _extract_value() # Description : # We're using other ComponentState object data to fill out the data for this # object. Also, we're taking data that could be represented in either a # scalar or an array. If data is in an array, we'll need to capture only the # value relevant to this object. # Arguments : # cs_name - scalar - Which CS object we're querying (for err msg) # cs_obj - ComponentState - Object that we're querying # field - scalar - Field name we're retrieving # port_num - scalar - Port number we're querying # Returns : scalar - Retrieved value ################################################################################ sub _extract_value { $Log->enter() if $may_enter; my (%args, $cs_obj, $field, $port_num, $value, ); %args = @_; $cs_obj = $args{cs_obj}; $field = $args{field}; $port_num = $args{port_num}; $value = $cs_obj->$field(); if (defined($value)) { if (ref($value) eq 'ARRAY') { # System has multiple links. Retrieve value for only requested link. if (scalar(@{$value}) == 1) { # Single entry that is common for all links $Log->exit() if $may_exit; return $value->[0]; } else { # Each link has separate value $Log->exit() if $may_exit; return $value->[$port_num]; } } else { NATE::BaseException->throw( "We expected an ARRAYREF for the field '$field' in the " . "ComponentState object '$args{cs_name}'. This could be a " . 'NACL BURT.' ); } } return; } # End _extract_value() ################################################################################ # Function : _fetch_link_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_link_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(); } # Check for invalid fields passed in - particularly for underscores used # instead of dashes. $pkg->_check_for_invalid_requested_filter_fields( command_interface => $master_opts{command_interface}, filter => \%filter, requested_fields => $master_opts{requested_fields}, ); # 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 (@state_objs, ); $filter{node} = $node_name; @state_objs = _fetch_link_cli_worker( $pkg, filter => \%filter, %master_opts, ); if (@state_objs) { # Found object(s) that matched filters push(@objs, @state_objs); } } # End FILTERED_NODES_LOOP $Log->exit() if $may_exit; return @objs; } # End _fetch_link_cli() ################################################################################ # Function : _fetch_link_cli_worker() # Description : # Called by fetch_link_cli(), when in the conext of CLI. This is the method # that does the work of populating data fields for CS objects. # Arguments : # command_interface - NACL::C::SystemNode object # filter - string - Query filter # requested_fields - arrayref - List of fields to populate # Returns : NACL::CS::SystemHaInterconnectLinkInfo object(s) ################################################################################ sub _fetch_link_cli_worker { $Log->enter() if $may_enter; my ( $config_cs, $command_interface, $filter, $filter_config_show, $filter_port_show, $filter_status_show, @fields_config_show, @fields_port_show, @fields_status_show, %opts, $pkg, $port_cs, $requested_fields, %requested_fields, @state_objs, ); $pkg = shift; %opts = validate(@_, $pkg->_fetch_backend_validate_spec()); $command_interface = $opts{command_interface}; $filter = $opts{filter}; $requested_fields = $opts{requested_fields}; #Check if any of the requested fields is invalid before going any further $pkg->_check_for_invalid_requested_filter_fields( command_interface => $command_interface, filter => $filter, requested_fields => $requested_fields, ); # Build individual filter for each command to manipulate them further below %{$filter_config_show} = %{$filter}; %{$filter_port_show} = %{$filter}; # For 'status show', we can only easily support filtering by node. Since all # field names in 'status show' are modified to fit this module, there is no # easy 1:1. So, for now we'll only support node filter on 'status show' # queries. $filter_status_show = {node => $filter->{node}}; # Remove filters from 'config show' command for fields that don't exist there delete($filter_config_show->{'is-active-link'}); delete($filter_config_show->{'link-status'}); delete($filter_config_show->{'port-mtu'}); delete($filter_config_show->{'port-number'}); delete($filter_config_show->{'port-name'}); # Remove filters from 'port show' command for fields that don't exist there delete($filter_port_show->{'is-active-link'}); delete($filter_port_show->{'link-status'}); delete($filter_port_show->{'port-mtu'}); delete($filter_port_show->{'port-name'}); # Have to do a few things here: # 1. Since all downstream references and ONTAP reference the field as # 'port', however we want to call it 'port-number', if 'port-number' is # passed in as a filter item we need to rename it to 'port'. # 2. If the filter for 'port-number' is passed in as a scalar (single-link), # we need to convert it to an array for the 'port show' command called # through NACL::CS::SystemHaInterconnectPort->fetch() if (defined($filter_port_show->{'port-number'})) { my ($filter_port_num, ); $filter_port_num = delete($filter_port_show->{'port-number'}); if (!(ref($filter_port_num) eq 'ARRAY')) { $filter_port_show->{port} = [$filter_port_num]; } else { # Already passed in as ArrayRef $filter_port_show->{port} = $filter_port_num; } } # End if(defined($filter_port_show->{port-number})) # Define the full list of attributes related to this CS object for each of # the three commands that will be executed. @fields_port_show = qw( is-active-link link-layer-state phy-layer-state phy-link-down-count phy-link-up-count port ); @fields_config_show = qw(flags ipaddress); @fields_status_show = qw( link-status port-base-lid port-data-rate port-gid port-link-info port-mtu port-qsfp-part-number port-qsfp-serial-number port-qsfp-type port-qsfp-vendor port-rm-lid ); # VSIM won't give port-name in status show, but does provide it in config show. # burt1055340 opened to remedy. _is_vsim() subroutine can go away if burt is fixed. if (_is_vsim(ci => $command_interface, node_name => $filter->{node})) { push(@fields_config_show, 'port-name'); } else { push (@fields_status_show, 'port-name'); } # Setup the list of requested fields for each command based on # 'requested_fields' parameter. %requested_fields = _setup_requested_fields( all_port_show => \@fields_port_show, all_config_show => \@fields_config_show, all_status_show => \@fields_status_show, requested_fields => $requested_fields, ); # Create Instant CS objects for each command to capture attribute values # CS for 'config show' if (@{$requested_fields{config_show}}) { instant_component( command_interface => $command_interface, component_name => 'NACL::C::SystemHaInterconnectConfig', ); $config_cs = NACL::CS::SystemHaInterconnectConfig->fetch( command_interface => $command_interface, filter => $filter_config_show, requested_fields => $requested_fields{config_show}, ); } # End if($requested_fields{config_show}) # CS for 'port show' # We may need this if any fields from 'status show' are requested, as well. if (@{$requested_fields{port_show}} || @{$requested_fields{status_show}}) { instant_component( command_interface => $command_interface, component_name => 'NACL::C::SystemHaInterconnectPort', ); $port_cs = NACL::CS::SystemHaInterconnectPort->fetch( command_interface => $command_interface, filter => $filter_port_show, requested_fields => $requested_fields{port_show}, ); } # End if($requested_fields{port_show}) # Create an object for each available link PORTLOOP: foreach my $port_num ($port_cs->port()) { my ($channel_cs, %link ,$obj, %status_show_fields, ); $obj = $pkg->new(command_interface => $command_interface); # Retrieve values for object from various CS sources foreach my $port_field (@{$requested_fields{port_show}}) { $link{$port_field} = _extract_value( cs_name => 'NACL::CS::SystemHaInterconnectPort', cs_obj => $port_cs, field => $port_field, port_num => $port_num, ); } foreach my $config_field (@{$requested_fields{config_show}}) { $link{$config_field} = _extract_value( cs_name => 'NACL::CS::SystemHaInterconnectConfig', cs_obj => $config_cs, field => $config_field, port_num => $port_num, ); } if (@{$requested_fields{status_show}}) { # Retrieve fields from 'status show' %status_show_fields = _get_status_show_fields( filter => $filter_status_show, port_cs => $port_cs, port_num => $port_num, requested_fields => $requested_fields{status_show}, ); } if ($requested_fields{channel_status}) { require NACL::CS::SystemHaInterconnectChannel; my ($port_name, ); # Hack for vsims if (_is_vsim(ci => $command_interface, node_name => $filter->{node})) { $port_name = $link{'port-name'}; } else { $port_name = $status_show_fields{'port-name'}; } # Get NACL::CS::SystemHaInterconnectChannel object for link # Setting 'allow_empty' to 1 here for now. If cables are cross- # connected, one node only shows channels for 1 port, not both. As a # result, one port returns no channels. $channel_cs = NACL::CS::SystemHaInterconnectChannel->fetch( command_interface => $command_interface, filter => { 'node' => $filter->{node}, 'port-name' => $port_name, }, allow_empty => 1, ); } # We got the "port" attribute from 'port-show'. Now we need to change # this back to "port-number". $link{'port-number'} = delete($link{port}); # Add node element to object. Coming into this routine, the filter # should be set to a single node, so this should be safe. $link{node} = $filter->{node}; # Finally, populate values into our CS object. $obj->_set_fields(row => \%link); $obj->_set_fields(row => \%status_show_fields); $obj->_set_fields(row => {channel_status => $channel_cs}); # Add the final object to the main list before exit the loop push(@state_objs, $obj); } # End PORTLOOP $Log->exit() if $may_exit; return @state_objs; } # End _fetch_link_cli_worker() ################################################################################ # Function : _get_status_show_fields() # Description : # Fields from 'status show' command need special handling. Depending on the # platform, some fields will be either 'link-status' or 'link#-status'. # Also, on single-link platforms, need to figure out if # Arguments : # filter - hashref - Filter list for status show # port_num - scalar - Port number we're looking at # port_cs - ComponentState - Object for 'interconnect port show' # requested_fields - arrayref - List of requested 'status show' fields # Returns : hash # key - attribute name # value - attribute value ################################################################################ sub _get_status_show_fields { $Log->enter() if $may_enter; my ( %args, $config_cs, $port_cs, $port_num, $requested_fields, $ss_cs, %ss_data, ); %args = @_; $config_cs = $args{config_cs}; $port_cs = $args{port_cs}; $port_num = $args{port_num}; $requested_fields = $args{requested_fields}; # Create CS object for 'config show' instant_component( command_interface => $port_cs->command_interface(), component_name => 'NACL::C::SystemHaInterconnectStatus', ); $ss_cs = NACL::CS::SystemHaInterconnectStatus->fetch( command_interface => $port_cs->command_interface(), filter => $args{filter}, ); REQUESTED_FIELD: foreach my $req_field (@{$requested_fields}) { # To reduce a level of if/if/if here, let's process based on single vs. # multiple links. This code *may* not be automatically scalable if we # ever go n-way. However, if the formatting of 'status show' changes in # response to n-way, we'd have to re-write this anyway. So, for now, I'm # trying to write with readability in mind. if ($ss_cs->link_status() eq q(-)) { # We have multiple links $ss_data{$req_field} = _get_status_show_fields_multi_link( %args, field => $req_field, ss_cs => $ss_cs, ); } else { # We have only 1 link $ss_data{$req_field} = _get_status_show_fields_single_link( %args, field => $req_field, ss_cs => $ss_cs, ); } } # End REQUESTED_FIELD $Log->exit() if $may_exit; return %ss_data; } # End _get_status_show_fields() ################################################################################ # Function : _get_status_show_fields_single_link() # Description : # Here, we're doing the work to get each particular field for systems with # a single link. # Arguments : # field - scalar - particular field we're retrieving # port_num - scalar - Port number we're looking at # port_cs - ComponentState - Object for 'interconnect port show' # ss_cs - ComponentState - Object for 'interconnect status show' # Returns : hash # key - attribute name # value - attribute value ################################################################################ sub _get_status_show_fields_single_link { $Log->enter() if $may_enter; my (%args, $field, $link_num, $port_cs, $ss_cs, ); %args = @_; $field = $args{field}; $port_cs = $args{port_cs}; $ss_cs = $args{ss_cs}; # 'link-status' attribute is verbatim on systems with one link if ($field eq 'link-status') { $Log->exit() if $may_exit; return $ss_cs->link_status(); } # For remaining items, port number in attribute name is one more than actual # port number. $link_num = $args{port_num} + 1; # "port-name" attribute is "port#-port-name" in 'status show' output if ($field eq 'port-name') { $field = "port$link_num" . '-port-name'; $Log->exit() if $may_exit; return $ss_cs->$field(); } # For all others, attribute name is modified from 'port-' to # 'port#-' $field =~ s/^port/port$link_num/; $Log->exit() if $may_exit; return $ss_cs->$field(); } # End _get_status_show_fields_single_link() ################################################################################ # Function : _get_status_show_fields_multi_link() # Description : # Here, we're doing the work to get each particular field for systems with # multiple links. # Arguments : # field - scalar - particular field we're retrieving # port_num - scalar - Port number we're looking at # port_cs - ComponentState - Object for 'interconnect port show' # Returns : hash # key - attribute name # value - attribute value ################################################################################ sub _get_status_show_fields_multi_link { $Log->enter() if $may_enter; my (%args, $field, $link_num, $port_cs, $port_num, $ss_cs, ); %args = @_; $field = $args{field}; $port_cs = $args{port_cs}; $ss_cs = $args{ss_cs}; # For systems with multiple links, things are inconsistent up in ONTAP. Port # number from 'port show' (which is "port_num" passed in here) will match # "link#-<>" attributes. However, for "port#-<>" attributes, the port number # has to be incremented by 1. $link_num = $args{port_num}; $port_num = $link_num+1; # "link-status" attribute is "link#-status" in 'status show' output if ($field eq 'link-status') { $field =~ s/^link/link$link_num/; $Log->exit() if $may_exit; return $ss_cs->$field(); } # "port-name" attribute is "port#-port-name" in 'status show' output if ($field eq 'port-name') { $field = "port$port_num" . '-port-name'; $Log->exit() if $may_exit; return $ss_cs->$field(); } # Remaining attributes are 'port#-' $field =~ s/^port/port$port_num/; $Log->exit() if $may_exit; return $ss_cs->$field(); } # End _get_status_show_fields_multi_link() ################################################################################ # Function : _is_vsim() # Description : # Determines if node is a vsim. # Arguments : # ci - NACL::C::SystemNode - (Required) Command Interface # node_name - scalar - (Required) Name of node to query # Returns : boolean # 0 - Node is NOT vsim # 1 - Node is vsim ################################################################################ sub _is_vsim { $Log->enter() if $may_enter; my (%args, $is_vsim, $response, ); %args = @_; $response = $args{ci}->apiset()->system_ha_interconnect_config_show( node => $args{node_name}, )->get_parsed_output(); if ($response->[0]->{transport} =~ /mvia/i) { $is_vsim = 1; } else { $is_vsim = 0; } $Log->exit() if $may_exit; return $is_vsim; } # End _is_vsim() ################################################################################ # Function : _setup_requested_fields() # Description : # Sets up lists of requested fields on a per command basis. These lists are # used in ONTAP calls to only retrieve requested information. This does not # affect what is returned. # Arguments : # all_config_show - arrayref - List of fields available for 'config show' # all_port_show - arrayref - List of fields available for 'port show' # all_status_show - arrayref - List of fields available for 'status show' # requested_fields - arrayref - 'requested_fields' parameter # Returns : hash # key - command name # value - list of requested fields for that command ################################################################################ sub _setup_requested_fields { $Log->enter() if $may_enter; my ( %args, $req_channel_status, @req_config_show, @req_port_show, @req_status_show, ); %args = @_; # If no specific fields have been requested, user implies they want all # fields. if (scalar(@{$args{requested_fields}}) == 0) { $Log->exit() if $may_exit; return ( channel_status => 1, config_show => $args{all_config_show}, port_show => $args{all_port_show}, status_show => $args{all_status_show}, ); } # End if (no requested_fields) # For channel_status, this is a single element and not reliant on other # commands like the remaining requested_fields. So, just set a boolean here. if (any {'channel_status' eq $_} @{$args{requested_fields}}) { # 'port-name' is required to get Channel Show CS objects filtered by # port. push(@{$args{requested_fields}}, 'port-name'); $req_channel_status = 1; } else { $req_channel_status = 0; } @req_config_show = List::Compare->new( $args{all_config_show}, $args{requested_fields}, )->get_intersection(); @req_port_show = List::Compare->new( $args{all_port_show}, $args{requested_fields}, )->get_intersection(); # If @req_fields_port_show is defined then make sure 'port' field exists # in the final list. If not, then add it to the list so that it is fetched # by SystemHaInterconnectPort down below to construct @ports list properly if (@req_port_show) { if (none {$_ eq 'port'} @req_port_show) { push(@req_port_show, 'port'); } } else { # If @req_port_show is empty, and 'port-number' has been requested, # add port to @req_port_show. This gets transliterated to port-number. if (any {$_ eq 'port-number'} @{$args{requested_fields}}) { push(@req_port_show, 'port'); } } @req_status_show = List::Compare->new( $args{all_status_show}, $args{requested_fields}, )->get_intersection(); $Log->exit() if $may_exit; return ( channel_status => $req_channel_status, config_show => \@req_config_show, port_show => \@req_port_show, status_show => \@req_status_show, ); } # End _setup_requested_fields() 1; ## @pod here =head1 NAME NACL::CS::SystemHaInterconnectLinkInfo =head1 DESCRIPTION C is a derived class of NACL::CS::ComponentState::ONTAP. This ComponentState object represents the state of the HA Interconnect Link(s). Unless a node is specified in the filter, the link information returned is limited to the context of the HA Pair that the node 'command_interface' belongs to. It uses the following commands to derive its attribute values: * I * I * I See also, L which represents the overall state of HA Interconnect. =head1 ATTRIBUTES =over =item C<< channel_status >> Returns a NACL::CS::SystemHaInterconnectChannel object for the link. Filled in for CMode CLI. =item C<< flags >> Returns the interface flags of the port. Filled in for CMode CLI. =item C<< ipaddress >> Returns the the IP address of the port. Filled in for CMode CLI. =item C<< is_active_link >> Check to see if the link is in an active state. Returns 'true' if active or 'false' if not active. =item C<< link_layer_state >> Current state of the link layer. Filled in for CMode CLI. List of possible values from the following L =item C<< link_status >> Current overall status of the link. Filled in for CMode CLI. =item C<< phy_layer_state >> Current state of the physical link. Filled in for CMode CLI. List of possible values from the following L =item C<< phy_link_down_count >> Number of times the link has gone to a 'down' state Filled in for CMode CLI. =item C<< phy_link_up_count >> Number of times the link has gone to an 'up' state. Filled in for CMode CLI. =item C<< port_base_lid >> Display the Base Local Identifier for the port. Filled in for CMode CLI. =item C<< port_data_rate >> Display the Data Rate of the port. Filled in for CMode CLI. =item C<< port_gid >> Display the Global Identifier for the port. Filled in for CMode CLI. =item C<< port_link_info >> Displays link information of the port. Filled in for CMode CLI. =item C<< port_mtu >> Display the Maximum Transmission Unit of the port. Filled in for CMode CLI. =item C<< port_name >> Returns the port name that is assigned to the link. Filled in for CMode CLI. =item C<< port_number >> The port number assigned to the link. Filled in for CMode CLI. List of possible values from the following L =item C<< port_qsfp_part_number >> Display the QSFP Part Number of the port. Filled in for CMode CLI. =item C<< port_qsfp_serial_number >> Display the QSFP Serial Number of the port. Filled in for CMode CLI. =item C<< port_qsfp_type >> Display the QSFP Type of the port. Filled in for CMode CLI. =item C<< port_qsfp_vendor >> Display the QSFP Vendor of the port. Filled in for CMode CLI. =item C<< port_rm_lid >> Display the Remote Local Identifier for the port. Filled in for CMode CLI. =back =head1 METHODS =head2 fetch my $HaInterconnectLinkInfo = NACL::CS::SystemHAInterconnectLinkInfo->fetch(command_interface=>$ci,...); my @HaInterconnectLinkInfo = NACL::CS::SystemHAInterconnectLinkInfo->fetch(command_interface=>$ci,...); =over =item C<< Exceptions >> NACL::Exceptions::NoElementsFound 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