CFEngine 3 Get Started Guide


Next: , Previous: (dir), Up: (dir)

CFEngine-Modularization

COMPLETE TABLE OF CONTENTS

Summary of contents


Next: , Previous: Top, Up: Top

1 Changes in cfengine

CFEngine is a suite of programs for integrated autonomic management of either individual or networked computers. It has existed a This document represents cfengine versions 3.0.0 and later, which are a radical departure from earlier versions.

CFEngine 3 has been changed so as to be both a more powerful and much simpler. CFEngine 3 is not backwards compatible with the cfengine 2 configuration language, but it interoperates with cfengine 2 so that it is "run-time compatible". This means that you can change over to version 3 slowly, with low risk and at your own speed.

With cfengine 3 you can install, configure and maintain computers using powerful hands-free tools. You can also integrate knowledge management and diagnosis into the processes.

CFEngine differs from most management systems in being

CFEngine 3 consists of a number of components:

cf-agent
Active agent
cf-execd
Scheduler
cf-graph
Graph data extractor
cf-know
Knowledge modelling agent
cf-monitord
Passive monitoring agent
cf-promises
Promise validator
cf-runAgent
Remote run agent
cf-serverd
Server agent
cf-show
Self-knowledge extractor

The starred components are new. The daemon formally called cfenvd i previous versions of cfengine is now called cf-monitord.

Unlike previous versions of cfengine, which had no consistent model for its features, you can recognize everything in cfengine 3 from just a few concepts.

Concept Quick description Promise A statement about the state we desire to maintain</td> Promise bundles A collection of promises</td> Promise bodies A part of a promise which details and constrains its nature</td> Data types An interpretation of a scalar value: string, integer or real number</td> Variables An association of the form "LVALUE represents RVALUE", where rval may be a scalar value or a list of scalar values Functions Built-in parameterized rvalues Classes Cfengine's boolean classifiers that describe context

If you have used cfengine before then the most visible part of cfengine 3 will be its new language interface. Although it has been clear for a long time that the organically grown language used in cfengines 1 and 2 developed many problems, it was not immediately clear exactly what would be better. It has taken years of research to simplify the successful features of cfengine to a single overarching model. To understand the new cfengine, it is best to set aside any preconceptions about what cfengine is today. CFEngine 3 is a genuine "next generation" effort, which is will be a springboard into the future of system management.


Next: , Previous: Changes in cfengine, Up: Changes in cfengine

1.1 Why change the language?

Many attempts at improving the user interface of cfengine have been proposed but none of them have been sufficiently impressive to make the change worthwhile before now. Some have gone in for an Object Oriented approach, but this imposes a hierarchical model that does not fit cfengine's autonomous peer model. The main goal in changing the language is to simplify and improve the robustness and functionality without sacrificing the basic freedoms and concepts. Concepts such as explicit loops and and tests have long been banished from cfengine and proposals to reintroduce them have been dismissed — something better is needed. The difficulty, of course is to provide a genuine simplification and improvement that is robust and lasting: this requires a deep understanding of the problem.

CFEngine 3's new language is a direct implementation of a model developed at Oslo University College over the past four years, known colloquially as "promise theory". Promises were originally introduced by Mark Burgess as a way to talk about cfengine's model of autonomy and have since become a powerful way of modelling cooperative systems. CFEngine 3 is a generic implementation of the language of promises that allows all of the aspects of configuration management to be unified under a single umbrella.

Why talk about promises instead of simply talking about changes? After all, the trend in business and IT management today is to talk about Change Management, e.g. in the IT Infrastructure Library (ITIL) terminology. This comes from a long history of process management thinking. But we are not really interested in change – we are interested in being in a state where we don't need to make any changes. In other words we want to be able to promise that the system is correct, verify this and only make changes if our promises are not kept.

To put it another way, cfengine is not really a change management system, it is a maintenance system. Maintenance is the process of making small changes or corrections to a model. A "model" is just another word for a template or a specification of how we want the system to work. CFEngine's model is based on the idea of promises, which means that it focuses on what is stable and lasting about a system – not about what is changing.

