Read Pro Puppet Online

Authors: Jeffrey McCune James Turnbull

Pro Puppet (15 page)

BOOK: Pro Puppet
4Mb size Format: txt, pdf, ePub
ads

By requiring the whole class, it doesn’t matter how many packages we add to the
ssh::install
class – Puppet knows to install packages before managing configuration files, and we don’t have to update a lot of resources every time we make a change.

Tip
In our current example we could make use of arrays to extend the variables in the
ssh::params
class. For example, by changing
$ssh_package_name
to an array, we could specify multiple packages to be installed without needing to create another Package resource in the
ssh::install
class. Puppet is smart enough to know that if you specify a variable with a value of an array then it should expand the array, so changing the value of the
$ssh_package_name
variable to
[ openssh, package2, package3 ]
would result in the
ssh::install
class installing all three packages. This greatly simplifies the maintenance of our
ssh
module, as we only need to change values in one place to manage multiple configuration items.

The ssh::service Class

Let’s look at our last class,
ssh::service
, and update it to reflect our new practice:

class ssh::service {
  service { $ssh::params::ssh_service_name:
    ensure => running,
    hasstatus => true,
    hasresstart => true,
    enable => true,
    require => Class["ssh::config"],
  }
}

We’ve added our new variable,
$ssh_service_name
, to the
ssh:params
class too:

class ssh::params {
  case $operatingsystem {
    Solaris {
      $ssh_package_name = 'openssh'
      $ssh_service_config = '/etc/ssh/sshd_config'
      $ssh_service_name = 'sshd'
  }

}

Let’s also look at our
Service[$ssh::params::ssh_service_name]
resource (at the start of this section), as this is the first service we’ve seen managed. You’ll notice two important attributes,
ensure
and
enable
, which specify the state and status of the resource respectively. The state of the resource specifies whether the service is running or stopped. The status of the resource specifies whether it is to be started at boot, for example as controlled by the
chkconfig
or
enable-rc.d
commands.

Puppet understands how to manage a variety of service frameworks, like SMF and init scripts, and can start, stop and restart services. It does this by attempting to identify the service framework your platform uses and executing the appropriate commands. For example, on Red Hat it might execute:

$ service sshd restart

If Puppet can’t recognize your service framework, it will revert to simple parsing of the process table for processes with the same name as the service it’s trying to manage. This obviously isn’t ideal, so it helps to tell Puppet a bit more about your services to ensure it manages them appropriately. The
hasstatus
and
hasrestart
attributes we specified in the
ssh::service
class is one of the ways we tell Puppet useful things about our services. If we specify
hasstatus
as true, then Puppet knows that our service framework supports status commands of some kind. For example, on Red Hat it knows it can execute the following:

$ service sshd status

This enables it to determine accurately whether the service is started or stopped. The same principle applies to the
hasrestart
attribute, which specifies that the service has a restart command.

Now we can see Puppet managing a full service, if we include our new
ssh
module in our Puppet nodes, as shown in
Listing 2-5
.

Listing 2-5.
Adding the
ssh
Module

class base {
  include sudo, ssh
}
node 'puppet.example.com' {
  include base
}
node 'web.example.com' {
  include base
}
node 'db.example.com' {
  include base
}
node 'mail.example.com' {
  include base
}

Here we’ve created a class called
base
, in which we’re going to place the modules that will be base or generic to all our nodes. Thus far, these are our
sudo
and
ssh
modules. We then
include
this class in each node statement.

Note
We talked earlier about node inheritance and some of its scoping issues. As we explained there, using a class instead of node inheritance helps avoids these issues. You can read about it at
http://projects.puppetlabs.com/projects/puppet/wiki/Frequently_Asked_Questions#Common+Misconceptions
.

With a basic SSH module in place, and we can now manage the SSH daemon and its configuration.

Creating a Module to Manage Postfix

Let’s now create a module to manage Postfix on
mail.example.com
. We start with a similar structure to our SSH module. In this case, we know which platform we’re going to install our mail server on so we don’t need to include any conditional logic. However, if we had multiple mail servers on different platforms, it would be easy to adjust our module using the example we’ve just shown to cater for disparate operations systems.

postfix
postfix/files/master.cf
postifx/manifests/init.pp
postfix/manifests/install.pp
postfix/manifests/config.pp
postfix/manifests/service.pp
postfix/templates/main.cf.erb
The postfix::install class

We also have some similar resources present in our Postfix module that we saw in our SSH module, for example in the
postfix::install
class we install two packages,
postfix
and
mailx
:

class postfix::install {
  package { [ "postfix", "mailx" ]:
    ensure => present,
  }
}

Note that we’ve used an array to specify both packages in a single resource statement this is a useful shortcut that allows you specify multiple items in a single resource.

The postfix::config class

Next, we have the
postfix::config
class, which we will use to configure our Postfix server.

class postfix::config {
  File {
    owner => "postfix",
    group => "postfix",
    mode => 0644,
  }
  file { "/etc/postfix/master.cf":
    ensure = > present,
    source => "puppet:///modules/postfix/master.cf",
    require => Class["postfix::install"],
    notify => Class["postfix::service"],
  }
  file { "/etc/postfix/main.cf":
    ensure = > present,
    content => template("postfix/main.cf.erb"),
    require => Class["postfix::install"],
    notify => Class["postfix::service"],
  }
}

You may have noticed some new syntax: We specified the File resource type capitalized and without a title. This syntax is called a resource default, and it allows us to specify defaults for a particular resource type. In this case, all File resources within the
postfix::config
class will be owned by the user
postfix
, the group
postfix
and with a mode of
0644
. Resource defaults only apply to the current scope, but you can apply global defaults by specifying them in your
site.pp
file.

A common use for global defaults is to define a global “filebucket” for backing up the files Puppet changes. You can see the filebucket type and an example of how to use it globally at
http://docs.puppetlabs.com/references/stable/type.html#filebucket
.

Tip
A common use for global defaults is to define a global “filebucket” for backing up the files Puppet changes. You can see the filebucket type and an example of how to use it globally at
http://docs.puppetlabs.com/references/stable/type.html#filebucket
.

METAPARAMETER DEFAULTS

Like resource defaults, you can also set defaults for metaparameters, such as
require
, using Puppet variable syntax. For example:

class postfix::config {
  $require = Class["postfix::install"]
  …
}

This would set a default for the
require
metaparameter in the
postfix::config
class and means we could remove all the
require => Class["postfix::install"]
statements from our resources in that class.

We’ve also introduced a new attribute in our
File["/etc/postfix/main.cf"]
resource –
content
. We’ve already seen the
source
attribute, which allows Puppet to serve out files, and we’ve used it in one of our File resources,
File["/etc/postfix/master.cf"]
. The
content
attribute allows us to specify the content of the file resources as a string. But it also allows us to specify a template for our file. The template is specified using a function called
template
.

As previously mentioned, functions are commands that run on the Puppet master and return values or results. In this case, the
template
function allows us to specify a Ruby ERB template (
http://ruby-doc.org/stdlib/libdoc/erb/rdoc/
), from which we can create the templated content for our configuration file. We specify the template like this:

