=head1 NAME

B<check_interfaces> - spong-network module to check for down intefaces via SNMP

=head1 DESCRIPTION

This is a plugin module for the Spong L<spong-network> program. It is a core
Spong module. The B<check_interfaces> module checks for down network interfaces
on a host by polling via SNMP. It reports any interfaces that are
administratively up but operationally not up.

=cut

# Register the routine with the plugin registry
$PLUGINS{'interfaces'} = \&check_interfaces;

use BER "0.57";			# should be in eval
use SNMP_Session "0.59";	# should be in eval
use SNMP_util "0.57";		# should be in eval
use Socket;


# This check will connect to a unit and ask it to return the system group
# If it can do that, then we assume it is ok - if it can't then something is wrong.

sub check_interfaces {
	my ($host ) = @_;
	my ($color, $summary, $message ) = ( "green", "", "" );
	my( $snmp_session, @skipped_interfaces );

	my ($snmp_community) = $HOSTS{$host}->{'snmp_community'} ||
                               $HOSTS_DEFAULTS{'snmp_community'} ||
                               'public';

        # Find the list of interfaces to ignore/skip
        @skipped_interfaces = @{$HOSTS{$host}{'ignore_interfaces'}};
        @skipped_interfaces = @{$HOSTS_DEFAULTS{'ignore_interfaces'}}
                                if( ! @skipped_interfaces );

        # Append mandatory interfaces to skip
        @skipped_interfaces = ( @skipped_interfaces,
                                @{$HOSTS_ALL{'ignore_interfaces'}} );

	$snmp_session = SNMP_Session->open ($host, $snmp_community, 161);

        if (! $snmp_session ) { 
           $color = 'red';
           $summary = "Error creating session to $host";
           $message = $SNMP_Session::errmsg . "\n";;
           return ( $color, $summary, $message );
        }

	snmpmapOID('sysObjectID' => '1.3.6.1.2.1.1.2.0');

	$SNMP_Session::suppress_warnings = 2;
	my ($ifNumber) =
		snmpget("$snmp_community\@$host",
			'ifNumber');

        if ( ! defined $ifNumber )  {
           $color = 'red';
           $summary = 'Error retrieving ifNumber';
           $message = $SNMP_Session::errmsg . "\n";
           return ( $color, $summary, $message );
        }

	debug ("interfaces - $ifNumber",8 );
	if ($ifNumber == 0) {
		$color = "yellow";
		$summary = "$host has no interfaces";
		$message = "$host has no interfaces\n";
		debug ("interfaces - $host - $color, $summary", 4 );
		return ($color, $summary, $message );
	}

	my (%sifdesc, %siftype, %sifadminstatus, %sifoperstatusi, $res);

	$res = $snmp_session->map_table ([
				   [1,3,6,1,2,1,2,2,1,1], # ifIndex
				   [1,3,6,1,2,1,2,2,1,2], # ifDescr
				   [1,3,6,1,2,1,2,2,1,3], # ifType
				   [1,3,6,1,2,1,2,2,1,7], # ifAdminStatus
				   [1,3,6,1,2,1,2,2,1,8]  # ifOperStatus
				  ],
			          sub ($@) {
					my ($rowindex,$index,$ifdescr,$iftype,$ifadminstatus,$ifoperstatus) = @_;

					grep (defined $_ && ($_=pretty_print $_), ($index,$ifdescr,$iftype,$ifadminstatus,$ifoperstatus));
					debug("interfaces - seen $ifdescr",8);
					$sifdesc{$index} = $ifdescr;
					$siftype{$index} = $iftype;
					$sifadminstatus{$index} = $ifadminstatus;
					$sifoperstatus{$index} = $ifoperstatus;
				  });

        if ( ! defined $res )  {
           $color = 'red';
           $summary = 'Error retreiving ifNumber';
           $message = $SNMP_Session::errmsg . "\n";
           return ( $color, $summary, $message );
        }

	my $index;
        my $redcount = 0;
	foreach $index ( sort { $a <=> $b } keys %sifdesc) {
		$message .= "interface $index status:\n";
		$message .= "\tifDescr: $sifdesc{$index}\n";
		$message .= "\tifType: $siftype{$index}\n";
		$message .= "\tifAdminStatus: $sifadminstatus{$index}\n";
		$message .= "\tifOperStatus: $sifoperstatus{$index}\n";
      # If interface is not up and is admin up, we got a problem
		if ($sifoperstatus{$index} != 1 && $sifadminstatus{$index} == 1) {
                        my $skip = 0;
			if( @skipped_interfaces ) {
			   foreach my $intf (@skipped_interfaces) {
			      if( $sifdesc{$index} eq $intf ) {
			         $skip = 1; last;
			      }
			   }
			}
			$redcount++ unless $skip;
		}
	}

	if ($redcount > 0) {
		$color = "red";
		$summary = "$redcount interfaces are down";
	} else {
		$summary = "all interfaces up";
	}
   
	debug ("interfaces - $host - $color, $summary", 4 );
	return ($color, $summary, $message );
}