This is an important philosophical shift. It means we are focused mainly on what is right and not on what is wrong. By saying what "right" is (the ideal state of our system) we are focussed on the actual behaviour. If we focus too much on the changes, i.e. the differences between now and the future, we might forget to verify that what we assume is working now in fact works.

Models that talk about change management tend to forget that after every change there is a litany of incidents during which it is necessary to repair the system or return it to its intended state. But if we know what we have promised, it is easy to verify whether the promise is kept. This means that it is the promises about how the system should be that are most important, not the actual changes that are made in order to keep them.


Next: , Previous: Why change the language, Up: Changes in cfengine

1.2 Testing as a non-privilieged user

One of the practical advantages of cfengine is that you can test it without the need for root or administrator privileges. This is recommended for all new users of cfengine 3.

CFEngine operates with the notion of a work-directory. The default work directory for the root user is /var/cfengine (except on Debian Linux and various derivatives which prefer /var/lib/cfengine). For any other user, the work directory lies in the user's home directory, named ~/.cfagent. CFEngine prefers you to keep certain files here. You should not resist this too strongly or you will make unnecessary trouble for yourself. The decision to have this `known directory' was made to simplify a lot of configuration.

To test cfengine as an ordinary user, do the following:

You can test the software and play with configuration files by editing the basic get-started files directly in the ~/.cfagent/inputs directory. For example, try the following:

     host$ ~/.cfagent/bin/cf-promises
     host$ ~/.cfagent/bin/cf-promises --verbose

This is always the way to start checking a configuration in cfengine 3. If a configuration does not pass this check/test, you will not be allowed to use it, and cf-agent will look for the file failsafe.cf.

Notice that the cfengine 3 binaries have slightly different names than the cfengine 2 binaries. They all start with the cf- prefix.

     host$ ~/.cfagent/bin/cf-agent


Next: , Previous: Testing as a non-privilieged user, Up: Changes in cfengine

1.3 The bear necessities of a cfengine 3

     #######################################################
     #
     # The starting point for every configuration
     #
     #######################################################
     
     body common control
     
     {
     any::
     
       bundlesequence  => { "testbundle" };
     }
     
     #######################################################
     
     bundle agent testbundle
     
     {
     
     }
     

If you try to process this using the cf-promises command, you will see something like this:

     atlas$ ~/LapTop/Cfengine3/trunk/src/cf-promises -f ./unit_null_config.cf
     cf3:./unit_null_config.cf:21,1: syntax error, near token '}'
     Summarizing promises as text to ./unit_null_config.cf.txt
     Summarizing promises as html to ./unit_null_config.cf.html

Examine the two files produced:

     cat ./unit_null_config.cf.txt
     firefox ./unit_null_config.cf.html


Previous: The bear necessities of a cfengine 3, Up: Changes in cfengine

1.4 Familiarizing yourself

To familiarize yourself with cfengine 3, type or paste in the following example text:

     ########################################################
     #
     # Simple test execution
     #
     ########################################################
     
     body common control
     
     {
     bundlesequence  => { "testbundle" };
     }
     
     ########################################################
     
     bundle agent testbundle
     
     {
     vars:
     
       "size" int => "46k";
       "rand" int => randomint("33","$(size)");
     
     
     commands:
     
       "/bin/echo Hello"
     
          # additional args that do not become path of $(this.promiser)
     
          args => "world - $(size)/$(rand)",
     
          contain => standard,
          classes => mydefine("followup","alert");
     
     
       followup::
     
          "/bin/ls"
            contain => standard;
     
     reports:
     
       alert::
     
          "What happened?";
     
     }
     
     ######################################################################
     
     body contain standard
     
     {
     exec_owner => "mark";
     useshell => "true";
     }
     
     ######################################################################
     
     body classes mydefine(class,alert)
     
     {
     on_change => { "$(class)" };
     on_failure => { "$(alert)" };
     }

If you are familiar with cfengine's history, this will look quite strange to you, but fear not.

