Custom Puppet Facts

I really like the idea of using Puppet and similar tools for automating as much of server configuration as possible. As I slowly “puppet-ify” things I find the need to sometimes add custom facts to facter, the part of Puppet that provides information about the host system.

This would be easy were it not for the small problem that custom facts are written in Ruby, a language I’m not fluent in, although this gives me a reason to learn the basics. I’m familiar with various other languages so it shouldn’t be that hard, here’s what I’ve managed to cobble together so far…

The first fact returns a list of the Linux software RAID block devices on the host. This is just parsing the /proc/mdstat file that should exist on any Linux distribution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Facter.add('software_raid') do
  confine :kernel => :linux
  setcode do
    devices = []
    if FileTest.exists?('/proc/mdstat')
      File.open('/proc/mdstat', 'r') do |f|
        while line = f.gets
          if line =~ /^(md\d+)/
            devices.push($1)
          end
        end
      end
    end
    devices.sort.join(',')
  end
end

This next recipe creates one fact per bonded network interface on Linux, containing the list of enslaved interfaces. This is specific to CentOS, Fedora, Red Hat or any other similar distributions that use the /etc/sysconfig configuration files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require 'find'
 
Facter.value(:interfaces).split(',').each do |interface|
  if interface =~ /^bond\d+$/
    Facter.add("#{interface}_slaves") do
      confine :kernel => :linux, :operatingsystem => %w{CentOS Fedora RedHat}
      setcode do
        slaves = []
        Find.find('/etc/sysconfig/network-scripts') do |path|
          if FileTest.directory?(path)
            next
          else
            if path =~ /ifcfg-(.+)$/
              device = $1
              File.open(path, 'r') do |f|
                while line = f.gets
                  if line =~ /^MASTER\s*=\s*#{interface}$/
                    slaves.push(device)
                  end
                end
              end
            end
          end
        end
        slaves.sort.join(',')
      end
    end
  end
end

I use these two facts within my Puppet manifests as the basis for configuring additional Nagios tests to make sure these two pieces of functionality are working correctly.

Tags: , ,

3 Responses to “Custom Puppet Facts”

  1. Hello,

    Thank you for the nice post!

    I decided to improve upon your bonding Facter fact to make it less dependent on a particular Linu distribution.

    The code is available here:

    https://github.com/kwilczynski/facter-facts/blob/master/bonding.rb

    Let me know whether it works for you and what you think! 🙂

    KW

    • matt says:

      Thanks for the reply.

      Your code logic looks fine, although I don’t have a machine with bonding to hand to test it with. I think /sys is maybe preferred to /proc these days by the Linux people but /proc isn’t going to disappear anytime soon.

      Matt

  2. Hello,

    You are right about /sys, although it is a little bit of a pain to work with an /proc was convenient.

    I will look towards a possible re-factor.

    Said that, /proc is a little bit like IPv4 — here to stay for some time 🙂

    KW

Leave a Reply