diff options
Diffstat (limited to 'include/nms')
| -rw-r--r-- | include/nms/snmp.pm | 91 | ||||
| -rw-r--r-- | include/nms/util.pm | 141 | ||||
| -rwxr-xr-x | include/nms/web.pm | 112 | 
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;  | 
