Iteration in Puppet

People often ask how to do iteration in Puppet. Most Puppet users have a background in imperative programming, and are already very familiar with for loops. Puppet is sometimes confusing at first, because it is actually (or technically, contains) a declarative, domain-specific language. In general, DSL’s aren’t always Turing complete, nor do they need to support loops, but this doesn’t mean you can’t iterate.

Until recently, Puppet didn’t have an explicit looping construct, and it is quite possible to build complex modules without using this new construct. There are even some who believe that the language shouldn’t even contain this feature. I’ll abstain from that debate for now, but instead, I would like to show you some iteration techniques that you can use to get your desired result.

Recursion

puppets-all-the-way-downMany people forget that recursion is a form of iteration. Even more don’t realize that you can do recursion in Puppet:

#!/usr/bin/puppet apply

define recursion(
    $count
) {
    # do something here...
    notify { "count-${count}":
    }
    $minus1 = inline_template('<%= count.to_i - 1 %>')
    if "${minus1}" == '0' {
        notify { 'done counting!':
        }
    } else {
        # recurse
        recursion { "count-${minus1}":
            count => $minus1,
        }
    }
}

# kick it off...
recursion { 'start':
    count => 4,
}

If you really want to Push Puppet, even more advanced recursion is possible. In general, I haven’t found this technique very useful for module design, but it’s worth mentioning as a form of iteration. If you do find a legitimate use of this technique, please let me know!

Type iteration

We’re used to seeing simple type declarations such as:

user { 'james':
    ensure => present,
    comment => 'james is awesome!',
}

In fact, the namevar can actually accept a list:

$users = ['kermit', 'beaker', 'statler', 'waldorf', 'tom']
user { $users:
    ensure => present,
    comment => 'who gave these muppets user accounts?',
}

Which will cause Puppet to effectively iterate across the elements in $users. This is the most important type of iteration in Puppet. Please get familiar with it.

This technique can be used with any type. It can even be used to express a many-to-one dependency relationship:

# where $bricks is a list of gluster::brick names
Gluster::Brick[$bricks] -> Gluster::Volume[$name]    # volume requires bricks

Suppose you’d like to use type iteration, but you’d also like to know the index of each element. This can be useful to avoid duplicate sub-types, or to provide a unique index:

define some_module::process_array(
    $foo,
    $array    # pass in the original $name
) {
    #notice(inline_template('NAME: <%= name.inspect %>'))

    # do something here...

    # build a unique name...
    $length = inline_template('<%= array.length %>')
    $ulength = inline_template('<%= array.uniq.length %>')
    if ( "${length}" != '0' ) and ( "${length}" != "${ulength}" ) {
        fail('Array must not have duplicates.')
    }
    # if array had duplicates, this wouldn't be a unique index
    $index = inline_template('<%= array.index(name) %>')

    # iterate, knowing your index
    some::type { "${foo}:${index}":
        foo => 'hello',
        index => "${index}",
    }
}

# a list
$some_array = ['a', 'b', 'c']    # must not have duplicates

# using the type requires that you pass in $some_array twice!
some_module::process_array { $some_array:    # array
    foo => 'bar',
    array => $some_array,    # same array as above
}

While this example might seem contrived, it is actually a modified excerpt from a module that I wrote.

create_resources

This is a similar technique for when you want to specify different arguments for each type:

$defaults = {
    ensure => present,
    comment => 'a muppet',
}
$data = {
    'kermit' => {
        comment => 'the frog',
    },
    'beaker' => {
        comment => 'keep him away from your glassware',
    },
    'fozzie' => {
        home => '/home/fozzie',
    },
    'tom' => {
        comment => 'the swedish chef',
    }
}
create_resources('user', $data, $defaults)

This creates each user resource with its own arguments. If an argument isn’t given in the $data, it is taken from the $defaults hash. A similar example, and the official documentation is found here.

Template iteration

You might want to iterate to perform a simple computation, or to modify an array in some way. For static value computations, you can often use a template. Remember that the template will get executed at compile time on the Puppet Master, so code accordingly. Here are a few contrived examples:

