/usr/share/perl5/Munin/Node/SpoolWriter.pm is in munin-node 2.0.25-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | package Munin::Node::SpoolWriter;
# $Id$
use strict;
use warnings;
use Carp;
use IO::File;
use Fcntl qw(:DEFAULT :flock);
use Munin::Common::Defaults;
use Munin::Common::SyncDictFile;
use Munin::Node::Logger;
use constant DEFAULT_TIME => 86_400; # put 1 day of results into a spool file
use constant MAXIMUM_AGE => 7; # remove spool files more than a week old
sub _snap_to_epoch_boundary { my $self = shift; return $_[0] - ($_[0] % $self->{interval_size}) }
sub new
{
my ($class, %args) = @_;
$args{spooldir} or croak "no spooldir provided";
opendir $args{spooldirhandle}, $args{spooldir}
or croak "Could not open spooldir '$args{spooldir}': $!";
$args{metadata} = _init_metadata($args{spooldir});
if(!$args{interval_size} && (my $interval_size = get_metadata(\%args, "interval_size"))) {
$args{interval_size} = $interval_size;
}
if(!$args{interval_keep} && (my $interval_keep = get_metadata(\%args, "interval_keep"))) {
$args{interval_keep} = $interval_keep;
}
if(!$args{hostname} && (my $hostname = get_metadata(\%args, "hostname"))) {
$args{hostname} = $hostname;
}
$args{interval_size} = DEFAULT_TIME unless ($args{interval_size});
$args{interval_keep} = MAXIMUM_AGE unless ($args{interval_keep});
$args{hostname} = "unknown" unless ($args{hostname});
set_metadata(\%args, "interval_size", $args{interval_size}) if $args{interval_size} != get_metadata(\%args, "interval_size");
set_metadata(\%args, "interval_keep", $args{interval_keep}) if $args{interval_keep} != get_metadata(\%args, "interval_keep");
set_metadata(\%args, "hostname", $args{hostname}) if $args{hostname} ne get_metadata(\%args, "hostname");
# TODO: paranoia check? except dir doesn't (currently) have to be
# root-owned.
# TODO: set umask
return bless \%args, $class;
}
#prepare tied hash for metadata persisted to $spooldir/SPOOL-META
#should we pull these methods into a base class or create a spool manager class?
sub _init_metadata
{
my $spooldir = shift;
my %metadata;
tie %metadata, 'Munin::Common::SyncDictFile', $spooldir . "/SPOOL-META";
return \%metadata;
}
#retrieve metadata value for key
sub get_metadata
{
my ($self, $key) = @_;
return ${ $self->{metadata} }{$key};
}
#set metadata key:value and persist
sub set_metadata
{
my ($self, $key, $value) = @_;
${ $self->{metadata} }{$key} = $value;
}
# writes the results of a plugin run to the appropriate spool-file
# need to remove any lines containing only a '.'
sub write
{
my ($self, $timestamp, $service, $data) = @_;
my $fmtTimestamp = $self->_snap_to_epoch_boundary($timestamp);
open my $fh , '>>', "$self->{spooldir}/munin-daemon.$service.$fmtTimestamp." . $self->{interval_size}
or die "Unable to open spool file: $!";
flock($fh, LOCK_EX);
print {$fh} "timestamp $timestamp\n";
# squash the $service name with the same rules as the munin-update when using plain TCP
# Closes D:710529
my $service_squashed = $service; $service_squashed =~ tr/.:/__/;
print {$fh} "multigraph $service_squashed\n" unless $data->[0] =~ m{^multigraph};
foreach my $line (@$data) {
# Ignore blank lines and "."-ones.
next if (!defined($line) || $line eq '' || $line eq '.');
print {$fh} $line, "\n" or logger("Error writing results: $!");
}
return;
}
# removes files from the spooldir older than MAXIMUM_AGE
sub cleanup
{
my ($self) = @_;
my $maxage = $self->{interval_size} * $self->{interval_keep};
opendir my $dir, $self->{spooldir} or die $!;
foreach my $file (readdir $dir) {
next unless $file =~ m{munin-daemon\.\w+\.\d+\.\d+$};
my $filename = "$self->{spooldir}/$file";
my $mtime = (stat $filename)[9];
next unless (time - $mtime) > $maxage;
unlink $filename or die "Unable to unlink '$filename': $!\n";
}
return;
}
1;
__END__
=head1 NAME
Munin::Node::SpoolWriter - Writing side of the spool functionality
=head1 SYNOPSIS
my $spool = Munin::Node::SpoolWriter->new(spooldir => $spooldir);
$spool->write(1234567890, 'cpu', \@results);
=head1 METHODS
=over 4
=item B<new(%args)>
Constructor. 'spooldir' key should be the directory
L<Munin::Node::SpoolReader> is reading from.
=item B<write($timestamp, $service, \@results)>
Takes a timestamp, service name, and the results of running config and fetch on
it. Writes it to the spool directory for L<Munin::Node::SpoolReader> to read.
=item B<cleanup($timestamp)>
Removes any items in the spool directory older than $timestamp.
=back
=cut
# vim: sw=4 : ts=4 : et
|