My cfengine policies explained – part 3

One good thing in cfengine 3 is the enhanced ability to create reusable snippets of code containing promises (bundles) or parameters (bodies); plus, you can make them parametric for added flexibility. The cfengine standard library, also known as Community Open Promise Body Library (COPBL), is a comprehensive collection of reusable bodies and bundles that can simplify the task of writing policies. However, although comprehensive, it can't fit all possible needs. But there is good news: you can write your own libraries, too, with either completely new bodies/bundles, or by modifying those already available in the standard library!

As promised, we are about to see a library called opera-lib.cf that complements the standard library, addressing needs specific to our installation. It's quite small however, which is a a further demonstration that the standard library covers almost everything.

Rather than listing the whole file, we'll either examine each the library's body/bundle separately, or in small groups. … Continue reading

My cfengine policies explained – part 2

The second policy we are going to explore is the one contained in the puppet.cf file. There are two use cases for it: stop puppet from changing the system, or remove it entirely. We'll see the whole code first, and then we'll see how cfengine handles it in each of the two cases. … Continue reading

My cfengine policies explained – part 1

As announced on twitter, my cfengine policies are now in production. It's a few dozen servers, so not a big installation, but it saved me a lot of headache already. I thought it would be nice to give back to the community some of the help I got from it, so I'll publish and comment some of the code here. Some of the policies, I'll show in whole; others, I'll have to cut and obfuscate things here and there; there are also a few I cannot publish, sorry about that.

Some may think: why I didn't put this stuff in the design center? Well, some of them are rather rough and not really ready for the prime time. I'll do that eventually, when I feel they're nice enough.

But before we start, let me make a quick note about the design.

I massively took advantage of the "methods" promises, which provided me some interesting features.

First and foremost: they allow me to organise my policies in "layers":

  • promises.cf, the topmost set of promises, is just a collection of generic requests to the agent: print this, check that, configure here, install there…
  • one level down, you have more specific bundles which, in turn, may either perform an action, or be another level of abstraction, with specific actions bundled into other method calls
  • at the low level, you only have those bundles that actually perform actions on the system

Besides:

  • specific functionalities are encapsulated in small, reusable bundles, possibly parametric bundles
  • each layer offers a different level of complexity: the higher layers are the simpler ones, and don't require a lot of cfengine knowledge to be written; every time you go down one level, you get closer to the actions performed directly on the system
  • it should be easier to distribute the task of writing a policy across people with different levels of knowledge (if I am not left alone in this effort 🙂
  • enforcing a specific order to the actions you want to perform is much easier if you encapsulate related actions in separate bundles, and then you invoke them sequentially; the order of execution is not always important, but when it is, it's nice to be able to enforce it.

Enough talk, let's roll! … Continue reading

Turning the wireless ESSID into a class

I think it could be handy to have a class in cfengine that tells us something about the ESSID of the wireless network we are connected to. Unfortunately, cfengine doesn't define one by default, and I thought it could be a good idea to make a module for that. It has been buzzing in my head for a while, and tonight I took some 30 minutes to make it happen.

On Linux, we have the command iwgetid that tells us the ESSID of the wireless networks we are connected to. However, ESSIDs may contain characters that are not allowed in a class name, so we need to "escape" them. The following, tiny bash script reads the output of iwgetid and returns the corresponding class names to be defined:

#!/bin/bash

PATH="/sbin:/usr/sbin:/usr/bin:/bin"

for ESSID in $( iwgetid --raw )
do
  CESSID=$( echo $ESSID | tr -c "a-zA-Z0-9_" "_" | sed -e 's/_*$//' )
  CLASS="essid_${CESSID}"

  echo "+${CLASS}"
done

exit 0

On the laptop I am currently using, it returns:

root@cooper:/var/cfengine/inputs# modules/essid.sh 
+essid_WiFiOslo

And here it is! When we use this script as a module in cfengine, it will define a class named essid_WiFiOslo.

For more information about cfengine's module protocol, see the reference.

When “bulk” is just too much…

Managing software packages with cfengine is, normally, no problem: you have "packages" promises, and you have pre-canned bodies for a number of package managers, plus the ubiquitous "generic" that should help you on almost all systems where cfengine can run. However, I hit one of those corner cases where you have to build your own package_method body — although stealing almost everything from one in the standard library 😉 … Continue reading

cfengine vs vpn: 1-0

I had a quite annoying problem on my laptop, that I solved using cfengine.

When the VPN software runs, it creates a virtual tun0 interface and changes a few things in the network configuration (e.g.: routes, /etc/resolv.conf,…). A problem arises when the DHCP lease is renewed on the physical interface, eth0 or wlan0: in fact, resolv.conf gets rewritten, and I can't resolve internal network addresses any more until I put a valid resolv.conf back in place.

A few days ago, while on vacation, I finally adapted my existing policies to run on my laptop. One of the policies keeps an eye on resolv.conf while I am on VPN, and rewrites it if dhclient does the smartass. I am testing it today for the first time, and I am really pleased to find this message in my mailbox:

Subject: community [cooper/192.168.0.5]
Date: Thu, 19 Jul 2012 20:46:34 +0200
From: cfengine@localhost
To: bronto@localhost

R: Repaired resolver configuration in /etc/resolv.conf

So I'm pretty safe: if dhclient messes with my resolver, cfengine will set it back in <5 minutes time. Isn't that nice? 😉

Oh, and of course it does more than that. Depending on the location I am in, and whether I am in VPN or not, it reconfigures ntpd and restarts it, so that I always use the best configuration. But I don't want to bother you with the gory details, so I'll stop here 😉

The hectic week of the leap second

Last week has been an hectic period:

  • we’ve been hit by the leap second announcement bug
  • others around the internet have been, as well
  • …not to mention those hit by the leap second itself
  • Wired put my name on an article twice, which started a “citation spree” around the globe
  • I’ve finally realized my proposition to start on Twitter (@brontolinux)
  • I’ve been appointed as one of the CFEngine champions 2012

I’ll try to sum up, and conclude with a take-away lesson for next leap second.

Continue reading

Who’s my cfengine policy hub?

This could be either a trivial question, or an arcane mistery. For me it was the second one, until I did some research and I found out that the answer was quite easy 🙂

So, how does cfengine know what's your policy hub, once you have bootstrapped a client?

The surprisingly simple answer is: by reading the contents of $(sys.workdir)/policy_server.dat, usually /var/cfengine/policy_server.dat

Good to know, when you are planning to point a client to a different hub for any reason 🙂

Why I gave up puppet and chose cfengine 3

But before even beginning to tell the story, I'd like to say that I have nothing against the puppet community, which is in fact a great one: be it on IRC or in a mailing list, they were always helpful, never rude; yes, I hated when they said "well, puppet is not designed to do that", but at least they were honest 😉

The fact is: I never fell in love with puppet. I've always been frustrated by its unpredictability, badly implemented functionalities (fileserver anyone?), bad design decisions (e.g.: why use the hostname as a key in the hosts file, where all systems use the IP as the key?), and no scalability out-of-the-box (e.g.: no provisions for hierarchical puppetmaster architectures, need for external resources like an nginx reverse proxy to make it scale…) to mention the first ones that come to mind.

With all this bad, how did it happen that I got trapped into puppet?

It's a long story, so take your time and relax. … Continue reading