1;


__END__

=head2 Output Returned

=over 4

=item Status

If all interfaces are operationally up, a 'green' status is returned. If a host
is found to have no interfaces a 'yellow' status is returned. Any interfaces
that are operationally down and administratively up, a 'red' status is
returned. Any SNMP session problems will also result in a 'red' status being
returned.

=item Summary Field

In normal operation, the status field will show "all interfaces up". If one or
more network interfaces are down, it will show "some interfaces are down".
Otherwise the summary field will have a description of what the problem or
anamoly is.

=item Detail Message Field

In normal opereration the detail message field will have a list of all of the
network interfaces in the I<MIBII ifTable> table along with the interface
description (I<ifDesc>), type (I<ifType>), administrative status
(I<ifAdminStatus>) and operational status (I<ifOperStatus>). Otherwise this
field will have a detailed description of the cause of an error.

=back

=head2 Configuration

=over 4

=item SNMP Community Name

The default SNMP Community name is I<public>. To provide an alternate default
SNMP Community name add it to the
L<$HOSTS_DEFAULTS|spong.hosts/"$HOSTS_DEFAULTS"> entry in the B<spong.conf> file
(i.e. $HOSTS_DEFAULTS{'snmp_community'} = 'secret';).

To override the default name on a per host basis, specify a C<snmp_ community>
attribute in a host's entry in the I<%HOSTS> variabile of the L<spong.conf>
configuration file. See the L<"EXAMPLES"> section for a detailed
example.

=item Ignored Interfaces

You can also specify a list of network interfaces for the B<check_interfaces>
module to ignore. Add an 'ignore_interfaces' attribute to a host entry in
$HOSTS with a list of network interface to ignore.

A default list of interfaces to ignore is added an 'ignore_interfaces' entry
to the $HOSTS_DEFAULTS variable.  A list of interfaces to ignore in all hosts
is created by adding an 'ignore_interfaces' entry to the $HOSTS_ALL varirable.
See the L<Examples|"EXAMPLES"> section for a detailed example.

=back

=head1 EXAMPLES

 %HOSTS = ( 'hostname.my-inc.com' => { 'services'  => 'interfaces',
                                       'ip_addr'   => ['192.168.13.123'],
                                       'community' => 'local-read',
                                       'ignore_interfaces' => ['ppp1','ppp2'];
                                       },
            );

 $HOSTS_DEFAULTS{'snmp_community'} = 'mysecret';

 # Ignore the unused default intefaces on more servers
 $HOSTS_DEFAULTS{'ignore_interfaces'} = ['et0','lan0'];

 # Skip the loopback interface on the linux boxes
 $HOSTS_ALL('ignore_interfaces} = ['lo0']; 


=head1 SEE ALSO

L<spong-network>, L<check_snmp>,
L<spong-network Modules Template|spong-network-mod-template>,
L<Spong Developer Guide|developer-guide>

=head1 NOTES

The B<check_interfaces> module use SNMP to poll a host. It retrieves the
B<ifIndex>, B<ifDesc>, B<ifType>, B<ifAdminStatus>, and  B<ifOperStatus> fields
for every entry in the B<ifTable> table. The module then scans all of all of
the network interfaces inretrieved from the table. Any interface that is
administratively up and is not operationally up will result in an critical
status (red) being return.

=head1 RESTRICTIONS

B<check_interfaces> uses the C<SNMP_Session>, C<SNMP_utils> and C<BER> modules
from the B<SNMP_Session> package. The B<SNMP_Session> package must be installed
in order for this module to work.

The latest version of B<SNMP_Session> package can be obtained from:

  http://www.switch.ch/misc/leinen/snmp/perl/index.html 

=head1 AUTHOR

The original author is Mike Bayliss <F<mbayliss@datax.be>>. Extra debug code
and enhancements added by Stephen L Johnson <F<sjohnson@monsters.org>>.

