CFEngine Classes

UnderscoreClasses | Descriptive Classes | Naming Conventions | Services & Roles | Class Scope

CFEngine uses classes (also know as groups) to associate groups of systems with configuration actions. This page outlines the setup and usage of classes in a heterogeneous environment. Under this setup, the CFEngine configuration files are organized into different files by Operating System (cf.macosx, cf.redhat), application (cf.app_clamav, cf.app_squid), and other logical groups. CFEngine import statements are used to join these files together from cfagent.conf. Most classes are defined in the cf.groups file.

Keep in mind classes under CFEngine 2 share a single, flat namespace. For reference, run the cfagent -pv command to show the built-in and calculated classes.

Consult the book Automating Linux and Unix System Administration for another opinion on how CFEngine classes and file imports should be managed.

UnderscoreClasses

The UnderscoreClasses variable only affects a small list of builtin classes. Consider instead setting all local classes to begin with an underscore, or differentiate between hostname and builtin classes by using unique prefixes on classes, such as restart_ or role_. Be aware that without UnderscoreClasses set, a hostname such as darwin will conflict with the darwin hard class on Mac OS X.

Descriptive Classes

I do not recommend configuration based on hostname, as services and roles change over time. Instead, group hosts into descriptive class names, and use the custom class to configure services. That is, instead of using darwin:: for the spool directory cleanup shown above, create a class of systems where the spool directory cleanup must be done, then add darwin to that class. If additional systems need the spool cleanup, simply add them to the custom class.

classes:
any::
needs_spool_cleanup = ( darwin evolution )

tidy:
needs_spool_cleanup::
/var/spool/MD-Quarantine pattern=* age=7 r=inf ifelapsed=1439

A better name instead of needs_spool_cleanup might be one named for the application (in this case MIMEDefang) or the role the hosts are providing (a mail proxy versus a mail client system). If unsure, consult with your peers or online for class naming conventions. I favor using underscores in custom class names, to avoid clashes with hostnames, such as app_mimedefang and mail_proxy.

Naming Conventions

Each workplace should negotiate class naming conventions, to improve configuration consistency and to avoid namespace conflicts. One convention would be the use of UnderscoreClasses, covered above. Others are outlined in the following list.

Services & Roles

Use service classes for global configuration, and role classes for specific configuration of a particular service. For example, all sys_nfs_server systems will need portmap and other processes running, while different Network File System (NFS) server configuration (/etc/exports and so forth) should be assigned to a role class.

classes:
any::
sys_nfs_server = ( darwin evolution )

role_nfs_development = ( darwin )
role_nfs_homedirs = ( evolution )

control:
role_nfs_development::
nfs_exports_config = ( ${masterfiles}/role/nfs/development/exports )

role_nfs_homedirs::
nfs_exports_config = ( ${masterfiles}/role/nfs/homedirs/exports )

copy:
sys_nfs_server::
${nfs_exports_config}
dest=/etc/exports
define=restart_nfs

processes:
redhat.sys_nfs_server::
"nfsd" restart "/sbin/service nfs start"
"portmap" restart "/sbin/service portmap start"
"rpc.statd" restart "/sbin/service nfslock start"

shellcommands:
redhat.restart_nfs::
"/usr/sbin/exportfs -ar"

Class Scope

Some groups of classes will account for all systems in an organization, while other class groups will only cover a subset of the systems. The subset classes may have logic problems, as !backup_server covers both backup_client systems and other systems outside of the backup_* umbrella. Why is this important? Logic errors can result in unintended actions being carried out, such as cfexecd being killed on all the Windows systems. Sorry, Alex! :)

classes:
any::
ntp_primary = ( tick tock )
ntp_client = ( any -ntp_primary )

backup_server = ( hindsight )
backup_client = ( darwin evolution tick )

To target systems not being backed up, the unwieldy expression !(backup_server|backup_client) would be required. If a new, exclusive backup class was added (for example backup_proxy), all the negative rules would need to be changed to !(backup_server|backup_client|backup_proxy). On the other hand, to add a ntp_secondary class, the addition need only be done under the classes section:

classes:
any::
ntp_primary = ( tick tock )
ntp_secondary = ( darwin )
ntp_client = ( any -ntp_primary -ntp_secondary )

Another option entails using SingleCopy nirvana to workaround copy blocks with complicated someclass.!anotherclass logic.

The following example shows configuration for the Network Time Protocol (NTP) daemon. Either the system is a server, or a client, and each class requires a different ntp.conf. If Operating Systems use different locations for ntp.conf, account for this in the control section with various ntp_config_dest definitions.

control:
any::
ntp_config_dest = ( /etc/ntp.conf )

ntp_client::
ntp_config_src = ( ${masterfiles}/conf/ntp/client/ntp.conf )

ntp_primary::
ntp_config_src = ( ${masterfiles}/conf/ntp/server/ntp.conf )

copy:
any::
${ntp_config_src}
dest=${ntp_config_dest}
mode=0444
define=restart_ntpd

More CFEngine Examples.