#!/usr/bin/perl
#
# Generate list of nodes to load from /etc/munin/munin.conf to check
# all the machines reporting to sitesummary.

use strict;
use warnings;

use SiteSummary;
use Getopt::Std;
use Socket;
use File::Temp qw(tempfile);

sub usage {
    print <<EOF;
Usage: $0 [-hmnw]

 -h  Show usage information
 -m  Generate munin configuration for all munin clients
 -n  Generate nagios configuration for all nagios clients
 -w  List all client DNS/IP-addresses and MAC addresses
EOF
}

# Idea for way to provide overrides for the functions in this script
#eval 'require "/etc/sitesummary/sitesummary-nodes-override"';
#if ($@ && $@ !~ qr{^Can't locate /etc/sitesummary/sitesummary-nodes-override}) {
#    die $@;
#};

my %opts;
getopts("hmnw", \%opts) || (usage(), exit(1));

my %hostnames;
my $server_hostid = get_localhost_hostid() ||
    die "Unable to figure out hostid for the local host";

for_all_hosts(\&handle_host);

if ($opts{'h'}) {
    usage();
    exit 0;
} elsif ($opts{'m'}) {
    print_munin_list();
} elsif ($opts{'w'}) {
    print_ip_hw_list();
} elsif ($opts{'n'}) {
    generate_nagios_config();
} else {
    print_list();
}
exit 0;

sub handle_host {
    my $hostid = shift;
    my $address = get_dns_address($hostid);
    $hostnames{$address} = $hostid;
}

sub print_list {
    for my $hostname (sort keys %hostnames) {
        print "$hostname\n";
    }
}

sub get_localhost_hostid {
    my ($fh, $filename) = tempfile();
    `/sbin/ifconfig -a > $filename`;
    my $localhost_hostid = get_unique_ether_id($filename);
    unlink $filename;
    return $localhost_hostid;
}

# Get an IP address, try to resolve it in DNS , and return the IP
# address if no DNS reverse entry was found.
sub get_dnsnameorip {
    my $ipaddr = shift;
    my $resolved;
    $resolved = gethostbyaddr(inet_aton($ipaddr), AF_INET);
    return $resolved || $ipaddr;
}

sub is_munin_client {
    my $hostid = shift;
    return is_pkg_installed($hostid, "munin-node");
}

sub is_nagios_client {
    my $hostid = shift;
    return is_pkg_installed($hostid, "nagios-nrpe-server") ||
        is_pkg_installed($hostid, "nagios-text") ||
        is_pkg_installed($hostid, "nagios2") ||
        is_pkg_installed($hostid, "nagios3");
}

sub print_munin_list {
    for my $hostname (sort keys %hostnames) {
        next unless (is_munin_client($hostnames{$hostname}));

    # Using hostname as address, to avoid hardcoding IP addresses in
    # the file.  Might be an idea to fetch the IP address from
    # system/ifconfig-a
        print <<EOF;
[$hostname]
    address $hostname
    use_node_name yes

EOF
    }
}

sub print_ip_hw_list {
    for my $hostname (sort keys %hostnames) {
        my $macaddress = get_primary_macaddress($hostnames{$hostname});
        print "$hostname $macaddress\n";
    }
}

sub is_remote_nagios_client {
    my $hostid = shift;
    return is_pkg_installed($hostid, "nagios-nrpe-server") &&
        $server_hostid ne $hostid;
}

# Return information about the switches connected to a given host, as
# reported by cdpr (and perhaps lldp in the future?)
sub get_switch_info {
    my $hostid = shift;
    my %switch = ();
    for my $if (qw(eth0 eth1)) {
        my $path = get_filepath_current($hostid, "/system/cdpr.$if");
        my ($id, $addr);
        if (open(my $fh, $path)) {
            while (<$fh>) {
                chomp;
                if (m/^Device ID$/) {
                    $id = <$fh>;
                    chomp $id;
                    $id =~ s/^\s+value:\s+(\S)\s*/$1/;
                }
                if (m/^Addresses$/) {
                    $addr = <$fh>;
                    chomp $addr;
                    $addr =~ s/^\s+value:\s+(\S)\s*/$1/;
                    $switch{$addr} = $id;
                }
            }
            close($fh);
        }
    }
    return %switch ? %switch : ();
}


