The New File Server: Puppet and Modules

On to Puppet. I’ve not yet factored everything about the new server out into separate modules or classes; that’ll come later. But things that will either get reused on other systems (e.g., Active Directory ties) or things that need to be generated consistently and repeatedly (e.g., Amanda configurations) have been factored out. The new server’s manifest looks like:

node ch208r {
    include cae-host

    # ch208r (new file server) needs:

    # Disk tools (XFS support, LVM support, parted)
    package {
        [ "xfsdump", "lvm2", "parted" ]: ensure => installed;
    }
    # Backup tools (see amandaconfig module for details)
    include amanda
    amanda::amandaconfig {
        "holding":
            labelstr   => "HOLDING",
            dumpcycle  => 1,
            tapecycle  => 2,
            runtapes   => 1;
        "daily":
            labelstr   => "DAILY",
            dumpcycle  => 35,
            tapecycle  => 10,
            runtapes   => 1;
        "archival":
            labelstr   => "ARCH[2-9]",
            dumpcycle  => 0,
            tapecycle  => 1000,
            runtapes   => 4;
    }

    cron {
        "amdump-daily":
            command => "/usr/local/sbin/mkdisklist > /etc/amanda/daily/disklist; /usr/sbin/amdump daily; /usr/sbin/amdump holding ; du -shc /opt/amanda/work | tail -1 | mutt -s 'Amanda Disk Usage' 1234567890@vtext.com",
            ensure  => present,
            user    => backup,
            hour    => 0,
            minute  => 15;
    }

    file { 
        "/etc/amanda/holding/disklist": 
            source => "puppet:///files/apps/amanda-server/disklist.holding"; 
    }

    # Environmental monitoring
    package {
        [ "ipmitool", "openipmi" ]: ensure => installed;
    }
    file {
        "/etc/modules":
            source => "puppet:///files/apps/ipmitool/modules";
    }

    mount { "/home":
        atboot  => true,
        device  => "/dev/md1000/home",
        ensure  => mounted,
        fstype  => "xfs",
        options => "defaults",
        dump    => "0",
        pass    => "1",
        require => [ Package["xfsdump"], Package["lvm2"] ];
    }

    include active-directory-fileserver
    # - NFS export of /home/CAE
    package {
        [ "nfs-kernel-server" ]: ensure => installed;
    }

    # - scponly for remote file access (no shell access allowed)
    package {
        [ "scponly" ]:
            ensure => installed;
    }

}

Since the active-directory-fileserver class ties strongly into an active-directory-member class, and that class warrants an entire followup artcle for my authentication notes, I’ll hold off on that part for now which can be found here. But the Amanda configuration code, that’s worth talking about now.

If you’ve never used Amanda, it’s arguably the best multi-system, multi-platform Free Software backup solution out there. We’ve kept it in constant service since late 2002, making this the third major fileserver we’ve put it on. It’s a little fiddly to get installed properly:

Create the config directory (eg. /usr/local/etc/amanda/confname) and copy the example/ files into that directory. Edit these files to be correct for your site, consulting the amanda(8) man page if necessary. You can also send mail to mailto://amanda-users@amanda.org if you are having trouble deciding how to set things up. You will also need to create the directory for the log and database files for the configuration to use (eg /usr/local/var/amanda/confname), and the work directory on the holding disk. These directories need to agree with the parameters in amanda.conf. Don’t forget to make all these directories writable by the dump user!

but worth it. Since I have no less than three different Amanda configurations to set up (one for rotating daily backups, one for monthly archival backups, and one to back up the Amanda holding disk as disaster insurance), I figured it was time to make Puppet create my configuration files in addition to copying them to the file server. It wasn’t nearly as difficult as I’d expected, and hopefully this will provide an example that’s easy enough to follow, complicated enough to be useful, and uses enough of Puppet’s module capabilities to keep others from hitting the same walls I did. I don’t claim any of them are perfect, but they’re currently working and relatively clean.

First, I made a modules directory in the puppet root (/etc/puppet, in my case). Then I added the following entry into puppet’s fileserver.conf:

[modules]
  # path isn't actually used here
  allow A.B.C.0/24

where A.B.C.0/24 corresponds to the class C subnet that can access the modules. In the modules folder, I created an amandaconfig folder, which currently has the following contents:

/etc/puppet/modules# ls -lR amandaconfig
amandaconfig:
total 12
drwxr-xr-x 3 root root 4096 2007-08-02 15:36 files
drwxr-xr-x 3 root root 4096 2007-08-02 15:36 manifests
drwxr-xr-x 3 root root 4096 2007-08-02 15:36 templates

amandaconfig/files:
total 12
-rw-r----- 1 root root 1827 2007-08-02 09:41 disklist.systems
-rwxr-xr-x 1 root root  416 2007-07-31 14:31 mkdisklist
-rwxr-xr-x 1 root root  390 2007-07-31 14:35 mkdisklist.archival

amandaconfig/manifests:
total 4
-rw-r--r-- 1 root root 2554 2007-08-01 17:21 init.pp

amandaconfig/templates:
total 8
-rw-r--r-- 1 root root 1418 2007-08-02 09:52 amanda.conf.erb
-rw-r--r-- 1 root root   13 2007-07-25 21:05 changer.conf.erb

The files directory should contain auxiliary files for the module that don’t need to be edited on a per-configuration basis. Mine contains a couple of shell scripts to create amanda disklist files by iterating over the file server’s home directories, and a list of system partitions on other servers and workstations I need to back up daily. The manifests directory contains the Puppet code to create a series of working Amanda configurations, and the templates directory contains files that need to have some search/replace operations done on a per-configuration basis.

