Pro Puppet (31 page)

Read Pro Puppet Online

Authors: Jeffrey McCune James Turnbull

BOOK: Pro Puppet
3.98Mb size Format: txt, pdf, ePub
Adding the Puppet Schema

Now we need to add the Puppet schema to our LDAP directory's configuration.

Caution
You may need to tweak or translate the default LDAP schema for some directory servers, but it is suitable for OpenLDAP.

The Puppet schema document is available in the Puppet source package in the
ext/ldap/puppet.schema
file, or you can take it from the project's Git repository at
https://github.com/puppetlabs/puppet/blob/master/ext/ldap/puppet.schema
.

We need to add it to our schema directory and
slapd.conf
configuration file. For example, on an Ubuntu or Debian host, the schema directory is
/etc/ldap/schema
, and the
slapd.conf
configuration is located in the
/etc/ldap
directory. On Red Hat, the configuration file is located in /etc/openldap and the schemas are located in /etc/openldap/schema. Copy the
puppet.schema
file into the appropriate directory, for example on Ubuntu:

$ cp puppet/ext/ldap/puppet.schema /etc/ldap/schema

Now you can add an
include
statement to your
slapd.conf
configuration file; there should be a number of existing statements you can model:

include        /etc/ldap/schema/puppet.schema

Or you can add a schema to a running OpenLDAP server, like so:

$ ldapadd -x -H ldap://ldap.example.com/ -D "cn=config" -W -f puppet.ldif

To update OpenLDAP with the new schema, you may also now need to restart your server.

# /etc/init.d/slapd restart

Now that you've added the schema and configured the LDAP server, you need to tell Puppet to use an LDAP server as the source of its node configuration.

Configuring LDAP in Puppet

LDAP configuration is very simple. Let's look at the required configuration options from the [master] section of the puppet.conf configuration file in
Listing 5-7
.

Listing 5-7.
LDAP configuration in Puppet

[master]
node_terminus = ldap
ldapserver = ldap.example.com
ldapbase = ou=Hosts,dc=example,dc=com

First, we set the
node_terminus
option to
ldap
to tell Puppet to look to an LDAP server as our node source. Next, we specify the hostname of our LDAP server, in this case
ldap.example.com
, in the
ldapserver
option. Lastly, in the
ldapbase
option, we specify the base search path. Puppet recommends that hosts be stored in an OU called
Hosts
under our main directory structure, but you can configure this to suit your environment.

If required, you can specify a user and password using the
ldapuser
and
ldappassword
options and override the default LDAP port of 389 with the
ldapport
option. There is some limited support for TLS or SSL, but only if your LDAP server does not require client-side certificates.

Tip
You can see a full list of the potential LDAP options at
http://docs.puppetlabs.com/references/stable/configuration.html
.

After configuring Puppet to use LDAP nodes, you should restart your Puppet master daemon to ensure that the new configuration is updated.

Now you need to add your node configuration to the LDAP server. Let's take a quick look at the Puppet LDAP schema in
Listing 5-9
.

Listing 5-8.
The LDAP schema

