Cfengine3: Check for running services

In the last post about Cfengine 3 we talked about a simple script which creates a file and writes something into it. Today, we will create a simple example script which checks if two specified services are running.

Writing the script
Connect to your Linux box and create the following file: /var/lib/cfengine3/inputs/example2.cf

body common control {
version => "1.0";
bundlesequence => { "check_services" };
}

bundle agent check_services {
vars:
"services" slist => { "apache2", "mysql" };
"init_scripts_path" string => "/etc/init.d";

processes:
"$(services)"
comment => "Check if the processes for '$(services)'",
restart_class => "restart_$(services)";

commands:
"${init_scripts_path}/${services} start"
comment => "Restarting the service",
ifvarclass => "restart_${services}";
}

Save the file and check if the syntax is correct:

cf-promises -f /var/lib/cfengine3/inputs/example2.cf

The output should not contain any errors. In this example, I have installed Apache2 and MySQL 5 on my Linux Mint VM. The script above checks if both daemons are running – if they aren’t, they will be started.
Of course you should stop those services before running the script and you can replace the service names with your own ones.

Now let the cf-agent start Apache2 and MySQL5:

cf-agent -f /var/lib/cfengine3/inputs/example2.cf

Running pstree, I now see that both services are up again and running. The output of cf-agent acknowledges this fact:

cf3> Initiate variable convergence...
cf3> -> Immunizing against parental death
cf3> -> Bundlesequence => {'check_services'}
cf3>
cf3> *****************************************************************
cf3> BUNDLE check_services
cf3> *****************************************************************
cf3>
cf3>
cf3> =========================================================
cf3> vars in bundle check_services (1)
cf3> =========================================================
cf3>
cf3>
cf3> + Private classes augmented:
cf3>
cf3> - Private classes diminished:
cf3>
cf3>
cf3>
cf3> =========================================================
cf3> processes in bundle check_services (1)
cf3> =========================================================
cf3>
cf3> Observe process table with /bin/ps -eo user,pid,ppid,pgid,pcpu,pmem,vsz,pri,rss,nlwp,stime,time,args
cf3>
cf3> .........................................................
cf3> Promise handle:
cf3> Promise made by: apache2
cf3>
cf3> Comment: Check if the processes for 'apache2'
cf3> .........................................................
cf3>
cf3> -> Making a one-time restart promise for apache2
cf3>
cf3> .........................................................
cf3> Promise handle:
cf3> Promise made by: mysql
cf3>
cf3> Comment: Check if the processes for 'mysql'
cf3> .........................................................
cf3>
cf3> -> Making a one-time restart promise for mysql
cf3>
cf3> =========================================================
cf3> commands in bundle check_services (1)
cf3> =========================================================
cf3>
cf3> -> Promiser string contains a valid executable (/etc/init.d/apache2) - ok
cf3>
cf3> .........................................................
cf3> Promise handle:
cf3> Promise made by: /etc/init.d/apache2 start
cf3>
cf3> Comment: Restarting the service
cf3> .........................................................
cf3>
cf3> -> Executing '/etc/init.d/apache2 start' ...(timeout=-678,owner=-1,group=-1)
cf3> -> (Setting umask to 77)
cf3> -> Finished command related to promiser "/etc/init.d/apache2 start" -- succeeded
cf3> Q: "....d/apache2 star": * Starting web server apache2
Q: "....d/apache2 star": apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName
Q: "....d/apache2 star": ...done.
cf3> I: Last 3 quoted lines were generated by promiser "/etc/init.d/apache2 start"
cf3> -> Completed execution of /etc/init.d/apache2 start
cf3> -> Promiser string contains a valid executable (/etc/init.d/mysql) - ok
cf3>
cf3> .........................................................
cf3> Promise handle:
cf3> Promise made by: /etc/init.d/mysql start
cf3>
cf3> Comment: Restarting the service
cf3> .........................................................
cf3>
cf3> -> Executing '/etc/init.d/mysql start' ...(timeout=-678,owner=-1,group=-1)
cf3> -> (Setting umask to 77)
cf3> -> Finished command related to promiser "/etc/init.d/mysql start" -- succeeded
cf3> Q: "...it.d/mysql star": Rather than invoking init scripts through /etc/init.d, use the service(8)
Q: "...it.d/mysql star": utility, e.g. service mysql start
Q: "...it.d/mysql star": Since the script you are attempting to invoke has been converted to an
Q: "...it.d/mysql star": Upstart job, you may also use the start(8) utility, e.g. start mysql
Q: "...it.d/mysql star": mysql start/running, process 4235
cf3> I: Last 5 quoted lines were generated by promiser "/etc/init.d/mysql start"
cf3> -> Completed execution of /etc/init.d/mysql start
cf3>
cf3> =========================================================
cf3> vars in bundle check_services (2)
cf3> =========================================================
cf3>
cf3>
cf3> + Private classes augmented:
cf3>
cf3> - Private classes diminished:
cf3>
cf3>
cf3>
cf3> =========================================================
cf3> processes in bundle check_services (2)
cf3> =========================================================
cf3>
cf3> -> Reusing cached process state
cf3>
cf3> =========================================================
cf3> commands in bundle check_services (2)
cf3> =========================================================
cf3>
cf3>
cf3> =========================================================
cf3> vars in bundle check_services (3)
cf3> =========================================================
cf3>
cf3>
cf3> + Private classes augmented:
cf3>
cf3> - Private classes diminished:
cf3>
cf3>
cf3>
cf3> =========================================================
cf3> processes in bundle check_services (3)
cf3> =========================================================
cf3>
cf3> -> Reusing cached process state
cf3>
cf3> =========================================================
cf3> commands in bundle check_services (3)
cf3> =========================================================
cf3>
cf3> Outcome of version 1.0 (agent-0): Promises observed to be kept 0%, Promises repaired 100%, Promises not repaired 0%
cf3> Estimated system complexity as touched objects = 256, for 10 promises

