Recipes/cron

Converting puppetd to run using cron

This page contains two possible solutions for using Puppet via cron

Goals

Our goals were to:
- Run puppet as cron
- conserve memory usage
- not use --splay (since puppetd still hangs in memory)
- spread out the puppetd times they run and manually load balance (if needed)

Puppet Recipe

                $timeoffset1 = generate('/usr/bin/env', '/etc/puppet/modules/puppet/bin/randomnum.pl', "$fqdn", "30")
                $timeoffset2 = generate('/usr/bin/env', '/etc/puppet/modules/puppet/bin/math.pl', "$timeoffset1", "+", "30")
                ## for 0.24.6+, uncomment this line and comment previous line.                
                # $timeoffset2 = $timeoffset1 + 30 
                cron { "puppet":
                        ensure  => present,
                        command => "/usr/sbin/puppetd --onetime --no-daemonize --logdest syslog > /dev/null 2>&1",
                        user    => 'root',
                        minute  => [ $timeoffset1, $timeoffset2 ],
                        # Probably want to move these into their own resource.
                        package { "perl": ensure => "installed", },
                        file { "/etc/puppet/modules/puppet/etc/": ensure => "directory", },
                }

See below for the randomnum and addnum scripts.

You can also create a tag to have some nodes run as daemon and others run as cron.

Random Number Script

Generates a random number and stores the entry so it can be recalled. If you want to load balance your puppetd, just edit the files in /etc/puppet/modules/puppet/etc/ folder.

You must create the folder and have the proper perms on /etc/puppet/modules/puppet/etc/ in order for it to work. Adjust according to your specific setup.

#!/usr/bin/perl
#### /etc/puppet/modules/puppet/bin/randomnum.pl
use strict;
umask 066;

die "Usage: <file basename> <run interval>" unless $ARGV[1];

my $puppetclientetcdir="/etc/puppet/modules/puppet/etc/";
my $keyfile=$puppetclientetcdir.$ARGV[0].".key";

if (!-e $keyfile) {
        my $range=$ARGV[1];
        my $random_number = int rand $range;
        open my $OUT,'>', $keyfile or die "Couldn't write to $keyfile: !$";
        print $OUT $random_number;
        close $OUT;
}

open my $IN,"$keyfile" or die "Couldn't open $keyfile: !$";
foreach my $line (<$IN>) {
  if ($line =~ /^\d+(:?\n|)$/) {
    chomp $line;
    print $line;
    close $IN;
    exit;
  }
}
close $IN;

# If we get to here something is wrong, just print the XKCD random number
# and exit with an error.
print "4";
exit 1;
#### /etc/puppet/modules/puppet/bin/randomnum.pl END OF FILE

Additional Number Script

Creates the second time within an hour to run cron (add 30 min)

#!/usr/bin/perl
# $Id$
#### /etc/puppet/modules/puppet/bin/math.pl
#### No operator sanity checking, potential security consideration. :)
use strict;
die "Usage: $0 <operand1> <operator> <operand2>\n Example: $0 1 + 2\n Example: $0 2 \\* 8" unless $ARGV[2];

my $result = eval("return $ARGV[0] $ARGV[1] $ARGV[2];"); warn $@ if $@;

print int $result;
#### /etc/puppet/modules/puppet/bin/math.pl END OF FILE

Setting Cron using a Puppet custom function

An alternative cleaner implementation to the above is to use a "random" hash value for cron based on the IP address of the machine.

Here is a Puppet custom function to do this:

Usage Example

cron { "puppet":
  ensure  => present,
  command => "/usr/sbin/puppetd --onetime --no-daemonize --logdest syslog > /dev/null 2>&1",
  user    => 'root',
  minute  => ip_to_cron(2)
}

Cron random custom function

The content of the following file needs to be added as a function, usually to your

modulename/lib/puppet/parser/functions/ip_to_cron.rb

cat ip_to_cron.rb

# provides a "random" value to cron based on the last byte of the machine IP address.
# used to avoid starting a certain cron job at the same time on all servers.
# if used with no parameters, it will return a single value between 0-59
# first argument is the occurrence within a timeframe, for example if you want it to run 2 times per hour
# the second argument is the timeframe, by default its 60 minutes, but it could also be 24 hours etc
# ohadlevy@gmail.com
#
# example usage
# ip_to_cron()     - returns one value between 0..59
# ip_to_cron(2)    - returns an array of two values between 0..59
# ip_to_cron(2,24) - returns an array of two values between 0..23

module Puppet::Parser::Functions
	newfunction(:ip_to_cron, :type => :rvalue) do |args|
		occours = (args[0] || 1).to_i	
		scope   = (args[1] || 60).to_i
		ip      = IPAddr.new(lookupvar('ipaddress')).to_s.split('.')[3].to_i
		base    = ip % scope
		if occours == 1
			base
		else
			cron = Array.new
			(1..occours).each do |i|
				cron << ((base - (scope / occours * i)) % scope)
			end
			return cron.sort
		end
	end
end