diff options
Diffstat (limited to 'clients')
-rwxr-xr-x | clients/accesspoints.pl | 76 | ||||
-rwxr-xr-x | clients/admintool.sh | 19 | ||||
-rwxr-xr-x | clients/dhcptail.pl | 74 | ||||
-rwxr-xr-x | clients/ipv6-dns.pl | 133 | ||||
-rwxr-xr-x | clients/ipv6-stats.pl | 89 | ||||
-rwxr-xr-x | clients/portnames.pl | 18 | ||||
-rwxr-xr-x | clients/smanagrun.pl | 167 | ||||
-rw-r--r-- | clients/snmp.sql | 24 | ||||
-rwxr-xr-x | clients/snmpfetch.pl | 281 | ||||
-rwxr-xr-x | clients/update-public-nms.sh | 12 | ||||
-rw-r--r-- | clients/update-switch-placements.pl | 53 |
11 files changed, 946 insertions, 0 deletions
diff --git a/clients/accesspoints.pl b/clients/accesspoints.pl new file mode 100755 index 0000000..d998252 --- /dev/null +++ b/clients/accesspoints.pl @@ -0,0 +1,76 @@ +#! /usr/bin/perl +use strict; +use warnings; +use BER; +use DBI; +use POSIX; +use Time::HiRes; +use Net::Ping; +require 'SNMP_Session.pm'; + +use lib '../include'; +use nms; +use threads; + +poll_loop(); + +sub poll_loop { + my $dbh = nms::db_connect(); + my $qcores = $dbh->prepare('SELECT DISTINCT coreswitches.sysname, coreswitches.switch, coreswitches.ip, coreswitches.community FROM uplinks JOIN switches AS coreswitches ON (uplinks.coreswitch = coreswitches.switch)'); + my $qaps = $dbh->prepare("SELECT switches.sysname, switches.switch, uplinks.blade, uplinks.port FROM uplinks NATURAL JOIN switches WHERE uplinks.coreswitch = ?"); + my $qpoll = $dbh->prepare("UPDATE ap_poll SET model=?, last_poll=now() WHERE switch = ?"); + + while (1) { + $qcores->execute(); + my $cores = $qcores->fetchall_hashref("sysname"); + + foreach my $core (keys %$cores) { + my $ip = $cores->{$core}{'ip'}; + my $community = $cores->{$core}{'community'}; + printf "Polling %s (%s)\n", $core, $ip; + eval { + my $session = SNMPv2c_Session->open($ip, $community, 161) or die "Couldn't talk to switch"; + $qaps->execute($cores->{$core}{'switch'}); + while (my $aps = $qaps->fetchrow_hashref()) { + my $sysname = $aps->{'sysname'}; + my $blade = $aps->{'blade'}; + my $port = $aps->{'port'}; + my $oid = BER::encode_oid(1,3,6,1,2,1,105,1,1,1,9,$blade,$port); # POWER-ETHERNET-MIB...pethPsePortType + my $mode = fetch_snmp($session, $oid); + $qpoll->execute($mode, $aps->{'switch'}); + printf "%s (%s:%s/%s): %s\n", $sysname, $core, $blade, $port, $mode; + } + }; + if ($@) { + mylog("ERROR: $@ (during poll of $ip)"); + $dbh->rollback; + } + } + sleep 2; + } +} + +# Kokt fra snmpfetch.pl, vi bør nok lage et lib +sub fetch_snmp { + my ($session, $oid) = @_; + + if ($session->get_request_response($oid)) { + my ($bindings) = $session->decode_get_response ($session->{pdu_buffer}); + my $binding; + while ($bindings ne '') { + ($binding,$bindings) = &decode_sequence ($bindings); + my ($oid,$value) = &decode_by_template ($binding, "%O%@"); + return BER::pretty_print($value); + } + } + die "Couldn't get info from switch"; +} + +sub mylog { + my $msg = shift; + my $time = POSIX::ctime(time); + $time =~ s/\n.*$//; + printf STDERR "[%s] %s\n", $time, $msg; +} + + diff --git a/clients/admintool.sh b/clients/admintool.sh new file mode 100755 index 0000000..889dd19 --- /dev/null +++ b/clients/admintool.sh @@ -0,0 +1,19 @@ +while :; do + ( + for i in $( cut -d" " -f1 pingswitches.txt ); do + ADMINADDR=$( echo $i | perl -pi -le '@x = split /\./; $x[3] += 2; $_ = join(".", @x);' ) + ( ( + if ping -c2 -W3 -q $ADMINADDR >/dev/null; then + grep $i pingswitches.txt | sed 's/^/PONGER: /' + else + grep $i pingswitches.txt | sed 's/^/PONGER IKKE: /' + fi + ) & ) + done + ) > pong.new + while pidof ping > /dev/null; do sleep 1; done + mv pong.new pong + echo "sleeping" + sleep 10 +done + diff --git a/clients/dhcptail.pl b/clients/dhcptail.pl new file mode 100755 index 0000000..e7898aa --- /dev/null +++ b/clients/dhcptail.pl @@ -0,0 +1,74 @@ +#! /usr/bin/perl +use DBI; +use POSIX; +use lib '../include'; +use nms; +use strict; +use warnings; + +BEGIN { + require "../include/config.pm"; + eval { + require "../include/config.local.pm"; + }; +} + +my $year = $nms::config::tgname; +$year =~ s/tg/20/; # hihi + +my %months = ( + Jan => 1, + Feb => 2, + Mar => 3, + Apr => 4, + May => 5, + Jun => 6, + Jul => 7, + Aug => 8, + Sep => 9, + Oct => 10, + Nov => 11, + Dec => 12 +); + +my ($dbh, $q, $cq); +open(SYSLOG, "tail -n 9999999 -F /var/log/syslog |") or die "Unable to tail syslog: $!"; +while (<SYSLOG>) { + /(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(\d+)\s+(\d+:\d+:\d+).*DHCPACK on (\d+\.\d+\.\d+\.\d+) to (\S+)/ or next; + my $date = $year . "-" . $months{$1} . "-" . $2 . " " . $3; + my $machine = $5; + my $owner_color; + + if ($machine eq '00:15:c5:42:ce:e9') { + $owner_color = '#00ff00'; # Steinar + } elsif ($machine eq '00:1e:37:1c:d2:65') { + $owner_color = '#c0ffee'; # Trygve + } elsif ($machine eq '00:16:d3:ce:8f:a7') { + $owner_color = '#f00f00'; # Jon + } elsif ($machine eq '00:16:d4:0c:8a:1c') { + $owner_color = '#ff99ff'; # Jørgen + } elsif ($machine eq '00:18:8b:aa:2f:f8') { + $owner_color = '#663300'; # Kjetil + } elsif ($machine eq '00:15:58:29:14:e3') { + $owner_color = '#f1720f'; # BÃ¥rd + } else { + $owner_color = "#000000"; # Unknown + } + + if (!defined($dbh) || !$dbh->ping) { + $dbh = nms::db_connect(); + $q = $dbh->prepare("UPDATE dhcp SET last_ack=? WHERE ?::inet << network AND ( last_ack < ? OR last_ack IS NULL )") + or die "Couldn't prepare query"; + $cq = $dbh->prepare("UPDATE dhcp SET owner_color=? WHERE ?::inet << network AND owner_color IS NULL") + or die "Couldn't prepare query"; + } + + print STDERR "$date $4\n"; + $q->execute($date, $4, $date) + or die "Couldn't push $1 into database"; + if (defined($owner_color)) { + $cq->execute($owner_color, $4) + or die "Couldn't push $1 into database"; + } +} +close SYSLOG; diff --git a/clients/ipv6-dns.pl b/clients/ipv6-dns.pl new file mode 100755 index 0000000..d965acc --- /dev/null +++ b/clients/ipv6-dns.pl @@ -0,0 +1,133 @@ +#! /usr/bin/perl +use DBI; +use Net::DNS; +use Net::IP; +use lib '../include'; +use nms; +use strict; +use warnings; + +BEGIN { + require "../include/config.pm"; + eval { + require "../include/config.local.pm"; + }; +} + +my $dbh = nms::db_connect() or die "Can't connect to database"; +my $res = Net::DNS::Resolver->new; + +$res->nameservers("$nms::config::pri_a.$nms::config::tgname.gathering.org"); + +my $kname = 'DHCP_UPDATER'; + +sub get_reverse { + my ($ip) = shift; + $ip = new Net::IP($ip) or return 0; + my $a = $res->query($ip->reverse_ip, 'PTR') or return 0; + foreach my $n ($a->answer) { + return $n->{'ptrdname'}; # Return first element, ignore the rest (= + } + return 0; +} + +sub any_quad_a { + my ($host) = shift; + my $a = $res->query($host, 'AAAA') or return 0; + foreach my $n ($a->answer) { + return 1 if ($n->{'type'} eq 'AAAA'); + } + return 0; +} + +print "Running automagic IPv6 DNS updates\n"; +while (1) { + + # Fetch visible IPv6 addresses from the last three minutes + my $q = $dbh->prepare("SELECT DISTINCT ON (ipv6.mac) ipv6.address AS v6, ipv6.mac, ipv4.address AS v4, ipv6.time - ipv6.age*'1 second'::interval FROM ipv6 LEFT JOIN ipv4 ON ipv4.mac = ipv6.mac WHERE ipv6.time > NOW()-'3 min'::interval ORDER BY ipv6.mac, ipv6.time DESC, mac") + or die "Can't prepare query"; + $q->execute() or die "Can't execute query"; + + my $aaaas = 0; + my $aaaa_errors = 0; + my $ptrs = 0; + my $ptr_errors = 0; + my $update; + my $v6; + while (my $ref = $q->fetchrow_hashref()) { + my $hostname = get_reverse($ref->{'v4'}); + if ($hostname) { + $v6 = $ref->{'v6'}; + my @parts = split('\.', $hostname); + my $hostname = shift @parts; + my $domain = join('.', @parts); + my $v6arpa = (new Net::IP($v6))->reverse_ip; + + # Don't add records for nets we don't control + next if not $v6arpa =~ m/$nms::config::ipv6zone/; + + # Add IPv6 reverse + if (!get_reverse($ref->{'v6'})) { + $update = Net::DNS::Update->new($nms::config::ipv6zone); + $update->push(pre => nxrrset("$v6arpa IN PTR")); # Only update if the RR is nonexistent + $update->push(update => rr_add("$v6arpa IN PTR $hostname.$domain.")); + $update->sign_tsig($kname, $nms::config::ddns_key); + my $reply = $res->send($update); + if ($reply->header->rcode eq 'NOERROR') { + $ptrs++; + } else { + $ptr_errors++; + } + } + + # Add IPv6 forward + if (!any_quad_a("$hostname.$domain")) { + $update = Net::DNS::Update->new($domain); + $update->push(pre => nxrrset("$hostname.$domain. IN AAAA $v6")); # Only update if the RR is nonexistent + $update->push(update => rr_add("$hostname.$domain. IN AAAA $v6")); + $update->sign_tsig($kname, $nms::config::ddns_key); + my $reply = $res->send($update); + if ($reply->header->rcode eq 'NOERROR') { + $aaaas++; + } else { + $aaaa_errors++; + } + } + } + } + print "Added $ptrs PTR records. $ptr_errors errors occured.\n"; + print "Added $aaaas AAAA records. $aaaa_errors errors occured.\n"; + + + # Remove old PTR records, that is, for hosts we haven't seen the last four + # hours, but not older than four and a half hours, as it would take forever to + # try to delete everything. FIXME: Query the zone file and diff against the + # database, to avoid running as many NS-updates as tuples in the result set. + + $q = $dbh->prepare("SELECT DISTINCT address AS v6 FROM ipv6 WHERE time < NOW() - '4 hours'::interval AND time > NOW() - '4 hours 30 minutes'::interval") + or die "Can't prepare query"; + $q->execute() or die "Can't execute query"; + + my $i = 0; + my $errors = 0; + while (my $ref = $q->fetchrow_hashref()) { + $v6 = $ref->{'v6'}; + if (get_reverse($v6)) { + my $v6arpa = (new Net::IP($v6))->reverse_ip; + my $update = Net::DNS::Update->new($nms::config::ipv6zone); + $update->push(pre => yxrrset("$v6arpa PTR")); # Only update if the RR exists + $update->push(update => rr_del("$v6arpa IN PTR")); + $update->sign_tsig($kname, $nms::config::ddns_key); + my $reply = $res->send($update); + if ($reply->header->rcode eq 'NOERROR') { + $i++; + } else { + $errors++; + } + } + } + + print "Deleted $i old PTR records. $errors errors occured.\n"; + print "Sleeping for two minutes.\n"; + sleep(120); +} diff --git a/clients/ipv6-stats.pl b/clients/ipv6-stats.pl new file mode 100755 index 0000000..59dacbd --- /dev/null +++ b/clients/ipv6-stats.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use lib '../include'; +use nms qw(switch_connect switch_exec switch_disconnect); +use Net::Telnet::Cisco; + +BEGIN { + require "../include/config.pm"; + eval { + require "../include/config.local.pm"; + }; +} + +# +# Fetch list of MAC addresses and IPv6 addresses +# +sub query_router { + my ($host) = @_; + + my $ios = Net::Telnet::Cisco->new( + Host => $host, + Errmode => 'return'); + if (not defined $ios) { + warn "Can't connect to $host: $!, skipping"; + return (); + } + if (not $ios->login($nms::config::ios_user, $nms::config::ios_pass)) { + warn "Can't login to $host. Wrong username or password?"; + return (); + } + $ios->autopage(0); + $ios->cmd("terminal length 0"); + my @v6data = $ios->cmd('show ipv6 neighbors') + or warn "$host wouldn't let me run show ipv6 neighbors."; + my @v4data = $ios->cmd('show ip arp') + or warn "$host wouldn't let me run show ip arp."; + + # Remove useless header and footer +# shift @v6data; + pop @v6data; +# shift @v4data; + pop @v4data; + + return { 'v6' => \@v6data, 'v4' => \@v4data }; +} + +while (1) { + print "Gathering IPv6 and IPv4 stats\n"; + # Connect to DB + my $dbh = nms::db_connect(); + $dbh->{AutoCommit} = 0; + + my ($v4, $v6) = 0; + foreach my $router (@nms::config::distrobox_ips) { + my $data = query_router($router); + # IPv6 + foreach my $line (@{$data->{'v6'}}) { + my ($address, $age, $mac, undef, undef) = split('\s+', $line); + if ($mac =~ /[a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4}/ && # Sanity check MAC address + $address !~ /^FE.*/) { # Filter out non-routable addresses + my $q = $dbh->prepare('INSERT INTO ipv6 (address, age, mac, time) VALUES (?, ?, ?, timeofday()::timestamp)') + or die "Can't prepare query: $!"; + $q->execute($address, $age, $mac) + or die "Can't execute query: $!"; + $v6++; + } + } + # IPv4 + foreach my $line (@{$data->{'v4'}}) { + my (undef, $address, $age, $mac, undef, undef) = split('\s+', $line); + if ($mac =~ /[a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4}/) {# Sanity check MAC address + $age = 0 if $age eq '-'; + my $q = $dbh->prepare('INSERT INTO ipv4 (address, age, mac, time) VALUES (?, ?, ?, timeofday()::timestamp)') + or die "Can't prepare query: $!"; + $q->execute($address, $age, $mac) + or die "Can't execute query: $!"; + $v4++; + } + } + } + print "Added $v6 IPv6 addresses and $v4 IPv4 addresses.\n"; + $dbh->commit; + $dbh->disconnect; + + print "Sleeping for two minutes.\n"; + sleep 120; # Sleep for two minutes +} diff --git a/clients/portnames.pl b/clients/portnames.pl new file mode 100755 index 0000000..2b29189 --- /dev/null +++ b/clients/portnames.pl @@ -0,0 +1,18 @@ +#! /usr/bin/perl + +my ($host,$switchtype,$community) = @ARGV; + +open SNMP, "snmpwalk -Os -c $community -v 2c $host ifDescr |" + or die "snmpwalk: $!"; + +print "begin;\n"; +print "delete from portnames where switchtype='$switchtype';\n"; + +while (<SNMP>) { + chomp; + /^ifDescr\.(\d+) = STRING: (.*)$/ or next; + + print "insert into portnames (switchtype,port,description) values ('$switchtype',$1,'$2 (port $1)');\n"; +} + +print "end;\n"; diff --git a/clients/smanagrun.pl b/clients/smanagrun.pl new file mode 100755 index 0000000..4d03696 --- /dev/null +++ b/clients/smanagrun.pl @@ -0,0 +1,167 @@ +#!/usr/bin/perl +# +# + +use warnings; +use strict; +use Net::Telnet; +use DBI; +use POSIX; +use lib '../include'; +use nms; +use Data::Dumper::Simple; + +BEGIN { + require "../include/config.pm"; + eval { + require "../include/config.local.pm"; + }; +} +# Tweak and check +my $password = ''; +my $timeout = 15; +my $delaytime = 30; +my $poll_frequency = 60; + +my $dbh = db_connect(); +$dbh->{AutoCommit} = 0; + +my $spoll = $dbh->prepare(" +SELECT + addr, + sysname +FROM + squeue +WHERE + processed = 'f' AND + disabled = 'f' AND + (locked='f' OR now() - updated > '10 minutes'::interval) AND + (delay IS NULL OR delay < now()) +ORDER BY + priority DESC, + added +LIMIT 1"); +my $sgetallpoll = $dbh->prepare(" +SELECT + id, + gid, + addr, + sysname, + cmd +FROM + squeue +WHERE + sysname = ? AND + disabled = 'f' AND + processed = 'f' +ORDER BY + priority DESC, + added"); + +my $slock = $dbh->prepare("UPDATE squeue SET locked = 't', updated=now() WHERE sysname = ?") + or die "Unable to prepare slock"; +my $sunlock = $dbh->prepare("UPDATE squeue SET locked = 'f', updated=now() WHERE sysname = ?") + or die "Unable to prepare sunlock"; +my $sresult = $dbh->prepare("UPDATE squeue SET updated = now(), result = ?, + processed = 't' WHERE id = ?") + or die "Unable to prepare sresult"; +my $sdelay = $dbh->prepare("UPDATE squeue SET delay = now() + delaytime, updated=now(), result = ? WHERE sysname = ?") + or die "Unable to prepae sdelay"; + +## Send a command to switch and return the data recvied from the switch +#sub switch_exec($$) { +# my ($cmd, $conn) = @_; +# +# # Send the command and get data from switch +# my @data = $conn->cmd($cmd); +# my @lines = (); +# foreach my $line (@data) { +# # Remove escape-7 sequence +# $line =~ s/\x1b\x37//g; +# push (@lines, $line); +# } +# +# return @data; +#} +# +#sub switch_connect($) { +# my ($ip) = @_; +# +# my $conn = new Net::Telnet( Timeout => $timeout, +# Dump_Log => '/tmp/dumplog-queue', +# Errmode => 'return', +# Prompt => '/DGS-3100\#/i'); +# my $ret = $conn->open( Host => $ip); +# if (!$ret || $ret != 1) { +# return (undef); +# } +# # XXX: Just send the password as text, I did not figure out how to +# # handle authentication with only password through $conn->login(). +# #$conn->login(»·Prompt => '/password[: ]*$/i', +# # Name => $password, +# # Password => $password); +# $conn->cmd($password); +# # Get rid of banner +# $conn->get; +# return ($conn); +#} +# +sub mylog { + my $msg = shift; + my $time = POSIX::ctime(time); + $time =~ s/\n.*$//; + printf STDERR "[%s] %s\n", $time, $msg; +} + +while (1) { + $spoll->execute() or die "Could not execute spoll"; + my $switch = $spoll->fetchrow_hashref(); + if (!defined($switch)) { + $dbh->commit; + mylog("No available switches in pool, sleeping."); + sleep 10; + next; + } + $slock->execute($switch->{sysname}); + $dbh->commit(); + + if ($switch->{'locked'}) { + mylog("WARNING: Lock timed out on $switch->{'ip'}, breaking lock"); + } + + mylog("Connecting to $switch->{sysname} on $switch->{addr}"); + my $conn = switch_connect($switch->{addr}); + if (!defined($conn)) { + mylog("Could not connect to ".$switch->{sysname}."(".$switch->{addr}.")"); + $sdelay->execute("Could not connect to switch, delaying...", $switch->{sysname}); + $sunlock->execute($switch->{sysname}); + $dbh->commit(); + next; + } + my $error; + $error = $sgetallpoll->execute($switch->{sysname}); + if (!$error) { + print "Could not execute sgetallpoll\n".$dbh->errstr(); + $conn->close; + next; + } + while (my $row = $sgetallpoll->fetchrow_hashref()) { + print "sysname: ".$row->{sysname}." cmd: ".$row->{cmd}."\n"; + my @data; + my @commands = split(/[\r\n\000]+/, $row->{cmd}); + for my $cmd (@commands) { + next unless $cmd =~ /\S/; # ignorer linjer med kun whitespace + push @data, "# $cmd"; + if ($cmd =~ s/^!//) { + push @data, switch_exec($cmd, $conn, 1); + } else { + push @data, switch_exec($cmd, $conn); + } + } + my $result = join("\n", @data); + $sresult->execute($result, $row->{id}); + } + $conn->close(); + $sunlock->execute($switch->{sysname}); +} + diff --git a/clients/snmp.sql b/clients/snmp.sql new file mode 100644 index 0000000..47b4458 --- /dev/null +++ b/clients/snmp.sql @@ -0,0 +1,24 @@ +create table switchtypes ( + switchtype varchar not null primary key, + ports varchar not null +); + +create table switches ( + switch serial not null primary key, + ip inet not null, + sysname varchar not null, + switchtype varchar not null references switchtypes, + last_updated timestamp, + locked boolean not null default 'f' +); + +create table poll ( + time timestamp not null, + switch integer not null references switches, + port integer not null, + bytes_in bigint not null, + bytes_out bigint not null, + + primary key ( time, switch, port ) +); +create index poll_switch_port on poll ( switch, port ); diff --git a/clients/snmpfetch.pl b/clients/snmpfetch.pl new file mode 100755 index 0000000..487ae11 --- /dev/null +++ b/clients/snmpfetch.pl @@ -0,0 +1,281 @@ +#! /usr/bin/perl +use BER; +use DBI; +use POSIX; +use Time::HiRes; +use Net::Telnet; +use strict; +use warnings; +require 'SNMP_Session.pm'; + +use lib '../include'; +use nms; +use threads; + +# normal mode: fetch switches from the database +# instant mode: poll the switches specified on the command line +if (defined($ARGV[0])) { + poll_loop(@ARGV); +} else { + my $threads = 50; + for (1..$threads) { + threads->create(\&poll_loop); + } + poll_loop(); +} + +sub poll_loop { + my @switches = @_; + my $instant = (scalar @switches > 0); + my $timeout = 15; + + my $dbh = nms::db_connect(); + $dbh->{AutoCommit} = 0; + + my $qualification; + if ($instant) { + $qualification = "sysname=?"; + } else { + $qualification = <<"EOF"; + (last_updated IS NULL OR now() - last_updated > poll_frequency) + AND (locked='f' OR now() - last_updated > '15 minutes'::interval) + AND ip is not null +EOF + } + + my $qswitch = $dbh->prepare(<<"EOF") +SELECT + *, + DATE_TRUNC('second', now() - last_updated - poll_frequency) AS overdue +FROM + switches + NATURAL LEFT JOIN switchtypes +WHERE $qualification +ORDER BY + priority DESC, + overdue DESC +LIMIT 1 +FOR UPDATE OF switches +EOF + or die "Couldn't prepare qswitch"; + my $qlock = $dbh->prepare("UPDATE switches SET locked='t', last_updated=now() WHERE switch=?") + or die "Couldn't prepare qlock"; + my $qunlock = $dbh->prepare("UPDATE switches SET locked='f', last_updated=now() WHERE switch=?") + or die "Couldn't prepare qunlock"; + my $qpoll = $dbh->prepare("INSERT INTO polls (time, switch, port, bytes_in, bytes_out, errors_in, errors_out) VALUES (timeofday()::timestamp,?,?,?,?,?,?)") + or die "Couldn't prepare qpoll"; + my $qtemppoll = $dbh->prepare("INSERT INTO temppoll (time, switch, temp) VALUES (timeofday()::timestamp,?::text::int,?::text::float)") + or die "Couldn't prepare qtemppoll"; + my $qcpupoll = $dbh->prepare("INSERT INTO cpuloadpoll (time, switch, entity, value) VALUES (timeofday()::timestamp,?::text::int,?,?)") + or die "Couldn't prepare qtemppoll"; + + while (1) { + my $sysname; + if ($instant) { + $sysname = shift @ARGV; + exit if (!defined($sysname)); + $qswitch->execute($sysname) + or die "Couldn't get switch"; + } else { + # Find a switch to grab + $qswitch->execute() + or die "Couldn't get switch"; + } + my $switch = $qswitch->fetchrow_hashref(); + + if (!defined($switch)) { + $dbh->commit; + + if ($instant) { + mylog("No such switch $sysname available, quitting."); + exit; + } else { + mylog("No available switches in pool, sleeping."); + sleep 15; + next; + } + } + + $qlock->execute($switch->{'switch'}) + or die "Couldn't lock switch"; + $dbh->commit; + + if ($switch->{'locked'}) { + mylog("WARNING: Lock timed out on $switch->{'ip'}, breaking lock"); + } + + my $msg; + if (defined($switch->{'overdue'})) { + $msg = sprintf "Polling ports %s on %s (%s), %s overdue.", + $switch->{'ports'}, $switch->{'ip'}, $switch->{'sysname'}, $switch->{'overdue'}; + } else { + $msg = sprintf "Polling ports %s on %s (%s), never polled before.", + $switch->{'ports'}, $switch->{'ip'}, $switch->{'sysname'}; + } + mylog($msg); + + my $ip = $switch->{'ip'}; + + if ($ip eq '127.0.0.1') { + mylog("Polling disabled for this switch, skipping."); + $qunlock->execute($switch->{'switch'}) + or die "Couldn't unlock switch"; + $dbh->commit; + next; + } + + my $community = $switch->{'community'}; + my $start = [Time::HiRes::gettimeofday]; + eval { + my $session; + if ($switch->{'wide_counters'}) { + $session = SNMPv2c_Session->open($ip, $community, 161) + or die "Couldn't talk to switch"; + } else { + $session = SNMP_Session->open($ip, $community, 161) + or die "Couldn't talk to switch"; + } + my @ports = expand_ports($switch->{'ports'}); + + for my $port (@ports) { + my $in = fetch_data($session, $port, 0, $switch->{'wide_counters'}); + die $switch->{'switch'}.":$port: failed reading in" if !defined $in; + my $out = fetch_data($session, $port, 1, $switch->{'wide_counters'}); + die $switch->{'switch'}.":$port: failed reading out" if !defined $out; + my $ine = fetch_errors($session, $port, 0); + die $switch->{'switch'}. ":$port: failed reading in-errors" if !defined $ine; + my $oute = fetch_errors($session, $port, 1); + die $switch->{'switch'}. ":$port: failed reading out-errors" if !defined $oute; + + $qpoll->execute($switch->{'switch'}, $port, $in, $out, $ine, $oute) || die "%s:%s: %s\n", $switch->{'switch'}, $port, $in; + } + $session->close; + }; + if ($@) { + mylog("ERROR: $@ (during poll of $ip)"); + $dbh->rollback; + } + + my $elapsed = Time::HiRes::tv_interval($start); + $msg = sprintf "Polled $switch->{'ip'} in %5.3f seconds.", $elapsed; + mylog($msg); + + $qunlock->execute($switch->{'switch'}) + or warn "Couldn't unlock switch"; + $dbh->commit; + } +} + +sub fetch_data { + my ($session, $port, $out, $wide_counters) = @_; + + my $oid; + if ($wide_counters) { + if ($out) { + $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 10, $port); # interfaces.ifTable.ifEntry.ifHCOutOctets + } else { + $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, $port); # interfaces.ifTable.ifEntry.ifHCInOctets + } + } else { + if ($out) { + $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 16, $port); # interfaces.ifTable.ifEntry.ifOutOctets + } else { + $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 10, $port); # interfaces.ifTable.ifEntry.ifInOctets + } + } + + return fetch_snmp($session, $oid); +} +sub fetch_errors { + my ($session, $port, $out) = @_; + + my $oid; + if ($out) { + $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 20, $port); # interfaces.ifTable.ifEntry.ifOutErrors + } else { + $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 14, $port); # interfaces.ifTable.ifEntry.ifInErrors + } + + return fetch_snmp($session, $oid); +} + +sub fetch_snmp { + my ($session, $oid) = @_; + + if ($session->get_request_response($oid)) { + my ($bindings) = $session->decode_get_response ($session->{pdu_buffer}); + my $binding; + while ($bindings ne '') { + ($binding,$bindings) = &decode_sequence ($bindings); + my ($oid,$value) = &decode_by_template ($binding, "%O%@"); + return BER::pretty_print($value); + } + } + die "Couldn't get info from switch"; +} + +sub expand_ports { + my $in = shift; + my @ranges = split /,/, $in; + my @ret = (); + + for my $range (@ranges) { + if ($range =~ /^\d+$/) { + push @ret, $range; + } elsif ($range =~ /^(\d+)-(\d+)$/) { + for my $i ($1..$2) { + push @ret, $i; + } + } else { + die "Couldn't understand '$range' in ports"; + } + } + + return (sort { $a <=> $b } @ret); +} + +sub mylog { + my $msg = shift; + my $time = POSIX::ctime(time); + $time =~ s/\n.*$//; + printf STDERR "[%s] %s\n", $time, $msg; +} + +#sub switch_exec { +# my ($cmd, $conn) = @_; +# +# # Send the command and get data from switch +## $conn->dump_log(*STDOUT); +# my @data = $conn->cmd($cmd); +# my @lines = (); +# foreach my $line (@data) { +# # Remove escape-7 sequence +# $line =~ s/\x1b\x37//g; +# push @lines, $line; +# } +# +# return @lines; +#} + +#sub switch_connect { +# my ($ip) = @_; +# +# my $conn = new Net::Telnet( Timeout => $timeout, +# Dump_Log => '/tmp/dumplog-tempfetch', +# Errmode => 'return', +# Prompt => '/es-3024|e(\-)?\d+\-\dsw>/i'); +# my $ret = $conn->open( Host => $ip); +# if (!$ret || $ret != 1) { +# return (0); +# } +# # XXX: Just send the password as text, I did not figure out how to +# # handle authentication with only password through $conn->login(). +# #$conn->login( Prompt => '/password[: ]*$/i', +# # Name => $password, +# # Password => $password); +# my @data = $conn->cmd($password); +# # Get rid of banner +# $conn->get; +# return $conn; +#} + diff --git a/clients/update-public-nms.sh b/clients/update-public-nms.sh new file mode 100755 index 0000000..ed1807c --- /dev/null +++ b/clients/update-public-nms.sh @@ -0,0 +1,12 @@ +#!/bin/sh +DIR=/srv/www/nms-public.tg13.gathering.org + +wget -qO$DIR/nettkart-trafikk.png.new http://nms.tg13.gathering.org/nettkart.pl +wget -qO$DIR/nettkart-dhcp.png.new http://nms.tg13.gathering.org/dhcpkart.pl +wget -qO$DIR/led.txt.new http://nms.tg13.gathering.org/led.pl +mv $DIR/nettkart-trafikk.png.new $DIR/nettkart-trafikk.png +mv $DIR/nettkart-dhcp.png.new $DIR/nettkart-dhcp.png +mv $DIR/led.txt.new $DIR/led.txt + +/usr/bin/perl -i -pe 'use POSIX qw(strftime); my $timestamp = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time())); s/Sist oppdatert:.*/Sist oppdatert: $timestamp/g;' /srv/www/nms-public.tg13.gathering.org/dhcp.html +/usr/bin/perl -i -pe 'use POSIX qw(strftime); my $timestamp = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time())); s/Sist oppdatert:.*/Sist oppdatert: $timestamp/g;' /srv/www/nms-public.tg13.gathering.org/trafikk.html diff --git a/clients/update-switch-placements.pl b/clients/update-switch-placements.pl new file mode 100644 index 0000000..d635f87 --- /dev/null +++ b/clients/update-switch-placements.pl @@ -0,0 +1,53 @@ +#! /usr/bin/perl + +print "begin;\n"; +print "delete from placements;\n"; + +open PATCHLIST, "../patchlist.txt" + or die "../patchlist.txt: $!"; + +my $RANGE = "87.76."; + +my $i = 1; +while (<PATCHLIST>) { + chomp; + my ($name, $distro, $port) = split / /; + + $name =~ /e(\d+)-(\d+)/; + my ($e, $s) = ($1, $2); + + my $x = int(168 + $e * 11); + my $y; + + $x += 1 if ($e >= 11); + $x += 2 if ($e >= 15); + $x += 2 if ($e >= 15 && $e < 45 && $s > 2); + $x += 2 if ($e >= 21); + $x += 2 if ($e >= 27 && $e < 45 && $s > 2); + $x += 9 if ($e >= 29); + $x += 1 if ($e >= 31); + $x += 2 if ($e >= 35); + $x += 15 if ($e >= 45); + $x += 2 if ($e >= 51); + $x += 11 if ($e >= 61); + $x += 1 if ($e >= 67); + $x += 1 if ($e >= 71); + $x += 1 if ($e >= 75); + $x += 1 if ($e >= 81); + + if ($s > 2) { + $y = 152 + 88 - 88 * ($s-3); + } else { + $y = 357 + 88 - 88 * ($s-1); + } + + my $xx = $x + 16; + my $yy = $y + 88; + + # Justeringer + + print "insert into placements (switch, placement) values ($i, box '(($x,$y),($xx,$yy))');\n"; + $i++; +} + +print "end;\n"; |