This example shows all of the main features of cfengine: bundles, bodies, control, variables, and promises. To the casual eye it might look complex, but that is because it is explicit about all of the details. Fortunately it is easy to hide many of these details to make the example simpler without sacrificing any functionality.

The first thing to try with this example is to verify it – did we make any mistakes? Are there any inconsistencies? To do this we use the new cfengine program cf-promises. Let's assume that you typed this into a file called test.cf in the current directory.

     cf-promises -f ./test.cf

If all is well, typing this command shows no output. Try now running the command with verbose output.

     cf-agent -f ./test.cf -v

Now you see a lot of information

     Reference time set to Sat Aug  2 11:26:06 2008
     
     cf3 CFEngine - 3.0.0
     Free Software Foundation 1994-
     Donated by Mark Burgess, Oslo University College, Norway
     cf3 ------------------------------------------------------------------------
     cf3 Host name is: atlas
     cf3 Operating System Type is linux
     cf3 Operating System Release is 2.6.22.18-0.2-default
     cf3 Architecture = x86_64
     cf3 Using internal soft-class linux for host linux
     cf3 The time is now Sat Aug  2 11:26:06 2008
     cf3 ------------------------------------------------------------------------
     cf3 Additional hard class defined as: 64_bit
     cf3 Additional hard class defined as: linux_2_6_22_18_0_2_default
     cf3 Additional hard class defined as: linux_x86_64
     cf3 Additional hard class defined as: linux_x86_64_2_6_22_18_0_2_default
     cf3 GNU autoconf class from compile time: compiled_on_linux_gnu
     cf3 Interface 1: lo
     cf3 Trying to locate my IPv6 address
     cf3 Looking for environment from cfenvd...
     cf3 Unable to detect environment from cfMonitord
     ---------------------------------------------------------------------
     Loading persistent classes
     ---------------------------------------------------------------------
     
     ---------------------------------------------------------------------
     Loaded persistent memory
     ---------------------------------------------------------------------
     cf3   > Parsing file ./test.cf
     ---------------------------------------------------------------------
     Agent's basic classified context
     ---------------------------------------------------------------------
     
     
     Defined Classes = ( any Saturday Hr11 Min26 Min25_30 Q2 Hr11_Q2 Day2
     August Yr2008 linux atlas 64_bit linux_2_6_22_18_0_2_default x86_64
     linux_x86_64 linux_x86_64_2_6_22_18_0_2_default
     linux_x86_64_2_6_22_18_0_2_default__1_SMP_2008_06_09_13_53_20__0200
     compiled_on_linux_gnu net_iface_lo )
     
     Negated Classes = ( )
     
     Installable classes = ( )
     cf3 Wrote expansion summary to promise_output_common.html
     cf3 Inputs are valid

The last two lines of this are of interest. Each time a component of cfengine 3 parses a number of promises, it summarizes the information in an HTML file called generically promise_output_component-type.html. In this case the cf-promises command represents all possible promises, by the type "common". You can view this output file in a suitable web browser to see exactly what cfengine has understood by the configuration. The output looks something like this:


Next: , Previous: Changes in cfengine, Up: Top

2 A simple crash course in concepts


Next: , Previous: A simple crash course, Up: A simple crash course

2.1 Rules are promises

Everything in cfengine 3 can be interpreted as a promise. Promises can be made about all kinds of different subjects, from file attributes, to the execution of commands, to access control decisions and knowledge relationships.

This simple but powerful idea allows a very practical uniformity in cfengine syntax. There is only one grammatical form for statements in the language that you need to know and it looks generically like this:

     
      type:
     
        classes::
     
         "promiser" -> { "promisee1", "promisee2", ... }
     
            attribute_1 => value_1,
            attribute_2 => value_2,
            ...
            attribute_n => value_n;
     

