Read Pro Puppet Online

Authors: Jeffrey McCune James Turnbull

Pro Puppet (52 page)

BOOK: Pro Puppet
6.97Mb size Format: txt, pdf, ePub
ads
reports=store,log,summary

After we restarted the Puppet master and performed a Puppet run, the new report would be generated. In our case, the final report is contained in a file called
summary.txt
and looks something like this:

Changes:  
            Total: 1
Events:
          Success: 1
            Total: 1
Resources:
          Changed: 1
      Out of sync: 1
            Total: 8
Time:
   Config retrieval: 0.19
             File: 0.05
       Filebucket: 0.00
         Schedule: 0.00

Tip
You can see other examples of how to use and extract reporting data from the code of the existing reports, at
https://github.com/puppetlabs/puppet/tree/master/lib/puppet/reports

Summary

In this chapter, we've demonstrated the basics of Puppet reporting, including how to configure reporting and some details on each report type and its configuration.

We've also shown you how to create custom reports of your own, making use of the report data in its YAML form or via processing with a custom report processor.

Resources
C H A P T E R  10

Extending Facter and Puppet

Among the most powerful features of Puppet are its flexibility and extensibility. In addition to the existing facts, resource types, providers, and functions, you can quickly and easily add custom code specific to your environment or to meet a particular need.

In the first part of this chapter we're going to examine how to add your own custom facts. Adding custom facts is highly useful for gathering and making use of information specific to your environment. Indeed, we've used Facter extensively in this book to provide information about our hosts, applications and services, and you've seen the array of facts available across many platforms. You may have noted, though, that Facter isn't comprehensive; many facts about your hosts and environments are not available as Facter facts.

In the second part of the chapter, we're going to examine how to add your own custom types, providers and functions to Puppet and how to have Puppet distribute these, and we'll discuss how to make use of them. These are among Puppet's most powerful features, and are at the heart of its flexibility and extensibility. Being able to add your own enhancements in addition to the existing resources types, providers and functions, you can quickly and easily add custom code specific to your environment or to meet a particular need.

Writing and Distributing Custom Facts

Creating your own custom facts to Puppet is a very simple process. Indeed, it only requires a basic understanding of Ruby. Luckily for you, Ruby is incredibly easy to pick up and there are lots of resources available to help (refer to the “Resources” section at the end of the chapter for some helpful links).

In the following sections, you'll see how to successfully extend Facter. We first configure Puppet so we can write custom facts, then we test our new facts to confirm they are working properly.

Note
If the idea of learning any Ruby is at all daunting, a fast alternative way to add a fact without writing any Ruby code is via Facter's support of environmental variables. Any environmental variables set by the user Facter is running as (usually the
root
user) that are prefixed with
FACTER_
will be added to Facter as facts. So, if you were to set an environmental variable of
FACTER_datacenter
with a value of
Chicago
, then this would become a fact called
datacenter
with the value of
Chicago
.

Configuring Puppet for Custom Facts

The best way to distribute custom facts is to include them in modules, using a Puppet concept called “plug-ins in modules.” This concept allows you to place your custom code inside an existing or new
Puppet module and then use that module in your Puppet environment. Custom facts, custom types, providers, and functions are then distributed to any host that includes a particular module.

Modules that distribute facts are no different from other modules, and there are two popular approaches to doing so. Some people distribute facts related to a particular function in the module that they use to configure that function. For example, a fact with some Bind data in it might be distributed with the module you use to configure Bind. This clusters facts specific to a function together and allows a greater portability. Other sites include all custom facts (and other items) in a single, central module, such as a module called
facts
or
plugins
. This centralizes facts in one location for ease of management and maintenance.

Each approach has pros and cons and you should select one that suits your organization and its workflow. We personally prefer the former approach because it limits custom facts and other items to only those clients that require them, rather than all hosts. For some environments, this may be a neater approach. We're going to use this approach in this section when demonstrating managing custom facts.

So where in our modules do facts go? Let's create a simple module called
bind
as an example:

bind/
bind/manifests
bind/manifests/init.pp
bind/files
bind/templates
bind/lib/facter

Here we've created our standard module directory structure, but we've added another directory, lib. The lib directory contains any “plug-ins” or additional facts, types or functions we want to add to Puppet. We're going to focus on adding facts; these are stored in the
lib/facter
directory.

In addition to adding the
lib/facter
directory to modules that will distribute facts, you need to enable “plug-ins in modules” in your Puppet configuration. To do this, enable options in the
[main]
section of the Puppet master's
puppet.conf
configuration file, as you can see on the next line:

0
 [main]
pluginsync = true

When set to true, the
pluginsync
setting turns on the “plug-ins in modules” capability. Now, when clients connect to the master, each client will check its modules for facts and other custom items. Puppet will take these facts and other custom items and sync them to the relevant clients, so they can then be used on these clients.