sub is_remote_nrpe_config_active {
    my $hostid = shift;

    my $path = get_filepath_current($hostid, "/nagios/sitesummary-nrpe.cfg");
    if (open(my $fh, $path)) {
        while (<$fh>) {
            if (m/^dont_blame_nrpe=1$/) {
                close($fh);
                return "args";
            }
        }
        close($fh);
        return "noargs";
    }
    return undef;
}


sub print_nagios_service_check {
    my ($remote, $hostname, $description, $check, $check_args) = @_;
    my $template = "server-service";
    my $cmd;
    if ($remote) {
        $cmd = "check_nrpe!$check";
        if (defined $check_args) {
            $check_args =~ s/!/ /g;
            $cmd .= " -a $check_args";
        }
    } else {
        $cmd = "$check";
        $cmd .= "!$check_args" if defined $check_args;
    }
    print <<EOF;
define service {
        use                 $template;
        host_name           $hostname
        service_description $description
        check_command       $cmd
}
EOF
}

sub nagios_hostgroup_namewash {
    my $name = shift;
    $name =~ s/[^0-9a-zA-Z_-]+/-/g; # Avoid illegal characteres
    return $name;
}

sub print_nagios_hostgroup {
    my ($name, $alias) = @_;

    print <<EOF;
define hostgroup {
        hostgroup_name      $name
EOF
    print "        alias           $alias\n" if $alias;
    print "}\n";
}

sub print_nagios_host_check {
    my ($template, $hostname, $address, $parent, @hostgroups) = @_;
    $template = $template || "server-host";
    print <<EOF;
##################### $hostname #######################
define host {
        use                 $template
        host_name           $hostname
        address             $address
EOF
    print "        parents             $parent\n" if ($parent);
    if (@hostgroups) {
        print "        hostgroups          " . join(",", @hostgroups), "\n";
    }
    print "}\n";
}

sub generate_nagios_config {
    my %hosts;
    my %hostgroup;
    for my $hostname (sort keys %hostnames) {
        my @groups = ();
        my $hostid = $hostnames{$hostname};

        my $address = get_dns_address($hostid);
        my $localhostname = get_localhostname($hostid);

        my $redirect = "";
        my $nagiosclient = is_nagios_client($hostid);
        my $remote = is_remote_nagios_client($hostid);
        my $nrpestatus = is_remote_nrpe_config_active($hostid);

        # Only check laptops that have the nagios tools installed
        if (is_laptop($hostid) && ! $remote && ! $nagiosclient) {
            print "# Skipping laptop $hostname\n";
            next;
        }

        my $site = get_site($hostid) || "none";
        my $sitegroup = get_sitegroup($hostid);
        my $debversion = get_debian_ver($hostid);
        my $eduprofile = get_debian_edu_profile($hostid);
        my $eduversion = get_debian_edu_ver($hostid);
        push(@groups, nagios_hostgroup_namewash("site-$site"));
        if ($sitegroup) {
            push(@groups, nagios_hostgroup_namewash("site-$site-$sitegroup"));
        }
        push(@groups, nagios_hostgroup_namewash("debian-version-$debversion"))
            if defined $debversion;
        if ($eduprofile) {
            $eduprofile =~ s/^"|"$//g; # Remove "" around the values
            for my $profile (split(/,\s*/, $eduprofile)) {
                push(@groups,
                     nagios_hostgroup_namewash("edu-profile-$profile"));
            }
        }
        if ($eduversion) {
            $eduversion =~ s/^"|"$//g; # Remove "" around the values
            push(@groups, nagios_hostgroup_namewash("edu-version-$eduversion"));
        }

        my $hostclass = get_hostclass($hostid) || "none";
        push(@groups, nagios_hostgroup_namewash("hostclass-$hostclass"));

        for my $group ( @groups ) {
            $hostgroup{$group} = 1;
        }
        my $defaultrouteip = get_default_route($hostid);
        my $defaultroute;
        $defaultroute = get_dnsnameorip($defaultrouteip)
            if defined $defaultroute;

        # Also check default route host
        if (defined $defaultroute && !exists $hosts{$defaultroute}) {
            print_nagios_host_check(undef, $defaultroute, $defaultroute,
                                    undef, "router");
            $hosts{$defaultroute} = $defaultroute;
            $hostgroup{"router"} = 1;
            print_nagios_service_check(0, $defaultroute, "ping",
                                       "check_ping", "100.0,20%!500.0,60%");
        }

        my %switch = get_switch_info($hostid);
        my @parents = ();
        for my $addr (keys %switch) {
            $hostgroup{"switch"} = 1;
            print_nagios_host_check("switch-host", $switch{$addr}, $addr,
                                    undef, "switch")
                unless (exists $hosts{$switch{$addr}});
            $hosts{$switch{$addr}} = $addr;
            push(@parents, $switch{$addr}) if $remote;
        }

        print_nagios_host_check(undef, $hostname, $address,
                                join(",", @parents), @groups)
            unless (exists $hosts{$hostname});
        $hosts{$hostname} = $address;

        # first, check ping to see if the other checks should be performed
        print_nagios_service_check(0, $hostname, "ping",
                                   "check_ping", "100.0,20%!500.0,60%");

        my %tcpservices =
        (
         139  => { name => 'samba',   package => 'samba' },
         389  => { name => 'ldap',    package => 'slapd' },
         4949 => { name => 'munin',   package => 'munin-node' },
         );

        for my $port (sort { $a <=> $b } keys %tcpservices) {
            next if (exists $tcpservices{$port}->{package} && !
                     is_pkg_installed($hostid,
                                      $tcpservices{$port}->{package}));
            my $servicename = $tcpservices{$port}->{name};
            print_nagios_service_check(0, $hostname, $servicename,
                                       "check_tcp", $port);
        }

        # Check SSH server
        print_nagios_service_check(0, $hostname, "ssh",
                                   "check_ssh")
            if is_pkg_installed($hostid, "openssh-server");

        print_nagios_service_check(0, $hostname, "http",
                                   "check_http")
            if (is_pkg_installed($hostid, "apache") ||
                is_pkg_installed($hostid, "apache2"));

        # Check XFS port only if we can see that it is listening on TCP
        if (is_pkg_installed($hostid, "xfs")) {
            my $path = get_filepath_current($hostid, "/system/x11-fs-config");
            if ( -e $path ) {
                my $tcp = 1;
                open (my $fh, "<", $path) || die "unable to read from $path";
                while (<$fh>) {
                    chomp;
                    s/\#.+$//;
                    $tcp = 0 if m/^no-listen\s*=\s*tcp\s*$/;
                }
                close($fh);
                print_nagios_service_check(0, $hostname, "xfs",
                                           "check_tcp", 7100)
                    if ($tcp);
            }
        }

        print_nagios_service_check(0, $hostname, "cups queue",
                                   "check_cups_queue")
            if (is_pkg_installed($hostid, "cups") &&
                is_pkg_installed($hostid, "cups-client"));

        # The rest of the checks only work if NRPE is installed and configured
        next unless ((!$remote && $nagiosclient)
                     || ($remote && defined $nrpestatus));

        # These work without any argument passing.

        # Check for APT upgrades
        print_nagios_service_check($remote, $hostname, "apt-updates",
                                   "check_apt");

        # Check if a kernel reboot is needed
        print_nagios_service_check($remote, $hostname, "kernel status",
                                   "check_kernel_status");

        # Detect bad DNS servers
        print_nagios_service_check($remote, $hostname, "/etc/resolv.conf",
                                   "check_etc_resolv");

        # Detect hosts entries not matching DNS entries
        print_nagios_service_check($remote, $hostname, "/etc/hosts",
                                   "check_etc_hosts");

        # Detect a shutdown in progress
        print_nagios_service_check($remote, $hostname, "shutdown status",
                                   "check_shutdown");


#        print_nagios_service_check($remote, $hostname, "dhcp",
#                                   "check_dhcp")
#            if is_pkg_installed($hostid, "dhcp3-server");

        # Check DNS server
        print_nagios_service_check($remote, $hostname, "dns",
                                   "check_dns", $localhostname)
            if (is_pkg_installed($hostid, "pdns-server") ||
                is_pkg_installed($hostid, "bind9"));

        # Check FTP server
        print_nagios_service_check($remote, $hostname, "ftp",
                                   "check_ftp")
            if is_pkg_installed($hostid, "proftpd");

        # Check IMAPS server
        print_nagios_service_check($remote, $hostname, "imaps",
                                   "check_imaps")
            if is_pkg_installed($hostid, "courier-imap-ssl");

        # check software raid status if any is active
        if ( -e get_filepath_current($hostid, "/system/mdstat")) {
            my $fh;
            if (open($fh, get_filepath_current($hostid, "/system/mdstat")) &&
                grep(/^md\d+ :/, <$fh>)) {
                print_nagios_service_check($remote, $hostname, "sw-raid",
                                           "check_linux_raid");
            }
            close($fh);
        }

        # Check NFS server
        print_nagios_service_check($remote, $hostname, "nfs",
                                   "check_nfs")
            if is_pkg_installed($hostid, "nfs-kernel-server");

        print_nagios_service_check($remote, $hostname, "smtp",
                                   "check_smtp")
            if (is_pkg_installed($hostid, "exim4-daemon-heavy") ||
                is_pkg_installed($hostid, "exim4-daemon-light"));

        # These need argument passing (as in dont_blame_nrpe=1)
        next unless ((!$remote && $nagiosclient)
                     || ($remote && "args" eq $nrpestatus));

        print_nagios_service_check($remote, $hostname, "swap",
                                   "check_swap", "10%!5%");
        print_nagios_service_check($remote, $hostname, "current users",
                                   "check_users", "20!50");
        print_nagios_service_check($remote, $hostname, "processes total",
                                   "check_procs", "500!1000");
        print_nagios_service_check($remote, $hostname, "processes zombie",
                                   "check_procs_zombie", "20!100");
        # Check unix load
        print_nagios_service_check($remote, $hostname, "load as in top",
                                   "check_load", "75,75,75!90,90,90");

        # check disk free space
        my $path = get_filepath_current($hostid, "/system/procmounts");
        if ( -e $path ) {
            open (F, "<", $path) || die "unable to read from $path";
            my %checked;
            while (<F>) {
                chomp;
                my ($device, $partition, $fs, $opts) = split;
                next if (exists $checked{$device});
                # Avoid system file systems and non-local file systems.
                next if ($fs eq "devpts" ||
                         $fs eq "anon_inodefs" ||
                         $fs eq "autofs" ||
                         $fs eq "bdev" ||
                         $fs eq "binfmt_misc" ||
                         $fs eq "cgroup" ||
                         $fs eq "cifs" ||
                         $fs eq "cpuset" ||
                         $fs eq "debugfs" ||
                         $fs eq "fuse.ltspfs" ||
                         $fs eq "fusectl" ||
                         $fs eq "hugetlbfs" ||
                         $fs eq "inotifyfs" ||
                         $fs eq "iso9660" ||
                         $fs eq "mqueue" ||
                         $fs eq "nfs" ||
                         $fs eq "nfs4" ||
                         $fs eq "nfsd" ||
                         $fs eq "oprofilefs" ||
                         $fs eq "pipefs" ||
                         $fs eq "proc" ||
                         $fs eq "ramfs" ||
                         $fs eq "rootfs" ||
                         $fs eq "rpc_pipefs" ||
                         $fs eq "securityfs" ||
                         $fs eq "smb" ||
                         $fs eq "sockfs" ||
                         $fs eq "sysfs" ||
                         $fs eq "tmpfs" ||
                         $fs eq "devtmpfs" ||
                         $fs eq "usbfs");

                $checked{$device} = 1;
                my $warn = 10;
                my $crit = 5;
                print_nagios_service_check($remote, $hostname,
                                           "disk $partition",
                                           "check_disk",
                                           "$warn%!$crit%!$partition");
            }
        }

        # check munin if munin-node is installed
        # check hw raid status
        # check hardware status

        # check LDAP and LDAPS using the protocol, module present in
        # nagios-plugins-basic
        print_nagios_service_check(0, $hostname, "ldap root DSE",
                                   "check_ldap_root")
            if is_pkg_installed($hostid, "slapd");

        # Check Squid web proxy
        print_nagios_service_check($remote, $hostname, "squid",
                                   "check_squid", "3128!http://www")
            if is_pkg_installed($hostid, "squid");

        print_nagios_service_check($remote, $hostname, "ntp time server",
                                   "check_ntp", "-H!localhost")
            if (is_pkg_installed($hostid, "ntp") ||
                is_pkg_installed($hostid, "ntp-server"));

        # Detect if cron no longer is running
        print_nagios_service_check($remote, $hostname, "process - cron",
                                   "check_procs_cron", "1:15!1:25")
            if (is_pkg_installed($hostid, "cron"));
    }

    if (%hostgroup) {
        print <<EOF;
##################### host groups #######################
EOF
        for my $name (sort keys %hostgroup) {
            print_nagios_hostgroup($name);
        }
    }
}