    content => template("postfix/main.cf.erb"),

We’ve specified the name of the function, “template,” and inside brackets the name of the module that contains the template and the name of the template file. Puppet knows when we specify the name of the module to look inside the
postfix/templates
directory for the requisite file – here,
main.cf.erb
.

THE REQUIRE FUNCTION

In addition to the
include
function, Puppet also has a function called
require
. The
require
function works just like the
include
function except that it introduces some order to the inclusion of resources. With the
include
function, resources are not included in any sequence. The only exception is individual resources, which have relationships (using metaparameters, for example) that mandate some ordering. The
require
function tells Puppet that all resources being required must be processed first. For example, if we specified:

class ssh {
require ssh::params
include ssh::install, ssh::config, ssh::service
}

then the contents of
ssh::params
would be processed before any other includes or resources in the
ssh
class. This is useful as a simple way to specify some less granular ordering to your manifests than metaparameter relationships, but it’s not
recommended as a regular approach. The reason it is not recommended is that Puppet does this by creating relationships between all the resources in the required class and the current class. This can lead to cyclical dependencies between resources. It’s cleaner, more elegant and simpler to debug if you use metaparameters to specify the relationships between resources that need order.

BOOK: Pro Puppet
4Mb size Format: txt, pdf, ePub
ads

Other books

Turkey Day Murder by Leslie Meier
A Life Sublime by Billy London
Very Bad Poetry by Kathryn Petras
The Shaman: And other shadows by Manzetti, Alessandro
The Remains of the Day by Kazuo Ishiguro
Refuge: Kurt's Quest by Doug Dandridge
Talk of the Town by Suzanne Macpherson