# filter out all the integers less than zero
$array_in = [-4,3,-8,-2,1,4,-2,1,5,-1,-7,9,-3,2,6,-8,5,3,5,-6,8,9,7,-5,9,3,-3]
$array_out = split(inline_template('<%= array_in.delete_if {|x| x < 0 }.join(",") %>'), ',')
notice($array_out)

We can also use the ruby map:

# build out a greeting string
$names = ['animal', 'gonzo', 'rowlf']
# NOTE: you can also use a regular multi-line template for readability
$message = inline_template('<% if names == [] %>Hello... Anyone there?<% else %><%= names.map {|x| "Hello "+x.capitalize}.join(", ") %>.<% end %>')
notice($message)

Use your imagination! Remember that you can also write a custom function if necessary, but first check that there isn’t already a built-in function, or a stdlib function that suits your needs.

Advanced template iteration

When you really need to get fancy, it’s often time to call in a custom function. Custom functions require that you split them off into separate files, and away from the module logic, instead of keeping the functions inline and accessible as lambdas. The downside to using these “inline_template” lambdas instead, is that they can quickly turn into parlous one-liners.

# transform the $data hash
$data = {
    'waldorf' => {
        'heckles' => 'absolutely',
        'comment' => 'a critic',
    },
    'statler' => {
        'heckles' => 'all the time',
        'comment' => 'another critic!',
    },
}
# rename and filter on the 'heckles' key
$yaml = inline_template('<%= data.inject({}) {|h, (x,y)| h[x] = {"applauds" => y.fetch("heckles", "yes")}; h}.to_yaml %>')
$output = parseyaml($yaml) # parseyaml is in the puppetlabs-stdlib
notice($output)

As with simple template iteration, the key problem is transferring the data in and out of the template. In the simple case, arrays can be joined and split as long as there is a reserved character that won’t be used in the data. For the advanced template iteration, we rely on the YAML transformation functions.

Some reminders

If you properly understand the functionality that your module is trying to model/manage, you can usually break it up into separate classes and defined types, such that re-use via type iteration can fulfill your needs. Usually you’ll end up with a more properly designed module.

Test using the same version of Ruby that will run your module. Newer versions of Ruby have some incompatible changes, and new features, with respect to older versions of Ruby.

Remember that templates and functions run on the Puppet Master, but facts and types run on the client (agent).

The Puppet language is mostly declarative. Because this might be an unfamiliar paradigm, try not to look for all the imperative features that you’re used to. Having a programming background can help, because there’s certainly programming mixed in, whether you’re writing custom functions, or erb templates.

Future parser

For completeness, I should mention that the future parser now supports native iteration. If you need it, it probably means that you’re writing a fairly advanced module, and you’re comfortable manual diving. If you have a legitimate use case that isn’t possible with the existing constructs, and isn’t only a readability improvement, please let me know.

Conclusion

I hope you enjoyed this article. The next time someone asks you how to iterate in Puppet, feel free to link them this way.

Happy hacking,

James

Pushing Puppet at Puppet Camp DC, LISA 2013

Hi there,

I hope you enjoyed my “Pushing Puppet (to its limit)” talk and demos from Puppet Camp D.C., LISA 2013. As requested, I’ve posted the code and slides.

Here is the code:

https://github.com/purpleidea/puppet-pushing

This module will require three modules as dependencies. The dependencies are:

Each example doesn’t require all the dependencies, so if you’re only interested in the FSM, you only need that module.

Here are the slides:

https://github.com/purpleidea/puppet-pushing/blob/master/talks/pushing-puppet.pdf

Here is the bug fix to fix my third Exec[‘again’] demo:

https://github.com/purpleidea/puppet-common/commit/df3d004044f013415bb6001a2defd64b587d3b85

It’s my fault that I added the fancy –delta support, but forgot to test the simpler, version again. Woops.

I’ve previously written about some of this puppet material. Read through these articles for more background and details:

I haven’t yet written articles about all the techniques used during my talk. I’ll try to write future articles about these topics if you’re interested.

If anyone has some photos from the talk, I’d love for you to send me a copy.

Special thanks to Kara, Dawn and Puppet Labs for asking me to present.