Taking a closer look at the script

body common control {
version => "1.0";
bundlesequence => { "check_services" };
}

The first section begins with “body common control” and defines a version for this document. This string has no big meaning and is only used within reports and error messages. The term “bundlesequence” points to the promise “check_services”. Of course you can provide multiple promises and their order here.

bundle agent check_services {
vars:
"services" slist => { "apache2", "mysql" };
"init_scripts_path" string => "/etc/init.d";

The next section is starting with a bundle with the type “agent” and the name “check_services”. The type has to be one of the pre-defined types of Cfengine3. “agent” seems to be one of the most used types and is often called the “executive bundle”. With the help of agent bundles the wanted (configuration) changes are implemented in the system (e.g. editing config files). This is also where the free and commercial version of Cfengine3 differ. While the free edition allows interacting with commands, files, methods, packages, processes, storage and devices within agent bundles, the commercial edition lets the script also interact with databases, virtual environments, Windows services and gives control about the log level.

With “vars” we tell Cfengine3 that we want to define promises of the type “vars” (which obviously stands for variables). These vars can be included in every type of bundle and are defined similar to programming languages: “type” => “value”.
The quoted string before the variable type is provided as an attribute and tells the reader what is stored inside the variable.
In our example we defined two promises of the type “vars”. One contains the name of the services to be looked for and the other one will lead Cfengine3 to the path of the init scripts later.

processes:
"$(services)"
comment => "Check if the processes for '$(services)'",
restart_class => "restart_$(services)";

The next section defines promises of the type “processes”. For each list item within the defined “services” the promise will be executed. In this case, a restart class is defined.

commands:
"${init_scripts_path}/${services} start"
comment => "Restarting the service",
ifvarclass => "restart_${services}";
}

The last section defines promises of the type “commands”. The two variables are concatenated and serve with the full path of the “ifvarclass” which will be responsible for restarting the service(s).

That’s it! I hope that I made no mistakes while presenting this simple Cfengine3 script example to you. You can download today’s script here.

6 thoughts on “Cfengine3: Check for running services

  1. I just used this example to implement starting ntpd – worked like charm (CentOS 5.8 and Fedora 17 clients).
    The only variation was to separate CentOS from the newer Fedora as the later doesn’t use the init.d mechanism – I got something like:

    processes:
    “ntpd”
    comment => “The NTP daemon – must be always running, otherwise the RTC will start drifting away from the actual time of day.”,
    restart_class => “restart_ntpd”;

    commands:
    centos::
    “/sbin/chkconfig ntpd on”;
    ntp_configured.centos::
    “/sbin/service ntpd restart”;
    fedora::
    “/bin/systemctl enable ntpd.service”;
    ntp_configured.fedora::
    “/bin/systemctl restart ntpd.service”;
    restart_ntpd.centos::
    “/sbin/service ntpd start”;
    restart_ntpd.fedora::
    “/bin/systemctl start ntpd.service”;

  2. I’m pretty new to CFEngine3 but this looks like you’re checking to see if there is a command to start a service and if that command exists to start the service. I might be missing something; the script does not check the status of the service, it just starts it no matter what.

  3. Hi dudeman,

    thanks for your comment. As you can see, the process promise checks for the services stored in the variable $(services). Cfengine 3 does that by really looking for running processes. If they are not found, the init scripts are called accordingly.

    You can test that 🙂 Simply stop Apache and look what happens.

Comments are closed.