Getting started with CFengine

Master/Slave pull model

CFengine works on a client pull model. The master never "pushes"
changes to the client. In fact, the only purpose of the master servers
are to act as a file repository for both the configs and files to
distribute.

The clients are completely autonomous. They only contact the
master server(s) to check for updated configs and files. They pull the
configs and then act on them. If the master servers are unavailable, the
clients continue to maintain their systems with their last known
configs, just as a submarine would execute their last known orders if
conact with HQ has been severed.

Promise theory

CFEngine works on a simple notion of promises. Everything in CFEngine
can be thought of as a promise to be kept by different resources in the
system.

Combining promises with patterns to describe where and when promises should apply is what CFEngine is all about.

Bodies and bundles

Bundles

Bundles are collections of promises under a single name, very much like a subroutine.

Let's take a look at the basic structure of a typical bundle:

 
bundle agent my_bundle
{

classes:
vars:

packages:

files:

processes:

commands:

methods:

}


This bundle can be used to manage anything really. If you wanted to
maintain a certain application or category on a system, for example you
"bundle" up all of your security related promises you could do so in
this fashion.

Now let's break down the sections within the bundle:

 classes

Since classes are the fundamental "if/then" statement in CFE, You may
want to define classes based on certain criteria at runtime. This
example shows defining a class based on whether a file exists to flag
that an oracle client is installed:

 
classes:
   "ORACLE_CLIENT_INSTALLED" or => { fileexists("/u01/app/oracle/product/current/somefile") };

We execute a function, fileexists() and based on a true or false return we define the ORACLE_CLIENT_INSTALLED class.


vars

Vars are just variables that store values for later use. In this
example we define the variable "foobar" and give it a value based on the
output of a script. Later in the bundle the variable can ce referenced
by using $(foobar) anywhere.

 
vars:
   "foobar" string => execresult("/usr/local/bin/somescript","noshell");


packages

Cfengine can maintain OS packages as well. This method relies on the
underlying package management system of the OS as each variant (RPMS,
deb, etc..) is handled differently. Dependencies must be handled just as
if you were at a shell.

 
packages:
   redhat::
      "some-rhel-pkg"   package_policy => "add", package_method => yum;
   debian:: 
      "some-deb-pkg" package_policy => "add", package_method => apt;



 files

Any action that will be taken on a file (or directory). These actions
could be setting permissions, editing a file, copying from the master
server, etc.. In the classes example above we defined the
ORACLE_CLIENT_INSTALLED class. We can now act on that class if we need
to make sure the oracle client is to be installed:

 
files:
   ORACLE_CLIENT.!ORACLE_CLIENT_INSTALLED::
      "/u01/oracle_client_11g.tar.gz"  
         perms => mog("700","root","root"), 
         copy_from => secure_cp("$(g.repo)/oracle_client/oracle_client_11g.tar.gz","@(g.phost)");



If the machine is defined as an oracle client, and the class is NOT defined for the oracle client being installed, then lets copy the client
tarball from the master server.

commands

There comes a time when you need to execute a command or script.
Sometimes you need to restart a daemon after the config changes, or run
some clean up. In the files example above we copied the oracle client
tarball from the master server. But that in itself is useless and would
require manual installation after the fact. The commands we could
trigger the installation of the tarball:

 
commands:
   ORACLE_CLIENT.!ORACLE_CLIENT_INSTALLED::
      "/bin/tar -z -C / -x -f /u01/oracle_client_11g.tar.gz";


 processes

This section can monitor processes. Let's say for example we have a
server that runs apache. If for some reason the https process dies
CFengine can restart the httpd process by setting a class which can be
triggered by a command in the commands section:

 
processes:
   "httpd"
      comment => "Make sure apache is running",
         restart_class => "RESTART_HTTPD";


This will define RESTART_HTTPD if httpd is not running.

 methods

Methods are ways of calling entire bundles for use. The most common
use is "looping" through a list. Lets assume we have a list of users in
our first bundle. We may want to execute another bundle for each user.

 
bundle agent example
{
vars:
   "userlist" slist => { "mark", "jeang", "jonhenrik", "thomas", "eben" };

methods:
  "any" usebundle => fixuser("$(userlist)");
}


Now here is our bundle for fixing users:

 
bundle agent fixuser(user)
{
commands:
   "/bin/echo Fix $(user)";

reports:
   linux::
      "Finished doing stuff for $(user)";
}


Bodies

Bodies are macros for simplifying complex sets of attributes that can be reused over and over.

 
bundle agent maintain_my_file {
files:
   "/foo/myfile"
      perms => my_perms
}

body perms my_perms {
   mode => 644,
   owner => "myuser",
   group => "mygroup";
}

my_perms can be now used over and over or put into a library.

Classes

CFengine defines classes (similar to 'facts' in the puppeteer world),
And you can act on them at any time. at runtime, CFengine will define
builtin classes based on the environment, Some examples include:

 
Tuesday
Morning
September
redhat_6
x86_64
ipv4_192_168_1
ipv4_192_168_1_2


You can act on them inside your bundles:

 
files:
   redhat_5::
      # Do somethign specific for RH5 

   redhat_6::
      # do something specific for RH6


You can group classes together with "." for AND, and "|" for OR, and "!" for NOT:

 
files:
   !redhat_2.!redhat_3.!redhat_4::
      # Do something for systems that are NOT redhat 2 AND NOT redhat 3 AND NOT redhat 4.

   oracle_db.x86_64.!saturn::
      # Do something for systems defined ad oracle DBs that are only 64 bit, but dont do anything on host "saturn". 



Modules: Defining classes based on system environment

Modules are essentially plugins which are executed on each node. When a module is executed CFEngine will process the output.

Module output which begin with a + sign is treated as a class to
be defined, whilst lines which begin with a - sign are treated as
classes to be undefined.

As an example the following module, when executed, will always
define the class "QA", and always unset the class "NOTLIVE" (if
present):

 
#!/bin/sh

echo "+QA"
echo "-NOTLIVE"


A shell script can be created  called
class_definer. This script gets distributed to all hosts and execute on
each pass of cf-agent.

A simple example of this module would be the following script
which attempts to detect whether the current host is either a Xen host, a
Xen guest, or a Xen-free system:

 
#!/bin/sh
#  module:xen
#
#  Define one of "dom0", "domU", or "noxen".
#
test -d /proc/xen && test -x /usr/sbin/xm && echo "+dom0" && exit
test -d /proc/xen && echo "+domU" && exit
echo "+noxen"


Actions can be taken based on the output of this module:

 
files:
   dom0::
      # do something


Comments

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.