If you’d like to invite me to teach, talk or consult, I’d love to come visit your {$SCHOOL, $WORK, $CITY, etc}. Contact me! I’ll be around in D.C. till Friday if you’d like to meet up and hack on some of the code or examples that I’ve published.

If you’re interested in looking at some of the “real work” modules that I’ve written, have a look through my github repositories. Puppet-Gluster and Puppet-IPA, are two that you might find most interesting.

There are a few that I haven’t yet published, so if you’re looking for a fancy module to do X, let me know and I might be a few commits away from something helpful that I haven’t made public yet.

I hope you enjoyed hacking on puppet with me, and please don’t be shy — leave me a comment about my talk, and ask questions if you have any.

Happy Hacking,

James

 

Gluster Community Day, LISA 2013, Monday

I’m here at LISA 2013 at the Gluster Community Day. I’ve been asked by Joe Brockmeier to give a little recap about what’s been going on. So here it is!

Wesley Duffee-Braun started off with a nice overview talk about GlusterFS. The great thing about his talk was that he gave a live demo, running on virtual machines, on his laptop. If you’re a new GlusterFS user, this is good exposure to help you get started. He also has the nicest slides I’ve ever seen. Someone needs to send me the .odp template!

Eco Willson gave the next talk about geo-replication, and discussed a few other GlusterFS topics too. I particularly enjoyed when he talked to us about the upcoming changes. I understood that this would include the ability to have multiple geo-replication daemons across each distributed part of each volume, and, HA between the N brick replicas. This way, your workload is split across the volume, and if a replica is down, one of the other copies can take over and continue the sync.

During lunch, I got to meet Jeff Darcy and talk a bit about storage and feature ideas on my wish-list. He knew my twitter/IRC handle, so he instantly gets 200 bonus points. You should probably checkout his blog if you haven’t already.

After lunch I gave my talk about puppet-gluster, and successfully gave two live demos. I’m really due for a blog post about some of the new features that I’ve added. I’ll try to put together a screen cast in the future. If you’re really keen on trying out some of the new features, I’m happy to share a screen session on your hosts and walk you through it. When run fully automatically, it takes between five and ten minutes to deploy a featureful GlusterFS!

I won’t be able to hack on this project for free, forever. If you’re able to donate test hardware, VM time, or sponsor a feature, it would be appreciated. I’m particularly interested in building a GlusterFS microserver cluster. If you’re interested in this too, let me know.

Wesley came back for round two to demo the GlusterForge (hi John Mark!) and Glusterinfo, which I hadn’t ever used. Nobody knew why certain projects are still “incubating”, when they’re mostly likely cooked through by now.

At this point, we had some left over time for questions and discussion. Jeff, Eco, and I formed a loosely organized “panel” to answer questions. Joe took a photo, and now owes me a copy, since I’ve never really been on a “panel” before. Jeff knows his GlusterFS internals very well. I should have come prepared with some hard questions!

To whoever was looking for Ben England’s RHS performance talk, it’s available here.

Overall, it was a very nice little event, and I hope the attendees enjoyed it. If I forgot anything, please feel free to add it into the comments. I’d also love to hear about what you enjoyed or didn’t enjoy about my talk. Let me know!

Happy Hacking,

James

Speaking at LISA 2013 about Puppet and GlusterFS

I’m speaking at LISA 2013, the “Large Installation System Administration” conference. This conference runs all week in Washington. I’ll be giving two talks during the week, and attending at least one BOF.

My first talk is on Monday during the Gluster Community Day. I’ll be speaking about puppet-gluster, and giving a live demo. I’ll be showing some new features too. If you’d like to talk more about puppet-gluster, or want to attend the talk, give me a shout, or sign up at the above Gluster Community Day link.

On Tuesday, I’ll be giving a talk during Puppet Camp DC. My talk will be about “Pushing Puppet (to the limit)”. I’ve prepared lots of fun puppet hacks, and live demos, so I expect you’ll thoroughly enjoy this.

I’ve got lots of technical hacks, and great code that I’ve published lately, but that I haven’t blogged about yet. If you follow along with my git commits, you’ll be able to figure out most of it, but I’ve got a bunch of articles coming anyways.