Caution
The sync of facts and other items occurs during the Puppet run. In some cases, the custom items synchronized may not be available in that initial Puppet run. For example, if you sync a fact during a Puppet run and rely on the value of that fact in configuration you are using in the SAME run, then that configuration may fail. This is because Puppet has yet to re-run Facter and assign a value for the new custom fact you've provided. On subsequent runs, the new fact's value will be populated and available to Puppet.

Writing Custom Facts

After configuring Puppet to deliver our custom facts, you should actually create some new facts! Each fact is a snippet of Ruby code wrapped in a Facter method to add the result of our Ruby code as a fact. Let's look at a simple example in
Listing 10-1
.

Listing 10-1.
Our first custom fact

Facter.add("home") do
       setcode do
           ENV['HOME']
       end
end

In this example, our custom fact returns the value of the
HOME
environmental value as a fact called
home
, which in turn would be available in our manifests as the variable
$home
.

The
Facter.add
method allows us to specify the name of our new fact. We then use the
setcode
block to specify the contents of our new fact, in our case using Ruby's built-in
ENV
variable to access an environmental variable. Facter will set the value of our new fact using the result of the code executed inside this block.

In
Listing 10-2
, you can see a custom fact that reads a file to return the value of the fact.

Listing 10-2.
Another custom fact

Facter.add("timezone") do
       confine :operatingsystem => :debian
       setcode do
            File.readlines("/etc/timezone").to_a.last
       end
end

Here, we're returning the timezone of a Debian host. We've also done two interesting things. First, we've specified a
confine
statement. This statement restricts the execution of the fact if a particular criteria is not met. This restriction is commonly implemented by taking advantage of the values of other facts. In this case, we've specified that the value of the
operatingsystem
fact should be Debian for the fact to be executed. We can also use the values of other facts, for example:

confine :kernel => :linux

The previous
confine
is commonly used to limit the use of a particular fact to nodes with Linux-based kernels.

Second, we've used the
readlines
File method to read in the contents of the
/etc/timezone
file. The contents are returned as the fact
timezone
, which in turn would be available as the variable
$timezone
.

timezone => Australia/Melbourne

We've established how to confine the execution of a fact but we can also use other fact values to influence our fact determination, for example:

Facter.add("timezone") do
       setcode do
         if Facter.value(:operatingsystem) =~ /Debian|Ubuntu/
            File.readlines("/etc/timezone").to_a.last
         else
            tz = Time.new.zone
         end
       end
end

Here, if the operating system is Debian or Ubuntu, it will return a time zone value by returning the value from the
/etc/timezone
file. Otherwise, the fact will use Ruby's in-built time handling to return a time zone.

You could also use a case statement to select different fact values, for example as used in the
operatingsystemrelease
fact shown in
Listing 10-3
.

Listing 10-3.
Using a case statement to select fact values

Facter.add(:operatingsystemrelease) do
    confine :operatingsystem => %w{CentOS Fedora oel ovs RedHat MeeGo}
    setcode do
        case Facter.value(:operatingsystem)
        when "CentOS", "RedHat"
            releasefile = "/etc/redhat-release"
        when "Fedora"
            releasefile = "/etc/fedora-release"
        when "MeeGo"
            releasefile = "/etc/meego-release"
        when "OEL", "oel"
            releasefile = "/etc/enterprise-release"
        when "OVS", "ovs"
            releasefile = "/etc/ovs-release"
        end
        File::open(releasefile, "r") do |f|
            line = f.readline.chomp
            if line =~ /\(Rawhide\)$/
                "Rawhide"
            elsif line =~ /release (\d[\d.]*)/
                $1  
            end
        end
    end
end

You can use other fact values for any purpose you like, not just for determining how to retrieve a fact. Some facts return another fact value if they cannot find a way to determine the correct value. For example, the
operatingsystem
fact returns the current kernel,
Facter.value(:kernel)
, as the value of
operatingsystem
if Facter cannot determine the operating system it is being run on.

You can create more complex facts and even return more than one fact in your Ruby snippets, as you can see in
Listing 10-4
.

Listing 10-4.
A more complex fact

      netname = nil
       netaddr = nil
       test = {}
       File.open("/etc/networks").each do |line|
            netname = $1 and netaddr = $2 if line 
=~ /^(\w+.?\w+)\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/
            if netname != nil && netaddr != nil
                 test["network_" + netname] = netaddr
                 netname = nil
                 netaddr = nil
                 end
       end
          test.each{|name,fact|
                 Facter.add(name) do
                     setcode do
                        fact
                     end
                 end
       }
BOOK: Pro Puppet
6.97Mb size Format: txt, pdf, ePub
ads

Other books

In Memory of Angel Clare by Christopher Bram
Monster by Gadziala, Jessica
Traitor by Claire Farrell
Birds of Prey by David Drake
The Silencers by Donald Hamilton