#!/usr/bin/perl # # Plugin to monitor Forefront Client Security status in MOM database # # Copyright (c) 2008 Rune Nordbøe Skillingstad - # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. # # Parameters: # # config # autoconf # suggest # # Config variables # # dsn - If DSN name differs from hostname # dbuser - Valid MS SQL user (Windows authentication is possible using "DOMAIN\user") # dbpass - Password # # Install guide: # This plugin relies on correct configured ODBC for the MOM database # Prerequisits: # * Install and configure FreeTDS and DBD::Sybase (packages tdsodbc and libdbd-sybase-perl on Ubuntu) # - DBD::Sybase is preferred over ODBC because of strange TEXT field handling in DBD::ODBC # # Example # /etc/freetds/freetds.conf: # [MyHost] # host = MyHost.domain.tld # port = 1433 # tds version = 7.0 # # Copy this script to /usr/share/munin/plugins and run "munin-node-configure --shell" # If freetds.conf has one or more lines containing "host = ", the output will be something like this: # ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.Domain.tld_computers # ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.domain.tld_deployments # ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.domain.tld_status # # To manually add, symlink forefront_ to forefront_MyHost.domain.tld_computers, # forefront_MyHost.domain.tld_deployments and forefront_MyHost.domain.tld_status # # Add your DSN and user/password to /etc/munin/plugin-conf.d/munin-node: # [forefront_MyHost.domain.tld_*] # env.dsn MyHost # env.dbuser # env.dbpass # # On your munin server, add this to /etc/munin/munin.conf # # [MyHost.domain.tld] # address # use_node_name no # # Magic markers (optional - used by munin-config and some installation # scripts): # #%# family=auto #%# capabilities=autoconf suggest use strict; my $host = undef; my $stat = undef; my @stats = qw(computers status deployments); if($0 =~ /^(?:|.*\/)forefront_(.+?)_([^_]+)$/) { $host = $1; $stat = $2; $host =~ s/_/-/g; } my $dsn = $ENV{dsn} || $host; my $dbuser = $ENV{dbuser} || undef; my $dbpass = $ENV{dbpass} || undef; my $ret = undef; if(!eval "require DBI;") { $ret = "DBI not found "; } if(!eval "require DBD::Sybase;") { $ret .= "DBD::Sybase not found "; } if(!eval "require MIME::Base64;") { $ret .= "MIME::Base64 not found "; } else { use MIME::Base64; } if($ARGV[0] and $ARGV[0] eq "autoconf") { if($ret) { print "no ($ret)\n"; } else { print "yes\n"; } exit 0; } if($ARGV[0] and $ARGV[0] eq "suggest") { if("/etc/odbc.ini") { my $dsn = undef; my $host = undef; open(IN, ") { next if(/^[;#]/); if(/host\s*=\s*([\w.-]+)/) { my $host = $1; $host =~ s/-/_/g; print $host."_".join("\n".$host."_",@stats)."\n"; } } close(IN); exit 0; } exit 1; } if(!grep(/$stat/, @stats)) { print STDERR "\"$stat\" is not a valid script ending\n"; exit 1; } if($ARGV[0] and $ARGV[0] eq "config") { eval "&".$stat."_config()"; exit 0; } eval "&".$stat."_fetch()"; sub computers_config { print <connect("DBI:Sybase:$dsn", $dbuser, $dbpass, { PrintError => 1, AutoCommit => 1 }); my $sth = $dbh->prepare("SELECT Level, Name FROM AlertLevel ORDER BY Level", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { $alerts{$row[0]} = $row[1]; } print <connect("DBI:Sybase:$dsn", $dbuser, $dbpass, { PrintError => 1, AutoCommit => 1 }); my $sth = $dbh->prepare("SELECT COUNT(*) FROM Computer WHERE PendingAction = 0 AND LastHeartbeat < '$twentyeightdays'", {odbc_exec_direct => 1}); $sth->execute(); my($count) = $sth->fetchrow_array; print "missing672.value $count\n"; my $sth = $dbh->prepare("SELECT COUNT(*) FROM Computer WHERE PendingAction = 0 AND LastHeartbeat < '$sevendays' AND LastHeartbeat >= '$twentyeightdays'", {odbc_exec_direct => 1}); $sth->execute(); my($count) = $sth->fetchrow_array; print "missing168.value $count\n"; my $sth = $dbh->prepare("SELECT COUNT(*) FROM Computer WHERE PendingAction = 0 AND LastHeartbeat < '$oneday' AND LastHeartbeat >= '$sevendays'", {odbc_exec_direct => 1}); $sth->execute(); my($count) = $sth->fetchrow_array; print "missing.value $count\n"; $sth = $dbh->prepare("SELECT COUNT(*) FROM Computer WHERE PendingAction = 0 AND LastHeartbeat >= '$oneday'", {odbc_exec_direct => 1}); $sth->execute(); ($count) = $sth->fetchrow_array; print "computers.value $count\n"; $sth = $dbh->prepare("SELECT COUNT(*) FROM Computer WHERE PendingAction <> 0", {odbc_exec_direct => 1}); $sth->execute(); ($count) = $sth->fetchrow_array; print "pending.value $count\n"; } sub status_fetch { my %alerts; my $dbh = DBI->connect("DBI:Sybase:$dsn", $dbuser, $dbpass, { PrintError => 1, AutoCommit => 1 }); my $sth = $dbh->prepare("SELECT Level FROM AlertLevel ORDER BY Level", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { $alerts{$row[0]} = 0; } $sth = $dbh->prepare("SELECT al.Level, COUNT(a.AlertLevel) FROM Alert a, AlertLevel al WHERE a.AlertLevel = al.Level AND a.ResolutionState <> 255 GROUP BY al.Level", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { $alerts{$row[0]} = $row[1]; } foreach my $level (sort(keys(%alerts))) { printf "alert%d.value %d\n", $level, $alerts{$level}; } } sub deployments_fetch { my %profiles = &deployments_general(); foreach my $policy (sort(keys(%profiles))) { my $field = encode_base64($policy); chomp($field); print $field . ".value " . $profiles{$policy}{'count'} . "\n"; if($profiles{$policy}{'old'}) { print $field . "old.value " . $profiles{$policy}{'old'} . "\n"; } } } sub deployments_general { my %profiles; $profiles{'00000000-0000-0000-0000-000000000000'}{'name'} = "Unknown Policy"; $profiles{'00000000-0000-0000-0000-000000000000'}{'instance'} = ""; $profiles{'00000000-0000-0000-0000-000000000000'}{'count'} = 0; $profiles{'d3b75be9-7125-4db1-8b24-93004bd9d88e'}{'name'} = "No Policy"; $profiles{'d3b75be9-7125-4db1-8b24-93004bd9d88e'}{'instance'} = ""; $profiles{'d3b75be9-7125-4db1-8b24-93004bd9d88e'}{'count'} = 0; my $dbh = DBI->connect("DBI:Sybase:$dsn", $dbuser, $dbpass, { PrintError => 1, AutoCommit => 1}); my $sth = $dbh->prepare("SELECT Id, Name, LatestInstanceID From fcs_Profiles", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { $profiles{lc($row[0])}{'name'} = $row[1]; $profiles{lc($row[0])}{'instance'} = lc($row[2]); $profiles{lc($row[0])}{'count'} = 0; $profiles{lc($row[0])}{'old'} = 0; } $sth = $dbh->prepare("SELECT COUNT(*), a1.Value AS Policy, a2.Value AS Instance ". "FROM Attribute a1, Attribute a2, ClassDefinition cd, ClassAttribute ca1, ClassAttribute ca2 ". "WHERE ca2.ClassID = cd.ClassID ". "AND cd.Name = 'Microsoft Forefront Client Security Agent' ". "AND ca1.ClassAttributeName = 'Profile ID' " . "AND ca2.ClassAttributeName = 'Profile Instance ID' ". "AND a1.ClassAttributeID = ca1.ClassAttributeID ". "AND a2.ClassAttributeID = ca2.ClassAttributeID ". "AND a1.InstanceID = a2.InstanceID " . "GROUP BY a1.Value, a2.Value ". "ORDER BY Policy", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { if(!$row[1]) { $profiles{'00000000-0000-0000-0000-000000000000'}{'count'} += $row[0]; } elsif(lc($row[1]) eq 'd3b75be9-7125-4db1-8b24-93004bd9d88e') { $profiles{'d3b75be9-7125-4db1-8b24-93004bd9d88e'}{'count'} += $row[0]; } else { if(lc($row[2]) ne $profiles{lc($row[1])}{'instance'}) { $profiles{lc($row[1])}{'old'} += $row[0]; } else { $profiles{lc($row[1])}{'count'} += $row[0]; } } } return %profiles; } exit 0;