aboutsummaryrefslogtreecommitdiffstats
path: root/clients
diff options
context:
space:
mode:
Diffstat (limited to 'clients')
-rwxr-xr-xclients/accesspoints.pl76
-rwxr-xr-xclients/admintool.sh19
-rwxr-xr-xclients/dhcptail.pl74
-rwxr-xr-xclients/ipv6-dns.pl133
-rwxr-xr-xclients/ipv6-stats.pl89
-rwxr-xr-xclients/portnames.pl18
-rwxr-xr-xclients/smanagrun.pl167
-rw-r--r--clients/snmp.sql24
-rwxr-xr-xclients/snmpfetch.pl281
-rwxr-xr-xclients/update-public-nms.sh12
-rw-r--r--clients/update-switch-placements.pl53
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";