Xserve Cluster Setup
Apple's Server Assistant automatically launches when the Xserve reboots after imaging finishes. The Server Assistant then searches the attached disks for a folder "Auto Server Setup" for a plist file names with its serial number. The assistant then uses the values in this plist file to automatically configure itself. After configuration it will reboot.

This script generates the plist files that the Server Assistant uses for autoconfig of the Xserve. It uses an input file of serial numbers named serial_numbers which contains the dynamic portion of the serial numbers (50101Z) and generates a plist from the template plist (template.plist) setting hostname and IP address of the node. In our case the nodes were named darwin001 thru darwin100 with ip address of 10.0.0.1 thru 10.0.0.100. The resulting plist files are placed in a folder called config_files.


#!/usr/bin/perl

#
# These variables can be edited by the user to change the generated
# configuration files.
#
$name      = darwin;           # base hostname
$subnet    = "10.0.0";         # subnet for hosts (must be in format
"XXX.XXX.XXX")
$node      = 77;                  # starting node number
$dest      = "config_files";   # destination directory for generated files
$sn_prefix = "QP";             # prefix for serial numbers, may be left
blank
$sn_suffix = "PMX";            # suffix for serial numbers, may be left
blank

#
# END OF USER CONFIGURABLE OPTIONS
#

$subnet .= '.';

#
# replace "." with "\." so "." is treated literally in the pattern
#
($subnet_pattern = $subnet) =~ s/\./\\\./g;

# 
# This is a list of IP address on the subnet that we should not replace with
# the host IP address since we do a general search and replace on the string
# "$subnet.*".  Here we exclude the router and NTP server IP addresses.
#
@exclude_ips = ( "254", "106" );

foreach ( @exclude_ips ) {
    $_ = "$subnet_pattern$_";
}

$exclude_pattern = join( "|", @exclude_ips );

#
# read in the template
#
@template;
$file = "template.plist";
open( TEMPLATE, "<$file" ) or die "Failed to open $file: $!";

while ( <TEMPLATE> ) {
    push( @template, $_ );
}
close( TEMPLATE );

#
# read in the serial numbers
#
@serials;
$file = "serial_numbers";
open( SERIALS, "<$file" ) or die "Failed to open $file: $!";

while ( <SERIALS> ) {
    chomp;
    push( @serials, $_ );
}
close( SERIALS );

$n_serials = $#serials + 1;
print "Loaded $n_serials serial numbers\n";

if ( ! -d "config_files" ) {
    printf "Creating destination directory $dest\n";
    mkdir( "$dest", 0777 ) or die "Failed to mkdir() for $dest: $!";
}

#
# create the configuration files, one for each serial number we read
# in starting with the node number specified above
#
foreach ( @serials ) {
    $hostname = sprintf( "$name%03d", $node );
    $address  = "$subnet$node";
    $file     = "$dest/$sn_prefix$_.plist";

    if ( -f $file ) {
        print "Updating configuration $hostname ($sn_prefix$_$sn_suffix)\n";
    } else {
        print "Generating configuration $hostname
($sn_prefix$_$sn_suffix)\n";
    }

    open( CONFIG, ">$file" ) or die "Failed to open $file: $!";
    select( CONFIG );

    foreach ( @template ) {
        s/$name[0-9]{3}/$hostname/o;
        s/$subnet_pattern[0-9]{1,3}/$address/o unless /$exclude_pattern/o;
        print $_;
    }
    close( CONFIG );
    select( STDOUT );

    ++$node;
}
Next -> Radmind