Ohai, new Ohai plugins!

Extending Chef

When you start to use the Chef configuration management system, you will quickly encounter a tool it ships with called Ohai which collects information about the underlying system to expose to the Chef Client as attributes during its run. These attributes allows you to easily incorporate system-specific behavior into your Chef cookbooks, for example allowing you to create a single “package” resource in your cookbook which automatically detects whether to install packages from Yum when running on a Redhat based Linux distribution or from Apt when running on a Debian based distribution.

All of Ohai’s attribute collection behavior is defined in a collection of plugins, which are shipped with Ohai out of the box. These plugins allow it to collect information including:

  • The name and version of the operating system being used
  • The hostname of the server
  • The installed version of various programming languages

The flexibility of Ohai however lies in the fact that it also allows us to define our own plugins to extend the rich collection of attributes we collect about our systems. For example at Etsy, we use an Ohai plugin to collect information on the datacenter a particular server is situated in to allow our Chef cookbooks to configure the correct DNS and LDAP server for it to use.

To our cookbooks, this data is visible as the node[:datacenter] attribute, while under the hood the Ohai plugin queries our asset management system to locate and store this information – it allows us to easily capture this information to use in our recipes, without having to worry about how to collect it once we had created the plugin.

These plugins are written using a subset of Ruby known as a Domain Specific Language, or DSL, in which Ohai defines the necessary syntax and behavior of its plugins. Since early 2011, Ohai has shipped with version 6 of its plugin DSL – here’s an example of a plugin written using v6:

provides "awesome"
awesome Mash.new
awesome[:sauce] = "Sricacha"
awesome[:level] = "11"

As we see here, our plugin class we see here is a single monolithic Ruby file which contains no method definitions, which adds a new attribute Mash called “awesome” and creates two new keys called “sauce” and “level”. The v6 DSL we see in the above plugin is simple, works perfectly well, and has served Ohai well for over 3 years now – however it also has a number of weaknesses:

  • Plugins are treated as single blocks of code
    As v6 plugin files are treated as a single block of code, platform or operating system dependent logic often has to be manually configured or split into separate plugin classes.
  • Extending v6 plugins is sometimes hard
    If we want to extend an attribute defined in a v6 plugin by adding extra sub-attributes to it, we must require the plugin class which defines that attribute as we would require any other Ruby class – this requires locating the filename of the class which implements the plugin we want to extend, and requiring that. Particularly if extending stock ohai plugins which are often tucked away inside an installed Rubygem, this can often be a slightly tricky process.

To try and solve these issues, Chef are introducing version 7 of Ohai which is currently due to ship with Chef 11.12.0 at the end of April 2014. Ohai version 7 introduces a new DSL which is far more modular and usable with todays heterogenous infrastructures. Let’s take a look at these v6 plugin example we saw above, but using the v7 DSL:

Ohai.plugin(:Awesome) do
  provides "awesome"
  collect_data(:default) do
    awesome Mash.new
    awesome[:level] =  100
    awesome[:sauce] = "Sriracha"
  end
end

On the face of it, this new DSL is visually quite similar to the v6 DSL, but there are in fact some important differences:

The collect_data method

Rather than Ohai plugins being defined as a single monolithic file as in v6, in v7 plugins are now defined as modular Ohai.plugin blocks as we see above. Inside this block, the main entry point to our plugin which will be called by ohai is the collect_data method.
In the example we see above, we define a single collect_data method with the symbol :default passed as a parameter. On the face of it, this doesn’t actually help that much in solving the problems with v6 which we discussed earlier, but let’s look at how we would define a plugin which implements platform specific behaviour:

Ohai.plugin(:Awesome) do
  provides "awesome".
  def create_objects
    awesome Mash.new
  end
  collect_data(:default) do
    awesome Mash.new
    awesome[:level] =  100
    awesome[:sauce] = "Sriracha"
  end
  collect_data(:darwin) do
    create_objects
    awesome[:level] =  50
    awesome[:sauce] = "Cholula"
  end
end

Here we have defined two collect_data methods – one with the symbol of default as its parameter, and one with the symbol :darwin. When Ohai executes v7 plugins, it always looks to see if a specific collect_datamethod has been defined for the platform of the current node, falling back to the collect_data(:default) method if it cannot find one.
This means that if we were to run the above plugin on a Mac OS X machine, the collect_data(:darwin) method would be executed, but on a Linux system the :default method would be used. This behavior allows us to implement multi-platform Ohai plugins within the same class, which has the added benefit of allowing the platform specific collect_data methods to share as much code as possible, as we see here with the create_object smethod.

Ohai v7 currently supports defining collect_data methods for the following platforms:

:aix, :darwin, :hpux, :linux, :freebsd, :openbsd, :netbsd, :solaris2, :windows

Attribute-name based plugin loading

The other major feature introduced in Ohai v7 is the way in which we extend attributes defined by existing plugins. In Ohai version 6, if we wanted to extend our awesome attribute, we would have to manually require the file which defined that attribute like this:

require '/etc/ohai_handlers/awesome.rb'

In Ohai v7 however, it is now possible to extend attributes simply by specifying the name of the attribute we wish to extend using a depends statement like this:

depends 'awesome'

This drastically reduces the amount of work needed to extend an attribute defined by an existing Ohai plugin, especially plugins installed by default with Ohai and tucked away inside your Rubygems installation directory.

As mentioned above, Ohai v7 is currently scheduled for release with Chef 11.12.0 around the end of April 2014. If you’d like to experiment with Ohai v7 before then, you can read more about the new DSL on the Chef Documentation Site, and install a special version of the latest release of Chef client which includes Ohai v7 by running this command:

gem install chef -v 11.10.4.ohai7.0 --pre

The new features introduced with Ohai v7 drastically lower the barrier to entry for implementing complex plugins, an increasingly common occurrence in today’s fast moving world where ever more complex infrastructure is often no longer hosted with a single provider and application stacks make use of many disparate technologies and platforms. By introducing Ohai v7, Chef, Inc. are ensuring that the Chef platform remains current and relevant in today’s fast moving and ever changing Operations landscape.


To learn more about Ohai plugins, and other ways of customizing Chef, check out Jon Cowie’s new book Customizing Chef, now available in Early Release.

tags: , ,