Processes and Executions
In cfengine 2, we have two separate actions:
processes
shellcommands
In cfengine 3 we have
processes
executions
Cfengine 2 got this ontology about right intuitively, but not
quite. It allowed "restart" in a process promise which is really a
execution of a file object. This has been changed in cfengine 3 so
that there is a cleaner separation. Let's see why.
Executions are about jobs, services, scripts etc. They are properties
of an executable file. The referring "promising agent" is a file
object. On the other hand a process is a property of a "process
identifier" which is a kernel instantiation, a quite different object
altogether. So it makes sense to say that
Neither the file nor the pid necessarily promise to respond to these activations, but they
are nonetheless physically meaningful phenomena or attributes associated with these objects.
- Executable files do not listen for signals as they have no active state.
- PIDs do not run themselves or stop themselves with new arguments, but they can use signals as they are running.
Executions lead to processes for the duration of their lifetime, so these two issues
are related, although the promises themselves are not.
Services
A service is an abstraction that requires processes to run and files
to be configured. It makes a lot of sense to wrap services in modular
bundles. Starting and stopping a service can be handled in at least two ways.
Take the web service as an example.
We can start the service by promising an execution of the apache daemon.
Normally this execution does not terminate without intervention. We can
terminate it in one of two ways:
- Using a process signal, by promising a signal to processes matching a certain pid search
- Using an execution of a termination command, e.g. /etc/init.d/apache stop
The first case makes sense if we need to qualify the termination by
searching for the processes. The processes section of a cfengine 3
policy includes a control promise to search for matching processes.
If matches are found, signals can be sent to precisely each specific process.
Classes can also be defined, in principle triggering an execution of
the stop script, but then the class refers only to the presence of
matching pids, not to the individual pids concerned. So it becomes
the responsibility of the execution to locate and interact with the
pids necessary.
Want it running
How do we say simply that we want a service running
In the agent control promises, we could check each service
individually.
bundlesequence => { Update, Service("apache"), Service("nfsd") };
or
bundlesequence => { Update, @(globals.all_services) };
bundle agent Service("$(service)")
{
processes:
"$(service)"
process_count => up("$(service)");
executions:
"$daemons[$(service)]"
ifvarclass => "$(service)_up",
args => "$args[$(service)]";
}
An alternative would be self-contained:
bundle agent Service
{
vars:
"service" slist => { "apache", "nfsd", "bind" };
processes:
"$(service)"
process_count => up("$(service)");
executions:
"$daemons[$(service)]"
ifvarclass => "$(service)_up",
args => "$args[$(service)]";
}
######################
# Parameterized body
######################
body process_count("$(s)")
{
match_range => "[0,10]";
out_of_range_define => "$(s)_up";
}
A step backwards?
The cfengine 3 approach might seem like a step backwards from the simple:
processes:
"httpd" restart "/etc/init.d/apache restart"
However, it allows several improvements.
You can do other things in between stopping and starting the service, like
file editing, or security sweeps.
You can use templates to simplify the syntax in bulk for several process checks
or restarts.
processes:
"$(service.list)"
If you don't want any delay in stopping and starting the service, then
place these promises in a private bundle with nothing in between them.
Mark Burgess
Last modified: Thu May 1 17:06:38 CEST 2008