#!/usr/bin/env perl package INF::APC; use SNMP::MIB::Compiler; use Net::SNMP qw(oid_lex_sort); use Data::Dumper; sub get_mib() { my $compiled_dir = "/usr/local/share/inf/mibs/compiled"; my $mibname = 'PowerNet-MIB'; mkdir $compiled_dir; my $mib = new SNMP::MIB::Compiler; $mib->add_path('/usr/local/share/inf/mibs'); $mib->add_extension('.mib'); $mib->repository($compiled_dir); $mib->{'accept_smiv1'} = 1; $mib->{'accept_smiv2'} = 1; $mib->{'make_dump'} = 1; $mib->{'use_dump'} = 1; $mib->{'do_imports'} = 1; die "can't load" unless $mib->compile($mibname); return $mib; } my $mib = get_mib(); sub name_to_oid(@) { my (@name) = (@_); my @ocmp = (); for my $cmp (@name) { push @ocmp, $mib->resolve_oid($cmp); } return join( '.', @ocmp ); } sub oid_to_name($) { my $oid = shift; $mib->convert_oid($oid); } sub get_snmp($$) { my ( $host, $cmty ) = @_; my ( $session, $error ) = Net::SNMP->session( -hostname => $host, -community => $cmty, -port => 161, -localport => 32130 ); if ( !defined($session) ) { printf( "ERROR: %s.\n", $error ); exit 1; } return $session; } sub short_name($) { my $name = shift; my @s = split( /\./, $name ); my @n = (); do { $node = pop @s; unshift @n, $node; return $toid unless ( defined $node ); } until ( defined $mib->{nodes}->{$node} ); return join( '.', @n ); } sub expand_enum($$) { my ( $oid_name, $value ) = @_; my @s = split( /\./, $oid_name ); do { $node = pop @s; return $value unless ( defined $node ); } until ( defined $mib->{nodes}->{$node} ); return $value unless ( $mib->{nodes}->{$node}->{type} eq 'OBJECT-TYPE' ); return $value unless ( $mib->{nodes}->{$node}->{syntax}->{type} eq 'INTEGER' ); return $value unless ( defined $mib->{'nodes'}->{$node}->{syntax}->{values}->{$value} ); return $mib->{'nodes'}->{$node}->{syntax}->{values}->{$value}; } sub lookup_enum($$) { my ( $oid_name, $value ) = @_; my @s = split( /\./, $oid_name ); do { $node = pop @s; return $value unless ( defined $node ); } until ( defined $mib->{nodes}->{$node} ); return $value unless ( $mib->{nodes}->{$node}->{type} eq 'OBJECT-TYPE' ); return $value unless ( $mib->{nodes}->{$node}->{syntax}->{type} eq 'INTEGER' ); for my $i ( keys %{ $mib->{'nodes'}->{$node}->{syntax}->{values} } ) { return $i if ( $mib->{'nodes'}->{$node}->{syntax}->{values}->{$i} =~ /$value/ ); } return $value; } sub show_results($) { my ($result) = @_; my $v = undef; for my $oid ( oid_lex_sort( keys( %{$result} ) ) ) { my $name = oid_to_name($oid); $v = expand_enum( $name, $result->{$oid} ); # print "oid=$oid\n"; print short_name($name); print " => "; print $v; print "\n"; } return $v; } my $mib = get_mib(); sub get { my $self = shift; # print "Getting ",join('.',@_),"\n"; my $oid = name_to_oid(@_); my $name = oid_to_name($oid); my $result = $self->{snmp}->get_request( -varbindlist => [$oid] ); my @keys = keys %$result; if ( scalar(@keys) != 1 ) { return undef; } my $value = $result->{ $keys[0] }; $value = expand_enum( $name, $value ); return $value; } sub set { my $self = shift; my $value = shift; my $type = shift; my $oid = name_to_oid(@_); my $vbl = [ ( $oid, $type, $value ) ]; # print Dumper($vbl),oid_to_name($oid),"\n"; my $result = $self->{snmp}->set_request( -varbindlist => $vbl ); return $result; } sub show { my $self = shift; my $oid = name_to_oid(@_); my $name = oid_to_name($oid); my $result = $self->{snmp}->get_request( -varbindlist => [$oid] ); show_results($result); } sub name_get($) { my $self = shift; my $ret; if ( $self->{vmunit} ) { return $self->get( 'sPDUMasterConfigVMName', $self->{vmunit} ); } for my $node ('rPDUIdentName') { $ret = $self->get( $node, '0' ); return $ret if ( defined $ret ); } return undef; } sub name_set($$) { my ( $self, $name ) = @_; if ( $self->{vmunit} ) { return $self->set( $name, Net::SNMP::OCTET_STRING, 'sPDUMasterConfigVMName', $self->{vmunit} ); } for my $node ('rPDUIdentName') { $ret = $self->get( $node, '0' ); if ( defined $ret ) { return $self->set( $name, Net::SNMP::OCTET_STRING, $node, '0' ); } } return undef; } sub port_count($) { my $self = shift; my $ret; if ( $self->{vmunit} ) { return $self->get( 'sPDUMasterStatusVMOutletCount', $self->{vmunit} ); } for my $node ( 'sPDUOutletConfigTableSize', 'rPDUOutletDevNumCntrlOutlets' ) { $ret = $self->get( $node, '0' ); return $ret if ( defined $ret ); } return 8; } sub port_id_get_by_number($) { my ( $self, $i ) = @_; return $i; } sub port_name_get($$) { my ( $self, $i ) = @_; my $ret; if ( $self->{vmunit} ) { return $self->get( 'sPDUOutletConfigVMOutletName', $self->{vmunit}, 1, $i ); } for my $node ( 'rPDUOutletConfigOutletName', 'sPDUOutletName' ) { $ret = $self->get( $node, $i ); return $ret if ( defined $ret ); } return undef; } sub port_name_set($$$) { my ( $self, $i, $name ) = @_; if ( $self->{vmunit} ) { return $self->set( $name, Net::SNMP::OCTET_STRING, 'sPDUOutletConfigVMOutletName', $self->{vmunit}, 1, $i ); } for my $node ( 'rPDUOutletConfigOutletName', 'sPDUOutletName' ) { $ret = $self->get( $node, $i ); if ( defined $ret ) { return $self->set( $name, Net::SNMP::OCTET_STRING, $node, $i ); } } return undef; } sub port_state_get_no_cache($$) { my ( $self, $i ) = @_; my $ret; if ( $self->{vmunit} ) { return $self->get( 'sPDUOutletStatusVMOutletState', $self->{vmunit}, 1, $i ); } for my $node ( 'rPDUOutletStatusOutletState', 'sPDUOutletCtl' ) { $ret = $self->get( $node, $i ); return $ret if ( defined $ret ); } return undef; } sub port_state_get($$) { my ( $self, $i ) = @_; return $self->port_state_get_no_cache($i); } sub port_state_set($$$) { my ( $self, $i, $state ) = @_; my $ret; if ( $self->{vmunit} ) { my $ret = $self->get( 'sPDUOutletControlVMOutletCommand', $self->{vmunit}, 1, $i ); my $oid = name_to_oid( 'sPDUOutletControlVMOutletCommand', $self->{vmunit}, 1, $i ); my $name = oid_to_name($oid); if ( defined $ret ) { my $v = lookup_enum( $name, $state ); return undef if ( not defined $v ); return undef if ( int($v) ne $v ); return $self->set( $v, Net::SNMP::INTEGER, 'sPDUOutletControlVMOutletCommand', $self->{vmunit}, 1, $i ); } return undef; } for my $node ( 'rPDUOutletControlOutletCommand', 'sPDUOutletCtl' ) { my $ret = $self->get( $node, $i ); my $oid = name_to_oid( $node, $i ); my $name = oid_to_name($oid); if ( defined $ret ) { my $v = lookup_enum( $name, $state ); # print "lookup of state=$state gave value=$v\n"; return undef if ( not defined $v ); return undef if ( int($v) ne $v ); return $self->set( $v, Net::SNMP::INTEGER, $node, $i ); } } return undef; } sub port_cycle($$) { my ( $self, $i ) = @_; my $ret; for my $cmd ( 'immediateReboot', 'outletReboot', 'immediateRebootVM' ) { $ret = $self->port_state_set( $i, $cmd ); return $ret if ( defined $ret ); } return undef; } sub port_off($$) { my ( $self, $i ) = @_; my $ret; for my $cmd ( 'immediateOff', 'outletOff', 'immediateOffVM' ) { $ret = $self->port_state_set( $i, $cmd ); return $ret if ( defined $ret ); } return undef; } sub port_on($$) { my ( $self, $i ) = @_; my $ret; for my $cmd ( 'immediateOn', 'outletOn', 'immediateOnVM' ) { $ret = $self->port_state_set( $i, $cmd ); return $ret if ( defined $ret ); } return undef; } sub pdu_load_get($) { my $self = shift; my $l; if ( $self->{vmunit} ) { $l = $self->get( 'sPDUMasterStatusVMCurrentLoad', $self->{vmunit} ); } else { $l = $self->get( 'rPDULoadStatusLoad', '1' ); } return undef unless ( defined $l ); return ( 1.0 * $l ) / 10.0; } sub psu_status($) { my $self = shift; my $v; my @vr = (); my $ok = 'Ok'; my $unk = 'Unknown'; $v = $self->get( 'rPDUPowerSupply1Status', '0' ); if ( defined $v ) { $ok = 'Sick' if ( $v ne 'powerSupplyOneOk' ); push @vr, $v; $unk = undef; } $v = $self->get( 'rPDUPowerSupply2Status', '0' ); if ( defined $v ) { $ok = 'Sick' if ( $v ne 'powerSupplyTwoOk' ); push @vr, $v; $unk = undef; } $v = $self->get( 'rPDUPowerSupplyAlarm', '0' ); if ( defined $v ) { $ok = 'Sick' if ( $v ne 'allAvailablePowerSuppliesOK' ); push @vr, $v; $unk = undef; } return $unk if ( defined $unk ); return $ok . "(" . join( ' ', @vr ) . ")"; } sub show_oid { my $self = shift; my $baseoid = name_to_oid(@_); my $result = $self->{snmp}->get_table( -baseoid => $baseoid ); show_results($result); } sub show_all($) { my $self = shift; $self->show_oid('apc'); } sub logout($) { my $self = shift; } # my ( $mib, $snmp, $basename ) = @_; # # $baseoid=resolve($mib,$basename); # # my $result = $snmp->get_table( -baseoid => $baseoid ); # # results($mib,$result); #} #sub status_show($) { # my $self = shift; # # $n = $self->get_n_outlets(); # # print $self->{host}, " thinks it's called ", $self->get_name(), "\n"; # print "has ", $n, " outlets\n"; # # for ( my $i = 1 ; $i <= $n ; ++$i ) { # print "outlet $i is called ", $self->get_outlet_name($i), " and is ", # $self->get_outlet_status($i), "\n"; # } # # my $l = $self->get_load; # if ( defined $l ) { # $l = $l . ' Amps'; # } # else { # $l = 'Unknown'; # } # # print "load ", $l, "\n"; # # print "PSU status ", $self->get_psu_status(), "\n"; # #} sub new ($;$) { my ( $class, $parm ) = @_; my $self; $self->{host} = $parm->{host} || "redbus-ms20.mythic-beasts.com"; $self->{community} = $parm->{community} || "private"; if ( $self->{host} =~ /^(.*):(.*)$/ ) { $self->{iphost} = $1; $self->{vmunit} = $2; } else { $self->{iphost} = $self->{host}; } $self->{snmp} = get_snmp( $self->{iphost}, $self->{community} ); return bless $self, $class; } 1;