#!/usr/bin/perl use strict; use warnings; # process a log with plugins and place the results in a file # Copyright (C) 2011 Glen Pitt-Pladdy # # 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; either version 2 # of the License, or (at your option) any later version. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # # See: http://www.pitt-pladdy.com/blog/_20111116-201219%2B0000%20OpenVz%20User%20Beancounters%20%28UBC%29%20on%20Cacti%20via%20SNMP/ # my $VERSION = 20111113; my $CTPATH = "/var/lib/vz/private"; # usage: vzbeancheck my ( $statsfile ) = @ARGV; # santiy check if ( ! $statsfile ) { die "FATAL - stats file not specified\n"; } # read in the current stats if it exists my %stats; if ( -f $statsfile ) { open ST, $statsfile or die "FATAL - can't read previous statistics: $!\n"; while ( defined ( my $line = ) ) { chomp $line; if ( $line !~ /^([\w\-_:]+)=([\w\.\-]+)$/ ) { die "FATAL - don't understand line \"$line\" in stats\n"; } elsif ( $line !~ /^([\w\-_:]+failcnt)=(\d+)$/ ) { next; # we are only interested in in failcnt } $stats{$1} = $2; } close ST; } # initialise basics $stats{lastrun} = $^T; # read and check the beancounters against last my $header; my $id; my %messages; open BC, '/proc/user_beancounters' or die "FATAL - can't read \"/proc/user_beancounters\": $!\n"; while ( defined ( my $line = ) ) { chomp $line; if ( $line =~ /^Version/ ) { next; } if ( $line =~ /^\s+uid\s+resource\s+held\s+maxheld\s+barrier\s+limit\s+failcnt$/ ) { $header = $line; next; } if ( $line =~ s/^\s+(\d+):\s+/ / ) { $id = $1; } if ( $line !~ /^\s+(\w+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/ ) { warn "WARNING - don't understand line:\n$line\n"; next; } my ( $name, $held, $maxheld, $barrier, $limit, $failcnt ) = ( $1, $2, $3, $4, $5, $6 ); # process the lines if ( $id > 0 and defined ( $stats{"$id:$name:failcnt"} ) and $failcnt > $stats{"$id:$name:failcnt"} ) { $messages{$id} .= "New failure on $id (failcnt ".$stats{"$id:$name:failcnt"}." => $failcnt):\n$header\n$line\n"; } $stats{"$id:$name:held"} = $held; $stats{"$id:$name:maxheld"} = $maxheld; $stats{"$id:$name:barrier"} = $barrier; $stats{"$id:$name:limit"} = $limit; $stats{"$id:$name:failcnt"} = $failcnt; } close BC; # get machine hostnames from /etc/hostname and /etc/hosts for my $dir ( glob ( "$CTPATH/*" ) ) { if ( ! -f "$dir/etc/hostname" ) { next; } if ( $dir !~ /\/(\d+)$/ ) { next; } my $ct = $1; open HN, "$dir/etc/hostname" or die "FATAL - can't read \"$dir/etc/hostname\": $!\n"; $stats{"$ct:hostname"} = ; chomp $stats{"$ct:hostname"}; close HN; if ( ! -f "$dir/etc/hosts" ) { next; } open HST, "$dir/etc/hosts" or die "FATAL - can't read \"$dir/etc/hosts\": $!\n"; my $fullname = $stats{"$ct:hostname"}; while ( defined ( my $line = ) ) { chomp $line; my @names = split /\s+/, $line; if ( grep { $_ eq $stats{"$ct:hostname"} } @names ) { foreach my $name (@names) { if ( (split /\./, $name)[0] eq $stats{"$ct:hostname"} and length ( $name ) > length ( $fullname ) ) { $fullname = $name; } } } } close HST; $stats{"$ct:hostname"} = $fullname; } $stats{"0:hostname"} = 'host'; # do any complaining needed foreach my $message (sort keys %messages) { warn $messages{$message}; } # update for our run $stats{lastrun} = $^T; # write stats to temporary file open ST, ">$statsfile.TMP.$$" or die "FATAL - can't write \"$statsfile.TMP.$$\": $!\n"; print ST "lastrun=$stats{lastrun}\n"; delete $stats{lastrun}; # dump rest of stats foreach my $stat (sort keys %stats) { print ST "$stat=$stats{$stat}\n"; } close ST; rename "$statsfile.TMP.$$", $statsfile or die "FATAL - can't move \"$statsfile.TMP.$$\" to \"$statsfile\": $!\n";