aboutsummaryrefslogtreecommitdiffstats
path: root/include/nms
diff options
context:
space:
mode:
Diffstat (limited to 'include/nms')
-rw-r--r--include/nms/snmp.pm91
-rw-r--r--include/nms/util.pm141
-rwxr-xr-xinclude/nms/web.pm112
3 files changed, 344 insertions, 0 deletions
diff --git a/include/nms/snmp.pm b/include/nms/snmp.pm
new file mode 100644
index 0000000..26ada44
--- /dev/null
+++ b/include/nms/snmp.pm
@@ -0,0 +1,91 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+use SNMP;
+use nms;
+package nms::snmp;
+
+use base 'Exporter';
+our @EXPORT = qw();
+
+BEGIN {
+ # $SNMP::debugging = 1;
+
+ # sudo mkdir /usr/share/mibs/site
+ # cd /usr/share/mibs/site
+ # wget -O- ftp://ftp.cisco.com/pub/mibs/v2/v2.tar.gz | sudo tar --strip-components=3 -zxvvf -
+ SNMP::initMib();
+ SNMP::addMibDirs("/srv/tgmanage/mibs/StandardMibs");
+ SNMP::addMibDirs("/srv/tgmanage/mibs/JuniperMibs");
+
+ SNMP::loadModules('SNMPv2-MIB');
+ SNMP::loadModules('ENTITY-MIB');
+ SNMP::loadModules('IF-MIB');
+ SNMP::loadModules('LLDP-MIB');
+ SNMP::loadModules('IP-MIB');
+ SNMP::loadModules('IP-FORWARD-MIB');
+}
+
+sub snmp_open_session {
+ my ($ip, $community, $async) = @_;
+
+ $async //= 0;
+
+ my %options = (UseEnums => 1);
+ if ($ip =~ /:/) {
+ $options{'DestHost'} = "udp6:$ip";
+ } else {
+ $options{'DestHost'} = "udp:$ip";
+ }
+
+ if ($community =~ /^snmpv3:(.*)$/) {
+ my ($username, $authprotocol, $authpassword, $privprotocol, $privpassword) = split /\//, $1;
+
+ $options{'SecName'} = $username;
+ $options{'SecLevel'} = 'authNoPriv';
+ $options{'AuthProto'} = $authprotocol;
+ $options{'AuthPass'} = $authpassword;
+
+ if (defined($privprotocol) && defined($privpassword)) {
+ $options{'SecLevel'} = 'authPriv';
+ $options{'PrivProto'} = $privprotocol;
+ $options{'PrivPass'} = $privpassword;
+ }
+
+ $options{'Version'} = 3;
+ } else {
+ $options{'Community'} = $community;
+ $options{'Version'} = 2;
+ }
+
+ my $session = SNMP::Session->new(%options);
+ if (defined($session) && ($async || defined($session->getnext('sysDescr')))) {
+ return $session;
+ } else {
+ die 'Could not open SNMP session to ' . $ip;
+ }
+}
+
+# Not currently in use; kept around for reference.
+sub fetch_multi_snmp {
+ my ($session, @oids) = @_;
+
+ my %results = ();
+
+ # Do bulk reads of 40 and 40; seems to be about the right size for 1500-byte packets.
+ for (my $i = 0; $i < scalar @oids; $i += 40) {
+ my $end = $i + 39;
+ $end = $#oids if ($end > $#oids);
+ my @oid_slice = @oids[$i..$end];
+
+ my $localresults = $session->get_request(-varbindlist => \@oid_slice);
+ return undef if (!defined($localresults));
+
+ while (my ($key, $value) = each %$localresults) {
+ $results{$key} = $value;
+ }
+ }
+
+ return \%results;
+}
+
diff --git a/include/nms/util.pm b/include/nms/util.pm
new file mode 100644
index 0000000..64637b8
--- /dev/null
+++ b/include/nms/util.pm
@@ -0,0 +1,141 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+package nms::util;
+use Data::Dumper;
+
+use base 'Exporter';
+our @EXPORT = qw(guess_placement parse_switches_txt parse_switches parse_switch);
+
+# Parse a single switches.txt-formatted switch
+sub parse_switch {
+ my ($switch, $subnet4, $subnet6, $mgtmt4, $mgtmt6, $lolid, $distro) = split(/ /);
+ my %foo = guess_placement($switch);
+ my %ret = (
+ 'sysname' => "$switch",
+ 'subnet4' => "$subnet4",
+ 'subnet6' => "$subnet6",
+ 'mgmt_v4_addr' => "$mgtmt4",
+ 'mgmt_v6_addr' => "$mgtmt6",
+ 'traffic_vlan' => "$lolid",
+ 'distro' => "$distro"
+ );
+ %{$ret{'placement'}} = guess_placement($switch);
+ return %ret;
+}
+
+# Parses a switches_txt given as a filehandle on $_[0]
+# (e.g.: parse_switches_txt(*STDIN) or parse_switches_txt(whatever).
+sub parse_switches_txt {
+ my $fh = $_[0];
+ my @switches;
+ while(<$fh>) {
+ chomp;
+ my %switch = parse_switch($_);
+ push @switches, {%switch};
+ }
+ return @switches;
+}
+
+# Parses switches in switches.txt format given as $_[0].
+# E.g: parse_switches("e1-3 88.92.0.0/26 2a06:5840:0a::/64 88.92.54.2/26 2a06:5840:54a::2/64 1013 distro0")
+sub parse_switches {
+ my @switches;
+ my $txt = $_[0];
+ foreach (split("\n",$txt)) {
+ chomp;
+ my %switch = parse_switch($_);
+ push @switches, {%switch};
+ }
+ return @switches;
+}
+
+# Guesses placement from name to get a starting point
+# Largely courtesy of Knuta
+sub guess_placement {
+ my ($x, $y, $xx, $yy);
+
+ my $name = $_[0];
+ my $src = "unknown";
+ if ($name =~ /^e\d+-\d+$/) {
+ $name =~ /e(\d+)-(\d+)/;
+ my ($e, $s) = ($1, $2);
+ $src = "main";
+
+ $x = int(292 + (($e-1)/2) * 31.1);
+ $y = undef;
+
+ $x += 14 if ($e >= 13);
+ $x += 14 if ($e >= 25);
+ $x += 14 if ($e >= 41);
+ $x += 14 if ($e >= 59);
+
+ if ($s > 2) {
+ $y = 405 - 120 * ($s-2);
+ } else {
+ $y = 689 - 120 * ($s);
+ }
+
+ $xx = $x + 16;
+ $yy = $y + 120;
+
+ # Justeringer
+ $y += 45 if $name eq "e1-4";
+ $y += 20 if $name eq "e3-4";
+ $y += 15 if $name eq "e5-4";
+ $yy -= 25 if $name eq "e7-1";
+ $y += 10 if $name eq "e5-2";
+ $yy -= 25 if $name eq "e5-2";
+ $y += 20 if ($e >= 81 and $s == 2);
+ $yy -= 20 if ($e >= 79 and $s == 1);
+ $yy -= 30 if ($e >= 81 and $s == 1);
+
+ } elsif ($name =~ /^creativia(\d+)$/) {
+ my ($s) = ($1);
+ $src = "creativia";
+ $x = 1535;
+ $y = int(160 + 32.2 * $s);
+ $yy = $y + 20;
+ if ($s == 1) {
+ $xx = $x + 70;
+ } elsif ($s == 2) {
+ $xx = $x + 90;
+ } elsif ($s == 3) {
+ $xx = $x + 102;
+ } else {
+ $xx = $x + 142;
+ }
+
+ } elsif ($name =~ /^crew(\d+)-(\d+)$/) {
+ my ($s, $n) = ($1, $2);
+ $src = "crew";
+ $x = 550 + 65 * $n;
+ $y = int(759 + 20.5 * $s);
+ $xx = $x + 65;
+ $yy = $y + 14;
+ } elsif ($name =~ /^distro(\d)/) {
+ my $d = ($1);
+ $src = "distro";
+ $x = 292 + $d * 165;
+ $y = 415;
+ $xx = $x + 130;
+ $yy = $y + 20;
+ } else {
+ # Fallback to have _some_ position
+ $src = "random";
+ $x = int(rand(500));
+ $y = int(rand(500));
+ $xx = $x + 20;
+ $yy = $y + 130;
+ };
+
+
+ my %box = (
+ 'src' => "$src",
+ 'x1' => $x,
+ 'y1' => $y,
+ 'xx' => $xx,
+ 'yy' => $yy
+ );
+ return %box;
+}
diff --git a/include/nms/web.pm b/include/nms/web.pm
new file mode 100755
index 0000000..7c9339e
--- /dev/null
+++ b/include/nms/web.pm
@@ -0,0 +1,112 @@
+#! /usr/bin/perl
+# vim:ts=8:sw=8
+use strict;
+use warnings;
+use utf8;
+use DBI;
+use Data::Dumper;
+use JSON;
+use nms;
+use Digest::SHA;
+use FreezeThaw;
+use URI::Escape;
+package nms::web;
+
+use base 'Exporter';
+our %get_params;
+our %json;
+our @EXPORT = qw(finalize_output now json $dbh db_safe_quote %get_params get_input %json);
+our $dbh;
+our $now;
+our $when;
+our %cc;
+
+sub get_input {
+ my $in = "";
+ while(<STDIN>) { $in .= $_; }
+ return $in;
+}
+# Print cache-control from %cc
+sub printcc {
+ my $line = "";
+ my $first = "";
+ foreach my $tmp (keys(%cc)) {
+ $line .= $first . $tmp . "=" . $cc{$tmp};
+ $first = ", ";
+ }
+ print 'Cache-Control: ' . $line . "\n";
+}
+
+sub db_safe_quote {
+ my $word = $_[0];
+ my $term = $get_params{$word};
+ if (!defined($term)) {
+ if(defined($_[1])) {
+ $term = $_[1];
+ } else {
+ die "Missing CGI param $word";
+ }
+ }
+ return $dbh->quote($term) || die;
+}
+
+# returns a valid $when statement
+# Also sets cache-control headers if time is overridden
+# This can be called explicitly to override the window of time we evaluate.
+# Normally up to 15 minutes old data will be returned, but for some API
+# endpoints it is better to return no data than old data (e.g.: ping).
+sub setwhen {
+ $now = "now()";
+ my $window = '8m';
+ my $offset = '0s';
+ if (@_ > 0) {
+ $window = $_[0];
+ }
+ if (@_ > 1) {
+ $offset = $_[1];
+ }
+ if (defined($get_params{'now'})) {
+ $now = db_safe_quote('now') . "::timestamp with time zone ";
+ $cc{'max-age'} = "3600";
+ }
+ $now = "(" . $now . " - '" . $offset . "'::interval)";
+ $when = " time > " . $now . " - '".$window."'::interval and time < " . $now . " ";
+}
+
+sub finalize_output {
+ my $query;
+ my $hash = Digest::SHA::sha512_base64(FreezeThaw::freeze(%json));
+ $dbh->commit;
+ $query = $dbh->prepare('select extract(epoch from date_trunc(\'seconds\', ' . $now . ')) as time;');
+ $query->execute();
+
+ $json{'time'} = int($query->fetchrow_hashref()->{'time'});
+ $json{'hash'} = $hash;
+
+ printcc;
+
+ print "Etag: $hash\n";
+ print "Access-Control-Allow-Origin: *\n";
+ print "Access-Control-Allow-Methods: HEAD, GET\n";
+ print "Content-Type: text/json; charset=utf-8\n\n";
+ print JSON::XS::encode_json(\%json);
+ print "\n";
+}
+
+sub populate_params {
+ my $querystring = $ENV{'QUERY_STRING'} || "";
+ foreach my $hdr (split("&",$querystring)) {
+ my ($key, $value) = split("=",$hdr,"2");
+ $get_params{$key} = URI::Escape::uri_unescape($value);
+ }
+}
+
+BEGIN {
+ $cc{'stale-while-revalidate'} = "3600";
+ $cc{'max-age'} = "20";
+
+ $dbh = nms::db_connect();
+ populate_params();
+ setwhen();
+}
+1;