fix overwrite of variables on multiple responses
[snmp-json.git] / printer-callbacks.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4
5 use Net::SNMP;
6 use Data::Dump qw(dump);
7
8 my $dir = 'public/json/monitor/printers/';
9 $dir = "/tmp/printers-" unless -d $dir;
10
11 use JSON;
12 sub save_json {
13         my ( $ip, $json ) = @_;
14         my $path = $dir . $ip;
15         open(my $fh, '>', $path) || die "$path: $!";
16         print $fh encode_json $json;
17         close($fh);
18         warn "# $path ", -s $path, " bytes\n";
19 }
20
21 sub iso_datetime {
22         my ($ss,$mm,$hh,$d,$m,$y) = localtime(time);
23         return sprintf "%04d-%02d-%02dT%02d:%02d:%02d", $y+1900, $m, $d, $hh, $mm, $ss;
24 }
25
26 my $log_path = join('.', $dir . (split(/T/,iso_datetime,2))[0], 'json');
27 open(my $log, '>>', $log_path) || die "$log_path: $!";
28
29 my $community = 'public';
30 my @printers = qw(
31 10.60.0.20
32
33 10.60.0.40
34
35 10.60.3.15
36 10.60.3.17
37 );
38
39 @printers = @ARGV if @ARGV;
40
41 my %vars = qw[
42 info                            iso.3.6.1.2.1.1.1.0
43 hostname                        iso.3.6.1.2.1.43.5.1.1.16.1
44 serial                          iso.3.6.1.2.1.43.5.1.1.17.1
45 pages                           iso.3.6.1.2.1.43.10.2.1.4.1.1
46 @message                        iso.3.6.1.2.1.43.18.1.1.8
47 @consumable.name        iso.3.6.1.2.1.43.11.1.1.6.1
48 @consumable.max         iso.3.6.1.2.1.43.11.1.1.8.1
49 @consumable.curr        iso.3.6.1.2.1.43.11.1.1.9.1
50 @tray.dim_x                     iso.3.6.1.2.1.43.8.2.1.4.1
51 @tray.dim_y                     iso.3.6.1.2.1.43.8.2.1.5.1
52 @tray.max                       iso.3.6.1.2.1.43.8.2.1.9.1
53 @tray.capacity          iso.3.6.1.2.1.43.8.2.1.10.1
54 @tray.name                      iso.3.6.1.2.1.43.8.2.1.13.1
55 ];
56
57 our $response;
58
59 sub columns_cb {
60         my ( $session, $oid2name ) = @_;
61
62         my $ip = $session->hostname;
63
64         if ( ! defined $session->var_bind_list ) {
65                 warn "ERROR: $ip ", $session->error, "\n";
66                 warn dump($session);
67                 return;
68         }
69
70
71         warn "# $ip var_bind_list ", dump( $session->var_bind_list );
72         my $results = $session->var_bind_list;
73         $response->{$ip}->{ip} ||= $ip;
74         $response->{$ip}->{utime} ||= time();
75         # oid_lex_sort would be wonderfull to use here, but it doesn't work
76         foreach my $r_oid ( sort {
77                         my ($af,$bf) = ($a,$b);
78                         $af =~ s{\.(\d+)$}{sprintf("%03d",$1)}eg;
79                         $bf =~ s{\.(\d+)$}{sprintf("%03d",$1)}eg;
80                         $af cmp $bf
81         } keys %$results ) {
82                 my $var = $results->{$r_oid};
83                 my $oid = (grep {
84                         substr($r_oid,0,length($_)) eq $_
85                 } keys %$oid2name)[0] || die "no name for $r_oid in ",dump($oid2name);
86                 my $name = $oid2name->{$oid};
87                 if ( $name =~ m{^\@} ) {
88                         my $no_prefix = $name;
89                         $no_prefix =~ s{^\@}{};
90                         push @{ $response->{$ip}->{ $no_prefix } }, $var;
91                 } else {
92                         $response->{$ip}->{ $name } = $var;
93                 }
94         }
95
96         warn "## $ip response ",dump($response->{$ip});
97 }
98
99 foreach my $host ( @printers ) {
100
101         my ( $snmp, $err ) = Net::SNMP->session(
102                 -hostname => $host,
103                 -version => 1,
104                 -community => $community,
105                 -timeout => 2,
106                 -retries => 0,
107                 -nonblocking => 1,
108         );
109
110         if ( ! $snmp ) {
111                 warn "ERROR: $host $err\n";
112                 next;
113         }
114
115         my @columns;
116         my @vars;
117         my $oid2name;
118         while ( my ($name,$oid) = each %vars ) {
119                 warn "# $name $oid\n";
120                 $oid =~ s{^iso}{.1};
121                 if ( $name =~ m/^\@/ ) {
122                         push @columns, $oid;
123                 } else {
124                         push @vars, $oid;
125                 }
126                 $oid2name->{$oid} = $name;
127         }
128         $snmp->get_request( -varbindlist => [ @vars ], -callback => [ \&columns_cb, $oid2name ] );
129         $snmp->get_entries( -columns =>  [ @columns ], -callback => [ \&columns_cb, $oid2name ] );
130
131 }
132
133 warn "# dispatch requests for ",dump(@printers);
134 snmp_dispatcher;
135
136 foreach my $ip ( keys %$response ) {
137
138         my $status = $response->{$ip};
139         foreach my $group ( grep { /\w+\.\w+/ } keys %$status ) {
140                 my ( $prefix,$name ) = split(/\./,$group,2);
141                 if ( ref $status->{$group} eq 'ARRAY' ) { # some consumables are non-repeatable on low-end devices
142                         foreach my $i ( 0 .. $#{ $status->{$group} } ) {
143                                 $status->{$prefix}->[$i]->{$name} = $status->{$group}->[$i];
144                         }
145                 } else {
146                         $status->{$prefix}->[0]->{$name} = $status->{$group};
147                 }
148                 delete $status->{$group};
149         }
150
151         print "$ip ",dump($status);
152         save_json $ip => $response->{$ip};
153         print $log encode_json($response->{$ip}),"\n";
154 }
155
156 close($log);
157 warn "# log $log_path ", -s $log_path, " bytes\n";
158