attributetype ( 1.3.6.1.4.1.34380.1.1.3.10 NAME 'puppetClass'
        DESC 'Puppet Node Class'
        EQUALITY caseIgnoreIA5Match
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.4.1.34380.1.1.3.9 NAME 'parentNode'
        DESC 'Puppet Parent Node'
        EQUALITY caseIgnoreIA5Match
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
        SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.34380.1.1.3.11 NAME 'environment'
        DESC 'Puppet Node Environment'
        EQUALITY caseIgnoreIA5Match
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.4.1.34380.1.1.3.12 NAME 'puppetVar'
        DESC 'A variable setting for puppet'
        EQUALITY caseIgnoreIA5Match
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
objectclass ( 1.3.6.1.4.1.34380.1.1.1.2 NAME 'puppetClient' SUP top AUXILIARY
        DESC 'Puppet Client objectclass'
        MAY ( puppetclass $ parentnode $ environment $ puppetvar ))

The Puppet schema is made up of an object class,
puppetClient
, and four attributes:
puppetclass
,
parentnode
,
environment
and
puppetvar
. The object class
puppetClient
is assigned to each host that is a Puppet node. The
puppetclass
attribute contains all of the classes defined for that node. At this stage, you cannot add definitions, just classes. The
parentnode
attribute allows you to specify node inheritance,
environment
specifies the environment of the node, and
puppetvar
specifies any variables assigned to the node.

In addition, any attributes defined in your LDAP node entries are available as variables to Puppet. This works much like Facter facts (see
Chapter 1
); for example, if the host entry has the
ipHost
class, the
ipHostNumber
attribute of the class is available as the variable
$ipHostNumber
. You can also specify attributes with multiple values; these are created as arrays.

You can also define default nodes in the same manner as doing so in your manifest node definitions: creating a host in your directory called
default
. The classes assigned to this host will be applied to any node that does not match a node in the directory. If no default node exists and no matching node definition is found, Puppet will return an error.

You can now add your hosts, or the relevant object class and attributes to existing definitions for your hosts, in the LDAP directory. You can import your host definitions using LDIF files or manipulate your directory using your choice of tools such as phpldapadmin (
http://phpldapadmin.sourceforge.net/wiki/index.php/Main_Page
).

Listing 5-9
is an LDIF file containing examples of node definitions.

Listing 5-9.
LDIF nodes

# LDIF Export for: ou=Hosts,dc=example,dc=com
dn: ou=Hosts,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: Hosts
dn: cn=default,ou=Hosts,dc=example,dc=com
cn: default
description: Default
objectClass: device
objectClass: top
objectClass: puppetClient
puppetclass: base
dn: cn=basenode,ou=Hosts,dc=example,dc=com
cn: basenode
description: Basenode
objectClass: device
objectClass: top
objectClass: puppetClient
puppetclass: base
dn: cn=web,ou=Hosts,dc=example,dc=com
cn: web
description: Webserver
objectClass: device
objectClass: top
objectClass: puppetClient
parentnode: basenode
puppetclass: apache
dn: cn=web1.example.com, ou=Hosts,dc=example,dc=com
cn: web1
description: webserving host
objectclass: device
objectclass: top
objectclass: puppetClient
objectclass: ipHost
parentnode: web
ipHostNumber: 192.168.1.100

This listing includes a
default
node, a node called
basenode
, and a template node called
web
. Each node has particular classes assigned to it, and the
web
node has the
basenode
defined as its parent node and thus inherits its classes also. Lastly, we define a client node, called
web1
, which inherits the
web
node as a parent.

Summary

In this chapter we've explored how you can use both external node classification and the LDAP node terminus. Both of these allow you to scale to larger numbers of nodes without needing to maintain large numbers of nodes in your manifest files. In
Chapter 7
, we'll also look at how you can use Puppet Dashboard or the Foreman dashboard as an external node classifier.

Resources

The following links will take you to Puppet documentation related to external nodes:

C H A P T E R  6

Exporting and Storing Configuration

So far in the book, you've seen how Puppet models configuration on a single host. In many cases, however, you have configuration on multiple hosts that have a relationship; for example, your monitoring system needs to know about configuration on hosts being monitored. In this chapter we look at three features that exist in Puppet to help model resources on multiple hosts: virtual resources, exported resources, and stored configuration.

The first feature, virtual resources, is a method of managing resources where multiple configurations require a resource. For example, a user may be required on some hosts but not others. Virtual resources allow you to define a resource but be selective about where you instantiate that resource.

The second feature, exported resources, allows us to take resources defined on one host and use them on other hosts; for example, it allows us to tell a Puppet-managed load balancer about each of the workers available to it. Puppet collects and stores each of these resources when configuration runs occur, and then it provides these resources and their information to other hosts if they ask.

Lastly, stored configuration provides a mechanism to store these resources. Stored configurations allow Puppet to write resources into a SQL database. This database will then be queried by Puppet and required resources will be collected and included in the configuration catalog.

In this chapter you will learn how to use virtual and exported resources, including how to use the exported resource feature to collect specific resources from stored configuration. We cover a number of use cases, including the automatic management of SSH host keys, automated load balancer re-configuration, and automated monitoring with Nagios.

We demonstrate how to configure Puppet with a SQL server for stored configurations and how to prune old configuration data from the SQL database in order to prevent other systems from collecting stale resources. We also show you how to use message queuing to allow you to better scale your stored configuration environment and how to accommodate a multiple-Puppet-master environment, like we demonstrated in
Chapter 5
.

Virtual Resources

Virtual resources are closely related to the topic of exported resources. Because of the similarity, it's important to cover virtual resources first to provide a foundation for learning about exported resources.

Virtual resources are designed to address the situation where multiple classes require a single resource to be managed. This single resource doesn't clearly “belong” to any one class, and it is cumbersome to break each of these resources out into a unique class. Virtual resources also help solve the problem of duplicate resource declaration errors in Puppet.

To illustrate the problem, consider the Example.com operator. He would like the ability to declare user resources to manage the accounts for his colleagues, but each person should have their account
managed on only some systems. For example, all developer accounts need to be managed on all development and testing systems, while being absent from the production systems. Conversely, the system administrator accounts need to be present on every system. Finally, there are service accounts, e.g., the
apache
and
mysql
users and groups required by multiple Puppet classes, such as the
apache
,
mysql
, and
webapp
classes. The
webapp
class requires the
mysql
and
apache
service accounts, but should not declare the resource itself since the
mysql
class will likely have a conflicting resource declaration.

Virtual resources provide the ability for the Example.com operator to define a large set of user resources in once place and selectively add a smaller subset of those users to the configuration catalog. The operator doesn't need to worry about duplicate resource declarations, because the resources are only declared once and then instantiated, or “realized,” one or more times.

Declaring a virtual resource is easy, just add the
@
character to the beginning of the resource declaration to make the resource virtual. You can then use one of two methods to realize your virtual resources:

  • The “spaceship” syntax <| |>
    1
  • The realize function
Declaring and Realizing a Virtual Resource

Let's see how the Example.com operator might declare and realize the user and service accounts in
Listing 6-1
.

Listing 6-1.
Virtual user resources /accounts/virtual.pp

class accounts::virtual {
  @user { "mysql”:
    ensure => present,
    uid => 27,
    gid => 27,
    home => "/var/lib/mysql”,
    shell => "/bin/bash”,
  }
  @user { "apache”:
    ensure => present,
    uid => 48,
    gid => "apache”,
    home => "/var/www”,
    shell => "/sbin/nologin”,
  }
}

Resources declared virtually will not be managed until they're realized. Simply declaring the
accounts::virtual
class makes these virtual resources available, but is not enough to manage the
mysql
and
apache
user accounts.
Listing 6-2
shoes how the operator makes sure the
mysql
user account is present on the system.

______________________

1
So named because the syntax looks like a spaceship.

Listing 6-2.
Realizing a virtual resource using the spaceship operator

class webapp {
  include accounts::virtual
  package { "webapp": ensure => present }
  User <| title == "mysql" |>
}

In the last line of this
webapp
class, the operator uses the spaceship operator to find the user resource with the title of
mysql
. This syntax specifies a very specific resource to realize, however an error will not be thrown if there is no virtual user resource with the title
mysql
. The spaceship operator is analogous to a search function, where returning no results is perfectly valid. In situations where a specific resource is required, the realize function may be used to generate an error if the virtual resource is not found.

Applying the Realize Function

The
realize
function provides another method to make a virtual resource real. A specific resource identified by the type and title must be passed as an argument to the realize function. This requirement of a specific resource makes the realize function much less flexible than the collection syntax and spaceship operator. The realize function is more appropriate to use when an error should be thrown if the virtual resource has not been declared in the catalog. For example, the operator may want catalog compilation to fail if there is no
mysql
user resource, as you can see in
Listing 6-3
.

Listing 6-3.
The realize() function

class webapp {
  realize(User["mysql")
  package { "webapp":
    ensure => present,
  }
}

The configuration catalog resulting from the
webapp
class defined in
Listing 6-3
is the same as the configuration catalog generated from the
webapp
class shown in
listing 6-2
. We've seen the operator realize a specific resource, the
mysql
user, but how does he handle the situation where he'd like to make a number of virtual resources real? Puppet provides a convenient way to solve this problem without forcing the operator to specify each and every resource by name.

Making Virtual Resources Real

When using the spaceship operator, any parameter may be used to collect resources. This feature allows a large number of relationships to be managed in a concise and clear style. For example, if there are multiple user accounts with a primary group of “apache,” the operator may realize all of them using a single statement:

User <| gid == "apache" |>

So far you've seen how to realize collections of virtual resources using the spaceship operator and specific resources using the
realize
function. A key aspect of the Puppet model is specifying relationships between resources, and we haven't yet discussed how to establish a relationship to a
realized virtual resource. Prior to Puppet 2.6.0, this was very difficult to configure, but new syntax added in version 2.6.0 makes this problem very easy to solve.

In Puppet 2.6.0, resource collections may also have a block associated with them to add additional parameters. When realizing virtual resources, the relationship metaparameters may be specified to ensure the resource is managed in the correct order. Look at
Listing 6-4
to see how the Example.com operator ensures the
mysql
user account is always managed before the
webapp
package.

Listing 6-4.
Specifying parameters in a collection

class webapp {
  User <| title == mysql |> { before => Package["webapp"] }
  package { "webapp":
    ensure => present,
  }
}

As you can see, appending a block containing parameters after the collection will add the parameter to all of the realized resources. This also works for collections that contain many resources, such as:

User <| gid == "apache" |> { before => Package["apache"] }

In addition to a block associated with a collection, Puppet version 2.6.0 and newer also supports a new relationship-chaining syntax. This syntax allows relationships to be declared without using the metaparameters before, require, subscribe and notify as we'll see in the next section.

Relationship-Chaining Syntax

A major new feature in Puppet 2.6.0, the relationship-chaining syntax allows you to replace the before, require, subscribe and notify parameters with arrow operators. These new operators allow relationships to be declared outside of the blocks where resources themselves are declared.

For example, two resources may be declared without any relation to each other, and their relationship established at a later point in time.

define apache::account($ensure=present) {
  user { "apache":
    ensure => $ensure,
    gid => 48
  }
  group { "apache":
    ensure => $ensure,
    gid => 48,
  }
  if ($ensure == present) {
    Group["apache"] -> User["apache"]
  } else {
    User["apache”] -> Group["apache”]
  }
}

Other books

Barbarian Bride by Scott, Eva
Blackstone (Book 2) by Honor Raconteur
The Travel Writer by Jeff Soloway
The Price of Fame by Hazel Gower
The Game by Tom Wood