Puppet Data Centre Automation Solution, Part 4: Classes & Modules

Understanding Puppet classes and modules

We learned about some of the interesting ways Puppet helps automate things in Part 3. In the final part of this series, we look at the rules to follow to effectively organise Puppet configurations.

Classes and modules are the two main principles/disciplines that Puppet Labs asks any admin to follow while setting up configuration management through Puppet. This provides a structure to configuration management administration.

Class

A class is a name for grouping resources for a common goal or purpose. Why class? Well, it can inherit from other classes and have sub-classes too! Even in Puppet, we have the flexibility of inheritance. Yes, you heard me right — inheritance!

A class is introduced by the class keyword, and the contents are wrapped in curly brackets. How would you use it to group all similar resources into one category? Take the example of installing the Apache Web service in Ubuntu. We cannot install this service unless a package is installed. So, if we look at the following configuration, it specifies that a service named httpd (the application name might be different in different Linux distros) must be running, for which a package called apache2 needs to be installed:

class apache
{
    Package
    {
        apache2: ensure => installed 
    }
    Service
    {
        httpd: ensure => running, 
        require => Package["apache2"],
    }
}

Also, a problem with just using resources without grouping them is that they get applied to every client. We do not get the flexibility to classify the configuration management on the basis of an operating system, server, package, architecture, etc. Think of it as giving special attention to a specific student, or set of students, in a classroom, when that same attention need not be given to others who have already understood a certain subject.

In the earlier configuration example, how would we handle an Apache service installation for two different GNU/Linux operating systems like Ubuntu and Fedora? Let us look at two classes — apache and apache_installer.

The apache class

This class, defined in the file Apache.pp, says what service needs to be running, and that it needs to depend on the corresponding apache package. I also use a method to get the value from the sub-class that would be the package name: $title is what reads the parameter:

class apache
{
    define install_apache
    {
        $mypackage = $title
        Package
        {
            $mypackage: ensure => installed 
        }
        Service
        {
            httpd: ensure => running, 
            require => Package[$mypackage],
        }
    }
}

The apache_installer class

This class (in a file called, Installer.pp, for instance), depending on the type of OS, would define which package needs to be installed. It would pass the package as a parameter to the earlier method in the parent class.

import "apache.pp"
class apache_installer inherits apache
{
    case $operatingsystem
    {
        fedora: { apache::install_apache {"httpd":} }
        CentOS: { apache::install_apache {"httpd":} }
        ubuntu: { apache::install_apache {"apache2":} }
    }
}

For import, the .pp extension is not actually required, but I used it because I wanted to differentiate between the apache.pp file and the apache class (which is being inherited by the apache_installer class) inside the apache.pp file. You can always name your file and class at will. The only requirement is (obviously) the correct file in which the class is defined must be imported, before the class can be inherited.

Now if you look at the two classes above, in Fedora the httpd service will start after it verifies whether an httpd package has already been installed (and if not, it will install the package using YUM). In Ubuntu, on the other hand, it will ensure that a package named apache2 is installed. Simple, right?

How do we enable the class? First, import the class (in the above case, installer or installer.pp) with the folder path (if you have used a sub-folder to organise class files) into the site.pp or init.pp file. Then use the keyword include to call the class name (in our case, it would be apache_installer).

The main job is done by the definition inside the first class. We define a method/function to take arguments. Definitions can reuse resources inside a class, but cannot be inherited outside the class, or within another definition of the same class. One of the main reasons why we use a definition is because it has the power to use the values we pass as arguments to the variables $title and $name, which a class or any other resource cannot handle.

Note: Puppet will look for all possible automated installers like YUM, apt-get and other packages, depending on the operating system on which it is running, before it gives up (because it couldn’t find
an automated installer). The preferred option is YUM — it is best supported by Puppet.

We can use classes to even override a certain part of the class, for specific cases. For example, all UNIX-like OSs would have a common setting, but all FreeBSD operating systems could have a certain group overridden, as in the following example:

class unix
{
 file
 {
  '/etc/passwd':
     owner => 'root',
     group => 'root',
     mode => 440
 }
file
 {
  '/etc/shadow':
     owner => 'root',
     group => 'root',
     mode => 440
 }
}
class freebsd inherits unix
{
  File['/etc/passwd','/etc/shadow'] [group => wheel]
}

Module

A module could be:

  • a set of classes, definitions and resources configured for a specific purpose, or
  • a repository for configuration files (e.g., ntp.conf), that need to be overwritten on all clients, or
  • even templates that could be used as a base or format to write a certain configuration. In short, it is a collection of configurations (resources, classes, files, definitions and Templates).

Modules help us organise our configurations better.

The /etc/puppet/manifests/files folder is the location for all basic configuration files that need to be ported to client machines. The /etc/puppet/modules folder is the default location for creating modules. This can be changed in the configurations. So the path goes like this:

  • /etc/puppet/manifests/init.pp: This is the default, where a module is not explicitly defined.
  • /etc/puppet/modules/mymodule/manifests/init.pp: Here, mymodule is the defined module.

“Manifests” is a must to classify the constituents as a module. This means that the default structure is also read as a module, but it is placed just outside the module path. Modules give more structure than classes, and that is why we include classes inside modules.

The import keyword that we earlier used to call files from different relative locations inside the manifests folder is the same command used to activate modules as well. So when we say import ssh, it looks for either:

  • a file called ssh.pp in /etc/puppet/manifests/ or
  • a file called site.pp/init.pp in /etc/puppet/modules/ssh/manifests/ (ssh is looked up as the name of a module.)

After importing a module, we can use the include command to include just one class out of many possibly defined classes in a module. For example, include ssh::server.

Suppose we want to overwrite the /etc/ssh/sshd_config file on all machines, how would we go about it? Using modules, this can be done very easily. This is how modules act as a repository too:

file
{
 "/etc/ssh/sshd_config":
     owner =>" "root",
    group => "root",
    mode => "0664",

replace => true,
    source => "puppet:///ssh/sshd.config"
}

server_name:///module_name/file_name is the syntax for the source keyword. So what’s happening here? A file called /etc/ssh/sshd_config is created (or replaced, if it already exists) on all clients, with the contents of sshd.config on the Puppet Server, stored at /etc/puppet/ssh/manifests/file/sshd.config.

If you recollect, in Part 3 we had classified different resources under various categories. You must have noticed that we have now covered two new resources, one each under the categories Packaging and Services; the resource names are package and service.

Note: Load balancing is not covered because of the excessive work required in setting it up and in troubleshooting, which calls for in-depth knowledge of Puppet and GNU/Linux. You can read about it on the Puppet wiki.

I hope you enjoyed exploring these two main disciplines of Puppet, which help to better organise your configuration files. Don’t forget to check up on the official Puppet documentation for further information.

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
Open Source For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.