Using crankd to react to network events
I use Puppet (http://www.puppetlabs.com) as our in-house configuration management du jour. Puppet is excellent, but one area that bothered me was with our district laptops. I can commit a change to Puppet that will get pushed out whenever Puppet runs next (every hour for school machines), but if the laptop isn't on-network (or isn't on PERIOD) then the state will drift and the commit won't get applied until someone keeps the laptop on long enough for a puppet run.
This is where crankd comes in.
Crankd is a cool utility that's part of the Pymacadmin (http://code.google.com/p/pymacadmin/) suite of tools co-authored by Chris Adams and Nigel Kersten. In a nutshell, crankd is a Python script that lets you trigger shell scripts or access python modules based upon events like SystemConfiguration, NSWorkspace and FSEvents.
Use Cases and Installation.
Why use crankd? There are a couple of situations in which it would be handy to execute code in response to certain system events. For example...
- You have laptops bound to OD that frequently leave your work/school/organization. When these laptops go off-network and can't contact your corporation LDAP server, you want to be able to remove the LDAP Server from your search path (ever notice how your laptop gets the spinning wheel of death when you authenticate off-network? This could be why).
- You run Puppet, Munki, or some other Configuration Management System and would like the laptops to check in whenever they get a network connection. You'd also like them to NOT check in when they're OFF your corporate network
- You have a customized Firefox setup and would like it applied to ANY version of Firefox that users may download (or run - even if it's outside the Applications folder).
- You want to kill your VPN connection before you go to sleep and resume when you wake up.
- You would like to perform an action when you mount or unmount a specific volume.
To get crankd running, you'll need to download Pymacadmin (either from the google site at http://code.google.com/p/pymacadmin or from Github at http://github.com/acdha/pymacadmin) - I recommend downloading a .zip of the source from Github. Running the bundled install-crankd.sh shell script will copy crankd.py into /usr/local/sbin and throw all of the support libraries into /Library/Application Support/crankd.
If you've never used crankd, you can drop into Terminal and run /usr/local/sbin/crankd.py - this will alert you to the fact that you can run crankd.py with the --list-events argument to show all the events to which crankd will respond. It will also create a sample /Library/Preferences/com.googlecode.pymacadmin.crankd.plist file that looks like this image:
- A crankd plist file that responds to network events
- Code to determine whether a network connection is being made or broken
- Code to determine whether we're on or off-network
- Code to call Puppet
- A LaunchDaemon to keep crankd running
Putting it all Together
Now that you've explored all the steps you need to create a working crankd setup, let's piece it together and get something working. This will involve the following:
- Install crankd.
- Install your custom Python libraries.
- Install your crankd plist.
- Run crankd and test it out.
- Install the LaunchDaemon to keep crankd running.
- Packaging and deployment
1. Install crankd.
As I said before, I highly recommend visiting the Pymacadmin Github Repo (http://github.com/acdha/pymacadmin) and clicking the Downloads button to download a .zip of its source. Once you've unzipped the files, you can run the install-crankd.sh script to install crankd to /usr/local/sbin and its libraries to /Libraries/Application Support/crankd.
2. Install your custom Python libraries.
In step 1 in the previous list, we created a crankd plist that calls code from a custom Python library. This custom Python library (and any libraries called in your crankd plist) MUST be located in /Libraries/Application Support/crankd for crankd to be able to access it. Also, pay close attention to naming and capitalization, as it's very important. Let's look at the method call in my crankd plist:
<key>method</key>
<array>
<string>CrankTools</string>
<string>OnNetworkLoad</string>
</array>
The two strings in this method call refer to a Class name and then a Method name in our Python file. It's important that the filename and the class name be identical (down to capitalization) for this to work. As such, my file is called CrankTools.py and I've dropped a gist of it below. The specific method that's being called by crankd is named OnNetworkLoad and takes arguments of "self" as well as two catch-all arguments of "*args" and "**kwargs". When crankd notices a state change (in my case, it's the IPv4 change on the en0 and en1 interfaces), it will run all the code in the OnNetworkLoad method. Take a look below:
3. Install your crankd plist.
In step 1 on the previous list we created a plist file that crankd uses to respond to system events. It makes sense that this file should be installed in a logical place - /Library/Preferences and NOT ~/Library/Preferences - as its going to be called by crankd. I usually name this file com.huronhs.crankd.plist (substituting your organization name for huronhs), but beware as the launchd plist we create later will have the SAME name. You can choose to name them differently or keep them the same (as they're going to be located in separate places on your hard drive), but be sure you can distinguish between the two.
4. Run crankd and test it out.
It's now time to make sure everything works! Let's open up a Terminal window and type the following (should all be on one line, disregard Posterous' formatting):
sudo /usr/local/sbin/crankd.py --config=/Library/Preferences/com.huronhs.crankd.plist
If everything goes well, you should see that crankd is responding to all events you've specified in your plist. The cool thing about crankd is that it will automatically reload if you change its plist or the method that it's watching in a custom Python library. This is handy if you need to debug on-the-fly.
Make sure you test EVERYTHING that will happen to these computers! Pull the ethernet cable out, connect to an ethernet AND wireless network, put the machine to sleep, do a forced restart, connect with a non-working IP address, and etc... You may find that you need to tweak your code to account for certain events.
Once you're happy with your code and crankd setup, you can proceed to the next step...
5. Install the LaunchDaemon to keep crankd running.
We created a launchd plist in step 5 on the previous list, but now we need to install and load it. Install your launchd plist to /Library/LaunchDaemons - in this case mine is called com.huronhs.crankd.plist but you can rename it as you like. To load the LaunchDaemon, drop into Terminal and run this:
sudo launchctl load /Library/LaunchDaemons/com.huronhs.crankd.plist
This will load and start the launchd plist which should keep crankd running. There are MANY ways to test if crankd is currently running - a simple method is to open Activity Monitor.app and search for a Python process that was started by launchd.
6. Packaging and Deployment
There are a couple of ways to bundle up the code and all your custom libraries. The first method is to create a .pkg file with everything installed. You can use Packagemaker, Iceberg, JAMF Composer, or any number of tools to do this. I HIGHLY recommend using a piece of software called The Luggage (http://luggage.apesseekingknowledge.net/) which allows you to utilize makefiles for package creation. It allows for code review and you can quickly change any mistakes if you need to. It does require a bit of technical knowledge, but it's not terribly difficult at all (if you're writing Python code, you can/should use The Luggage).
A second method is to use a Configuration Management tool like cfengine, Chef, Puppet, and etc... to deploy crankd and all your config files. I DO use Puppet, so here's a copy of what my crankd manifest looks like:
In Closing...
There are many uses for crankd and not too many limits. It DOES require you to be comfortable with Python, but as a sysadmin you should have that skill in your bag already. I urge you to give it a shot and email me with any questions!
