# SNMP::Info::RapidCity # $Id$ # # Copyright (c) 2004-6 Eric Miller, Max Baker # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the University of California, Santa Cruz nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package SNMP::Info::RapidCity; $VERSION = '1.05'; use strict; use Exporter; use SNMP::Info; use Carp; @SNMP::Info::RapidCity::ISA = qw/SNMP::Info Exporter/; @SNMP::Info::RapidCity::EXPORT_OK = qw//; use vars qw/$VERSION $DEBUG %FUNCS %GLOBALS %MIBS %MUNGE $INIT/; %MIBS = ( 'RAPID-CITY' => 'rapidCity', ); %GLOBALS = ( 'serial' => 'rcChasSerialNumber', 'chassis' => 'rcChasType', 'slots' => 'rcChasNumSlots', 'tftp_host' => 'rcTftpHost', 'tftp_file' => 'rcTftpFile', 'tftp_action' => 'rcTftpAction', 'tftp_result' => 'rcTftpResult', 'rc_ch_rev' => 'rcChasHardwareRevision', 'rc_base_mac' => 'rc2kChassisBaseMacAddr', 'rc_virt_ip' => 'rcSysVirtualIpAddr', ); %FUNCS = ( # From RAPID-CITY::rcPortTable 'rc_index' => 'rcPortIndex', 'rc_duplex' => 'rcPortOperDuplex', 'rc_duplex_admin' => 'rcPortAdminDuplex', 'rc_speed_admin' => 'rcPortAdminSpeed', 'rc_auto' => 'rcPortAutoNegotiate', 'rc_alias' => 'rcPortName', # From RAPID-CITY::rc2kCpuEthernetPortTable 'rc_cpu_ifindex' => 'rc2kCpuEthernetPortIfIndex', 'rc_cpu_admin' => 'rc2kCpuEthernetPortAdminStatus', 'rc_cpu_oper' => 'rc2kCpuEthernetPortOperStatus', 'rc_cpu_ip' => 'rc2kCpuEthernetPortAddr', 'rc_cpu_auto' => 'rc2kCpuEthernetPortAutoNegotiate', 'rc_cpu_duplex_admin' => 'rc2kCpuEthernetPortAdminDuplex', 'rc_cpu_duplex' => 'rc2kCpuEthernetPortOperDuplex', 'rc_cpu_speed_admin' => 'rc2kCpuEthernetPortAdminSpeed', 'rc_cpu_speed_oper' => 'rc2kCpuEthernetPortOperSpeed', 'rc_cpu_mac' => 'rc2kCpuEthernetPortMgmtMacAddr', # From RAPID-CITY::rcVlanPortTable 'rc_i_vlan_if' => 'rcVlanPortIndex', 'rc_i_vlan_num' => 'rcVlanPortNumVlanIds', 'rc_i_vlan' => 'rcVlanPortVlanIds', 'rc_i_vlan_type' => 'rcVlanPortType', 'rc_i_vlan_pvid' => 'rcVlanPortDefaultVlanId', 'rc_i_vlan_tag' => 'rcVlanPortPerformTagging', # From RAPID-CITY::rcVlanTable 'rc_vlan_id' => 'rcVlanId', 'rc_vlan_name' => 'rcVlanName', 'rc_vlan_color' => 'rcVlanColor', 'rc_vlan_if' => 'rcVlanIfIndex', 'rc_vlan_stg' => 'rcVlanStgId', 'rc_vlan_type' => 'rcVlanType', 'rc_vlan_members' => 'rcVlanPortMembers', 'rc_vlan_no_join' => 'rcVlanNotAllowToJoin', 'rc_vlan_mac' => 'rcVlanMacAddress', 'rc_vlan_rstatus' => 'rcVlanRowStatus', # From RAPID-CITY::rcIpAddrTable 'rc_ip_index' => 'rcIpAdEntIfIndex', 'rc_ip_addr' => 'rcIpAdEntAddr', 'rc_ip_type' => 'rcIpAdEntIfType', # From RAPID-CITY::rcChasFanTable 'rc_fan_op' => 'rcChasFanOperStatus', # From RAPID-CITY::rcChasPowerSupplyTable 'rc_ps_op' => 'rcChasPowerSupplyOperStatus', # From RAPID-CITY::rcChasPowerSupplyDetailTable 'rc_ps_type' => 'rcChasPowerSupplyDetailType', 'rc_ps_serial' => 'rcChasPowerSupplyDetailSerialNumber', 'rc_ps_rev' => 'rcChasPowerSupplyDetailHardwareRevision', 'rc_ps_part' => 'rcChasPowerSupplyDetailPartNumber', 'rc_ps_detail' => 'rcChasPowerSupplyDetailDescription', # From RAPID-CITY::rcCardTable 'rc_c_type' => 'rcCardType', 'rc_c_serial' => 'rcCardSerialNumber', 'rc_c_rev' => 'rcCardHardwareRevision', 'rc_c_part' => 'rcCardPartNumber', # From RAPID-CITY::rc2kCardTable 'rc2k_c_ftype' => 'rc2kCardFrontType', 'rc2k_c_fdesc' => 'rc2kCardFrontDescription', 'rc2k_c_fserial' => 'rc2kCardFrontSerialNum', 'rc2k_c_frev' => 'rc2kCardFrontHwVersion', 'rc2k_c_fpart' => 'rc2kCardFrontPartNumber', 'rc2k_c_fdate' => 'rc2kCardFrontDateCode', 'rc2k_c_fdev' => 'rc2kCardFrontDeviations', 'rc2k_c_btype' => 'rc2kCardBackType', 'rc2k_c_bdesc' => 'rc2kCardBackDescription', 'rc2k_c_bserial' => 'rc2kCardBackSerialNum', 'rc2k_c_brev' => 'rc2kCardBackHwVersion', 'rc2k_c_bpart' => 'rc2kCardBackPartNumber', 'rc2k_c_bdate' => 'rc2kCardBackDateCode', 'rc2k_c_bdev' => 'rc2kCardBackDeviations', # From RAPID-CITY::rc2kMdaCardTable 'rc2k_mda_type' => 'rc2kMdaCardType', 'rc2k_mda_desc' => 'rc2kMdaCardDescription', 'rc2k_mda_serial' => 'rc2kMdaCardSerialNum', 'rc2k_mda_rev' => 'rc2kMdaCardHwVersion', 'rc2k_mda_part' => 'rc2kMdaCardPartNumber', 'rc2k_mda_date' => 'rc2kMdaCardDateCode', 'rc2k_mda_dev' => 'rc2kMdaCardDeviations', ); %MUNGE = ( 'rc_base_mac' => \&SNMP::Info::munge_mac, 'rc_vlan_mac' => \&SNMP::Info::munge_mac, 'rc_cpu_mac' => \&SNMP::Info::munge_mac, ); sub i_duplex { my $rapidcity = shift; my $interfaces = $rapidcity->interfaces(); my $rc_index = $rapidcity->rc_index(); my $rc_duplex = $rapidcity->rc_duplex(); my $rc_cpu_duplex = $rapidcity->rc_cpu_duplex(); my %i_duplex; foreach my $if (keys %$interfaces){ my $duplex = $rc_duplex->{$if}; next unless defined $duplex; $duplex = 'half' if $duplex =~ /half/i; $duplex = 'full' if $duplex =~ /full/i; $i_duplex{$if}=$duplex; } # Get CPU Ethernet Interfaces for 8600 Series foreach my $iid (keys %$rc_cpu_duplex){ my $c_duplex = $rc_cpu_duplex->{$iid}; next unless defined $c_duplex; $i_duplex{$iid} = $c_duplex; } return \%i_duplex; } sub i_duplex_admin { my $rapidcity = shift; my $interfaces = $rapidcity->interfaces(); my $rc_index = $rapidcity->rc_index(); my $rc_duplex_admin = $rapidcity->rc_duplex_admin(); my $rc_auto = $rapidcity->rc_auto(); my $rc_cpu_auto = $rapidcity->rc_cpu_auto(); my $rc_cpu_duplex_admin = $rapidcity->rc_cpu_duplex_admin(); my %i_duplex_admin; foreach my $if (keys %$interfaces){ my $duplex = $rc_duplex_admin->{$if}; next unless defined $duplex; my $auto = $rc_auto->{$if}||'false'; my $string = 'other'; $string = 'half' if ($duplex =~ /half/i and $auto =~ /false/i); $string = 'full' if ($duplex =~ /full/i and $auto =~ /false/i); $string = 'auto' if $auto =~ /true/i; $i_duplex_admin{$if}=$string; } # Get CPU Ethernet Interfaces for 8600 Series foreach my $iid (keys %$rc_cpu_duplex_admin){ my $c_duplex = $rc_cpu_duplex_admin->{$iid}; next unless defined $c_duplex; my $c_auto = $rc_cpu_auto->{$iid}; my $string = 'other'; $string = 'half' if ($c_duplex =~ /half/i and $c_auto =~ /false/i); $string = 'full' if ($c_duplex =~ /full/i and $c_auto =~ /false/i); $string = 'auto' if $c_auto =~ /true/i; $i_duplex_admin{$iid} = $string; } return \%i_duplex_admin; } sub set_i_duplex_admin { my $rapidcity = shift; my ($duplex, $iid) = @_; $duplex = lc($duplex); return undef unless ($duplex =~ /(half|full|auto)/ and $iid =~ /\d+/); # map a textual duplex to an integer one the switch understands my %duplexes = qw/full 2 half 1/; my $i_auto = $rapidcity->rc_auto($iid); if ($duplex eq "auto") { return $rapidcity->set_rc_auto('1', $iid); } elsif (($duplex ne "auto") and ($i_auto->{$iid} eq "1")) { return undef unless ($rapidcity->set_rc_auto('2', $iid)); return $rapidcity->set_rc_duplex_admin($duplexes{$duplex}, $iid); } else { return $rapidcity->set_rc_duplex_admin($duplexes{$duplex}, $iid); } return undef; } sub set_i_speed_admin { my $rapidcity = shift; my ($speed, $iid) = @_; return undef unless ($speed =~ /(10|100|1000|auto)/i and $iid =~ /\d+/); # map a textual duplex to an integer one the switch understands my %speeds = qw/10 1 100 2 1000 3/; my $i_auto = $rapidcity->rc_auto($iid); if ($speed eq "auto") { return $rapidcity->set_rc_auto('1', $iid); } elsif (($speed ne "auto") and ($i_auto->{$iid} eq "1")) { return undef unless ($rapidcity->set_rc_auto('2', $iid)); return $rapidcity->set_rc_speed_admin($speeds{$speed}, $iid); } else { return $rapidcity->set_rc_speed_admin($speeds{$speed}, $iid); } return undef; } sub i_vlan { my $rapidcity = shift; my $i_pvid = $rapidcity->rc_i_vlan_pvid() || {}; return $i_pvid; } sub i_vlan_membership { my $rapidcity = shift; my $rc_v_ports = $rapidcity->rc_vlan_members(); my $i_vlan_membership = {}; foreach my $vlan (keys %$rc_v_ports) { my $portlist = [split(//, unpack("B*", $rc_v_ports->{$vlan}))]; my $ret = []; # Convert portlist bit array to ifIndex array for (my $i = 0; $i <= scalar(@$portlist); $i++) { push(@{$ret}, $i) if (@$portlist[$i]); } #Create HoA ifIndex -> VLAN array foreach my $port (@{$ret}) { push(@{$i_vlan_membership->{$port}}, $vlan); } } return $i_vlan_membership; } sub set_i_pvid { my $rapidcity = shift; my ($vlan_id, $ifindex) = @_; return undef unless ($vlan_id =~ /\d+/ and $ifindex =~ /\d+/); my $pvid_rv = $rapidcity->set_rc_i_vlan_pvid($vlan_id, $ifindex); unless ($pvid_rv) { print "Error: Unable to change PVID to $vlan_id on IfIndex: $ifindex\n" if $rapidcity->debug(); return undef; } return 1; } sub set_i_vlan { my $rapidcity = shift; my ($new_vlan_id, $ifindex) = @_; return undef unless ($new_vlan_id =~ /\d+/ and $ifindex =~ /\d+/); my $i_pvid = $rapidcity->rc_i_vlan_pvid($ifindex); # Store current untagged VLAN to remove it from the port list later my $old_vlan_id = $i_pvid->{$ifindex}; print "Changing VLAN: $old_vlan_id to $new_vlan_id on IfIndex: $ifindex\n" if $rapidcity->debug(); # Check if port in forbidden list for the VLAN, haven't seen this used, but we'll check anyway return undef unless ($rapidcity->check_forbidden_ports($new_vlan_id, $ifindex)); # Add port to egress list for VLAN return undef unless ($rapidcity->add_to_egress_portlist($new_vlan_id, $ifindex)); # Remove port from old VLAN from egress list return undef unless ($rapidcity->remove_from_egress_portlist($old_vlan_id, $ifindex)); # Set new untagged / native VLAN # Some models/versions do this for us also, so check to see if we need to set $i_pvid = $rapidcity->rc_i_vlan_pvid($ifindex); my $cur_i_pvid = $i_pvid->{$ifindex}; print "Current PVID: $cur_i_pvid\n" if $rapidcity->debug(); unless ($cur_i_pvid ne $old_vlan_id) { return undef unless (set_i_pvid($old_vlan_id, $ifindex)); } print "Successfully changed VLAN: $old_vlan_id to $new_vlan_id on IfIndex: $ifindex\n" if $rapidcity->debug(); return 1; } sub set_add_i_vlan_tagged { my $rapidcity = shift; my ($vlan_id, $ifindex) = @_; return undef unless ($vlan_id =~ /\d+/ and $ifindex =~ /\d+/); print "Adding VLAN: $vlan_id to IfIndex: $ifindex\n" if $rapidcity->debug(); # Check if port in forbidden list for the VLAN, haven't seen this used, but we'll check anyway return undef unless ($rapidcity->check_forbidden_ports($vlan_id, $ifindex)); # Add port to egress list for VLAN return undef unless ($rapidcity->add_to_egress_portlist($vlan_id, $ifindex)); print "Successfully added IfIndex: $ifindex to VLAN: $vlan_id egress list\n" if $rapidcity->debug(); return 1; } sub set_remove_i_vlan_tagged { my $rapidcity = shift; my ($vlan_id, $ifindex) = @_; return undef unless ($vlan_id =~ /\d+/ and $ifindex =~ /\d+/); print "Removing VLAN: $vlan_id from IfIndex: $ifindex\n" if $rapidcity->debug(); # Remove port from egress list for VLAN return undef unless ($rapidcity->remove_from_egress_portlist($vlan_id, $ifindex)); print "Successfully removed IfIndex: $ifindex from VLAN: $vlan_id egress list\n" if $rapidcity->debug(); return 1; } # # Need to be able to construct a single set with multiple oids # #sub set_create_vlan { # my $rapidcity = shift; # my ($name, $vlan_id) = @_; # return undef unless ($vlan_id =~ /\d+/); # # my $activate_rv = $rapidcity->set_rc_vlan_rstatus(4, $vlan_id); # unless ($activate_rv) { # print "Error: Unable to activate VLAN: $vlan_id\n" if $rapidcity->debug(); # return undef; # } # my $rv = $rapidcity->set_rc_vlan_name($name, $vlan_id); # unless ($rv) { # print "Error: Unable to create VLAN: $vlan_id\n" if $rapidcity->debug(); # return undef; # } # return 1; #} sub set_delete_vlan { my $rapidcity = shift; my ($vlan_id) = shift; return undef unless ($vlan_id =~ /\d+/); my $rv = $rapidcity->set_rc_vlan_rstatus('6', $vlan_id); unless ($rv) { print "Error: Unable to delete VLAN: $vlan_id\n" if $rapidcity->debug(); return undef; } return 1; } sub check_forbidden_ports { my $rapidcity = shift; my ($vlan_id, $ifindex) = @_; return undef unless ($vlan_id =~ /\d+/ and $ifindex =~ /\d+/); my $iv_forbidden = $rapidcity->rc_vlan_no_join($vlan_id); my @forbidden_ports = split(//, unpack("B*", $iv_forbidden->{$vlan_id})); print "Forbidden ports: @forbidden_ports\n" if $rapidcity->debug(); if ( defined($forbidden_ports[$ifindex]) and ($forbidden_ports[$ifindex] eq "1")) { print "Error: IfIndex: $ifindex in forbidden list for VLAN: $vlan_id unable to add\n" if $rapidcity->debug(); return undef; } return 1; } sub add_to_egress_portlist { my $rapidcity = shift; my ($vlan_id, $ifindex) = @_; return undef unless ($vlan_id =~ /\d+/ and $ifindex =~ /\d+/); my $iv_members = $rapidcity->rc_vlan_members($vlan_id); my @egress_list = split(//, unpack("B*", $iv_members->{$vlan_id})); print "Original egress list for VLAN: $vlan_id: @egress_list \n" if $rapidcity->debug(); $egress_list[$ifindex] = '1'; # Some devices do not populate the portlist with all possible ports. # If we have lengthened the list fill all undefined elements with zero. foreach my $item (@egress_list) { $item = '0' unless (defined($item)); } print "Modified egress list for VLAN: $vlan_id: @egress_list \n" if $rapidcity->debug(); my $new_egress = pack("B*", join('', @egress_list)); my $egress_rv = $rapidcity->set_rc_vlan_members($new_egress, $vlan_id); unless ($egress_rv) { print "Error: Unable to add VLAN: $vlan_id to IfIndex: $ifindex egress list\n" if $rapidcity->debug(); return undef; } return 1; } sub remove_from_egress_portlist { my $rapidcity = shift; my ($vlan_id, $ifindex) = @_; return undef unless ($vlan_id =~ /\d+/ and $ifindex =~ /\d+/); my $iv_members = $rapidcity->rc_vlan_members($vlan_id); my @egress_list = split(//, unpack("B*", $iv_members->{$vlan_id})); print "Original egress list for VLAN: $vlan_id: @egress_list \n" if $rapidcity->debug(); # Some devices may remove automatically, so check state before set if ( defined($egress_list[$ifindex]) and ($egress_list[$ifindex] eq "1")) { $egress_list[$ifindex] = '0'; print "Modified egress list for VLAN: $vlan_id: @egress_list \n" if $rapidcity->debug(); my $new_egress = pack("B*", join('', @egress_list)); my $egress_rv = $rapidcity->set_rc_vlan_members($new_egress, $vlan_id); unless ($egress_rv) { print "Warning: Unable to remove IfIndex: $ifindex from VLAN: $vlan_id egress list\n" if $rapidcity->debug(); return undef; } } return 1; } 1; __END__ =head1 NAME SNMP::Info::RapidCity - SNMP Interface to the Nortel RapidCity MIB =head1 AUTHOR Eric Miller =head1 SYNOPSIS # Let SNMP::Info determine the correct subclass for you. my $rapidcity = new SNMP::Info( AutoSpecify => 1, Debug => 1, # These arguments are passed directly on to SNMP::Session DestHost => 'myswitch', Community => 'public', Version => 2 ) or die "Can't connect to DestHost.\n"; my $class = $rapidcity->class(); print "SNMP::Info determined this device to fall under subclass : $class\n"; =head1 DESCRIPTION SNMP::Info::RapidCity is a subclass of SNMP::Info that provides an interface to the C MIB. This MIB is used across the Nortel Ethernet Routing Switch and Ethernet Switch product lines (Formerly known as Passport, BayStack, and Acclear). Use or create in a subclass of SNMP::Info. Do not use directly. =head2 Inherited Classes None. =head2 Required MIBs =over =item RAPID-CITY =back =head1 GLOBAL METHODS These are methods that return scalar values from SNMP =over =item $rapidcity->chassis_base_mac() (B) =item $rapidcity->ch_serial() (B) =item $rapidcity->rc_ch_rev() (B) =item $rapidcity->chassis() (B) =item $rapidcity->slots() (B) =item $rapidcity->rc_virt_ip() (B) =item $rapidcity->tftp_host() (B) =item $rapidcity->tftp_file() (B) =item $rapidcity->tftp_action() (B) =item $rapidcity->tftp_result() (B) =back =head1 TABLE METHODS These are methods that return tables of information in the form of a reference to a hash. =over =item $rapidcity->i_duplex() Returns reference to map of IIDs to current link duplex. =item $rapidcity->i_duplex_admin() Returns reference to hash of IIDs to admin duplex setting. =item $rapidcity->i_vlan() Returns a mapping between ifIndex and the VLAN. =back =head2 RAPID-CITY Port Table (B) =over =item $rapidcity->rc_index() (B) =item $rapidcity->rc_duplex() (B) =item $rapidcity->rc_duplex_admin() (B) =item $rapidcity->rc_speed_admin() (B) =item $rapidcity->rc_auto() (B) =item $rapidcity->rc_alias() (B) =back =head2 RAPID-CITY CPU Ethernet Port Table (B) =over =item $rapidcity->rc_cpu_ifindex() (B) =item $rapidcity->rc_cpu_admin() (B) =item $rapidcity->rc_cpu_oper() (B) =item $rapidcity->rc_cpu_ip() (B) =item $rapidcity->rc_cpu_auto() (B) =item $rapidcity->rc_cpu_duplex_admin() (B) =item $rapidcity->rc_cpu_duplex() (B) =item $rapidcity->rc_cpu_speed_admin() (B) =item $rapidcity->rc_cpu_speed_oper() (B) =item $rapidcity->rc_cpu_mac() (B) =back =head2 RAPID-CITY VLAN Port Table (B) =over =item $rapidcity->rc_i_vlan_if() (B) =item $rapidcity->rc_i_vlan_num() (B) =item $rapidcity->rc_i_vlan() (B) =item $rapidcity->rc_i_vlan_type() (B) =item $rapidcity->rc_i_vlan_pvid() (B) =item $rapidcity->rc_i_vlan_tag() (B) =back =head2 RAPID-CITY VLAN Table (B) =over =item $rapidcity->rc_vlan_id() (B) =item $rapidcity->rc_vlan_name() (B) =item $rapidcity->rc_vlan_color() (B) =item $rapidcity->rc_vlan_if() (B) =item $rapidcity->rc_vlan_stg() (B) =item $rapidcity->rc_vlan_type() (B) =item $rapidcity->rc_vlan_members() (B) =item $rapidcity->rc_vlan_mac() (B) =back =head2 RAPID-CITY IP Address Table (B) =over =item $rapidcity->rc_ip_index() (B) =item $rapidcity->rc_ip_addr() (B) =item $rapidcity->rc_ip_type() (B) =back =head2 RAPID-CITY Chassis Fan Table (B) =over =item $rapidcity->rc_fan_op() (B) =back =head2 RAPID-CITY Power Supply Table (B) =over =item $rapidcity->rc_ps_op() (B) =back =head2 RAPID-CITY Power Supply Detail Table (B) =over =item $rapidcity->rc_ps_type() (B) =item $rapidcity->rc_ps_serial() (B) =item $rapidcity->rc_ps_rev() (B) =item $rapidcity->rc_ps_part() (B) =item $rapidcity->rc_ps_detail() (B) =back =head2 RAPID-CITY Card Table (B) =over =item $rapidcity->rc_c_type() (B) =item $rapidcity->rc_c_serial() (B) =item $rapidcity->rc_c_rev() (B) =item $rapidcity->rc_c_part() (B) =back =head2 RAPID-CITY 2k Card Table (B) =over =item $rapidcity->rc2k_c_ftype() (B) =item $rapidcity->rc2k_c_fdesc() (B) =item $rapidcity->rc2k_c_fserial() (B) =item $rapidcity->rc2k_c_frev() (B) =item $rapidcity->rc2k_c_fpart() (B) =item $rapidcity->rc2k_c_fdate() (B) =item $rapidcity->rc2k_c_fdev() (B) =item $rapidcity->rc2k_c_btype() (B) =item $rapidcity->rc2k_c_bdesc() (B) =item $rapidcity->rc2k_c_bserial() (B) =item $rapidcity->rc2k_c_brev() (B) =item $rapidcity->rc2k_c_bpart() (B) =item $rapidcity->rc2k_c_bdate() (B) =item $rapidcity->rc2k_c_bdev() (B) =back =head2 RAPID-CITY MDA Card Table (B) =over =item $rapidcity->rc2k_mda_type() (B) =item $rapidcity->rc2k_mda_desc() (B) =item $rapidcity->rc2k_mda_serial() (B) =item $rapidcity->rc2k_mda_rev() (B) =item $rapidcity->rc2k_mda_part() (B) =item $rapidcity->rc2k_mda_date() (B) =item $rapidcity->rc2k_mda_dev() (B) =cut