Pro Puppet (46 page)

Read Pro Puppet Online

Authors: Jeffrey McCune James Turnbull

BOOK: Pro Puppet
13.01Mb size Format: txt, pdf, ePub

The
hostclass
method is equivalent to the Puppet
class motd_location { … }
syntax and defines the new class. This
motd_location
class carries out three actions:

  • Assigns a local ruby variable named location
  • Assigns a local ruby variable named
    motd_content
  • Declares a file resource in the configuration catalog

Using the
scope.lookupvar
method, the developer obtains the value of the
enc_location
string set by the ENC. When accessing parameters set by the ENC, by Facter, or in the node definitions of
site.pp
,
scope.lookupvar
should be used to obtain the value. Assigning the value to a local variable also has the benefit of bringing the value into the local scope.

To define the contents of
/etc/motd
, the developer assigns another string variable, substituting the value of the
location
variable. In Ruby the
#{}
statement performs substring substitution and replaces the value of the variable contained inside the curly braces.

Finally, the Puppet file resource is declared using a similar syntax to the Puppet syntax. The
file
method is called, specifying the title of the resource as the first argument. In addition, a list of the properties of the file is also specified. This file method declares a file resource and is equivalent to
file { motd: … }
in Puppet syntax.

Testing the Ruby DSL

With this module and node classification configured, the developer is ready to test the Ruby DSL as shown in
Listing 8-25
.

Listing 8-25.
Testing the Ruby DSL with the motd_location class

$ puppet apply --noop /etc/puppet/manifests/site.pp
--- /etc/motd   2011-02-24 01:20:12.000000000 -0500
+++ /tmp/puppet-file20110224-19081-ekisu3-0     2011-02-24 01:41:41.000000000 -0500
@@ -0,0 +1 @@
+This system is in: Florida
notice: /Stage[main]/Motd_location/File[motd]/content: is
 {md5}d41d8cd98f00b204e9800998ecf8427e, should be {md5}3f9e49a378a930da4e06760635fcb810 (noop)

As we can see from the output of Puppet, the
/etc/motd
file would have a single line added containing the value of the
enc_location
parameter. This parameter was set by the ENC and declared in the
motd_location
module using the Ruby DSL.

Account Information from an ENC

With a basic module in place, the developer decides to extend the ENC script. The extended ENC script provides all of the information about the accounts to manage. This information will be provided and stored in a Hash data type, which is also new in Puppet 2.6. Once the data is defined, a new module named
accounts_ruby
will declare resources from the data. To accomplish this, the developer will iterate
over all of the information set by the ENC and declare user resources similar to the file resource declared in the
motd_location
module.

If the developer used the Puppet DSL instead of the Ruby DSL this task would be particularly difficult. The Puppet language does not have loops and cannot easily iterate over a set of data. Let's see how the developer solves this problem in
Listing 8-26
. First, the extended ENC script produces output containing the account information:

Listing 8-26.
ENC script with account information

$ /etc/puppet/resources_enc.rb
--
parameters:
  enc_location: Florida
  account_resources:
    alice:
      groups:
      - sudo
      - sudo_nopw
      - devel
      comment: Alice
      gid: 601
      uid: 601
      shell: /bin/bash
      password: "!!"
      home: /home/alice
    bob:
      groups:
      - sudo
      - sudo_nopw
      - ops
      comment: Bob
      gid: 602
      uid: 602
      shell: /bin/zsh
      password: "!!"
      home: /home/bob
classes:
- motd_location
- accounts_ruby

The output of the ENC script in
Listing 8-26
now contains considerably more information. Notice a second class named
accounts_ruby
has been added. In addition, a new parameter named
account_resources
contains a Hash key for each user account to be created. The value of the key is itself a Hash containing each parameter of the account resource. The ENC script producing this node classification is shown in
Listing 8-27
.

Listing 8-27.
ENC script for Ruby DSL accounts module

