#!/usr/bin/perl

use strict;
use warnings;

use LWP::UserAgent;
use Storable qw(lock_store);
use JSON::XS;
use Socket;

use Data::Dumper;

#
# This takes all network interfaces from LibreNMS, and creates a Perl Storable
# file that contains the interfaces sorted by device_id => ifindex with the 
# LibreNMS data stored after each interface. This can be used as a local cache
# to determine interface types/information without having to rely on hitting 
# the LibreNMS API. This also stores a pretag.map file for pmacct with the 
# associated interface types. See: 
#
# https://github.com/linsomniac/pmacct/blob/master/examples/pretag.map.example
#
# Example of the stored hash:
#
# device => {
#	3 => {
#		info => {
#			<LibreNMS Info>
#		},
#		interfaces => {
#			101 => 
#					<LibreNMS Info>
#				},
#		},
#	},
#}
#
# LibreNMS API doc:
# http://docs.librenms.org/API/API-Docs/
#

#
# TODO:
# Should we store a hostname => device_id set?
# Should we also store an IP => device_id set?
#

#
# Static iftype mappings:
# transit input interface type is 2
# public peering input interface type is 3
# private peering input interface type is 4
# core interface input type is 5
# any other input interface type is not set
#
# transit input interface type is 102
# public peering input interface type is 103
# private peering input interface type is 104
# core interface input type is 105
# any other input interface type is not set
#

#
my $apikey = '5eb64076b63c3363dca689cc20134278';
my $apiurl = 'http://192.168.20.137/api/v0';
#
my $pretagfile = "/opt/pmacct/etc/pretag.map";
my $storable = "/opt/flows/cache/librenms.stor";
#
my @pretag;
my %storeme;

my $ua = LWP::UserAgent->new(
	agent => "Flows ifIndex Cache",
	accept => 'application/json',
);
my $devreq = HTTP::Request->new(GET => $apiurl . '/devices');
$devreq->header('X-Auth-Token' => $apikey);
my $devresponse = $ua->request($devreq);
die "Request Failed: " . $devresponse->status_line . "\n" unless $devresponse->is_success;
my $devices = decode_json($devresponse->decoded_content);

foreach my $device (@{$devices->{devices}}) {
	my $device_id = $device->{device_id};
	$storeme{devices}{$device_id}{info} = $device;
	my $portreq = HTTP::Request->new(GET=> $apiurl . "/devices/$device_id/ports\?columns=\*");
	$portreq->header('X-Auth-Token' => $apikey);
	my $portresponse = $ua->request($portreq);
	next print "Request Failed: " . $portresponse->status_line . "\n" unless $portresponse->is_success;
	my $ports = decode_json($portresponse->decoded_content);
	foreach my $port (@{$ports->{ports}}) {
		my $ifIndex = $port->{ifIndex};
		$storeme{devices}{$device_id}{ports}{$ifIndex} = $port;		
	}

}

# Write the pre-tag mapping file for pmacct
foreach my $dev (keys %{$storeme{devices}}) {
	next unless $storeme{devices}{$dev}{info}{type} =~ /network/i;
	foreach my $ifindex (keys %{$storeme{devices}{$dev}{ports}}) {
		my $port_type = $storeme{devices}{$dev}{ports}{$ifindex}{port_descr_type};
		my ($tag2_in, $tag2_out);
		next unless $port_type;
		if ($port_type =~ /transit/i) {
			$tag2_in = 2;
			$tag2_out = 102;
		} elsif ($port_type =~ /publicpeering/i) {
			$tag2_in = 3;
			$tag2_out = 103;
		} elsif ($port_type =~ /peering/i) {
			$tag2_in = 4;
			$tag2_out = 104;
		} elsif ($port_type =~ /core/i) {
			$tag2_in = 5;
			$tag2_out = 105;
		} else {
			next;
		}
		my $ip = inet_ntoa(inet_aton($storeme{devices}{$dev}{info}{hostname})) or next;
		push(@pretag, qq{! $storeme{devices}{$dev}{ports}{$ifindex}{ifDescr} @ $storeme{devices}{$dev}{info}{hostname}\n});
		push(@pretag, qq{id2=$tag2_in ip=$ip in=$ifindex jeq=$dev\n});
		push(@pretag, qq{id2=$tag2_out ip=$ip out=$ifindex jeq=$dev\n});
	}
}
push(@pretag, "\n\n! Routers\n");
foreach my $dev (keys %{$storeme{devices}}) {
	next unless $storeme{devices}{$dev}{info}{type} =~ /network/i;
	my $ip = inet_ntoa(inet_aton($storeme{devices}{$dev}{info}{hostname})) or next;
	push (@pretag, qq{! $dev - > $storeme{devices}{$dev}{info}{hostname}\n});
	push (@pretag, qq{id=$dev ip=$ip\n});
}

my $fh;
open($fh, '>', $pretagfile) || die("Could not open $pretagfile : $!");
print $fh @pretag;
close($fh);

lock_store(\%storeme, $storable) || die("Could not store $storable : $!");