I also added an import "amandaconfig" to the top of my site.pp to make sure the new Amanda module was usable.

The contents of manifests/init.pp:

# Create a new Amanda configuration set.
class amanda {
    package {
        [ "amanda-server", "amanda-client", "mtx", "dump" ]:
            ensure => latest;
    }

    file {
        "/opt/amanda":
            ensure => directory,
            owner  => root,
            group  => root,
            mode   => 0755;

        "/opt/amanda/work":
            ensure => directory,
            owner  => backup,
            group  => backup,
            mode   => 0700;

        "/usr/local/sbin/mkdisklist":
            source => "puppet:///amandaconfig/mkdisklist",
            owner  => root,
            group  => backup,
            mode   => 0750;

        "/usr/local/sbin/mkdisklist.archival":
            source => "puppet:///amandaconfig/mkdisklist.archival",
            owner  => root,
            group  => backup,
            mode   => 0750;

        "/etc/amanda/disklist.systems":
            source => "puppet:///amandaconfig/disklist.systems",
            owner  => root,
            group  => backup,
            mode   => 0640;
    }

    define amandaconfig (
        $confdir = "/etc/amanda",
        $logdir = "/var/log/amanda",
        $libdir = "/var/lib/amanda",
        $user = "backup",
        $group = "backup",
        $dumpcycle = 1,
        $tapecycle = 2,
        $runtapes = 1,
        $labelstr = "LABEL"
    ) {

        file {
            "$confdir/$name":
                ensure => directory,
                owner  => $user,
                group  => $group,
                mode   => 0770;

            "$confdir/$name/amanda.conf":
                content => template("amandaconfig/amanda.conf.erb"),
                ensure  => present,
                owner   => $user,
                group   => $group,
                mode    => 0755;

            "$confdir/$name/changer.conf":
                content => template("amandaconfig/changer.conf.erb"),
                ensure  => present,
                owner   => $user,
                group   => $group,
                mode    => 0755;

            "$confdir/$name/tapelist":
                ensure => present,
                owner  => $user,
                group  => $group,
                mode   => 0600;

            "$libdir/$name":
                ensure => directory,
                owner  => $user,
                group  => $group,
                mode   => 0770;

            "$logdir/$name":
                ensure => directory,
                owner  => $user,
                group  => $group,
                mode   => 0770;

        }
    }
}

Everything in the amanda class outside the amandaconfig definition gets evaluated when the node manifest gets to its include amanda line. So the necessary holding disks are created, packages are installed, etc. After that, I can answer the questions for what makes each Amanda configuration unique in the amanda::amandaconfig stanza. For example:

  • the “holding” configuration that backs up the holding disk each day alternates between two tapes labeled HOLDING001 and HOLDING002.
  • the “daily” configuration backs up the file server’s system disks, several other systems’ system disks, and all the users’ and groups’ home directories each day into the holding disk, which we flush off to tape every few days to a series of tapes labeled DAILY001, DAILY002, …
  • the “archival” configuration backs up the users’ and groups’ home directories into the holding disk, which is immediately backed up to tapes labeled ARCH201, ARCH202, … ARCH999.

As for the templates, they’re ERB-formatted configuration files. amanda.conf is the only interesting one, since changer.conf.erb is actually a static file and should be moved into the files folder with mkdisklist and friends:

org "TTU CAE Network"
mailto "root"
dumpuser "<%= user %>"

inparallel 4
netusage 600
reserve 0

dumpcycle <%= dumpcycle %> days
tapecycle <%= tapecycle %> tapes

runtapes <%= runtapes %>
tpchanger "/usr/lib/amanda/chg-zd-mtx"
changerdev "/dev/sg3"
changerfile "changer"
tapedev "/dev/nst0"
tapetype PV-124T_LTO3
labelstr "^<%= labelstr %>[0-9][0-9]*$"

infofile "<%= libdir %>/<%= name %>/curinfo"
logdir "<%= logdir %>/<%= name %>"

indexdir "<%= libdir %>/<%= name %>/index"

holdingdisk opt {
    comment "/opt/amanda/work"
    directory "/opt/amanda/work"
    use -1 gb
}

define tapetype PV-124T_LTO3 {
    comment "Dell PowerVault 124T - LTO3"
    length 402432 mbytes
    filemark 0 kbytes
    speed 71064 kps
}

define dumptype holding-full {
    comment "Full dump of the holding disk always"
    program "GNUTAR"
    compress none
    holdingdisk never
    skip-incr yes
    priority high
    dumpcycle 0
}

define dumptype comp-root {
    comment "Entire partitions backed up with dump"
    compress fast
    index yes
    priority low
}

define dumptype homedir-full {
    comment "User home directories backed up with tar"
    program "GNUTAR"
    compress fast
    index yes
    priority medium
    holdingdisk auto
}

define dumptype homedir-full-archival {
    comment "User home directories backed up with tar"
    program "GNUTAR"
    compress fast
    index yes
    priority medium
    record no
}

You can read through the Amanda documentation for what all those settings actually mean. The relevant part is that Puppet will parse through the template looking for variables to replace, and will replace them with either the defaults used in the amandaconfig definition, or with ones passed as arguments to the amandaconfig call.

Join the Conversation

1 Comment

Leave a comment

Your email address will not be published. Required fields are marked *