$ cat /etc/puppet/resources_enc.rb
#!/usr/bin/env ruby
#
# Load the YAML library in ruby. Provide the to_yaml method for all
# Ruby objects.
require 'yaml'
# The output hash. Must contain the "parameters" and "classes" key.
# See: http://docs.puppetlabs.com/guides/external_nodes.html
@out = Hash.new
# Output Array of classes, Hash of Parameters
@out["classes"]    = Array.new
@out["parameters"] = Hash.new
# Add the motd_location class to the catalogs
@out["classes"] << "motd_location"
# And, add the accounts_ruby class to the catalog
@out["classes"] << "accounts_ruby"
# Add a location parameter
@out["parameters"]["enc_location"] = "Florida"
# Store account information dynamically in the account_resources
# parameter. These values could come from LDAP, SQL, etc...
@out["parameters"]['account_resources'] = Hash.new
@out["parameters"]['account_resources']["alice"] = {
  "comment"  => "Alice",
  "home"     => "/home/alice",
  "uid"      => 601,
  "gid"      => 601,
  "groups"   => [ "sudo", "sudo_nopw", "devel" ],
  "shell"    => "/bin/bash",
  "password" => "!!",
}
@out["parameters"]['account_resources']["bob"] = {
  "comment"  => "Bob",
  "home"     => "/home/bob",
  "uid"      => 602,
  "gid"      => 602,
  "groups"   => [ "sudo", "sudo_nopw", "ops" ],
  "shell"    => "/bin/zsh",
  "password" => "!!",
}
puts @out.to_yaml
exit(0)

This ENC script performs the following actions:

  • Loads the YAML ruby library providing the
    to_yaml
    method
  • Defines a hash named
    @out
    with two keys:
    classes
    and
    parameters
  • Adds the
    account_resources
    parameter to the parameter hash
  • Adds the account information for Bob and Alice to the
    account_resources
    hash
  • Puts the
    @out
    output hash as a YAML string to standard output
  • Exits with a status code of 0 indicating to Puppet that node classification is successful
Accounts Ruby DSL Module

With the account information defined by the ENC in a parameter named
account_resources
, the developer then writes the
accounts_ruby
class. This class declares user resources for all of the accounts. Once the developer writes the class new accounts only need to be added to node classification for Puppet to manage them. The Puppet module and manifests themselves need not be modified as people join the organization. This implementation cleanly separates code and data. The implementation also allows the developer the freedom to improve the ENC script without modifying Puppet. Information may be retrieved from data sources like the Human Resources directory, LDAP, or an SQL database. The complete Ruby DSL
accounts_ruby
class the developer has written is shown in
Listing 8-28
.

Listing 8-28.
The accounts_ruby class

$ cat /accounts_ruby/manifests/init.rb
# Define a new accounts_ruby class. This is equivalent to:
# class accounts_ruby { ... }
hostclass :accounts_ruby do
  # Bring the accounts resources defined in the ENC into a local
  # Ruby variable.
  accounts = scope.lookupvar("account_resources")
  # Perform a sanity check on the data provided by the ENC.
  raise Puppet::Error,
    "account_resources must be a Hash" unless accounts.kind_of?(Hash)
  # First declare groups required by the accounts. These groups may be
  # referenced in /etc/sudoers to grant sudo access and access without
  # a password entry.
  group([:sudo, :sudo_nopw], :ensure => "present")
  # Iterate over each account
  # The Hash key will be stored in the local title variable
  # The value of the hash entry will be stored in parameters
  # The parameters are the resource parameters for each user account.
  accounts.each do |title, parameters|
    # Some more sanity checking on the data passed in from the ENC.
    raise Puppet::Error,
      "account_resources[#{title}] must be a Hash" unless parameters.kind_of?(Hash)
    # Manage the home directory of this account with a file resource.
    file(parameters["home"],
         :ensure => "directory",
         :owner  => title,
         :group  => title,
         :mode   => 0700)
    # Each account should have a group of the same name.
    group(title,
          :ensure => "present",
          :gid    => parameters["gid"])
    # Declare the user resource with the parameters for this account.
    user(title,
         :ensure     => "present",
         :uid        => parameters["uid"],
         :gid        => parameters["gid"],
         :comment    => parameters["comment"],
         :groups     => parameters["groups"],
         :shell      => parameters["shell"],
         :password   => parameters["password"],
         :home       => parameters["home"],
         :managehome => false)
  end