Thanks to John Mark and RedHat for sponsoring my trip, Dawn Foster and Kara Sowles for organizing my Puppet talk and Hilary Hartman at USENIX for helping me register.

Happy Hacking,

James

PS: If you’re at LISA 2013, and you want to buy me dinner and get some light Puppet or Gluster consulting, contact me! You can also give me a shout if you just want to talk tech. If anyone wants to meet up and hack on things, please let me know too.

I'm speaking at LISA 2013

Easier strace of scripts with pidof -x

Here’s a one minute read, about a trick which I discovered today:

When running an strace, it’s common to do something like:

strace -p<pid>

Smarter hackers know that they can use some bash magic and do:

strace -p`pidof <process name>`

However, if you’re tracing a script named foo.py, this won’t work because the real process is the script’s interpreter, and pidof python, might return other unrelated python scripts.

strace -p`pidof foo.py` # won't work
[failure]
[user sifting through ps output for real pid]
[computer explodes]

The trick is to use the -x flag of pidof. This will let you pass in your script’s name, and pidof will take care of the rest:

strace -p`pidof -x foo.py` # works!
[user cheering]
[normal strace noise]

Awesome!

Happy hacking,

James

 

Desktop Notifications for Irssi in Screen through SSH in Gnome Terminal

I’m usually on IRC, but I don’t often notice incoming pings until after the fact. I had to both write, and modify various scripts to get what I wanted, but now it’s all done, and you can benefit from my hacking by following along…

The Setup

Laptop -> Gnome-Terminal -> SSH -> Screen -> Irssi

This way, I’m connected to IRC, even when my laptop isn’t. I run irssi in a screen session on an SSH server that I manage, and I use gnome-terminal on my laptop. If you don’t understand this setup, then you’ll need to get more comfortable with these tools first.

Fnotify

The first trick is getting irssi to store notifications in a uniform way. To do this, I modified an irssi script called fnotify. My changed version is available here. Installation is easy:

# on your ssh server:
cd /tmp; wget https://dl.dropboxusercontent.com/u/48553683/irssi/fnotify.pl
cp /tmp/fnotify.pl ~/.irssi/scripts/
# in irssi:
irssi> /load perl
irssi> /script load fnotify

When someone sends you a direct message, or highlights your nick on IRC, this script will append a line to the ~/.irssi/fnotify file on the SSH server.

Watching fnotify

On your local machine, we need a script to tail the fnotify file. This was surprisingly hard to get right. The fruit of my labour is available here. You’ll want to copy this script to your local ~/bin/ directory. I’ve named this script irssi-fnotify.sh. This script watches the remote fnotify file, and runs notify-send and paplay locally to notify you of any incoming messages, each time one comes in.

SSH Activation

We want the irssi-fnotify.sh script to run automatically when we connect to our SSH server. To do this, add the following lines to your ~/.ssh/config file:

# home
Host home
    HostName home.example.com
    PermitLocalCommand yes
    LocalCommand ~/bin/irssi-fnotify.sh --start %r@%h

You might also want to have other directives listed here as well, but that is outside the scope of this article. Now each time you run:

ssh home

The irssi-fnotify.sh command will automatically run.

Magic

I’ve left out some important details:

  • The LocalCommand that you use, must return before ssh will continue. As a result, it daemonizes itself into the background when you invoke it with –start.
  • My irssi-fnotify.sh program watches the parent ssh $PID. When it exits, it will run a cleanup routine to purge old notifications from the fnotify file. This requires a brief SSH connection back to the server. This is a useful feature!
  • You may wish to modify irssi-fnotify.sh to paplay a different alert sound, or to avoid making noise entirely. The choice is yours.
  • When irssi-fnotify.sh runs, it will tail the fnotify file over ssh. If there are “unread” messages, tail will try to “download” up to ten. You can edit this behaviour in irssi-fnotify.sh if you want a larger initial backlog.
  • The irssi-notify.sh script doesn’t attempt to prevent flooding, nor does it filter weird characters from incoming messages. You may want to add this yourself, and or /kb users who cause you to need these features.

Here’s a little screenshot (with shameless plug) of the result in action:

