#!/usr/bin/perl

use strict;
use warnings;
use lib 'scripts';
use IO::File;
use NetAddr::IP;
use Cisco::Reconfig;
use MyConfigCDBI;
$|++;

sub version_file { my $f = shift; $f =~ s/\.config$/\.version/; $f }

sub get_version
{
    my $fh = new IO::File $_[0];
    my $version;		# The output of show version
    my $class;

    unless ($fh)
    {
	warn "Failed to open version file $_[0]: $!\n";
	return (map { 'Could not read version file' } 1 .. 3);
    }

    do { local $/ = undef; $version = <$fh>; };
    close $fh;

    my $sw = ($version =~ m/^(.+Software .+ Version .+)$/m)[0] 
	|| 'Cannot find software version';
    my $hw = ($version =~ 
	      m/^(cisco .+ (processor .+|bytes of memory\.?))$/mi)[0] 
	      || 'Cannot find hardware version';
    my $sn = ($version =~ m/processor board ID (\w+( \([^\)]+\))?)/mi)[0]
	|| 'Cannot find serial number';

    if ($version =~ m/cisco\s(Cat6k-|WS-C(29|3[57]|40))/)
    {	$class = 'switch' }
    elsif ($version =~ /cisco\s(2[56]\d\d|18\d\d
|WS-C65\d\d|17\d\d|CISCO76\d\d)\s/ix)
    {	$class = 'router' }
    elsif ($version =~ /cisco\sAIR-AP/)
    {	$class = 'access-point' }

    return ($sw, $hw, $sn, $class);
}

sub get_base_info
{
    my %k = map { $_ => $_[0]->get($_) } 
    ('hostname', 'snmp-server contact', 'snmp-server location');

    return map { $k{$_} =~ s/^$_\s+//; chomp($k{$_}); $k{$_} } sort keys %k;
}

sub get_interfaces
{
    my $config = shift;
    my @ifs = ();
    for my $if ($config->get('interface')->all())
    {
	my @container = (($if->text =~ m/interface (\S+)/)[0]);
	next unless $container[0];
	my $desc = ($if->get('description')->text =~ m/description (.+)$/)[0];
	push @container, $desc;
	for my $addr ($if->get('ip address'))
	{
	    next if $addr->text =~ m/no ip address/;
	    next unless $addr->text;
	    my ($ip, $mask, $class) = 
		$addr->text =~ m/ip address (\S+) (\S+)\s*(secondary)?/;
	    $class ||= 'primary';
	    my $i = NetAddr::IP->new($ip, $mask);
	    next unless $i;
	    push @container, [ $i, $class ];
	}
	push @ifs, \@container;
    }
    return @ifs;
}

MyConfig::CDBI->connection('dbi:SQLite:dbname=config.db');

for my $c (@ARGV)
{
    my $config = readconfig($c);
    my ($dev, $loc, $ctc) = get_base_info($config);

    if (not defined $dev)
    {
	warn "$c does not define a device host name\n";
	next;
    }

    my $vc = version_file($c);
    my ($sw, $hw, $sn, $cl) = get_version($vc);

    # Populate our devices table

    my $device = MyConfig::CDBI::Device->find_or_create(
	device => $dev,
	location => $loc,
	contact => $ctc,
	serial => $sn,
	hardware => $hw,
	software => $sw,
	class => $cl
    );

    # Extract the interfaces from the configuration

    for my $if (get_interfaces($config))
    {
	my $ifname = lc shift @$if;
	my $desc = shift @$if;
	print "$c $device $ifname\n";
	my $interface = MyConfig::CDBI::Interface->find_or_create(
	    interface => $ifname,
	    device => $dev,
	    description => $desc
	);
	for my $ip (@$if)
	{
	    my $subnet = MyConfig::CDBI::Subnet->find_or_create(
		cidr => $ip->[0]->network->cidr,
		first => scalar $ip->[0]->network->numeric,
		last => scalar $ip->[0]->broadcast->numeric,
	    );
	    my $address = MyConfig::CDBI::Address->find_or_create(
		ip => scalar $ip->[0]->numeric,
		interface => lc $interface->interface,
		device => $device->device,
		cidr => $subnet->cidr,
		type => $ip->[1],
	    );
	}
    }
}