We speak of a promiser (the abstract oject making the promise), the promisee is the abstract object to whom the promise is made, and them there is a list of associations that we call the `body' of the promise, which together with the promiser-type tells us what it is all about.

Not all of these elements are necessary every time. Some promises contain a lot of implicit behaviour. In other cases we might want to be much more explicit. For example, the simplest promise looks like this:

     
     commands:
     
       "/bin/echo hello world";
     

This promise has default attributes for everything except the `promiser', i.e. the command string that promises to execute. A more complex promise contains many attributes:

     
     files:
     
       "/home/mark/tmp/test_plain" -> "system blue team",
     
            comment => "This comment follows the rule for knowledge integration",
            perms   => users("@(usernames)"),
            create  => "true";
     

The list of promisees is not used by cfengine except for documentation, just as the comment attribute (which can be added to any promise) has no actual function other than to provide more information to the user in error tracing and auditing.

You see several kinds of object in this example. All literal strings (e.g. "true") in cfengine 3 must be quoted. This provides absolute consistency and makes type-checking easy and error-correction powerful. All function-like objects (e.g. users("..")) are either builtin special functions or parameterized templates which contain the `meat' of the right hand side.


Next: , Previous: Rules are promises, Up: A simple crash course

2.2 Containers

In addition to statements (which we call simply promises), cfengine allows you to bundle things into containers. A container for promises is called simply a bundle.

     
     bundle agent identifier
     
     {
     commands:
     
       "/bin/echo These commands are a silly way to use cfengine";
       "/bin/ls -l";
       "/bin/echo But they illustrate a point";
     
     }
     

Bundles serve two purposes: they allow us to collect related promises under a single heading, like a subroutine, and they allow us to mix configuration for different parts of cfengine in the same file. The type of a bundle is the name of the component of cfengine for which it is intended.

For instance, we can make a self-contained example agent-server configuration by labelling the bundles:

     
     #
     # Not a complete example
     #
     
     bundle agent testbundle
     
     {
     files:
     
       "/home/mark/tmp/testcopy"
     
         copy_from    => mycopy("/home/mark/LapTop/words","127.0.0.1"),
         perms        => system,
         depth_search => recurse("inf");
     
     }
     
     #
     
     bundle server access_rules
     
     {
     access:
     
       "/home/mark/LapTop"
     
         admit   => { "127.0.0.1" };
     }
     

Another type of container in cfengine 3 is a `body' part. Body parts exist to hide complex parameter information in reusable containers. The right hand side of some attribute assignments use body containers to reduce the amount of in-line information and preserve readability. You cannot choose where to use bodies: either they are used or they are not used for a particular kind of attribute. What you can choose, however, is the name and number of parameters for the body; and you can make as many of them as you like: For example:

     
     body copy_from mycopy(from,server)
     
     {
     source      => "$(from)";
     servers     => { "$(server)" };
     copy_backup => "true";
     
     special_class::
     
       purge       => "true";
     }
     

Notice also that classes can be used in bodies as well as parameters so that you can hide environmental adaptations in these bodies also. The classes used here are effectively ANDed with the classes under which the calling promise is defined.


Previous: Containers, Up: A simple crash course

2.3 Types in cfengine 3

A key difference in cfengine 3 compared to earlier versions is the presence of data types. Data types are a mechanism for associating values and checking consistency in a language. Once again, there is a simple pattern to types in cfengine.

The principle is very simple: types exist in order to match like a plug-socket relationship. In the examples above, you can see two places where types are used to match templates:

Check these by identifying the words `agent' and `copy_from' in the examples above. Types are there to make configuration more robust.


Previous: A simple crash course, Up: Top

3 How to run cfengine 3 examples

These instructions assume that you have all of your configuration in a single test file, such as the example in the distriution directory tests/units.

  1. Test the file as a non-privileged user first, if you can.
  2. Always verify syntax first with cf-promises. This require no privileges an cf-agent will not execute a configuration that has not passed this test.
              
              host$ cf-promises -f ./inputfile.cf
              
         
  3. Run the examples like this, e.g.
              
              host$ make
              host$ src/cf-promises -f ./tests/units/unit_server_copy_localhost.cf
              host$ src/cf-serverd -f ./tests/units/unit_server_copy_localhost.cf
              host$ src/cf-agent -f ./tests/units/unit_server_copy_localhost.cf
              
         

Table of Contents