dropdown menu


A class is a code block in Puppet, which can be created in one place and invoked elsewhere. Using classes allows to reuse Puppet code. 
Defining a class makes it available by name, but doesnʼt automatically evaluate the code inside it. 
Declaring a class evaluates the code in the class, and applies all of its resources.

Defining a Class
Before you can use a class, you must define it, which is done with the class keyword (class names must start with a lowercase letters):
class unix { 
   file { 
      owner => 'superuser', 
      group => 'superuser', 
      mode => 644; 
   file {'/etc/shadow': 
      owner => 'vipin', 
      group => 'vipin', 
      mode => 440; 

Each class definition introduces a new variable scope. This means: Any variables you assign inside the class wonʼt be accessible by their short names outside the class; to get at them from elsewhere, you would have to use the fully-qualified name (e.g. $ntp::service_name)

You can assign new, local values to variable names that were already used at top scope. For example, you could specify a new local value for $fqdn.

Declaring a class
A class declaration occurs when a class is called in a manifest. A class declaration tells Puppet to evaluate the code within the class.  
A normal class declaration occurs when the "include" keyword is used:
include unix

This will cause Puppet to evaluate the code in our unix class. It has a limitation to include a single class only once.

The include function declares a class, if it hasnʼt already been declared somewhere else. If a class HAS already been declared, include will notice that and do nothing. This lets you safely declare a class in several places. If some class depends on something in another class, it can declare that class without worrying whether itʼs also being declared in site.pp.

Resource-like class declaration:
class {'ntp':}

This looks like a resource declaration, just with a resource type of "class". If Puppet tries to evaluate this and the class has already been declared, it will fail with a compilation error. (We can't declare same resource more than once.) However, unlike include, resource-like declarations let you specify class parameters.


Passing a parameter to a class:

To pass a parameter in a class, one can use the following construct:

class tomcat($version) { 
   ... class contents ... 
One key point to remember in Puppet is, classes with parameters are not added using the include function, rather the resulting class can be added as a definition.

node webserver { 
   class { tomcat: version => "1.2.12" } 
Default Values As Parameters in Class
class tomcat($version = "1.2.12",$home = "/var/www") { 
   ... class contents ... 



Modules are just directories with files, arranged in a specific structure. Puppet looks for modules in a specific place, known as the modulepath (which is a configurable setting).
If a class is defined in a module, you can declare that class by name in any manifest, Puppet will automatically find and load the manifest that contains the class definition. The manifest files within a module have to obey certain naming restrictions.

This means you can have several modules (directories) with sophisticated Puppet code in them, and your site.pp manifest can look like this:
# /etc/puppetlabs/puppet/manifests/site.pp
include ntp
include apache
include mysql
include mongodb
include build_e

It is considered best practice to use modules to organize almost all of your Puppet manifests.


The Modulepath

The modulepath is a set of directories that Puppet searches for module. The path to the location of modules on Master can be found in puppet.conf:

vardir = /var/opt/lib/pe-puppet
logdir = /var/log/pe-puppet
rundir = /var/run/pe-puppet
modulepath = /etc/puppetlabs/puppet/modules:/opt/puppet/share/puppet/modules

When using puppet apply, the modulepath parameter can be applied, so puppet will search modules in that path:
puppet apply --modulepath=/root/puppet/modules /root/puppet/site.pp


Module Structure

A module has a definite structure which needs to be followed:
A module is a directory. The moduleʼs name must be the name of the directory. It contains a manifests directory, which can contain any number of .pp files, but it should always contain an init.pp file. This init.pp file must contain a single class definition and the classʼs name must be the same as the moduleʼs name. Each manifest in a module should contain exactly one class.  Each manifestʼs filename must map to the name of the class it contains.(In our module directory, the static files (for example a config file) should be in a directory called "files", and the dynamically changing files (like scripts with variables) should be in the directory called "templates".) 

Subdirectories in a module:
manifests    - Contains all of the manifests in the module.
files        - Contains static files, which managed nodes can download.
templates    - Contains templates, which can be referenced from the moduleʼs manifests.
lib          - Contains plugins, like custom facts and custom resource

We created a module called "ntp", with this structure:
├── site.pp                      <--first entry point (in this example it contains only this 1 line: include ntp)
└── modules
   └── ntp                       <-- our module is called ntp, so all files related to it are in this ntp directory
       ├── files          <-- static files, like conf files which can be downloaded with puppet:///... (like ntp.conf.debian)      
       ├── manifests
       │   └── init.pp           <--this should contain our ntp class dfinition
       └── templates          <--.erb template files, content is dynamic with ruby code and variables (like ntp.conf.debian.erb)

Our init.pp looks like this:
# cat init.pp
class ntp {
  case $operatingsystem {
    centos, redhat: {
      $service_name = 'ntpd'
      $conf_file = 'ntp.conf.el'
    debian, ubuntu: {
      $service_name = 'ntp'
      $conf_file = 'ntp.conf.debian'

  package { 'ntp':
    ensure => installed,
  file { 'ntp.conf':
    path    => '/etc/ntp.conf',
    ensure  => file,
    require => Package['ntp'],
    #source  => "/root/ntp/${conf_file}"
    #source  => "puppet:///modules/ntp/${conf_file}",
    content => template("ntp/${conf_file}.erb"),
  service { 'ntp':
    name => $service_name,
    ensure => running,
    enable => true,
    subscribe => File['ntp.conf'],

We can run this module with puppet apply:
puppet apply --modulepath=/root/puppet/modules /root/puppet/site.pp
(or with verbose mode: puppet apply -v --modulepath=....)

The init.pp file is special, it always contains a class with the same name as the module. Every other file must contain a class with a name like this: <MODULE NAME>::<FILENAME>

So for example, if we had an apache module that contained a mod_passenger class, our file on disk would look like: apache/manifests/mod_passenger.pp
In mod_passenger.pp we would define the class like this: apache::mod_passenger

(if the file is inside a subdirectory of manifests/, it should be named: <MODULE NAME>::<SUBDIRECTORY NAME>::<FILENAME>)

installing 3rd party modules:
puppet module install puppetlabs-msql
puppet module list


Templates are documents that contain a mixture of static and dynamic content. Puppet doesnʼt have its own templating language; instead, it uses ERB, a common Ruby-based template language. By using conditional logic and variables, they let you maintain one source document that can be rendered into any number of final documents. Templates are saved as files with the .erb extension, and should be stored in the "templates" directory of any module. There can be any number of subdirectories inside templates. 

To use a template, you have to render it to produce an output string. The "template" function takes a path to one or more template files and returns an output string:

file {'/etc/foo.conf':
  ensure => file,
  require => Package['foo'],
  content => template('foo/foo.conf.erb'),

The whole erb file what we use in templates dir, should be written that when we run it, it produces an output string. Then we can use this output string as the value of the content attribute. The template function expects file paths to be in a specific format: <MODULE NAME>/<FILENAME INSIDE TEMPLATES DIRECTORY>. That is, template('foo/foo.conf.erb') would point to the file /etc/puppet/modules/foo/templates/foo.conf.erb.

Templates are powerful because they have access to all of the Puppet variables that are present when the template is rendered. Facts, global variables, and local variables from the current scope are available to a template as Ruby instance variables — instead of Puppetʼs $ prefix, they have an @ prefix. (e.g. @fqdn, @memoryfree, @operatingsystem, etc.). Like: 
<%= @fqdn %>

Variables from other scopes can be accessed with the scope.lookupvar method, which takes a long variable name without the $ prefix. (For example, scope.lookupvar('apache::user')

Here's an example of how to use inline_template in a manifest:
(inline_template is different from a template file)

cron { 'chkrootkit':
  command => '/usr/sbin/chkrootkit >
    /var/log/chkrootkit.log 2>&1',
  hour => inline_template('<%= @hostname.sum % 24 %>'),
  minute => '00',

Anything inside the string passed to inline_template is executed as if it were an ERB template. That is, anything inside the <%= and %> delimiters will be executed as Ruby code, and the rest will be treated as a string. In this example, we use inline_template to compute a different hour for this cron resource (a scheduled job) for each machine, so that the same job does not run at the same time on all machines.

No comments: