Puppet: System Administration Automated

Support

How to use External Nodes

To use an external node classifier, in addition to or rather than having to define a node entry for each of your hosts, you need to create a script that can take a hostname as an argument and return information about that host for puppet to use.

NOTE: You can use node entries in your manifests together with External nodes. You cannot however use external nodes and LDAP nodes together. You must use one of the two types.

Configuring puppetmasterd

First, configure your puppetmasterd to use an external nodes script:

[main]
external_nodes = /usr/local/bin/puppet_node_classifier

For version 0.24 and later an additional configuration option, node_terminus, needs to be specified to select the appropriate node terminus:

[main]
external_nodes = /usr/local/bin/puppet_node_classifier
node_terminus = exec

There are two different versions of External Node support, the format of the output required from the script changed drastically (and got a lot better) in version 0.23. In both versions, after outputting the information about the node, you should exit with code 0 to indicate success, if you want a node to not be recognized, and to be treated as though it was not included in the configuration, your script should exit with a non-zero exit code.

External node scripts for version 0.23 and later

Starting with version 0.23, the script must produce YAML output of a hash, which contains one or both of the keys classes and parameters. The classes value is an array of classes to include for the node, and the parameters value is a hash of variables to define. If your script doesn't produce any output, it may be called again with a different hostname, in my testing, the script would be called up to three times, first with hostname.example.com as an argument, then just with hostname, and finally with default. It will only be called with the shorter hostname or with default if the earlier run didn't produce any output:

#!/bin/sh
# Super-simple external_node script for versions 0.23 and later
cat <<"END"
---
classes:
  - common
  - puppet
  - dns
  - ntp
parameters:
  puppet_server: puppet.example.com
  dns_server: ns.example.com
  mail_server: mail.example.com
END
exit 0

This example will produce results basically equivalent to this node entry:

node default {
    $puppet_server = 'puppet.example.com'
    $dns_server = 'ns.example.com'
    $mail_server = 'mail.example.com'
    include common, puppet, dns, ntp
}

In both versions, the script should exit with code 0 after producing the desired output. Exit with a non-zero exit code if you want the node to be treated as though it was not found in the configuration.

External node scripts for versions before 0.23

Before 0.23, the script had to output two lines, the first was a parent node, and the second was a list of classes:

#!/bin/sh
# Super-simple external_node script for versions of puppet prior to 0.23
echo "basenode"
echo "common puppet dns ntp"
exit 0

This sample script is essentially the same as this node definition:

node default inherits basenode {
  include common, puppet, dns, ntp
}

More complex external node script

This is an early version of the script that I use in production. In my environment the hosts all have a hostname that describes the network they are located on, the type of server, and an ID number, so sample hostnames might look like dev1-app-01, dev1-mgmt-01, test-app-01, test-app-02, prod-app-01, prod-app-02, etc... There is also an alias address that aggregates each of the different types of hosts, so if you have test-app-01 through test-app-05, then there will also be a DNS alias for test-app that will round-robin to the various servers. This means that in my environment, one of the first things I have to set up is the puppet server address, so that each network location uses <network>-puppet as the server name.

This script breaks the hostnames into their component part, and uses that to assign classes that configure them appropriately. It also sets variables for various server addresses based on the network location:

#!/usr/bin/perl -w
use strict;
use warnings;
use YAML qw( Dump );

my $fullhost = shift || die "No hostname passed";

$fullhost =~ /^((\w+)-(\w+)-(\d+))\.example\.com$/
or die "Invalid hostname: $fullhost";

my ( $host, $net, $type, $id ) = ( $1, $2, $3, $4 );

my @classes = ( 'common', $type.'_server' );
my %parameters = (
  puppet_server   => $net."-puppet.example.com",
  file_server     => "puppet://".$net."-puppet.example.com/files",
);

print Dump( {
  classes     => \@classes,
  parameters  => \%parameters,
} );