irssi-fnotify.sh notification screenshot

Here’s an example of how this helps me to be more responsive in channel:

helping out in #gluster

helping out in #gluster

I hope you found this useful.

Happy Hacking,

James

GNOME Montreal Summit

This October 12th to 14th Montreal hosted the GNOME boston summit. Many thanks to Canonical for sponsoring breakfast, Savoir Faire Linux for hosting a great 6 à 10 with fancy snacks, and RedHat for sponsoring a pool night. What follows is some technical commentary about stuff that went on.

JHBuild

JHBuild is a tool to make it easy to download/clone (from git) and compile all the GNOME modules and applications. It was easy to get going. I (mostly) followed the steps listed on the JHBuild HowDoI wiki page. I kept my .jhbuildrc in my home directory instead of ~/.config/. On my Fedora 19 machine, I found certain unlisted dependencies were missing to build everything. You can figure these out yourself when your builds fail, or just run the following command to install them beforehand:

# yum install /usr/bin/{g++,a2x,gnome-doc-prepare} python-rdflib lua-devel

The abridged list of commands that I ran includes:

$ git clone git://git.gnome.org/jhbuild
$ ./autogen.sh --simple-install
$ make
$ make install
$ ln -s ~/.local/bin/jhbuild ~/bin/jhbuild
$ jhbuild sysdeps --install
$ jhbuild buildone gnome-themes-standard    # i was impatient to get this early
$ jhbuild update                            # at home on fast network
$ jhbuild build --no-network                # can be run offline if you like
$ jhbuild run gedit
$ jhbuild shell                             # then run `env` inside the shell

You want to properly understand the context and working directory for each of these, but it should help get you to understand the process more quickly. One thing to realize is that the jhbuild run actually just runs a command from your $PATH, but from within the jhbuild environment, with modified $PATH and prefix variables. Run:

$ jhbuild run env

to get a better understanding of what changes. When patching, I recommend you clone locally from the mirrored git clones into your personal ~/code/ hacking directory. This way JHBuild can continue to do its thing, and keep the checkouts at master without your changes breaking the process.

Boxes

Boxes is a simple virtual machine manager for the GNOME desktop.

Zeeshan talked about some of the work he’s been doing in boxes. William Jon McCann reviewed some of the UI bugs, and I proposed the idea of integrating puppet modules so that a user could pick a module, and have a test environment running in minutes. This could be a way for users to try GlusterFS or FreeIPA.

GNOME Continuous

GNOME continuous (formerly known as GNOME-OSTree) is a project by Colin Walters. It is amazing for two reasons in particular:

  1. It does continuous integration testing on a running OS image built from GNOME git master. This has enabled Colin to catch when commits break apps. It even takes screenshots so that you see what the running apps look like.
  2. It provides bootable GNOME images that you can run locally in a vm. The images are built from GNOME git master. While there are no security updates, and this is not recommended for normal use, it is a great way to test out the bleeding edge of GNOME and report bugs early. It comes with an atomic upgrade method to keep your image up to date with the latest commits.

My goal is to try to run the bootable image for an hour every month. This way, I’ll be able to catch GNOME bugs early before they trickle down into Fedora and other GNOME releases.

Abiword 3.0

Hubert Figuière released AbiWord 3.0 which supports GTK+ 3. Cool.

A11y

Some of the A11y team was present and hacking away. I learned more about how the accessibility tools are used. This is an important technology even if you aren’t using them at the moment. With age, and accident, the current A11y technologies might one day be useful to you!

Hacking Hearts

Karen Sandler was kind enough to tell me about her heart condition and the proprietary software inside of her pacemaker defibrillator. Hopefully hackers can convince manufacturers that we need to have access to the source.

Yorba and Geary

Jim Nelson is a captivating conversationalist, and it was kind of him to tell me about his work on Geary and Vala, and to listen to my ideas about GPG support in email clients. I look forward to seeing Geary’s future. I also learned a bit more about IMAP.

Future?

I had a great time at the summit, and it was a pleasure to meet and hangout with the GNOME hackers. You’re always welcome in Montreal, and I hope you come back soon.

Happy Hacking,

James