end

The
accounts_ruby
module class in
Listing 8-28
carries out a number of actions when declared in the Puppet catalog. These actions are:

  • Defines a new Puppet class named
    accounts_ruby
    using the
    hostclass
    method.
  • Sets a local
    accounts
    Ruby variable containing the information set by the ENC in the
    account_resources
    parameter.
  • Validates the data from the ENC is stored in something like a Hash
  • Declares two Group resources,
    sudo
    and
    sudo_nopw
    .
  • Iterates over every account entry and:
  • Declares a file, group and user resource for the account.

The Ruby code composing the
accounts_ruby
module may be a little much to absorb at first. Like Puppet, Ruby code is often quite readable; so let's see how the developer solves the accounts problem. First, he defines a new class named
accounts_ruby
using the
hostclass
method. He passes a Ruby Block to the
hostclass
method. This block will be evaluated when the class is declared in the catalog. Recall from
Listing 8-26
that the ENC script is declaring this class in the
classes
list.

With the new class defined in the
init.rb
file of the module manifests directory, the developer proceeds to bring the data defined in the ENC into the local scope. This is again accomplished with the
scope.lookupvar
method. In addition, the data is validated using the
kind_of?
method. This method returns
true
or
false
if the receiving object is a kind of the specified class. In this case the developer is checking to see if a Hash was actually passed into Puppet by the ENC or not. In the Puppet DSL, the
fail()
function may be used to abort catalog compilation if this check does not pass. In the Ruby DSL, an exception class named
Puppet::Error
is one way to abort catalog compilation if invalid data has been passed in.

With the data validated, the
sudo
and
sudo_pw
groups are declared, just like they would be in a manifest written in Puppet syntax. With the basic requirements established, the developer then uses the Ruby idiom of calling the
each
method on the accounts Hash to iterate over each entry supplied by the ENC. This method also takes a block and executes this block of code once for each entry in the Hash. Inside the block, the hash key and value are stored in the local variables title and parameters, indicating these variables represent the resource title and contain parameters about the resource.

Finally, inside the block the developer declares three resources. First, a file resource manages the home directory of the user account. Next, a new group with the same name as the account is declared. Finally, the user account itself is declared. The parameters for all of these resources are retrieved from the information passed in the ENC script.

Testing the Ruby DSL Accounts Module

Let's see, in
Listing 8-29
, how the
accounts_ruby
module looks when Puppet runs.

Listing 8-29.
Running Puppet with the accounts_ruby module

# puppet apply --verbose --noop /etc/puppet/manifests/site.pp
info: Applying configuration version '1298536173'
notice: /Stage[main]/Accounts_ruby/Group[alice]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/User[alice]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/File[/home/alice]/ensure: is absent, should be directory
 (noop)
notice: /Stage[main]/Accounts_ruby/Group[bob]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/User[bob]/ensure: is absent, should be present (noop)
notice: /Stage[main]/Accounts_ruby/File[/home/bob]/ensure: is absent, should be directory (noop)

Other books

Dead Man Living by Carol Lynne
More Than Friends by Jessica Jayne
Codename Spring by Aubrey Ross
Whispering Wishes by Miller, Jennifer
The French Bride by Evelyn Anthony
Run to Him by Nadine Dorries
My Notorious Life by Kate Manning
Betting Against the Odds by Morgan, Sabrina