The Complete CFEngine Reference

Table of Content

The reference documentation explains the available promise and bundle types, components, bodies, functions, variables, classes and attributes in detail. Language elements that belong together are typically documented on the same page.

See Also: All Promise Types, All Body Types


Components and Common Control

While promises to configure your system are entirely user-defined, the details of the operational behavior of the CFEngine software is of course hard-coded. You can still configure the details of this behavior using the control promise bodies. Control behavior is defined in bodies because the actual promises are fixed and you only change their details within sensible limits.

See the introduction for a high-level overview of the CFEngine components, and each component's reference documentation for the details about the specific control bodies.

Common Control

The common control body refers to those promises that are hard-coded into all the components of CFEngine, and therefore affect the behavior of all the components.

     body common control

     {
     inputs  => {
                "update.cf",
                "library.cf"
                };

     bundlesequence  => {
                        update("policy_host.domain.tld"),
                        "main",
                        "cfengine2"
                        };

     goal_categories => { "goals", "targets", "milestones" };
     goal_patterns   => { "goal_.*", "target.*" };

     output_prefix => "cfengine>";
     version => "1.2.3";
     }
bundlesequence

Description: The bundlesequence contains promise bundles to verify, in a specific order.

The bundlesequence determines which of the compiled bundles will be executed and in what order they will be executed. The list refers to the names of bundles (which might be parameterized, function-like objects).

There is no default value for bundlesequence, and the absence of a bundlesequence will cause a compilation error. A bundlesequence may also be specified using the -b or --bundlesequence command line option.

Type: slist

Allowed input range: .*

Example:

    body common control

    {
    bundlesequence  => {
                       update("policy_host.domain.tld"),
                       "main",
                       "cfengine2"
                       };
    }

The order in which you execute bundles can affect the outcome of your promises. In general you should always define variables before you use them.

The bundlesequence is like a genetic makeup of a machine. The bundles act like characteristics of the systems. If you want different systems to have different bundlesequences, distinguish them with classes

    webservers::

      bundlesequence => { "main", "web" };

    others::

      bundlesequence => { "main", "otherstuff" };

If you want to add a basic common sequence to all sequences, then use global variable lists to do this:

    body common control
    {
    webservers::

      bundlesequence => { @(g.bs), "web" };

    others::

      bundlesequence => { @(g.bs), "otherstuff" };

    }

    bundle common g
    {
    vars:

      "bs" slist => { "main", "basic_stuff" };
    }
cache_system_functions

Description: Controls the caching of the results of system functions, e.g. execresult() and returnszero() for shell execution and ldapvalue() and friends for LDAP queries. Without this setting, CFEngine's evaluation model will evaluate functions multiple times, which is a performance concern. See Functions.

Although you can override this to false, in practice you should almost never need to do so. The effect of having it true (the default) is that the expensive functions will be run just once and then their result will be cached.

Note that caching is per-process so results will not be cached between runs of e.g. cf-agent and cf-promises.

Type: boolean

Default value: true

Example:

    cache_system_functions => "true";

History: Was introduced in version 3.6.0.

domain

Description: The domain string specifies the domain name for this host.

There is no standard, universal or reliable way of determining the DNS domain name of a host, so it can be set explicitly to simplify discovery and name-lookup.

Type: string

Allowed input range: .*

Example:

    body common control
    {
    domain => "example.org";
    }
fips_mode

Description: The fips_mode menu option policy determines whether to activate full FIPS mode restrictions.

In CFEngine Enterprise, this value may be set to avoid the use of old deprecated algorithms that are no longer FIPS 140-2 compliant. If not set, there is some degree of compatibility with older versions and algorithms. During an upgrade, setting this parameter can cause a lot of recomputation of checksums etc. Government bodies starting with CFEngine Enterprise 2.0 or higher should set this to 'true' from the start.

Type: boolean

Default value: false

Example:

    body common control
    {
    fips_mode => "true";
    }
goal_patterns

Description: Contains regular expressions that match promisees/topics considered to be organizational goals

It is used as identifier to mark business and organizational goals in CFEngine Enterprise. CFEngine uses this to match promisees that represent business goals in promises.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body common control
    {
    goal_patterns => { "goal_.*", "target.*" };
    }

History: Was introduced in version 3.1.5, Nova 2.1.0 (2011)

host_licenses_paid

Deprecated: 3.5

Description: The value of host_licenses_paid represents the number of licenses that you promise to have paid for by setting this value (legally binding for CFEngine Enterprise).

Licensees of CFEngine Enterprise have to make a promise in acceptance of contract terms by setting this value to the number of licenses they have paid for. This is tallied with the number of licenses granted. This declaration should be placed in all separate configuration files, e.g. failsafe.cf, promises.cf.

Type: int

Allowed input range: 0,99999999999

Default value: 25

Example:

    body common control
    {
    host_licenses_paid => "1000";
    }
ignore_missing_bundles

Description: Determines whether to ignore missing bundles.

If ignore_missing_bundles is set to true, if any bundles in the bundle sequence do not exist, ignore and continue.

Type: boolean

Default value: false

Example:

    ignore_missing_bundles => "true";

Notes:

This authorizes the bundlesequence to contain possibly "nonexistent" pluggable modules. It defaults to false, whereupon undefined bundles cause a fatal error in parsing, and a transition to failsafe mode.

ignore_missing_inputs

Description: If any input files do not exist, ignore and continue

The inputs lists determines which files are parsed by CFEngine. Normally stringent security checks are made on input files to prevent abuse of the system by unauthorized users.

Sometimes however, it is appropriate to consider the automatic plug-in of modules that might or might not exist. This option permits CFEngine to list possible files that might not exist and continue 'best effort' with those that do exist. The default of all Booleans is false, so the normal behavior is to signal an error if an input is not found.

Type: boolean

Default value: false

Example:

    ignore_missing_inputs => "true";
inputs

Description: The inputs slist contains additional filenames to parse for promises.

The filenames specified are all assumed to be in the same directory as the file which references them (this is usually $(sys.workdir)/inputs, but may be overridden by the -f or --file command line option.

Type: slist

Allowed input range: .*

Example:

    body common control
    {
    inputs  => {
               "update.cf",
               "library.cf"
               };
    }

See also: inputs in body file control

Notes:

If no filenames are specified, no other filenames will be included in the compilation process.

Library contents are checked for duplication by path and by hash. For example, if you put library.cf twice in your inputs, the duplicate library.cf is noticed because the same path is included twice. A verbose-level message is emitted but otherwise there is no error.

In addition, if you include a file once with path /x/y/z.cf and again with path /x/./y/z.cf, the duplicate file will be rejected regardless of any path tricks or symbolic links. The contents are hashed, so the same file can't be included twice.

lastseenexpireafter

Description: The value of lastseenexpireafter is the number of minutes after which last-seen entries are purged. It is an enterprise-only feature.

Type: int

Allowed input range: 0,99999999999

Default value: One week

Example:

    body common control
    {
    lastseenexpireafter => "72";
    }
output_prefix

Description: The string prefix for standard output

Type: string

Allowed input range: (arbitrary string)

Example:

    body common control
    {
    output_prefix => "my_cf3";
    }

Notes:

On native Windows versions of CFEngine (Enterprise), this string is also prefixed messages in the event log.

protocol_version

Description: Defines the protocol to use for all outgoing connections.

Type: (menu option)

Allowed input range:

  • 0
  • undefined
  • 1
  • classic
  • 2
  • latest

Default value: classic

Note: If protocol_version is specified in a body copy_from, then the value there will override this setting.

See also: protocol_version in body copy_from, allowlegacyconnects

History: Introduced in CFEngine 3.6.0

require_comments

Description: The require_comments menu option policy warns about promises that do not have comment documentation.

When true, cf-promises will report loudly on promises that do not have comments. Variables promises are exempted from this rule, since they may be considered self-documenting. This may be used as a policy Quality Assurance measure, to remind policy makers to properly document their promises.

Type: boolean

Default value: false

Example:

    body common control

    {
    common::

    require_comments => "true";
    }
site_classes

Description: A site_classes contains classes that will represent geographical site locations for hosts. These should be defined elsewhere in the configuration in a classes promise.

This list is used to match against topics when connecting inferences about host locations in the knowledge map. Normally any CFEngine classes promise whose name is defined as a thing or topic under class locations:: will be assumed to be a location defining classifier. This list will add alternative class contexts for interpreting location.

Type: slist

Allowed input range: [a-zA-Z0-9_!&@@$|.()\[\]{}:]+

Each string is expected to be a class.

Example:

    body common control
    {
    site_classes => { "datacenters","datacentres"  }; # locations is by default
    }

History: Was introduced in version 3.2.0, Nova 2.1.0 (2011)

syslog_host

Description: The syslog_host contains the name or address of a host to which syslog messages should be sent directly by UDP.

This is the hostname or IP address of a local syslog service to which all CFEngine's components may promise to send data.

Type: string

Allowed input range: [a-zA-Z0-9_$(){}.:-]+

Default value: 514

Example:

    body common control
    {
    syslog_host => "syslog.example.org";
    syslog_port => "514";
    }
syslog_port

Description: The value of syslog_port represents the port number of a UDP syslog service.

It is the UDP port of a local syslog service to which all CFEngine's components may promise to send data.

Type: int

Allowed input range: 0,99999999999

Example:

    body common control
    {
    syslog_host => "syslog.example.org";
    syslog_port => "514";
    }
version

Description: The version string contains the scalar version of the configuration.

It is is used in error messages and reports.

Type: string

Allowed input range: (arbitrary string)

This string should not contain the colon ':' character, as this has a special meaning in the context of knowledge management. This restriction might be lifted later.

Example:

    body common control
    {
    version => "1.2.3";
    }

cf-agent

cf-agent evaluates policy code and makes changes to the system. Policy bundles are evaluated in the order of the provided bundlesequence (this is normally specified in the common control body). For each bundle, cf-agent groups promise statements according to their type. Promise types are then evaluated in a preset order to ensure fast system convergence to policy.

cf-agent keeps the promises made in common and agent bundles, and is affected by common and agent control bodies.

Command reference
  --bootstrap   , -B value - Bootstrap CFEngine to the given policy server IP, hostname or :avahi (automatic detection)
  --bundlesequence, -b value - Set or override bundlesequence from command line
  --debug       , -d       - Enable debugging output
  --define      , -D value - Define a list of comma separated classes to be defined at the start of execution
  --self-diagnostics, -x value - Run checks to diagnose a CFEngine agent installation
  --dry-run     , -n       - All talk and no action mode - make no changes, only inform of promises not kept
  --file        , -f value - Specify an alternative input file than the default
  --help        , -h       - Print the help message
  --inform      , -I       - Print basic information about changes made to the system, i.e. promises repaired
  --negate      , -N value - Define a list of comma separated classes to be undefined at the start of execution
  --no-lock     , -K       - Ignore locking constraints during execution (ifelapsed/expireafter) if "too soon" to run
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --version     , -V       - Output the version of the software
  --legacy-output, -l       - Use legacy output format
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
  --no-extensions, -E       - Disable extension loading (used while upgrading)
Automatic Bootstrapping

Automatic bootstrapping allows the user to connect a CFEngine Host to a Policy Server without specifying the IP address manually. It uses the Avahi service discovery implementation of zeroconf to locate the Policy Server, obtain its IP address, and then connect to it. To use automatic bootstrap, install the following Avahi libraries:

  • libavahi-client
  • libavahi-common

To make the CFEngine Server discoverable, it needs to register itself as an Avahi service. Run the following command:

    $ /var/cfengine/bin/cf-serverd -A

This generates the configuration file for Avahi in /etc/avahi/services and restarts the Avahi daemon in order to register the new service.

From this point on, the Policy Server will be discovered with the Avahi service. To verify that the server is visible, run the following command (requires avahi-utils):

    $ avahi-browse -atr | grep cfenginehub

The sample output looks like this:

    eth0 IPv4 CFEngine Community 3.5.0 Policy Server on policy_hub_debian7
    _cfenginehub._tcp local

Once the Policy Server is configured with the Avahi service, you can auto-bootstrap Hosts to it.

    $ /var/cfengine/bin/cf-agent -B :avahi

The Hosts require Avahi libraries to be installed in order to use this functionality. By default cf-agent looks for libraries in standard install locations. Install locations vary from system to system. If Avahi is installed in a non-standard location (i.e. compiled from source), set the AVAHI_PATH environmental variable to specify the path.

   $ AVAHI_PATH=/lib/libavahi-client.so.3 /var/cfengine/bin/cf-agent -B

If more than one server is found, or if the server has more than one IP address, the list of all available servers is printed and the user is asked to manually specify the IP address of the correct server by running the standard bootstrap command of cf-agent:

   $ /var/cfengine/bin/cf-agent --bootstrap <IP address>

If only one Policy Server is found in the network, cf-agent performs the bootstrap without further manual user intervention.

Note: Automatic bootstrapping support is ONLY for Linux, and it is limited only to one subnet.

Control Promises

Settings describing the details of the fixed behavioral promises made by cf-agent.

    body agent control
    {
    123_456_789::

      domain => "mydomain.com";

    123_456_789_111::

      auditing => "true";

    any::

      fullencryption => "true";

    }
abortclasses

Description: The abortclasses slist contains classes which if defined lead to termination of cf-agent.

Regular expressions are used for classes that cf-agent will watch out for. If any matching class becomes defined, it will cause the current execution of cf-agent to be aborted. This may be used for validation, for example. To handle class expressions, simply create an alias for the expression with a single name.

Type: slist

Allowed input range: .*

Example:

     body agent control

      {
      abortclasses => { "danger.*", "should_not_continue" };
      }
abortbundleclasses

Description: The abortbundleclasses slist contains classes which if defined lead to termination of current bundle.

Regular expressions are used for classes, or class expressions that cf-agent will watch out for. If any of these classes becomes defined, it will cause the current bundle to be aborted. This may be used for validation, for example.

Type: slist

Allowed input range: .*

Example: This example shows how to use the feature to validate input to a method bundle.

    body common control

    {
    bundlesequence  => { "testbundle"  };
    version => "1.2.3";
    }

    #################################

    body agent control

    {
    abortbundleclasses => { "invalid.*" };
    }

    #################################

    bundle agent testbundle
    {
    vars:

     "userlist" slist => { "xyz", "mark", "jeang", "jonhenrik", "thomas", "eben" };

    methods:

     "any" usebundle => subtest("$(userlist)");

    }

    #################################

    bundle agent subtest(user)

    {
    classes:

      "invalid" not => regcmp("[a-z]{4}","$(user)");

    reports:

     !invalid::

      "User name $(user) is valid at exactly 4 letters";

     # abortbundleclasses will prevent this from being evaluated
     invalid::

      "User name $(user) is invalid";
    }
addclasses

Description: The addclasses slist contains classes to be defined always in the current context.

This adds global, literal classes. The only predicates available during the control section are hard-classes.

Type: slist

Allowed input range: .*

Example:

    any::

      addclasses => { "My_Organization" }

    solaris::

      addclasses => { "some_solaris_alive", "running_on_sunshine" };

Notes:

Another place to make global aliases for system hardclasses. Classes here are added unequivocally to the system. If classes are used to predicate definition, then they must be defined in terms of global hard classes.

agentaccess

Description: A agentaccess slist contains user names that are allowed to execute cf-agent.

This represents a list of user names that will be allowed to attempt execution of the current configuration. This is mainly a sanity check rather than a security measure.

Type: slist

Allowed input range: .*

Example:

     agentaccess => { "mark", "root", "sudo" };
agentfacility

Type: (menu option)

Allowed input range:

   LOG_USER
   LOG_DAEMON
   LOG_LOCAL0
   LOG_LOCAL1
   LOG_LOCAL2
   LOG_LOCAL3
   LOG_LOCAL4
   LOG_LOCAL5
   LOG_LOCAL6
   LOG_LOCAL7

Default value: LOG_USER

Description: The agentfacility menu option policy sets the agent's syslog facility level.

Example:

    agentfacility => "LOG_USER";

Notes:

This is ignored on Windows, as CFEngine Enterprise creates event logs.

See Also: Manual pages for syslog.

allclassesreport

Description: The allclassesreport menu option policy determines whether to generate the allclasses.txt report.

If set to true, the state/allclasses.txt file will be written to disk during agent execution.

Type: boolean

Default value: false

Example:

    body agent control
    {
    allclassesreport => "true";
    }

Notes:

This functionality is retained only for CFEngine 2 compatibility. As of CFEngine 3.5, the classesmatching() function provides a more convenient way to retrieve a list of set classes at execution time.

History: Was introduced in 3.2.4, Enterprise 2.1.4 (2011)

alwaysvalidate

Description: The alwaysvalidate menu option policy is a true/false flag to determine whether configurations will always be checked before executing, or only after updates.

Type: boolean

Example:

    body agent control
    {
    Min00_05::

      # revalidate once per hour, regardless of change in configuration

      alwaysvalidate => "true";
    }

Notes:

The agents cf-agent and cfserverd can run cf-promises to validate inputs before attempting to execute a configuration. As of version 3.1.2 core, this only happens if the configuration file has changed to save CPU cycles. When this attribute is set, cf-agent will force a revalidation of the input.

History: Was introduced in version 3.1.2,Enterprise 2.0.1 (2010)

auditing

Deprecated: This menu option policy is deprecated, does nothing and is kept for backward compatibility.

binarypaddingchar

Deprecated: This attribute was deprecated in 3.6.0.

bindtointerface

Description: The bindtointerface string describes the interface to be used for outgoing connections.

On multi-homed hosts, the server and client can bind to a specific interface for server traffic. The IP address of the interface must be given as the argument, not the device name.

Type: string

Allowed input range: .*

Example:

    bindtointerface => "192.168.1.1";
hashupdates

Description: The hashupdates determines whether stored hashes are updated when change is detected in source.

If 'true' the stored reference value is updated as soon as a warning message has been given. As most changes are benign (package updates etc) this is a common setting.

Type: boolean

Default value: false

Example:

    body agent control
    {
    hashupdates => "true";
    }
childlibpath

Description: The childlibpath string contains the LD_LIBRARY_PATH for child processes.

This string may be used to set the internal LD_LIBRARY_PATH environment of the agent.

Type: string

Allowed input range: .*

Example:

    body agent control
    {
    childlibpath => "/usr/local/lib:/usr/local/gnu/lib";
    }
checksum_alert_time

Description: The value of checksum_alert_time represents the persistence time for the checksum_alert class.

When checksum changes trigger an alert, this is registered as a persistent class. This value determines the longevity of that class.

Type: int

Allowed input range: 0,60

Default value: 10 mins

Example:

    body agent control
    {
    checksum_alert_time => "30";
    }
defaultcopytype

Description: The defaultcopytype menu option policy sets the global default policy for comparing source and image in copy transactions.

Type: (menu option)

Allowed input range:

   mtime
   atime
   ctime
   digest
   hash
   binary

Example:

    body agent control
    {
    #...
    defaultcopytype => "digest";
    }
default_repository

Description: The default_repository string contains the path to the default file repository.

If defined the default repository is the location where versions of files altered by CFEngine are stored. This should be understood in relation to the policy for 'backup' in copying, editing etc. If the backups are time-stamped, this becomes effective a version control repository.

Type: string

Allowed input range: "?(/.*)

Default value: in situ

Example:

    body agent control
    {
    default_repository => "/var/cfengine/repository";
    }

Notes: When a repository is specified, the files are stored using the canonified directory name of the original file, concatenated with the name of the file. So, for example, /usr/local/etc/postfix.conf would ordinarily be stored in an alternative repository as _usr_local_etc_postfix.conf.cfsaved.

default_timeout

Description: The value of default_timeout represents the maximum time a network connection should attempt to connect.

The time is in seconds. It is not a guaranteed number, since it depends on system behavior.

Type: int

Allowed input range: 0,99999999999

Default value: 30 seconds

Example:

    body agent control
    {
    default_timeout => "10";
    }

Notes: Under Linux, the kernel version plays a role, since not all system calls seem to respect the signals.

dryrun

Description: The dryrun menu option, if set, makes no changes to the system, and will only report what it needs to do.

Type: boolean

Default value: false

Example:

    body agent control
    {
    dryrun => "true";
    }
editbinaryfilesize

Description: The value of editbinaryfilesize represents the limit on maximum binary file size to be edited.

This is a global setting for the file-editing safety-net for binary files, and may be overridden on a per-promise basis with max_file_size.

Type: int

Allowed input range: 0,99999999999

Default value: 100k

Example:

    body agent control
    {
    edibinaryfilesize => "10M";
    }

Notes: When setting limits, the limit on editing binary files should generally be set higher than for text files.

editfilesize

Description: The value of editfilesize is the limit on maximum text file size to be edited.

This is a global setting for the file-editing safety-net, and may be overridden on a per-promise basis with max_file_size.

Type: int

Allowed input range: 0,99999999999

Default value: 100000

Example:

    body agent control
    {
    editfilesize => "120k";
    }
environment

Description: The environment slist contains environment variables to be inherited by children.

This may be used to set the runtime environment of the agent process. The values of environment variables are inherited by child commands.

Type: slist

Allowed input range: [A-Za-z0-9_]+=.*

Example:

    body common control
    {
    bundlesequence => { "one" };
    }

    body agent control
    {
    environment => { "A=123", "B=456", "PGK_PATH=/tmp"};
    }

    bundle agent one
    {
    commands:

      "/usr/bin/env";
    }

Some interactive programs insist on values being set, for example:

    # Required by apt-cache, debian

    environment => { "LANG=C"};
expireafter

Description: The value of expireafter is a global default for time before on-going promise repairs are interrupted.

This represents the locking time after which CFEngine will attempt to kill and restart its attempt to keep a promise.

Type: int

Allowed input range: 0,99999999999

Default value: 1 min

Example:

    body action example
    {
    ifelapsed   => "120";   # 2 hours
    expireafter => "240";   # 4 hours
    }

See Also: body action expireafter, body contain exec_timeout, body executor control agent_expireafter

files_single_copy

Description: The files_single_copy slist contains filenames to be watched for multiple-source conflicts.

This list of regular expressions will ensure that files matching the patterns of the list are never copied from more than one source during a single run of cf-agent. This may be considered a protection against accidental overlap of copies from diverse remote sources, or as a first-come-first-served disambiguation tool for lazy-evaluation of overlapping file-copy promises.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body agent control
    {
    files_single_copy => { "/etc/.*", "/special/file" };
    }
files_auto_define

Description: The files_auto_define slist contains filenames to define classes if copied.

Classes are automatically defined by the files that are copied. The file is named according to the prefixed 'canonization' of the file name. Canonization means that non-identifier characters are converted into underscores. Thus /etc/passwd would canonize to _etc_passwd. The prefix auto_ is added to clarify the origin of the class. Thus in the example the copying of /etc/passwd would lead to the class auto__etc_passwd being defined automatically.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body agent control
    {
    files_auto_define => { "/etc/syslog\.c.*", "/etc/passwd" };
    }
hostnamekeys

Deprecated: Host identification is now handled transparently.

Description: The hostnamekeys menu option policy determines whether to label ppkeys by hostname not IP address.

This represents a client side choice to base key associations on host names rather than IP address. This is useful for hosts with dynamic addresses.

Type: boolean

Default value: false

Example:

    body server control
    {
    hostnamekeys => "true";
    }
ifelapsed

Description: The value of ifelapsed is a global default representing the time that must elapse before a promise will be rechecked.

This overrides the global settings. Promises which take a long time to verify should usually be protected with a long value for this parameter. This serves as a resource 'spam' protection. A CFEngine check could easily run every 5 minutes provided resource intensive operations are not performed on every run. Using time classes like Hr12 etc., is one part of this strategy; using ifelapsed is another which is not tied to a specific time.

Type: int

Allowed input range: 0,99999999999

Default value: 1

Example:

    #local

    body action example
    {
    ifelapsed   => "120";   # 2 hours
    expireafter => "240";   # 4 hours
    }

    # global

    body agent control
    {
    ifelapsed   => "180";   # 3 hours
    }
inform

Description: The inform menu option policy sets the default output level 'permanently' within the class context indicated.

It is equivalent to (and when present, overrides) the command line option '-I'.

Type: boolean

Default value: false

Example:

    body agent control
    {
    inform => "true";
    }
intermittency

Deprecated: This attribute does nothing and is kept for backward compatibility.

Type: boolean

Default value: false

max_children

Description: The value of max_children represents the maximum number of background tasks that should be allowed concurrently.

For the run-agent this is the maximum number of forked background processes allowed when parallelizing connections to servers. For the agent it represents the number of background jobs allowed concurrently. Background jobs often lead to contention of the disk resources slowing down tasks considerably; there is thus a law of diminishing returns.

Type: int

Allowed input range: 0,99999999999

Default value: 1 concurrent agent promise

Example:

    body agent control
    {
    max_children => "10";
    }
maxconnections

Description: The value of maxconnections represents the maximum number of outgoing connections to cf-serverd.

Type: int

Allowed input range: 0,99999999999

Default value: 30 remote queries

Example:

    # client side

    body agent control
    {
    maxconnections => "1000";
    }

Notes:

Watch out for kernel limitations for maximum numbers of open file descriptors which can limit this.

mountfilesystems

Description: The mountfilesystems menu option policy determines whether to mount any filesystems promised.

It issues the generic command to mount file systems defined in the file system table.

Type: boolean

Default value: false

Example:

    body agent control
    {
    mountfilesystems => "true";
    }
nonalphanumfiles

Description: The nonalphanumfiles menu option policy determines whether to warn about filenames with no alphanumeric content.

This test is applied in all recursive/depth searches.

Type: boolean

Default value: false

Example:

    body agent control
    {
    nonalphanumfiles => "true";
    }
repchar

Description: The repchar string represents a character used to canonize pathnames in the file repository.

Type: string

Allowed input range: .

Default value: _

Example:

    body agent control
    {
    repchar => "_";
    }

Notes:

refresh_processes

Description: The refresh_processes slist contains bundles to reload the process table before verifying the bundles named in this list (lazy evaluation).

If this list of regular expressions is non-null and an existing bundle is mentioned or matched in this list, CFEngine will reload the process table at the start of the named bundle, each time is is scheduled. If the list is null, the process list will be reloaded at the start of every scheduled bundle.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

    body agent control
    {
    refresh_processes => { "mybundle" };
    #refresh_processes => { "none" };
    }

This examples uses a non-empty list with the name 'none'. This is not a reserved word, but as long as there are no bundles with the name 'none' this has the effect of never reloading the process table. This keeps improves the efficiency of the agent.

History: Was introduced in version 3.1.3, Enterprise 2.0.2 (2010)

secureinput

Description: The secureinput menu option policy checks whether input files are writable by unauthorized users.

If this is set, the agent will not accept an input file that is not owned by a privileged user.

Type: boolean

Default value: false

Example:

    body agent control
    {
    secureinput => "true";
    }
sensiblecount

Description: The value of sensiblecount represents the minimum number of files a mounted filesystem is expected to have.

Type: int

Allowed input range: 0,99999999999

Default value: 2 files

Example:

    body agent control
    {
    sensiblecount => "20";
    }
sensiblesize

Description: The value of sensiblesize represents the minimum number of bytes a mounted filesystem is expected to have.

Type: int

Allowed input range: 0,99999999999

Default value: 1000 bytes

Example:

    body agent control
    {
    sensiblesize => "20K";
    }
skipidentify

Description: The skipidentify menu option policy determines whether to send an IP/name during server connection because address resolution is broken.

Hosts that are not registered in DNS cannot supply reasonable credentials for a secondary confirmation of their identity to a CFEngine server. This causes the agent to ignore its missing DNS credentials.

Type: boolean

Default value: false

Example:

    body agent control
    {
    skipidentify => "true";
    }
suspiciousnames

Description: The suspiciousnames slist contains names to warn about if found during any file search.

If CFEngine sees these names during recursive (depth) file searches it will warn about them.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body agent control
    {
    suspiciousnames => { ".mo", "lrk3", "rootkit" };
    }
syslog

Description: The syslog menu option policy determines whether to switch on output to syslog at the inform level.

Type: boolean

Default value: false

Example:

    body agent control
    {
    syslog => "true";
    }
track_value

Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.

timezone

Description: The timezone slist contains allowed timezones this machine must comply with.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body agent control
    {
    timezone => { "MET", "CET", "GMT+1" };
    }
verbose

Description: The verbose menu option policy determines whether to switch on verbose standard output.

It is equivalent to (and when present, overrides) the command line option '-v'. Sets the default output level 'permanently' for this promise.

Type: boolean

Default value: false

Example:

    body agent control
    {
    verbose => "true";
    }

cf-serverd

cf-serverd is a socket listening daemon providing two services: it acts as a file server for remote file copying and it allows an authorized cf-runagent to start a cf-agent run. cf-agent typically connects to a cf-serverd instance to request updated policy code, but may also request additional files for download. cf-serverd employs role based access control (defined in policy code) to authorize requests.

cf-serverd keeps the promises made in common and server bundles, and is affected by common and server control bodies.

Command reference
  --help        , -h       - Print the help message
  --debug       , -d       - Enable debugging output
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --version     , -V       - Output the version of the software
  --file        , -f value - Specify an alternative input file than the default
  --define      , -D value - Define a list of comma separated classes to be defined at the start of execution
  --negate      , -N value - Define a list of comma separated classes to be undefined at the start of execution
  --no-lock     , -K       - Ignore locking constraints during execution (ifelapsed/expireafter) if "too soon" to run
  --inform      , -I       - Print basic information about changes made to the system, i.e. promises repaired
  --diagnostic  , -x       - Activate internal diagnostics (developers only)
  --no-fork     , -F       - Run as a foreground processes (do not fork)
  --ld-library-path, -L value - Set the internal value of LD_LIBRARY_PATH for child processes
  --generate-avahi-conf, -A       - Generates avahi configuration file to enable policy server to be discovered in the network
  --legacy-output, -l       - Use legacy output format
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
Control Promises

Settings describing the details of the fixed behavioral promises made by cf-serverd. Server controls are mainly about determining access policy for the connection protocol: i.e. access to the server itself. Access to specific files must be granted in addition.

    body server control
    {
        allowconnects         => { "127.0.0.1" , "::1" ,  ".*\.example\.org" };
        allowallconnects      => { "127.0.0.1" , "::1" ,  ".*\.example\.org" };

        # Uncomment me under controlled circumstances
        #trustkeysfrom         => { "127.0.0.1" , "::1" ,  ".*\.example\.org" };
    }
allowallconnects

Description: List of IPs or hostnames that may have more than one connection to the server port

This list of regular expressions matches hosts that are allowed to connect an unlimited number of times up to the maximum connection limit. Without this, a host may only connect once (which is a very strong constraint, as the host must wait for the TCP FIN_WAIT to expire before reconnection can be attempted).

Note that 127.0.0.1 is a regular expression (i.e., "127 any character 0 any character 0 any character 1"), but this will only match the IP address 127.0.0.1. Take care with IP addresses and domain names, as the hostname regular expression www.domain.com will potentially match more than one hostname (e.g., wwwxdomain.com, in addition to the desired hostname www.domain.com).

Type: slist

Allowed input range: (arbitrary string)

Examples:

    allowallconnects      => {
         "127.0.0.1",
         "::1",
         "200\.1\.10\..*",
         "host\.domain\.tld",
         "host[0-9]+\.domain\.com"
         };
allowconnects

Description: List of IPs or hostnames that may connect to the server port

If a client's identity matches an entry in this list it is granted to permission to send data to the server port. Clients who are not in this list may not connect or send data to the server.

See also the warning about regular expressions in allowallconnects.

Type: slist

Allowed input range: (arbitrary string)

Examples:

    allowconnects => {
         "127.0.0.1",
         "::1",
         "200\.1\.10\..*",
         "host\.domain\.tld",
         "host[0-9]+\.domain\.com"
         };
allowlegacyconnects

Description: List of hosts from which the server accepts connections that are not using the latest protocol.

Set this attribute to an empty list to not allow any incoming connections using legacy protocol versions:

    allowlegacyconnects => { }

To define subnets or address ranges, use CIDR notation:

    allowlegacyconnects =>  { "192.168.1.0/24", "192.168.2.123" }

Absence of this attribute means that connections from all hosts are accepted.

Type: slist

Allowed input range: (arbitrary string)

See also: protocol_version

allowciphers

Description: List of ciphers the server accepts.

For a list of possible ciphers, see man page for "openssl ciphers".

Type: string

Allowed input range: (arbitrary string)

Default value: AES256-GCM-SHA384:AES256-SHA

See also: protocol_version

History: Introduced in CFEngine 3.6.0

allowusers

Description: List of usernames who may execute requests from this server

The usernames listed in this list are those asserted as public key identities during client-server connections. These may or may not correspond to system identities on the server-side system.

Type: slist

Allowed input range: (arbitrary string)

Example:

    allowusers => { "cfengine", "root" };
auditing

Deprecated: This menu option policy is deprecated, does nothing and is kept for backward compatibility.

Type: boolean

bindtointerface

Description: IP of the interface to which the server should bind on multi-homed hosts

On multi-homed hosts, the server and client can bind to a specific interface for server traffic. The IP address of the interface must be given as the argument, not the device name.

Type: string

Allowed input range: (arbitrary string)

    bindtointerface => "192.168.1.1";

To bind to all interfaces, including IPV6:

    bindtointerface => "::";

Note that a bug in netstat will not correctly report that cf-serverd is listening on both IPV4 and IPV6 interfaces. A test with netcat (nc) will confirm.

   # nc -v -4 172.16.100.1 5308
   Connection to 172.16.100.1 5308 port [tcp/cfengine] succeeded!
   ^C
   # nc -v -6 fe80:470:1d:a2f::2 5308
   Connection to fe80:470:1d:a2f::2 5308 port [tcp/cfengine] succeeded!
   ^C
cfruncommand

Description: Path to the cf-agent command or cf-execd wrapper for remote execution

It is normal for this to point to the location of cf-agent but it could also point to the cf-execd, or even another program or shell command at your own risk.

Type: string

Allowed input range: .+

    body server control
    {
    cfruncommand => "/var/cfengine/bin/cf-agent";
    }
call_collect_interval

CFEngine Enterprise only.

Description: The interval in minutes in between collect calls to the CFEngine Server offering a tunnel for report collection.

If option time is set, it causes the server daemon to peer with a policy hub by attempting a connection at regular intervals of the value of the parameter in minutes.

This feature is designed to allow Enterprise report collection from hosts that are not directly addressable from a hub data-aggregation process. For example, if some of the clients of a policy hub are behind NAT or firewall then the hub possibly is not able to open a connection to port 5308 of the client. The solution is to enable call_collect_interval on the client's cf-serverd. Note: also remember to admit the client's IP on the hub's collect_calls ACL (see resource_type in bundle server access_rules).

If this option is set, the client's cf-serverd will "peer" with the server daemon on a policy hub. This means that, cf-serverd on an unreachable (e.g. NATed) host will attempt to report in to the cf-serverd on its assigned policy hub and offer it a short time window in which to download reports over the established connection. The effect is to establish a temporary secure tunnel between hosts, initiated from the satellite host end. The connection is made in such a way that host autonomy is not compromised. Either hub may refuse or decline to play their role at any time, in the usual way (avoiding DOS attacks). Normal access controls must be set for communication in both directions.

Collect calling cannot be as efficient as data collection by the cf-hub, as the hub is not able to load balance. Hosts that use this approach should exclude themselves from the cf-hub data collection.

The sequence of events is this:

  • The host's cf-serverd connects to its registered CFEngine Server
  • The host identifies itself to authentication and access control and sends a collect-call pull-request to the server
  • The server might honor this, if the access control grants access.
  • If access is granted, the server has collect_window seconds to initiate a query to the host for its reports.
  • The server identifies itself to authentication and access control and sends a query request to the host to collect the reports.
  • When finished, the host closes the tunnel.

Type: int

Allowed input range: 0,99999999999

Example:

call_collect_interval => "5";

The full configuration would look something like this

        #########################################################
        # Server config
        #########################################################

        body server control
        {
        allowconnects         => { "10.10.10" , "::1" };
        allowallconnects      => { "10.10.10" , "::1" };
        trustkeysfrom         => { "10.10.10" , "::1" };

        call_collect_interval => "5";
        }

        #########################################################

        bundle server access_rules()

        {
        access:

          policy_server::

           "collect_calls"
               resource_type => "query",
                     admit   => { "10.10.10.10" };

          satellite_hosts::

            "delta"
                     comment => "Grant access to cfengine hub to collect report deltas",
               resource_type => "query",
                     admit   => { "policy_hub" };

            "full"
                    comment => "Grant access to cfengine hub to collect full report dump",
              resource_type => "query",
                    admit   => { "policy_hub"  };
        }

History: Was introduced in Enterprise 3.0.0 (2012)

collect_window

CFEngine Enterprise only.

Description: A time in seconds that a collect-call tunnel remains open to a hub to attempt a report transfer before it is closed

Type: int

Allowed input range: 0,99999999999

collect_window => "15";

Default value: 10.

History: Was introduced in Enterprise 3.0.0 (2012)

denybadclocks

Description: true/false accept connections from hosts with clocks that are out of sync

A possible form of attack on the fileserver is to request files based on time by setting the clocks incorrectly. This option prevents connections from clients whose clocks are drifting too far from the server clock (where "too far" is currently defined as "more than an hour off"). This serves as a warning about clock asynchronization and also a protection against Denial of Service attempts based on clock corruption.

Type: boolean

Default value: true

Example:

    body server control
    {
    denybadclocks => "true";
    }
denyconnects

Description: List of IPs or hostnames that may NOT connect to the server port

Hosts or IP addresses that are explicitly denied access. This should only be used in special circumstances. One should never grant generic access to everything and then deny special cases. Since the default server behavior is to grant no access to anything, this list is unnecessary unless you have already granted access to some set of hosts using a generic pattern, to which you intend to make an exception.

See also the warning about regular expressions in allowallconnects.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body server control
    {
    denyconnects => { "badhost\.domain\.evil", "host3\.domain\.com" };
    }
dynamicaddresses

Deprecated: This is now handled transparently.

List of IPs or hostnames for which the IP/name binding is expected to change

The addresses or hostnames here are expected to have non-permanent address-name bindings, we must therefor work harder to determine whether hosts credentials are trusted by looking for existing public keys in files that do not match the current hostname or IP.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body server control
    {
    dynamicaddresses => { "dhcp_.*" };
    }
hostnamekeys

Deprecated: Host identification is now handled transparently.

true/false store keys using hostname lookup instead of IP addresses

Client side choice to base key associations on host names rather than IP address. This is useful for hosts with dynamic addresses.

Type: boolean

Default value: false

Example:

    body server control
    {
    hostnamekeys => "true";
    }
keycacheTTL

Description: Maximum number of hours to hold public keys in the cache

Type: int

Allowed input range: 0,99999999999

Default value: 24

Example:

    body server control
    {
    keycacheTTL => "24";
    }

History: Was introduced in version 3.1.0b1,Enterprise 2.0.0b1 (2010)

logallconnections

Description: true/false causes the server to log all new connections to syslog

If set, the server will record connection attempts in syslog.

Type: boolean

Default value: false

Example:

    body server control
    {
    logallconnections => "true";
    }
logencryptedtransfers

Description: true/false log all successful transfers required to be encrypted

If true the server will log all transfers of files which the server requires to encrypted in order to grant access (see ifencrypted) to syslog. These files are deemed to be particularly sensitive.

Type: boolean

Default value: false

Example:

    body server control
    {
    logencryptedtransfers => "true";
    }
maxconnections

Description: Maximum number of connections that will be accepted

Watch out for kernel limitations for maximum numbers of open file descriptors which can limit this.

Type: int

Allowed input range: 0,99999999999

Default value: 30 remote queries

Example:

    # client side

    body agent control
    {
    maxconnections => "1000";
    }

    # server side

    body server control
    {
    maxconnections => "1000";
    }
port

Description: Default port for the CFEngine server

Type: int

Allowed input range: 1,65535

Default value: 5308

Example:

    body hub control
    {
    port => "5308";
    }

    body server control
    {
    specialhost::
     port => "5308";

    !specialhost::
     port => "5308";
    }

Notes:

The standard or registered port number is tcp/5308. CFEngine does not presently use its registered udp port with the same number, but this could change in the future.

Changing the standard port number is not recommended practice. You should not do it without a good reason.

serverfacility

Description: Menu option for syslog facility level

Type: (menu option)

Allowed input range:

LOG_USER
LOG_DAEMON
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7

See syslog notes.

Default value: LOG_USER

Example:

    body server control
    {
    serverfacility => "LOG_USER";
    }
skipverify

Description: This option is obsolete, does nothing and is retained for backward compatibility.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body server control
    {
    skipverify => { "special_host.*", "192.168\..*" };
    }
trustkeysfrom

Description: List of IPs from whom we accept public keys on trust

If connecting hosts' public keys have not already been trusted, this allows us to accept the keys on trust. Normally this should be an empty list except in controlled circumstances.

See also the warning about regular expressions in allowallconnects.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body server control
    {
    trustkeysfrom => { "10\.0\.1\.1", "192\.168\..*"};
    }
listen

Description: true/false enable server daemon to listen on defined port

This attribute allows to disable cf-serverd from listening on any port. Should be used in conjunction with call_collect_interval.

This setting only applies to CFEngine clients, the policy hub will not be affected. Changing this setting requires a restart of cf-serverd for the change to take effect.

Type: boolean

Default value: true

Example:

    body server control
    {

      listening_host_context::
        listen => "true";

      !listening_host_context::
        listen => "false";
    }

History: Was introduced in 3.4.0, Enterprise 3.0 (2012)


cf-execd

cf-execd is the scheduling daemon for cf-agent. It runs cf-agent locally according to a schedule specified in policy code (executor control body). After a cf-agent run is completed, cf-execd gathers output from cf-agent, and may be configured to email the output to a specified address. It may also be configured to splay (randomize) the execution schedule to prevent synchronized cf-agent runs across a network.

cf-execd keeps the promises made in common bundles, and is affected by common and executor control bodies.

Command reference
  --help        , -h       - Print the help message
  --debug       , -d       - Enable debugging output
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --dry-run     , -n       - All talk and no action mode - make no changes, only inform of promises not kept
  --version     , -V       - Output the version of the software
  --file        , -f value - Specify an alternative input file than the default
  --define      , -D value - Define a list of comma separated classes to be defined at the start of execution
  --negate      , -N value - Define a list of comma separated classes to be undefined at the start of execution
  --no-lock     , -K       - Ignore locking constraints during execution (ifelapsed/expireafter) if "too soon" to run
  --inform      , -I       - Print basic information about changes made to the system, i.e. promises repaired
  --diagnostic  , -x       - Activate internal diagnostics (developers only)
  --no-fork     , -F       - Run as a foreground processes (do not fork)
  --once        , -O       - Run once and then exit (implies no-fork)
  --no-winsrv   , -W       - Do not run as a service on windows - use this when running from a command shell (CFEngine Nova only)
  --ld-library-path, -L value - Set the internal value of LD_LIBRARY_PATH for child processes
  --legacy-output, -l       - Use legacy output format
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
Control Promises

These body settings determine the behavior of cf-execd,including scheduling times and output capture to WORKDIR/outputs and relay via email.

     body executor control
     {
         splaytime  => "5";
         mailto     => "cfengine@example.org";
         mailfrom   => "cfengine@$(host).example.org";
         smtpserver => "localhost";
         schedule   => { "Min00_05", "Min30_35" }
     }
agent_expireafter

Description: Maximum agent runtime (in minutes)

Sets a maximum time on any run of the command in exec_command. If no data is received from the pipe opened to the process created with exec_command after the time has elapsed, the process gets killed.

Note that if you have long-running jobs, they may get killed with this setting. Therefore, you should ensure it is higher than any run of cf-agent that you want to leave alone. Alternatively, you can make your jobs output something to STDOUT at least as often as this threshold. This will reset the timer.

Type: int

Allowed input range: 0,10080

Default value: 10080

Example:

    body executor control
    {
    agent_expireafter => "120";
    }

Notes: The setting will effectively allow you to set a threshold on the number of simultaneous agents that are running. For example, if you set it to 120 and you are using a 5-minute agent schedule, a maximum of 120 / 5 = 24 agents should be enforced.

See Also: body action expireafter, body contain exec_timeout, body agent control expireafter

executorfacility

Description: Menu option for syslog facility level

Type: (menu option)

Allowed input range:

LOG_USER
LOG_DAEMON
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7

See the syslog manual pages.

Default value: LOG_USER

Example:

    body executor control
    {
    executorfacility => "LOG_USER";
    }
exec_command

Description: The full path and command to the executable run by default (overriding builtin)

The command is run in a shell encapsulation so pipes and shell symbols may be used if desired.

Type: string

Allowed input range: "?(/.*)

Example:

exec_command => "$(sys.workdir)/bin/cf-agent -f failsafe.cf && $(sys.workdir)/bin/cf-agent";
mailfrom

Description: Email-address cfengine mail appears to come from

Type: string

Allowed input range: .*@.*

Example:

    body executor control
    {
        mailfrom => "mrcfengine@example.org";
    }
mailmaxlines

Description: Maximum number of lines of output to send by email

This limit prevents anomalously large outputs from clogging up a system administrator's mailbox. The output is truncated in the email report, but the complete original transcript is stored in WORKDIR/outputs/* where it can be viewed on demand. A reference to the appropriate file is given.

Type: int

Allowed input range: 0,1000

Default value: 30

Example:

    body executor control
    {
    mailmaxlines => "100";
    }
mailsubject

Description: The subject in the mail sent by CFEngine.

The subject can contain system variables, like for example IP address or architecture.

Type: string

Allowed input range: .*

Example:

    body executor control
    {
        mailsubject => "CFEngine report ($(sys.fqhost))";
    }
mailto

Description: Email-address cfengine mail is sent to

The address to whom email is sent if an smtp host is configured.

Type: string

Allowed input range: .*@.*

Example:

    body executor control
    {
        mailto => "cfengine_alias@example.org";
    }
schedule

Description: The class schedule used by cf-execd for activating cf-agent

The list should contain class expressions comprised of classes which are visible to the cf-execd daemon. In principle, any defined class expression will cause the daemon to wake up and schedule the execution of the cf-agent. In practice, the classes listed in the list are usually date- and time-based.

The actual execution of cf-agent may be delayed by splaytime, and may be deferred by promise caching and the value of ifelapsed. Note also that the effectiveness of the splayclass function may be affected by changing the schedule.

Type: slist

Allowed input range: (arbitrary string)

Default value:

schedule => { "Min00", "Min05", "Min10", "Min15", "Min20", "Min25",
          "Min30", "Min35", "Min40", "Min45", "Min50", "Min55" };

Example:

    body executor control
    {
    schedule => { "Min00", "(Evening|Night).Min15_20", "Min30", "(Evening|Night).Min45_50" };
    }
smtpserver

Description: Name or IP of a willing smtp server for sending email

This should point to a standard port 25 server without encryption. If you are running secured or encrypted email then you should run a mail relay on localhost and point this to localhost.

Type: string

Allowed input range: .*

Example:

  body executor control
  {
      smtpserver => "smtp.example.org";
  }
splaytime

Description: Time in minutes to splay this host based on its name hash

Whenever any class listed in the schedule attribute is present, cf-execd can schedule an execution of cf-agent. The actual execution will be delayed an integer number of seconds between 0-splaytime minutes. The specific amount of delay for "this" host is based on a hash of the hostname. Thus a collection of hosts will all execute at different times, and surges in network traffic can be avoided.

A general rule for scaling of small updates is to set the splay time to runinterval-1 minutes for up a few thousand hosts. For example, the default schedule executes once every 5 minutes, so the splay time should be set to no more than 4 minutes. The splaytime should be set to a value less than the cf-execd scheduling interval, else multiple clients might contend for data. In other words, splaytime + cf-agent run time should be less than the scheduling interval.

Type: int

Allowed input range: 0,99999999999

Default value: 0

The CFEngine default policy sets splaytime to 1.

Example:

  body executor control
  {
      splaytime => "2";
  }

See also: The splayclass() function for a task-specific means for setting splay times.


cf-promises

cf-promises is a tool for checking CFEngine policy code. It operates by first parsing policy code checking for syntax errors. Second, it validates the integrity of policy consisting of multiple files. Third, it checks for semantic errors, e.g. specific attribute set rules. Finally, cf-promises attempts to expose errors by partially evaluating the policy, resolving as many variable and classes promise statements as possible. At no point does cf-promises make any changes to the system.

In 3.6.0 and later, cf-promises will not evaluate function calls either. This may affect customers who use execresult for instance. Use the new --eval-functions yes command-line option (default is no) to retain the old behavior from 3.5.x and earlier.

cf-agent calls cf-promises to validate the policy before running it. In that case --eval-functions is not specified, so functions are not evaluated prematurely (as you would expect).

Command reference
  --eval-functions, - value - Evaluate functions during syntax checking (may catch more run-time errors). Possible values: 'yes', 'no'. Default is 'yes'
  --show-classes, -       - Show discovered classes, including those defined in common bundles in policy
  --show-vars   , -       - Show discovered variables, including those defined without dependency to user-defined classes in policy
  --help        , -h       - Print the help message
  --bundlesequence, -b value - Use the specified bundlesequence for verification
  --debug       , -d       - Enable debugging output
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --dry-run     , -n       - All talk and no action mode - make no changes, only inform of promises not kept
  --version     , -V       - Output the version of the software
  --file        , -f value - Specify an alternative input file than the default
  --define      , -D value - Define a list of comma separated classes to be defined at the start of execution
  --negate      , -N value - Define a list of comma separated classes to be undefined at the start of execution
  --inform      , -I       - Print basic information about changes made to the system, i.e. promises repaired
  --diagnostic  , -x       - Activate internal diagnostics (developers only)
  --reports     , -r       - Generate reports about configuration and insert into CFDB
  --policy-output-format, -p value - Output the parsed policy. Possible values: 'none', 'cf', 'json'. Default is 'none'. (experimental)
  --syntax-description, -s value - Output a document describing the available syntax elements of CFEngine. Possible values: 'none', 'json'. Default is 'none'.
  --full-check  , -c       - Ensure full policy integrity checks
  --warn        , -W value - Pass comma-separated <warnings>|all to enable non-default warnings, or error=<warnings>|all
  --legacy-output, -l       - Use legacy output format
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
  --tag-release , -T value - Tag a directory with promises.cf with cf_promises_validated and cf_promises_release_id

cf-monitord

cf-monitord is the monitoring daemon for CFEngine. It samples probes defined in policy using measurements type promises and attempts to learn the normal system state based on current and past observations. Current estimates are made available as special variables (e.g. $(mon.av_cpu)) to cf-agent, which may use them to inform policy decisions.

cf-monitord keeps the promises made in commonand monitor bundles, and is affected by common and monitor control bodies.

Command reference
  --help        , -h       - Print the help message
  --debug       , -d       - Enable debugging output
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --dry-run     , -n       - All talk and no action mode - make no changes, only inform of promises not kept
  --version     , -V       - Output the version of the software
  --no-lock     , -K       - Ignore system lock
  --file        , -f value - Specify an alternative input file than the default
  --inform      , -I       - Print basic information about changes made to the system, i.e. promises repaired
  --diagnostic  , -x       - Activate internal diagnostics (developers only)
  --no-fork     , -F       - Run process in foreground, not as a daemon
  --histograms  , -H       - Ignored for backward compatibility
  --tcpdump     , -T       - Interface with tcpdump if available to collect data about network
  --legacy-output, -l       - Use legacy output format
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
Standard measurements:

The cf-monitord service monitors a number of variables as standard on Unix and Windows systems. Windows is fundamentally different from Unix and currently has less support for out-of-the-box probes.

  1. users: Users logged in
  2. rootprocs: Privileged system processes
  3. otherprocs: Non-privileged process
  4. diskfree: Free disk on / partition
  5. loadavg: % kernel load utilization
  6. netbiosns_in: netbios name lookups (in)
  7. netbiosns_out: netbios name lookups (out)
  8. netbiosdgm_in: netbios name datagrams (in)
  9. netbiosdgm_out: netbios name datagrams (out)
  10. netbiosssn_in: netbios name sessions (in)
  11. netbiosssn_out: netbios name sessions (out)
  12. irc_in: IRC connections (in)
  13. irc_out: IRC connections (out)
  14. cfengine_in: CFEngine connections (in)
  15. cfengine_out: CFEngine connections (out)
  16. nfsd_in: nfs connections (in)
  17. nfsd_out: nfs connections (out)
  18. smtp_in: smtp connections (in)
  19. smtp_out: smtp connections (out)
  20. www_in: www connections (in)
  21. www_out: www connections (out)
  22. ftp_in: ftp connections (in)
  23. ftp_out: ftp connections (out)
  24. ssh_in: ssh connections (in)
  25. ssh_out: ssh connections (out)
  26. wwws_in: wwws connections (in)
  27. wwws_out: wwws connections (out)
  28. icmp_in: ICMP packets (in)
  29. icmp_out: ICMP packets (out)
  30. udp_in: UDP dgrams (in)
  31. udp_out: UDP dgrams (out)
  32. dns_in: DNS requests (in)
  33. dns_out: DNS requests (out)
  34. tcpsyn_in: TCP sessions (in)
  35. tcpsyn_out: TCP sessions (out)
  36. tcpack_in: TCP acks (in)
  37. tcpack_out: TCP acks (out)
  38. tcpfin_in: TCP finish (in)
  39. tcpfin_out: TCP finish (out)
  40. tcpmisc_in: TCP misc (in)
  41. tcpmisc_out: TCP misc (out)
  42. webaccess: Webserver hits
  43. weberrors: Webserver errors
  44. syslog: New log entries (Syslog)
  45. messages: New log entries (messages)
  46. temp0: CPU Temperature core 0
  47. temp1: CPU Temperature core 1
  48. temp2: CPU Temperature core 2
  49. temp3: CPU Temperature core 3
  50. cpu: %CPU utilization (all)
  51. cpu0: %CPU utilization core 0
  52. cpu1: %CPU utilization core 1
  53. cpu2: %CPU utilization core 2
  54. cpu3: %CPU utilization core 3

Slots with a higher number are used for custom measurement promises in CFEngine Enterprise.

These values collected and analyzed by cf-monitord are transformed into agent variables in the $(mon.name) context.

Note: There is no way for force a refresh of the monitored data.

Control Promises

Settings describing the details of the fixed behavioral promises made by cf-monitord. The system defaults will be sufficient for most users. This configurability potential, however, will be a key to developing the integrated monitoring capabilities of CFEngine.

    body monitor control
    {
        #version => "1.2.3.4";

        forgetrate => "0.7";
        tcpdump => "false";
        tcpdumpcommand => "/usr/sbin/tcpdump -i eth1 -n -t -v";
    }
forgetrate

Description: Decimal fraction [0,1] weighting of new values over old in 2d-average computation

Configurable settings for the machine-learning algorithm that tracks system behavior. This is only for expert users. This parameter effectively determines (together with the monitoring rate) how quickly CFEngine forgets its previous history.

Type: real

Allowed input range: 0,1

Default value: 0.6

Example:

    body monitor control
    {
    forgetrate => "0.7";
    }
histograms

Deprecated: Ignored, kept for backward compatibility

cf-monitord now always keeps histograms information, so this option is a no-op kept for backward compatibility. It used to cause CFEngine to learn the conformally transformed distributions of fluctuations about the mean.

Type: boolean

Default value: true

Example:

    body monitor control
    {
    histograms => "true";
    }
monitorfacility

Description: Menu option for syslog facility

Type: (menu option)

Allowed input range:

LOG_USER
LOG_DAEMON
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7

Default value: LOG_USER

Example:

body monitor control
{
monitorfacility => "LOG_USER";
}
tcpdump

Description: true/false use tcpdump if found

Interface with TCP stream if possible.

Type: boolean

Default value: false

body monitor control
{
tcpdump => "true";
}
tcpdumpcommand

Description: Path to the tcpdump command on this system

If this is defined, the monitor will try to interface with the TCP stream and monitor generic package categories for anomalies.

Type: string

Allowed input range: "?(/.*)

Example:

    body monitor control
    {
    tcpdumpcommand => "/usr/sbin/tcpdump -i eth1";
    }

cf-key

The CFEngine key generator makes key pairs for remote authentication.

Command reference
  --help        , -h       - Print the help message
  --debug       , -d       - Enable debugging output
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --version     , -V       - Output the version of the software
  --output-file , -f value - Specify an alternative output file than the default (localhost)
  --show-hosts  , -s       - Show lastseen hostnames and IP addresses
  --remove-keys , -r value - Remove keys for specified hostname/IP
  --force-removal, -x       - Force removal of keys (USE AT YOUR OWN RISK)
  --install-license, -l value - Install license file on Enterprise server (CFEngine Enterprise Only)
  --print-digest, -p value - Print digest of the specified public key
  --trust-key   , -t value - Make cf-serverd/cf-agent trust the specified public key. Argument value is of the form [[USER@]IPADDR:]FILENAME where FILENAME is the local path of the public key for client at IPADDR address
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'

cf-runagent

cf-runagent connects to a list of running instances of cf-serverd. It allows foregoing the usual cf-execd schedule to activate cf-agent. Additionally, a user may send classes to be defined on the remote host. Two kinds of classes may be sent: classes to decide on which hosts cf-agent will be started, and classes that the user requests cf-agent should define on execution. The latter type is regulated by cf-serverd's role based access control.

Command reference
  --help        , -h       - Print the help message
  --background  , -b value - Parallelize connections (50 by default)
  --debug       , -d       - Enable debugging output
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --dry-run     , -n       - All talk and no action mode - make no changes, only inform of promises not kept
  --version     , -V       - Output the version of the software
  --file        , -f value - Specify an alternative input file than the default
  --define-class, -D value - Define a list of comma separated classes to be sent to a remote agent
  --select-class, -s value - Define a list of comma separated classes to be used to select remote agents by constraint
  --inform      , -I       - Print basic information about changes made to the system, i.e. promises repaired
  --remote-options, -o value - Pass options to a remote server process
  --diagnostic  , -x       - Activate internal diagnostics (developers only)
  --hail        , -H value - Hail the following comma-separated lists of hosts, overriding default list
  --interactive , -i       - Enable interactive mode for key trust
  --timeout     , -t value - Connection timeout, seconds
  --legacy-output, -l       - Use legacy output format
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
Control Promises

Settings describing the details of the fixed behavioral promises made by cf-runagent. The most important parameter here is the list of hosts that the agent will poll for connections. This is easily read in from a file list, however when doing so always have a stable input source that does not depend on the network (including a database or directory service) in any way: introducing such dependencies makes configuration brittle.

     body runagent control
     {
         # default port is 5308
         hosts => { "127.0.0.1:5308", "eternity.iu.hio.no:80", "slogans.iu.hio.no" };

         #output_to_file => "true";
     }
hosts

Description: List of host or IP addresses to attempt connection with

The complete list of contactable hosts. The values may be either numerical IP addresses or DNS names, optionally suffixed by a ':' and a port number. If no port number is given, the default CFEngine port 5308 is assumed.

Type: slist

Allowed input range: (arbitrary string)

Example:

    body runagent control
    {
    network1::
      hosts => { "host1.example.org", "host2", "host3" };

    network2::
      hosts => { "host1.example.com", "host2", "host3" };
    }
port

Description: Default port for CFEngine server

Type: int

Allowed input range: 1,65535

Default value: 5308

Example:

    body hub control
    {
    port => "5308";
    }

    body server control
    {
    specialhost::
     port => "5308";

    !specialhost::
     port => "5308";
    }

Notes:

The standard or registered port number is tcp/5308. CFEngine does not presently use its registered udp port with the same number, but this could change in the future.

Changing the standard port number is not recommended practice. You should not do it without a good reason.

force_ipv4

Description: true/false force use of ipv4 in connection

Type: boolean

Default value: false

Example:

    body copy_from example
    {
    force_ipv4 => "true";
    }

Notes: IPv6 should be harmless to most users unless you have a partially or misconfigured setup.

trustkey

Description: true/false automatically accept all keys on trust from servers

If the server's public key has not already been trusted, this allows us to accept the key in automated key-exchange.

Note that, as a simple security precaution, trustkey should normally be set to 'false', to avoid key exchange with a server one is not one hundred percent sure about, though the risks for a client are rather low. On the server-side however, trust is often granted to many clients or to a whole network in which possibly unauthorized parties might be able to obtain an IP address, thus the trust issue is most important on the server side.

As soon as a public key has been exchanged, the trust option has no effect. A machine that has been trusted remains trusted until its key is manually revoked by a system administrator. Keys are stored in WORKDIR/ppkeys.

Type: boolean

Default value: false

Example:

    body copy_from example
    {
    trustkey => "true";
    }
encrypt

Description: true/false encrypt connections with servers

Client connections are encrypted with using a Blowfish randomly generated session key. The initial connection is encrypted using the public/private keys for the client and server hosts.

Type: boolean

Default value: false

Example:

    body copy_from example
    {
    servers  => { "remote-host.example.org" };
    encrypt => "true";
    }
background_children

Description: true/false parallelize connections to servers

Causes cf-runagent to attempt parallelized connections to the servers.

Type: boolean

Default value: false

Example:

    body runagent control
    {
    background_children => "true";
    }
max_children

Description: Maximum number of simultaneous connections to attempt

For the run-agent this represents the maximum number of forked background processes allowed when parallelizing connections to servers. For the agent it represents the number of background jobs allowed concurrently. Background jobs often lead to contention of the disk resources slowing down tasks considerably; there is thus a law of diminishing returns.

Type: int

Allowed input range: 0,99999999999

Default value: 50 runagents

Example:

    body runagent control
    {
    max_children => "10";
    }
output_to_file

Description: true/false whether to send collected output to file(s)

Filenames are chosen automatically and placed in the WORKDIR/outputs/hostname_runagent.out.

Type: boolean

Default value: false

Example:

    body runagent control
    {
    output_to_file => "true";
    }
output_directory

Description: Directory where the output is stored

Defines the location for parallelized output to be saved when running cf-runagent in parallel mode.

Type: string

Allowed input range: "?(/.*)

Example:

    body runagent control
    {
    output_directory => "/tmp/run_output";
    }

History: Was introduced in version 3.2.0, Enterprise 2.1.0 (2011)

timeout

Description: Connection timeout in seconds

Type: int

Allowed input range: 1,9999

Examples:

    body runagent control
    {
    timeout => "10";
    }

cf-hub

cf-hub connects to cf-serverd instances to collect data about a host managed by CFEngine. cf-agent and cf-monitord both store data at host in local databases. cf-hub connects to a cf-serverd instance running at a host and collect the data into its own central database. cf-hub automatically schedules data collection from hosts that have registered a connection with a collocated cf-serverd

cf-hub keeps the promises made in common, and is affected by common and hub control bodies.

cf-hub collects data generated from the default run only, what you'd get if you ran cf-agent without specifying a file name. This is to avoid reporting on data generated by test or extraordinary executions.

Command reference
  --continuous  , -c       - Continuous update mode of operation
  --debug       , -d value - Set debugging level 0,1,2,3
  --no-fork     , -F       - Run as a foreground processes (do not fork)
  --file        , -f value - Specify an alternative input file than the default
  --help        , -h       - Print the help message
  --no-lock     , -K       - Ignore locking constraints during execution (ifelapsed/expireafter) if "too soon" to run
  --logging     , -l       - Enable logging of report collection and maintenance to hub_log in the working directory
  --dry-run     , -n       - All talk and no action mode - make no changes, only inform of promises not kept
  --splay_updates, -s       - Splay/load balance full-updates, overriding bootstrap times, assuming a default 5 minute update schedule.
  --query       , -q value - Collect reports from remote host. Value is 'full' or 'delta'. -H option is required.
  --query-host  , -H value - Remote hosts to gather reports from (for -q)
  --version     , -V       - Output the version of the software
  --verbose     , -v       - Output verbose information about the behaviour of the agent
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
Control Promises
    body hub control
    {
    export_zenoss => "/var/www/reports/summary.z";
    }
export_zenoss

Description: Generate report for Zenoss integration

Type: string

Allowed input range: .+

Example:

body hub control
{
am_policy_hub::

  export_zenoss => "/var/www/reports/summary.z";
}

Notes:

For integration with the Zenoss monitoring software.

History: Was introduced in version 3.1.0b1, Enterprise 2.0.0b1 (2010)

exclude_hosts

Description: A list of IP addresses of hosts to exclude from report collection

This list of IP addresses will not be queried for reports by cf-hub, even though they are in the last-seen database.

The lists may contain network addresses in CIDR notation or regular expressions to match the IP address. However, host names are currently not supported.

Type: slist

Allowed input range: (arbitrary string)

Example:

body hub control
{
exclude_hosts => { "192.168.12.21", "10.10", "10.12.*" };
}

Notes:

History: Was introduced in 3.3.0, Enterprise 2.1.1 (2011)

hub_schedule

Description: The class schedule used by cf-hub for report collation

Type: slist

Allowed input range: (arbitrary string)

Example:

body hub control
{
hub_schedule => { "Min00", "Min30", "(Evening|Night).Min45_50" };
}

History: Was introduced in version 3.1.0b1, Enterprise 2.0.0b1 (2010)

port

Description: Default port for contacting hosts

Type: int

Allowed input range: 1024,99999

Default value: 5308

Examples:

    body hub control
    {
    port => "5308";
    }

    body server control
    {
    specialhost::
     port => "5308";

    !specialhost::
     port => "5308";
    }

Notes:

The standard or registered port number is tcp/5308. CFEngine does not presently use its registered udp port with the same number, but this could change in the future.

Changing the standard port number is not recommended practice. You should not do it without a good reason.

client_history_timeout

Description: If the hub can't reach a client for this many (or more) hours, it will not collect the missed reports and it will continue collection from current time. This is done to speed-up report collection and minimize data transfer. The default value is 6 hours.

Type: int

Allowed input range: 1,65535

Default value: 6

Examples:

    body hub control
    {
    client_history_timeout => 6;
    }

History: Was introduced in version 3.6.4 and is not compatible with older CFEngine versions. <!--- End include: /home/jenkins/workspace/build-documentation-3.6/label/DOCUMENTATION_x86_64_linux_debian_6/documentation/reference/components/cf-hub.markdown -->


file control

    body file control
    {
    namespace => "name1";
    }

    bundle agent private
    {
    ....
    }

History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)

This directive can be given multiple times within any file, outside of body and bundle definitions.

Only soft classes from common bundles can be used in class decisions inside file control bodies.

inputs

Description: The inputs slist contains additional filenames to parse for promises.

The filenames specified are all assumed to be relative to the directory of the file which references them. Use an absolute file name if you need an absolute path. Use sys.libdir (absolute library path), sys.local_libdir (library path relative to the current masterfiles), and this.promise_dirname (the directory of the currently processed file) to avoid hard-coding paths.

See also: inputs in body common control

History: Was introduced in CFEngine 3.6.0

namespace

Description: The namespace string identifies a private namespace to switch to in order to protect the current file from duplicate definitions.

Type: string

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

    body file control
    {
    namespace => "name1";
    }

Notes:

History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)

This directive can be given within any file, outside of body and bundle definitions, to change the namespace of subsequent bundles and bodies.


Promise Types and Attributes

Within a bundle, the promise types are executed in a round-robin fashion in the following normal ordering. Which promise types are available depends on the bundle type:

Promise Type common agent server monitor
meta - information about promise bundles x x x x
vars - a variable, representing a value x x x x
defaults - a default value for bundle parameters x x x x
classes - a class, representing a state of the system x x x x
users - add or remove users x
files - configure a file x
packages - install a package x
guest_environments x
methods - take on a whole bundle of other promises x
processes - start or terminate processes x
services - start or stop services x
commands - execute a command x
storage - verify attached storage x
databases - configure a database x
access - grant or deny access to file objects x
roles - allow certain users to activate certain classes x
measurements - measure or sample data from the system x
reports - report a message x x x x

See each promise type's reference documentation for detailed lists of available attributes.

Common Attributes

The following attributes are available to all promise types.

action

Type: body action

action_policy

Description: Determines whether to repair or report about non-kept promises

The action settings allow general transaction control to be implemented on promise verification. Action bodies place limits on how often to verify the promise and what classes to raise in the case that the promise can or cannot be kept.

Type: (menu option)

Allowed input range:

    fix
    warn
    nop

Example:

The following example shows a simple use of transaction control:

     body action warn_only
     {
     action_policy => "warn";
     ifelapsed => "60";
     }

Note that actions can be added to sub-bundles like methods and editing bundles, and that promises within these do not inherit action settings at higher levels. Thus, in the following example there are two levels of action setting:

     body common control
     {
     bundlesequence  => { "testbundle" };
     }

     bundle agent testbundle
     {
     files:

       "/var/cfengine/inputs/.*"

            edit_line => DeleteLinesMatching(".*cfenvd.*"),
            action => WarnOnly;
     }

     bundle edit_line DeleteLinesMatching(regex)
     {
       delete_lines:

         "$(regex)" action => WarnOnly;

     }

     body action WarnOnly
     {
       action_policy => "warn";
     }

Notes: The action setting for the files promise means that file edits will not be committed to disk, only warned about. This is a master-level promise that overrides anything that happens during the editing. The action setting in the edit_line bundle means that the internal memory modeling of the file will only warn about changes rather than committing them to the memory model. This makes little difference to the end result, but it means that CFEngine will report

    Need to delete line - ... - but only a warning was promised

Instead of

    Deleting the promised line ... Need to save file - but only a warning was promised

In either case, no changes will be made to the disk, but the messages given by cf-agent will differ.

ifelapsed

Description: The number of minutes before next allowed assessment of a promise is set using ifelapsed. This overrides the global settings. Promises which take a long time to verify should usually be protected with a long value for this parameter.

This serves as a resource 'spam' protection. A CFEngine check could easily run every 5 minutes provided resource intensive operations are not performed on every run. Using time classes such as Hr12 is one part of this strategy; using ifelapsed is another, which is not tied to a specific time.

Type: int

Allowed input range: 0,99999999999

Default value: control body value

Example:

     #local

     body action example
     {
     ifelapsed   => "120";  # 2 hours
     expireafter => "240";  # 4 hours
     }

     # global

     body agent control
     {
     ifelapsed   => "180";  # 3 hours
     }
expireafter

Description: The Number of minutes a promise is allowed to run before the agent is terminated.

Note: Not to be confused with body contain exec_timeout in commands type promises, the original agent does not terminate the promise. When a subsequent agent notices that a promise actuation has persisted for longer than expireafter the subsequent agent will kill the agent that appears to be stuck on the long running promise.

Type: int

Allowed input range: 0,99999999999

Default value: control body value

Example:

     body action example
     {
     ifelapsed   => "120";  # 2 hours
     expireafter => "240";  # 4 hours
     }

See Also: body contain exec_timeout, body agent control expireafter, body executor control agent_expireafter

log_string

Description: The message to be written to the log when a promise verification leads to a repair.

The log_string works together with log_kept, log_repaired, and log_failed to define a string for logging to one of the named files depending on promise outcome, or to standard output if the log file is stipulated as stdout. Log strings on standard output are denoted by an L: prefix.

Note that log_string does not interact with log_level, which is about regular system output messages.

Type: string

Allowed input range: (arbitrary string)

Example:

    promise-type:

     "promiser"

       attr => "value",
       action => log_me("checked $(this.promiser) in promise $(this.handle)");

    # ..

    body action log_me(s)
    {
    log_string => "$(s)";
    }

Hint: The promise handle $(this.handle) can be a useful referent in a log message, indicating the origin of the message. In CFEngine Enterprise, promise handles make it easy to interpret report data.

log_kept
log_repaired
log_failed

Description: The names of files to which log_string will be saved for kept, repaired and failed promises.

When used together with log_string, the current promise will log its status using the log string to the respective file.

If these log names are absent, the default logging destination for the log string is syslog, but only for non-kept promises. Only the log_string is affected by this setting. Other messages destined for logging are sent to syslog.

Type: string

Allowed input range: stdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)

This string should be the full path to a text file which will contain the log, or one of the following special values:

  • stdout

Send the log message to the standard output, prefixed with an L: to indicate a log message.

  • udp_syslog

Log messages to syslog_host as defined in body common control over UDP. Please note UDP is unreliable.

Example:

     bundle agent test
     {
     vars:

       "software" slist => { "/root/xyz", "/tmp/xyz" };

     files:

       "$(software)"

         create => "true",
          action => logme("$(software)");

     }

     body action logme(x)
     {
     log_kept => "/tmp/private_keptlog.log";
     log_failed => "/tmp/private_faillog.log";
     log_repaired => "/tmp/private_replog.log";
     log_string => "$(sys.date) $(x) promise status";
     }

     body action immediate_syslog(x)
     {
     log_repaired => "udp_syslog";
     log_string => "CFEngine repaired promise $(this.handle) - $(x)";
     }

It is intended that named file logs should be different for the three cases: promise kept, promise not kept and promise repaired.

log_level

Description: Describes the reporting level sent to syslog.

Use this as an alternative to auditing if you wish to use the syslog mechanism to centralize or manage messaging from CFEngine. A backup of these messages will still be kept in WORKDIR/outputs if you are using cf-execd.

On the native Windows version of CFEngine Enterprise, using verbose will include a message when the promise is kept or repaired in the event log.

Type: (menu option)

Allowed input range:

    inform
    verbose
    error
    log

Example:

     body action example
     {
     log_level => "inform";
     }
log_priority

Type: (menu option)

Allowed input range:

    emergency
    alert
    critical
    error
    warning
    notice
    info
    debug

Description: The log_priority menu option policy is the priority level of the log message, as interpreted by a syslog server. It determines the importance of messages from CFEngine.

Example:

     body action low_priority
     {
     log_priority => "info";
     }
value_kept

Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.

value_repaired

Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.

value_notkept

Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.

audit

Description: A true/false switch for detailed audit records of a promise.

If this is set, CFEngine will perform auditing on this specific promise. This means that all details surrounding the verification of the current promise will be recorded in the audit database.

Type: boolean

Default value: false

Example:

     body action example
     {
     # ...

     audit => "true";
     }
background

Description: A true/false switch for parallelizing the promise repair.

If possible, perform the verification of the current promise in the background. This is advantageous only if the verification might take a significant amount of time, e.g. in remote copying of filesystem/disk scans.

On the Windows version of CFEngine Enterprise, this can be useful if we don't want to wait for a particular command to finish execution before checking the next promise. This is particular for the Windows platform because there is no way that a program can start itself in the background here; in other words, fork off a child process. However, file operations can not be performed in the background on Windows.

Type: boolean

Default value: false

Example:

     body action example
     {
     background => "true";
     }
report_level

Description: Defines the reporting level for standard output for this promise.

cf-agent can be run in verbose mode (-v), inform mode (-I) and just print errors (no arguments). This attribute allows to set these three output levels on a per promise basis, allowing the promise to be more verbose than the global setting (but not less).

Type: (menu option)

Allowed input range:

    inform
    verbose
    error
    log

Default value: none

Example:

     body action example
     {
     report_level => "verbose";
     }
measurement_class

Description: If set, performance will be measured and recorded under this identifier.

By setting this string you switch on performance measurement for the current promise, and also give the measurement a name.

Type: string

Allowed input range: (arbitrary string)

Example:

     body action measure
     {
     measurement_class => "$(this.promiser) long job scan of /usr";
     }

The identifier forms a partial identity for optional performance scanning of promises of the form:

    ID:promise-type:promiser.
classes

Type: body classes

scope

Description: Scope of the class set by this body.

Type: (menu option)

Allowed input range:

    namespace
    bundle

Default value: namespace

Example:

    body classes bundle_class
    {
      scope => "bundle";
      promise_kept => { "bundle_context" };
    }

History: This attribute was introduced in CFEngine 3.5

See also: scope in classes promises

promise_repaired

Description: Classes to be defined globally if the promise was 'repaired'.

If the classes are set, a corrective action had to be taken to keep the promise.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     promise_repaired => { "change_happened" };
     }

Important: Complex promises can report misleadingly; for example, files promises that set multiple parameters on a file simultaneously.

The classes for different parts of a promise are not separable. Thus, if you promise to create and file and change its permissions, when the file exists with incorrect permissions, cf-agent will report that the promise_kept for the file existence, but promise_repaired for the permissions. If you need separate reports, you should code two separate promises rather than 'overloading' a single one.

repair_failed

Description: Classes to be defined globally if the promise could not be kept.

If the classes are set, the corrective action to keep the promise failed for some reason.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     repair_failed => { "unknown_error" };
     }
repair_denied

Description: Classes to be defined globally if the promise could not be repaired due to denied access to required resources.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     repair_denied => { "permission_failure" };
     }

In the above example, a promise could not be kept because access to a key resource was denied.

repair_timeout

Description: Classes to be defined globally if the promise could not be repaired due to timeout.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     repair_timeout => { "too_slow", "did_not_wait" };
     }

In the above example, a promise maintenance repair timed-out waiting for some dependent resource.

promise_kept

Description: Classes to be defined globally if the promise was kept without any corrective action.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     promise_kept => { "success", "kaplah" };
     }

The class in the above example is set if no action was necessary by cf-agent, because the promise concerned was already kept without further action required.

Note: Complex promises can report misleadingly. For example, filespromises that set multiple parameters on a file simultaneously.

The classes for different parts of a promise are not separable. Thus, if you promise to create and file and change its permissions, when the file exists with incorrect permissions, cf-agent will report that the promise_kept for the file existence, but promise_repaired for the permissions. If you need separate reports, you should code two separate promises rather than 'overloading' a single one.

cancel_kept

Description: Classes to be canceled if the promise is kept.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     cancel_kept => { "success", "kaplah" };
     }

In the above example, if the promise was already kept and nothing was done, cancel (undefine) any of the listed classes so that they are no longer defined.

History: This attribute was introduced in CFEngine version 3.0.4 (2010)

cancel_repaired

Description: Classes to be canceled if the promise is repaired.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     cancel_repaired => { "change_happened" };
     }

In the above example, if the promise was repaired and changes were made to the system, cancel (undefine) any of the listed classes so that they are no longer defined.

History: This attribute was introduced in CFEngine version 3.0.4 (2010)

cancel_notkept

Description: Classes to be canceled if the promise is not kept for any reason.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Note that any strings passed to this list are automatically canonified, so it is unnecessary to call a canonify function on such inputs.

Example:

     body classes example
     {
     cancel_notkept => { "failure" };
     }

In the above example, if the promise was not kept but nothing could be done, cancel (undefine) any of the listed classes so that they are no longer defined.

History: This attribute was introduced in CFEngine version 3.0.4 (2010)

kept_returncodes

Description: Return codes that indicate a kept commands promise.

Currently, the attribute has impact on the following command-related promises:

  • All promises of type commands:
  • files-promises containing a transformer-attribute
  • The package manager change command in packages-promises (e.g. the command for add, remove, etc.)

If none of the attributes kept_returncodes, repaired_returncodes, or failed_returncodes are set, the default is to consider a return code zero as promise repaired, and nonzero as promise failed.

Type: slist

Allowed input range: [-0-9_$(){}\[\].]+

Note that the return codes may overlap, so multiple classes may be set from one return code. In Unix systems the possible return codes are usually in the range from 0 to 255.

Example:

     bundle agent cmdtest
     {
     commands:
       "/bin/false"
        classes => example;

     reports:
       waskept::
         "The command-promise was kept!";
     }

     body classes example
     {
     kept_returncodes => { "0", "1" };
     promise_kept => { "waskept" };
     }

In the above example, a list of integer return codes indicates that a command-related promise has been kept. This can in turn be used to define classes using the promise_kept attribute, or merely alter the total compliance statistics.

History: Was introduced in version 3.1.3, Nova 2.0.2 (2010)

repaired_returncodes

Description: Return codes that indicate a repaired commands promise

Currently, the attribute has impact on the following command-related promises:

  • All promises of type commands:
  • files-promises containing a transformer-attribute
  • The package manager change command in packages-promises (e.g. the command for add, remove, etc.)

If none of the attributes kept_returncodes, repaired_returncodes, or failed_returncodes are set, the default is to consider a return code zero as promise repaired, and nonzero as promise failed.

Type: slist

Allowed input range: [-0-9_$(){}\[\].]+

Note that the return codes may overlap, so multiple classes may be set from one return code. In Unix systems the possible return codes are usually in the range from 0 to 255.

Example:

     bundle agent cmdtest
     {
     commands:
       "/bin/false"
        classes => example;

     reports:
       wasrepaired::
         "The command-promise got repaired!";
     }

     body classes example
     {
     repaired_returncodes => { "0", "1" };
     promise_repaired => { "wasrepaired" };
     }

In the above example, a list of integer return codes indicating that a command-related promise has been repaired. This can in turn be used to define classes using the promise_repaired attribute, or merely alter the total compliance statistics.

History: Was introduced in version 3.1.3, Nova 2.0.2 (2010)

failed_returncodes

Description: A failed_returncodes slist contains return codes indicating a failed command-related promise.

Currently, the attribute has impact on the following command-related promises:

  • All promises of type commands:
  • files-promises containing a transformer-attribute
  • The package manager change command in packages-promises (e.g. the command for add, remove, etc.)

If none of the attributes kept_returncodes, repaired_returncodes, or failed_returncodes are set, the default is to consider a return code zero as promise repaired, and nonzero as promise failed.

Type: slist

Allowed input range: [-0-9_$(){}\[\].]+

Note that the return codes may overlap, so multiple classes may be set from one return code. In Unix systems the possible return codes are usually in the range from 0 to 255.

Example:

     body common control
     {
     bundlesequence => { "cmdtest" };
     }

     bundle agent cmdtest
     {
     files:
     "/tmp/test"
       copy_from => copy("/etc/passwd");


     "/tmp/test"
       classes => example,
       transformer => "/bin/grep -q lkajfo999999 $(this.promiser)";

     reports:
       hasfailed::
         "The files-promise failed!";
     }

     body classes example
     {
     failed_returncodes => { "1" };
     repair_failed => { "hasfailed" };
     }

     body copy_from copy(file)
     {
     source => "$(file)";
     }

The above example contains a list of integer return codes indicating that a command-related promise has failed. This can in turn be used to define classes using the promise_repaired attribute, or merely alter the total compliance statistics.

History: Was introduced in version 3.1.3, Nova 2.0.2 (2010)

persist_time

Description: The number of minutes the specified classes should remain active.

By default classes are ephemeral entities that disappear when cf-agent terminates. By setting a persistence time, they can last even when the agent is not running.

Type: int

Allowed input range: 0,99999999999

Example:

     body classes example
     {
     persist_time => "10";
     }
timer_policy

Description: Determines whether a persistent class restarts its counter when rediscovered.

In most cases resetting a timer will give a more honest appraisal of which classes are currently important, but if we want to activate a response of limited duration as a rare event then an absolute time limit is useful.

Type: (menu option)

Allowed input range:

    absolute
    reset

Default value: reset

Example:

     body classes example
     {
     timer_policy => "reset";
     }
comment

Description: Describes the real intention of the promise.

Comments written in code follow the program, they are not merely discarded; they appear in reports and error messages.

Type: string

Allowed input range: (arbitrary string)

Example:

    comment => "This comment follows the data for reference ...",
depends_on

Description: A list of promise handles for promises that must have an outcome of KEPT or REPAIRED in order for the promise to be actuated.

This is a list of promise handles for whom this promise is a promisee. In other words, we acknowledge that this promise will be affected by the list of promises whose handles are specified. It has the effect of partially ordering promises.

As of version 3.4.0, this feature may be considered short-hand for setting classes. If one promise depends on a list of others, it will not be verified unless the dependent promises have already been verified and kept: in other words, as long as the dependent promises are either kept or repaired the dependee can be verified.

Handles in other namespaces may be referred to by namespace:handle.

Type: slist

Allowed input range: (arbitrary string)

Example:

body common control
{
      bundlesequence => { "one"  };
}

bundle agent one
{
  reports:

      "two"
      depends_on => { "handle_one" };

      "one"
      handle => "handle_one";

}

This policy can be found in /var/cfengine/share/doc/examples/depends_on.cf and downloaded directly from github.

handle

Description: A unique id-tag string for referring to this as a promisee elsewhere.

A promise handle allows you to refer to a promise as the promisee of depends_on client of another promise. Handles are essential for mapping dependencies and performing impact analyses.

Type: string

Allowed input range: (arbitrary string)

Handles may consist of regular identifier characters. CFEngine automatically `canonifies' the names of handles to conform to this standard.

Example:

access:

  "/source"

    handle  => "update_rule",
    admit   => { "127.0.0.1" };

Notes: If the handle name is based on a variable, and the variable fails to expand, the handle will be based on the name of the variable rather than its content.

ifvarclass

Description: Describes extended classes ANDed with context.

This is an additional class expression that will be evaluated after the class:: classes have selected promises. It is provided in order to enable a channel between variables and classes.

The result is thus the logical AND of the ordinary classes and the variable classes.

Type: string

Allowed input range: (arbitrary string)

Example:

The generic example has the form:

     promise-type:

       "promiser"

         ifvarclass = "$(program)_running|($(program)_notfoundHr12)";

A specific example would be:

    bundle agent example
    {
    commands:

     any::

        "/bin/echo This is linux"

           ifvarclass => "linux";

        "/bin/echo This is solaris"

           ifvarclass => "solaris";
    }

This function is provided so that one can form expressions that link variables and classes. For example:

    # Check that all components are running

    vars:

      "component" slist => { "cf-monitord", "cf-serverd" };

    processes:

      "$(component)" restart_class => canonify("start_$(component)");

    commands:

       "/var/cfengine/bin/$(component)"

           ifvarclass => canonify("start_$(component)");

Notice that the function canonify() is provided to convert a general variable input into a string composed only of legal characters, using the same algorithm that CFEngine uses.

meta

Description: User-data associated with policy, e.g. key=value strings.

It is sometimes convenient to attach meta-data of a more technical nature to policy. It may be used for arbitrary key=value strings for example.

Note that the inventory reporting of CFEngine Enterprise 3.6 and later uses the meta attributes inventory and attribute_name=, so these should be considered reserved for this purpose.

Type: slist

Allowed input range: (arbitrary string)

Example:

files:

  "/etc/special_file"

    comment => "Special file is a requirement. Talk to Fred X.",
    create => "true",

    meta => { "owner=John",  "version=2.0" };

History: Was introduced in 3.3.0, Nova 2.2.0 (2012)


roles

Roles promises are server-side decisions about which users are allowed to define soft-classes on the server's system during remote invocation of cf-agent. This implements a form of Role Based Access Control (RBAC) for pre-assigned class-promise bindings. The user names cited must be attached to trusted public keys in order to be accepted. The regular expression is anchored, meaning it must match the entire name.

    roles:

    "regex"

       authorize = { "usernames", ... };

It is worth re-iterating here that it is not possible to send commands or modify promise definitions by remote access. At best users may try to send classes when using cf-runagent in order to activate sleeping promises. This mechanism limits their ability to do this.

bundle server access_rules()

{
roles:

  # Allow mark

  "Myclass_.*"  authorize => { "mark" };
}

In this example user mark is granted permission to remotely activate classes matching the regular expression Myclass_.* hen using the cf-runagent to activate CFEngine.


Attributes
authorize

Description: List of public-key user names that are allowed to activate the promised class during remote agent activation

Part of Role Based Access Control (RBAC) in CFEngine. The users listed in this section are granted access to set certain classes by using the remote cf-runagent. The user-names will refer to public key identities already trusted on the system.

Type: slist

Allowed input range: (arbitrary string)

Example:

    roles:

      ".*"  authorize => { "mark", "marks_friend" };

databases

CFEngine can interact with commonly used database servers to keep promises about the structure and content of data within them.

There are two main cases of database management to address: small embedded databases and large centralized databases.

Databases are often centralized entities that have a single point of management. While large monolithic database can be more easily managed with other tools, CFEngine can still monitor changes and discrepancies. In addition, CFEngine can also manage smaller embedded databases that are distributed in nature, whether they are SQL, registry or future types.

For example, creating 100 new databases for test purposes is a task for CFEngine; but adding a new item to an important production database is not a recommended task for CFEngine.

There are three kinds of database supported by CFEngine:

  • LDAP - The Lightweight Directory Access Protocol

A hierarchical network database primarily for reading simple schema (Only CFEngine Enterprise).

  • SQL - Structured Query Language

A number of relational databases (currently supported: MySQL, Postgres) for reading and writing complex data.

  • Registry - Microsoft Registry

An embedded database for interfacing with system values in Microsoft Windows (Only CFEngine Enterprise)

In addition, CFEngine uses a variety of embedded databases for its own internals.

Embedded databases are directly part of the system and promises can be made directly. However, databases running through a server process (either on the same host or on a different host) are independent agents and CFEngine cannot make promises on their behalf, unless they promise (grant) permission for CFEngine to make the changes. Thus the pre-requisite for making SQL database promises is to grant a point of access on the server.

    databases:

    "database/subkey or table"

       database_operation => "create/delete/drop",
       database_type => "sql/ms_registry",
       database_columns => {
                           "name,type,size",
                           "name,type",
                           },

       database_server => body;

    body database_server name
    {
      db_server_owner = "account name";
      db_server_password = "password";
      db_server_host = "hostname or omit for localhost";
      db_server_type = "mysql/posgres";
      db_server_connection_db = "database we can connect to";
    }
    body common control
    {
    bundlesequence => { "databases" };
    }

    bundle agent databases

    {
    #commands:

    #  "/usr/bin/createdb cf_topic_maps",

    #        contain => as_user("mysql");

    databases:

      "cf_topic_maps/topics"

        database_operation => "create",
        database_type => "sql",
        database_columns => {
                            "topic_name,varchar,256",
                            "topic_comment,varchar,1024",
                            "topic_id,varchar,256",
                            "topic_type,varchar,256",
                            "topic_extra,varchar,26"
                            },

        database_server => myserver;



    }

    ################################################

    body database_server myserver
    {
    any::
     db_server_owner => "postgres";
     db_server_password => "";
     db_server_host => "localhost";
     db_server_type => "postgres";
     db_server_connection_db => "postgres";
    none::
     db_server_owner => "root";
     db_server_password => "";
     db_server_host => "localhost";
     db_server_type => "mysql";
     db_server_connection_db => "mysql";
    }

    body contain as_user(x)
    {
    exec_owner => "$(x)";
    }

The promiser in database promises is a concatenation of the database name and underlying tables. This presents a simple hierarchical model that looks like a file-system. This is the normal structure within the Windows registry for instance. Entity-relation databases do not normally present tables in this way, but no harm is done in representing them as a hierarchy of depth 1.


Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

database_server

Type: body database_server

db_server_owner

Description: The db_server_owner string represents the user name for a database connection.

Type: string

Allowed input range: (arbitrary string)

Example:

     db_server_owner => "mark";
db_server_password

Description: The db_server_password string represents the clear text password for a database connection.

Type: string

Allowed input range: (arbitrary string)

Example:

     db_server_password => "xyz.1234";
db_server_host

Description: The db_server_host string represents the hostname or address for a database connection.

A blank value is equal to localhost.

Type: string

Allowed input range: (arbitrary string)

Example: cf3 db_server_host => "sqlserv.example.org";

db_server_type

Description: The db_server_type string represents the type of database server being used.

Type: (menu option)

Allowed input range:

    postgres
    mysql

Default value: none

Example:

     db_server_type => "postgres";
db_server_connection_db

Description: The db_server_connection_db string is the name of an existing database to connect to in order to create/manage other databases.

In order to create a database on a database server (all of which practice voluntary cooperation), one has to be able to connect to the server. However, without an existing database this is not allowed. Thus, database servers provide a default database that can be connected to in order to thereafter create new databases. These are called postgres and mysql for their respective database servers.

Type: string

Allowed input range: (arbitrary string)

Example:

     body database_server myserver(x)
     {
     db_server_owner => "$(x)";
     db_server_password => "";
     db_server_host => "localhost";
     db_server_type => "$(mysql)";
     db_server_connection_db => "$(x)";
     }

where x is currently mysql or postgres.

database_type

Description: The database_type menu option is a type of database that is to be manipulated.

Type: (menu option)

Allowed input range:

    sql
    ms_registry

Default value: none

Example:

database_type => "ms_registry";
database_operation

Description: The database_operation menu option represents the nature of the promise.

Type: (menu option)

Allowed input range:

    create
    delete
    drop
    cache
    verify
    restore

Example:

database_operation => "create";
database_columns

Description: A database_columns slist defines column definitions to be promised by SQL databases.

Columns are a list of tuplets (Name,type,size). Array items are triplets, and fixed size data elements are doublets.

Type: slist

Allowed input range: .*

Example:

  "cf_topic_maps/topics"

    database_operation => "create",
    database_type => "sql",
    database_columns => {
                        "topic_name,varchar,256",
                        "topic_comment,varchar,1024",
                        "topic_id,varchar,256",
                        "topic_type,varchar,256",
                        "topic_extra,varchar,26"
                        },

    database_server => myserver;
database_rows

Description: database_rows is an ordered list of row values to be promised by SQL databases.

This constraint is used only in adding data to database columns. Rows are considered to be instances of individual columns.

Type: slist

Allowed input range: .*,.*

Example:

bundle agent databases
{
databases:

 windows::

  # Regsitry has (value,data) pairs in "keys" which are directories

  "HKEY_LOCAL_MACHINE\SOFTWARE\CFEngine AS\CFEngine"

    database_operation => "create",
    database_rows => { "value1,REG_SZ,new value 1", "value2,REG_DWORD,12345"} ,
    database_type     => "ms_registry";
}

Notes:

In the case of the system registry on Windows, the rows represent data on data-value pairs. The currently supported types (the middle field) for the Windows registry are REG_SZ (string), REG_EXPAND_SZ (expandable string) and REG_DWORD (double word).

registry_exclude

Description: An registry_exclude slist contains regular expressions to ignore in key/value verification.

During recursive Windows registry scanning, this option allows us to ignore keys of values matching a list of regular expressions. Some values in the registry are ephemeral and some should not be considered. This provides a convenient way of avoiding names. It is analogous to exclude_dirs for files.

Type: slist

Allowed input range: (arbitrary string)

Example:

databases:

 "HKEY_LOCAL_MACHINE\SOFTWARE"

    database_operation => "cache",

    registry_exclude => { ".*Windows.*CurrentVersion.*",
                          ".*Touchpad.*",
                          ".*Capabilities.FileAssociations.*",
                          ".*Rfc1766.*" ,
                          ".*Synaptics.SynTP.*",
                          ".*SupportedDevices.*8086",
                          ".*Microsoft.*ErrorThresholds"
                        },

    database_type     => "ms_registry";

classes

Classes promises may be made in any bundle. Classes that are set in common bundles are global in scope, while classes in all other bundles are local.

Note: The term class and context are sometimes used interchangeably.

    bundle common g
    {
    classes:

      "one" expression => "any";

      "client_network" expression => iprange("128.39.89.0/24");
    }

Attributes
and

Description: Combine class sources with AND

The class on the left-hand side is set if all of the class expressions listed on the right-hand side are true.

Type: clist

Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+

Example:

    classes:

      "compound_class" and => { classmatch("host[0-9].*"), "Monday", "Hr02" };

Notes:

If an expression contains a mixture of different object types that need to be ANDed together, this list form is more convenient than providing an expression.

dist

Description: Generate a probabilistic class distribution

Always set one generic class and one additional class, randomly weighted on a probability distribution.

Type: rlist

Allowed input range: -9.99999E100,9.99999E100

Example:

    classes:

      "my_dist"

        dist => { "10", "20", "40", "50" };

Notes:

In the example above the values sum up to 10+20+40+50 = 120. When generating the distribution, CFEngine picks a number between 1-120, and set the class my_dist as well as one of the following classes:

    my_dist_10 (10/120 of the time)
    my_dist_20 (20/120 of the time)
    my_dist_40 (40/120 of the time)
    my_dist_50 (50/120 of the time)
expression

Description: Evaluate string expression of classes in normal form

Set the class on the left-hand side if the expression on the right-hand side evaluates to true.

Type: class

Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+

Example:

    classes:

      "class_name" expression => "solaris|(linux.specialclass)";
      "has_toor"   expression => userexists("toor");
or

Description: Combine class sources with inclusive OR

The class on the left-hand side will be set if any one (or more) of the class expressions on the right-hand side are true.

Type: clist

Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+

Example:

classes:

    "compound_test"

      or => { classmatch("linux_x86_64_2_6_22.*"), "suse_10_3" };

Notes:

This is useful construction for writing expressions that contain functions.

persistence

Description: Make the class persistent to avoid re-evaluation

The value specifies time in minutes.

Type: int

Allowed input range: 0,99999999999

Example:

bundle common setclasses
{
classes:

  "cached_classes"
                or => { "any" },
       persistence => "1";

  "cached_class"
       expression => "any",
       persistence => "1";

}

Notes:

This feature can be used to avoid recomputing expensive classes calculations on each invocation. This is useful if a class discovered is essentially constant or only slowly varying, such as a hostname or alias from a non-standard naming facility.

For example, to create a conditional inclusion of costly class evaluations, put them into a separate bundle in a file classes.cf.

    # promises.cf

    body common control
    {
    persistent_classes::
      bundlesequence => { "test" };

    !persistent_classes::
      bundlesequence => {  "setclasses", "test" };

    !persistent_classes::
      inputs => { "classes.cf" };
    }


    bundle agent test
    {
    reports:

      !my_persistent_class::
       "no persistent class";

      my_persistent_class::
        "persistent class defined";
    }

Then create classes.cf

    # classes.cf

    bundle common setclasses
    {
    classes:

      "persistent_classes"            # timer flag
             expression => "any",
            persistence => "480";

      "my_persistent_class"
                    or => { ...long list or heavy function... } ,
           persistence => "480";

    }

History: Was introduced in CFEngine 3.3.0

not

Description: Evaluate the negation of string expression in normal form

The class on the left-hand side will be set if the class expression on the right-hand side evaluates to false.

Type: class

Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+

Example:

    classes:

       "others"  not => "linux|solaris";
       "no_toor" not => userexists("toor");

Notes:

Knowing that something is not the case is not the same as not knowing whether something is the case. That a class is not set could mean either. See the note on Negative Knowledge.

scope

Description: Scope of the class set by this promise.

Type: (menu option)

Allowed input range:

    namespace
    bundle

Default value: bundle in agent bundles, namespace in common bundles

Example:

    classes:
      "namespace_context"
          scope => "namespace";

      "bundle_or_namespace_context"; # without an explicit scope, depends on bundle type

      "bundle_context"
          scope => "bundle";

See also: scope in body classes

select_class

Description: Select one of the named list of classes to define based on host's fully qualified domain name, the primary IP address and the UID that cf-agent is running under.

The class is chosen deterministically (not randomly) but it is not possible to say which host will end up in which class in advance. Only that hosts will always end up in the same class every time.

Type: clist

Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+

Example:

    bundle common g
    {
    classes:
      "selection" select_class => { "one", "two" };

    reports:
      one::
        "One was selected";
      two::
        "Two was selected";
      selection::
         "A selection was made";
    }

Notes:

This feature is similar to the splayclass function. However, instead of selecting a class for a moment in time, it always chooses one class in the list; the same class each time for a given host. This allows hosts to be distributed across a controlled list of classes (e.g for load balancing purposes).

xor

Description: Combine class sources with XOR

The class on the left-hand side is set if an odd number of class expressions on the right-hand side matches. This is most commonly used with two class expressions.

Type: clist

Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+

Example:

    classes:

    "order_lunch" xor => { "Friday", "Hr11"}; # we get pizza every Friday

defaults

Defaults promises are related to variables. If a variable or parameter in a promise bundle is undefined, or its value is defined to be invalid, a default value can be promised instead.

CFEngine does not use Perl semantics: i.e. undefined variables do not map to the empty string, they remain as variables for possible future expansion. Some variables might be defined but still contain unresolved variables. To handle this you will need to match the $(abc) form of the variables.

    body common control
    {
    bundlesequence => { "main" };
    }

    bundle agent main
    {
    methods:

      "example"  usebundle => test("one","x","","$(four)");

    }

    bundle agent test(a,b,c,d)
    {
    defaults:

     "a" string => "default a", if_match_regex => "";
     "b" string => "default b", if_match_regex => "x";
     "c" string => "default c", if_match_regex => "";
     "d" string => "default d", if_match_regex => "\$\([a-zA-Z0-9_.]+\)";

    reports:

       "a = '$(a)', b = '$(b)', c = '$(c)' d = '$(d)'";
    }

Another example:

bundle agent example
{
defaults:

  "X" string => "I am a default value";
  "Y" slist => { "I am a default list item 1", "I am a default list item 2" };

methods:

 "example" usebundle => mymethod("","bbb");

reports:

   "The default value of X is $(X)";
   "The default value of Y is $(Y)";
}

###########################################################

bundle agent mymethod(a,b)
{
vars:

  "no_return" string => "ok"; # readfile("/dont/exist","123");

defaults:

  "a" string => "AAAAAAAAA",   if_match_regex => "";
  "b" string => "BBBBBBBBB",   if_match_regex => "";
  "no_return" string => "no such file";

reports:

     "The value of a is $(a)";
     "The value of b is $(b)";

     "The value of no_return is $(no_return)";
}

Attributes
if_match_regex

Description: If this regular expression matches the current value of the variable, replace it with default

If a parameter or variable is already defined in the current context, and the value matches this regular expression, it will be deemed invalid and replaced with the default value.

Type: string

Allowed input range: (arbitrary string)

Example:

    bundle agent mymethod(a,b)
    {
    defaults:

      "a" string => "AAAAAAAAA",   if_match_regex => "";
      "b" string => "BBBBBBBBB",   if_match_regex => "";
    }

methods

Methods are compound promises that refer to whole bundles of promises. Methods may be parameterized. Methods promises are written in a form that is ready for future development. The promiser object is an abstract identifier that refers to a collection (or pattern) of lower level objects that are affected by the promise-bundle. Since the use of these identifiers is for the future, you can simply use any string here for the time being.

    methods:

      "any"

         usebundle => method_id("parameter",...);

Methods are useful for encapsulating repeatedly used configuration issues and iterating over parameters. They are implemented as bundles that are run inline.

    bundle agent example
    {
      vars:

       "userlist" slist => { "mark", "jeang", "jonhenrik", "thomas", "eben" };
       "userinfo" data => parsejson('{ "mark": 10, "jeang":20, "jonhenrik":30, "thomas":40, "eben":-1 }');

      methods:
       # Activate subtest once for each list item
       "any" usebundle => subtest("$(userlist)");

       # Activate subtest once passing the entire list
       "amy" usebundle => subtest(@(userlist));

       # Pass a data type variable aka data container
       "amp" usebundle => subtest_c(@(userinfo));
    }

    bundle agent subtest(user)
    {
      commands:

       "/bin/echo Fix $(user)";

      reports:

        "Finished doing stuff for $(user)";
    }

    bundle agent subtest_c(info)
    {
      reports:
       "user ID of mark is $(info[mark])";
    }

Methods offer powerful ways to encapsulate multiple issues pertaining to a set of parameters.

Note in the above that a list can be passed as a implicitly iterated scalar and as a reference, while a data variable (a data container) can only be passed by reference.

As of version 3.5.0 a methods promise outcome is tied to the outcomes of its promises. For example if you activate a bundle and it has a promise that is not_kept, the bundle itself would have an outcome of not_kept. If you activate a bundle that has one promise that is repaired, and one promise that is kept, the bundle will have an outcome of repaired. A method will only have an outcome of kept if all promises inside that bundle are also kept. This acceptance test illustrates the behavior.

Starting from version 3.1.0, methods may be specified using variables. Care should be exercised when using this approach. In order to make the function call uniquely classified, CFEngine requires the promiser to contain the variable name of the method if the variable is a list.

    bundle agent default
    {
    vars:
        "m" slist  => { "x", "y" };
        "p" string => "myfunction";

    methods:
        "set of $(m)" usebundle => $(m)("one");
        "any"         usebundle => $(p)("two");
    }

Please note that method names must be either simple strings or slists. They can't be array references, for instance. As a rule, they can only look like $(name) where name is either a string or an slist. They can't be "$(a)$(b)", $(a[b]), and so on.

Here's a full example of how you might encode bundle names and parameters in a slist, if you need to pack and unpack method calls in a portable (e.g. written in a file) format.

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "todo" slist => { "call_1,a,b", "call_2,x,y", "call_2,p,q" };

  methods:
      "call" usebundle => unpack($(todo));
}

bundle agent unpack(list)
{
  vars:
      "split" slist => splitstring($(list), ",", "100");
      "method" string => nth("split", "0");
      "param1" string => nth("split", "1");
      "param2" string => nth("split", "2");

  methods:
      "relay" usebundle => $(method)($(param1), $(param2));
}

bundle agent call_1(p1, p2)
{
  reports:
      "$(this.bundle): called with parameters $(p1) and $(p2)";
}

bundle agent call_2(p1, p2)
{
  reports:
      "$(this.bundle): called with parameters $(p1) and $(p2)";
}

Output:

2013-12-11T13:33:31-0500   notice: /run/methods/'call'/unpack/methods/'relay'/call_1: R: call_1: called with parameters a and b
2013-12-11T13:33:31-0500   notice: /run/methods/'call'/unpack/methods/'relay'/call_2: R: call_2: called with parameters x and y
2013-12-11T13:33:31-0500   notice: /run/methods/'call'/unpack/methods/'relay'/call_2: R: call_2: called with parameters p and q

Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

inherit

Description: If true this causes the sub-bundle to inherit the private classes of its parent

Inheriting the variables is unnecessary as the child can always access the parent's variables through a qualified reference using its bundle name. For example: $(bundle.variable).

Type: boolean

Default value: false

Example:

    bundle agent name
    {
    methods:

      "group name" usebundle => my_method,
                     inherit => "true";
    }


    body edit_defaults example
    {
    inherit => "true";
    }

History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)

usebundle

Type: bundle agent

useresult

Description: Specify the name of a local variable to contain any result/return value from the child

Return values are limited to scalars.

Type: string

Allowed input range: `[a-zA-Z0-9_$(){}[].:]+

Example:

    bundle agent test
    {
    methods:

       "any" usebundle => child,
             useresult => "my_return_var";

    reports:
        "My return was: \"$(my_return_var[1])\" and \"$(my_return_var[2])\"";
    }

    bundle agent child
    {
    reports:
       # Map these indices into the useresult namespace

       "this is a return value"
          bundle_return_value_index => "1";

       "this is another return value"
          bundle_return_value_index => "2";
    }

History: Was introduced in 3.4.0 (2012)


vars

Variables in CFEngine are defined as promises that an identifier of a certain type represents a particular value. Variables can be scalars or lists of types string, int, real or data.

The allowed characters in variable names are alphanumeric (both upper and lower case) and undercore. Associative arrays using the string type and square brackets [] to enclose an arbitrary key are being deprecated in favor of the data variable type.

Scalar Variables
string

Description: A scalar string

Type: string

Allowed input range: (arbitrary string)

Example:

    vars:

     "xxx"    string => "Some literal string...";
     "yyy"    string => readfile( "/home/mark/tmp/testfile" , "33" );
int

Description: A scalar integer

Type: int

Allowed input range: -99999999999,9999999999

Example:

    vars:

     "scalar" int    => "16k";
     "ran"    int    => randomint(4,88);
     "dim_array" int =>  readstringarray(
         "array_name",
         "/etc/passwd",
         "#[^\n]*",
         ":",
         10,
         4000);

Notes:

Int variables are strings that are expected to be used as integer numbers. The typing in CFEngine is dynamic, so the variable types are interchangeable. However, when you declare a variable to be type int, CFEngine verifies that the value you assign to it looks like an integer (e.g., 3, -17, 16K).

real

Description: A scalar real number

Type: real

Allowed input range: -9.99999E100,9.99999E100

Example:

    vars:

     "scalar" real   => "0.5";

Notes:

Real variables are strings that are expected to be used as real numbers. The typing in CFEngine is dynamic, so the variable types are interchangeable, but when you declare a variable to be type real, CFEngine verifies that the value you assign to it looks like a real number (e.g., 3, 3.1415, .17, 6.02e23, -9.21e-17).

Real numbers are not used in many places in CFEngine, but they are useful for representing probabilities and performance data.

List variables

Lists are specified using curly brackets {} that enclose a comma-separated list of values. The order of the list is preserved by CFEngine.

slist

Description: A list of scalar strings

Type: slist

Allowed input range: (arbitrary string)

Example:

    vars:

     "xxx"    slist  => {  "literal1",  "literal2" };
     "xxx1"   slist  => {  "1", @(xxx) }; # interpolated in order
     "yyy"    slist  => {
                        readstringlist(
                                      "/home/mark/tmp/testlist",
                                      "#[a-zA-Z0-9 ]*",
                                      "[^a-zA-Z0-9]",
                                      15,
                                      4000
                                      )
                        };

     "zzz"    slist  => { readstringlist(
        "/home/mark/tmp/testlist2",
        "#[^\n]*",
        ",",
        5,
        4000)
        };

Notes:

Some functions return slists, and an slist may contain the values copied from another slist, rlist, or ilist. See policy.

ilist

Description: A list of integers

Type: ilist

Allowed input range: -99999999999,9999999999

Example:

    vars:

      "variable_id"

           ilist => { "10", "11", "12" };

      "xxx1" ilist  => {  "1", @(variable_id) }; # interpolated in order

Notes:

Integer lists are lists of strings that are expected to be treated as integers. The typing in CFEngine is dynamic, so the variable types are interchangeable, but when you declare a variable to be type ilist, CFEngine verifies that each value you assign to it looks like an integer (e.g., 3, -17, 16K).

Some functions return ilists, and an ilist may contain the values copied from another slist, rlist, or ilist. See policy

rlist

Description: A list of real numbers

Type: rlist

Allowed input range: -9.99999E100,9.99999E100

Example:

    vars:

      "varid" rlist => { "0.1", "0.2", "0.3" };

      "xxx1" rlist  => {  "1.3", @(varid) }; # interpolated in order

Notes:

Real lists are lists of strings that are expected to be used as real numbers. The typing in CFEngine is dynamic, so the variable types are interchangeable, but when you declare a variable to be type rlist, CFEngine verifies that each value you assign to it looks like a real number (e.g., 3, 3.1415, .17, 6.02e23, -9.21e-17).

Some functions return rlists, and an rlist may contain the values copied from another slist, rlist, or ilist. See policy

Data container variables

The data variables are obtained from functions that return data containers, such as readjson() or parsejson(), or from merging existing data containers with mergedata(). They can NOT be modified, once created.

Data containers can be passed to another bundle with the @(varname) notation, similarly to the list passing notation.

Some useful tips for using data containers:

  • they act like "classic" CFEngine arrays in many ways
  • getindices() and getvalues() work on any level, e.g. getvalues("container[x][y]")
  • in reports, you have to reference a part of the container that can be expressed as a string. So for instance if you have the container c with data { "x": { "y": 50 }, "z": [ 1,2,3] } we have two top-level keys, x and z. If you report on $(c[x]) you will not get data, since there is no string there. But if you ask for $(c[x][y]) you'll get 50, and if you ask for $(c[z]) you'll get implicit iteration on 1,2,3 (just like a slist in a "classic" CFEngine array).
  • read the examples below carefully to see some useful ways to access data container contents

Iterating through a data container is only guaranteed to respect list order (e.g. [1,3,20] will be iterated in that order). Key order for maps, as per the JSON standard, is not guaranteed. Similarly, calling getindices() on a data container will give the list order of indices 0, 1, 2, ... but will not give the keys of a map in any particular order. Here's an example of iterating in list order:

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "x" data => parsejson('[
 { "one": "a" },
 { "two": "b" },
 { "three": "c" }
]');

      # get the numeric indices of x: 0, 1, 2
      "xi" slist => getindices(x);

      # for each xi, make a variable xpiece_$(xi) so we'll have
      # xpiece_0, xpiece_1, xpiece_2. Each xpiece will have that
      # particular element of the list x.
      "xpiece_$(xi)" string => format("%S", "x[$(xi)]");

  reports:
      "$(xi): $(xpiece_$(xi))";
}

Output:

R: 0: {"one":"a"}
R: 1: {"two":"b"}
R: 2: {"three":"c"}

Often you need to iterate through the keys of a container, and the value is a key-value property map for that key. The example here shows how you can pass the "animals" container and an "animal" key inside it to a bundle, which can then report and use the data from the key-value property map.

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "animals" data => parsejson('
      {
         "dog": { "legs": 4, "tail": true, "names": [ "Fido", "Cooper", "Sandy" ] },
         "cat": { "legs": 4, "tail": true, "names": [ "Fluffy", "Snowball", "Tabby" ] },
         "dolphin": { "legs": 0, "tail": true, "names": [ "Flipper", "Duffy" ] },
         "hamster": { "legs": 4, "tail": true, "names": [ "Skullcrusher", "Kimmy", "Fluffadoo" ] },
      }');

      "keys_unsorted" slist => getindices("animals");
      "keys" slist => sort(keys_unsorted, "lex");

      "animals_$(keys)" data => mergedata("animals[$(keys)]");

  methods:
      # pass the container and a key inside it
      "any" usebundle => analyze(@(animals), $(keys));
}

bundle agent analyze(animals, a)
{
  vars:
      "names" slist => getvalues("animals[$(a)][names]");
      "names_str" string => format("%S", names);

  reports:
      "$(this.bundle): possible names for animal '$(a)': $(names_str)";
      "$(this.bundle): describe animal '$(a)' => name = $(a), legs = $(animals[$(a)][legs]), tail = $(animals[$(a)][tail])";
}

Output:

R: analyze: possible names for animal 'cat': { "Fluffy", "Snowball", "Tabby" }
R: analyze: describe animal 'cat' => name = cat, legs = 4, tail = true
R: analyze: possible names for animal 'dog': { "Fido", "Cooper", "Sandy" }
R: analyze: describe animal 'dog' => name = dog, legs = 4, tail = true
R: analyze: possible names for animal 'dolphin': { "Flipper", "Duffy" }
R: analyze: describe animal 'dolphin' => name = dolphin, legs = 0, tail = true
R: analyze: possible names for animal 'hamster': { "Skullcrusher", "Kimmy", "Fluffadoo" }
R: analyze: describe animal 'hamster' => name = hamster, legs = 4, tail = true
data

Description: A data container structure

Type: data

Allowed input range: (arbitrary string)

Example:

    vars:

     "loaded1" data => readjson("/tmp/myfile.json", 40000);
     "loaded2" data => parsejson('{"key":"value"}');
     "merged1" data => mergedata(loaded1, loaded2);

Attributes
policy

Description: The policy for (dis)allowing (re)definition of variables

Variables can either be allowed to change their value dynamically (be redefined) or they can be constant.

Type: (menu option)

Allowed input range:

    free
    overridable
    constant
    ifdefined

Default value:

policy = free

Example:

    vars:

      "varid" string => "value...",
              policy => "free";

Notes:

The policy free and overridable are synonyms. The policy constant is deprecated, and has no effect. All variables are free or overridable by default which means the variables values may be changed.

The policy ifdefined applies only to lists and implies that unexpanded or undefined lists are dropped. The default behavior is otherwise to retain this value as an indicator of the failure to quench the variable reference, for example:

    "one" slist => { "1", "2", "3" };

    "list" slist => { "@(one)", @(two) },
          policy => "ifdefined";

This results in @(list) being the same as @(one), and the reference to @(two) disappears. This is useful for combining lists.

For example:

example_com::
  "domain"
     string => "example.com",
    comment => "Define a global domain for hosts in the example.com domain";

# The promise above will be overridden by one of the ones below on hosts
# within the matching subdomain

one_example_com::
  "domain"
     string => "one.example.com",
    comment => "Define a global domain for hosts in the one.example.com domain";

two_example_com::
  "domain"
     string => "two.example.com",
    comment => "Define a global domain for hosts in the two.example.com domain";

storage

Storage promises refer to disks and filesystem properties.

      storage:

         "/disk volume or mountpoint"

           volume = volume_body,
           ...;
    bundle agent storage
    {
      storage:

        "/usr" volume  => mycheck("10%");
        "/mnt" mount   => nfs("nfsserv.example.org","/home");

    }

    body volume mycheck(free)   # reusable template

    {
      check_foreign  => "false";
      freespace      => "$(free)";
      sensible_size  => "10000";
      sensible_count => "2";
    }

    body mount nfs(server,source)

    {
      mount_type => "nfs";
      mount_source => "$(source)";
      mount_server => "$(server)";
      edit_fstab => "true";
    }

Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

mount

Type: body mount

edit_fstab

Description: true/false add or remove entries to the file system table ("fstab")

The default behavior is to not place edits in the file system table.

Type: boolean

Default value: false

Example:

     body mount example
     {
       edit_fstab => "true";
     }
mount_type

Description: Protocol type of remote file system

Type: (menu option)

Allowed input range:

    nfs
    nfs2
    nfs3
    nfs4

Example:

     body mount example
     {
     mount_type => "nfs3";
     }

Notes: This field is mainly for future extensions.

mount_source

Description: Path of remote file system to mount.

This is the location on the remote device, server, SAN etc.

Type: string

Allowed input range: "?(/.*)

Example:

     body mount example
     {
     mount_source "/location/disk/directory";
     }
mount_server

Description: Hostname or IP or remote file system server.

Type: string

Allowed input range: (arbitrary string)

Example:

     body mount example
     {
       mount_server => "nfs_host.example.org";
     }
mount_options

Description: List of option strings to add to the file system table ("fstab").

This list is concatenated in a form appropriate for the filesystem. The options must be legal options for the system mount commands.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body mount example
     {
       mount_options => { "rw", "acls" };
     }
unmount

Description: true/false unmount a previously mounted filesystem

Type: boolean

Default value: false

Example:

     body mount example
     {
     unmount => "true";
     }
volume

Type: body volume

check_foreign

Description: If true, verify storage that is mounted from a foreign system on this host.

CFEngine will not normally perform sanity checks on filesystems that are not local to the host. If true it will ignore a partition's network location and ask the current host to verify storage located physically on other systems.

Type: boolean

Default value: false

Example:

     body volume example
     {
       check_foreign  => "true";
     }
freespace

Description: Absolute or percentage minimum disk space that should be available before warning

The amount of free space that is promised on a storage device. Once this promise is found not to be kept (that is, if the free space falls below the promised value), warnings are generated. You may also want to use the results of this promise to control other promises.

Type: string

Allowed input range: [0-9]+[MBkKgGmb%]

Example:

     body volume example1
     {
     freespace => "10%";
     }

     body volume example2
     {
     freespace => "50M";
     }
sensible_size

Description: Minimum size in bytes that should be used on a sensible-looking storage device

Type: int

Allowed input range: 0,99999999999

Example:

     body volume example
     {
     sensible_size => "20K";
     }
sensible_count

Description: Minimum number of files that should be defined on a sensible-looking storage device.

Files must be readable by the agent. In other words, it is assumed that the agent has privileges on volumes being checked.

Type: int

Allowed input range: 0,99999999999

Example:

     body volume example
     {
     sensible_count => "20";
     }
scan_arrivals

Description: If true, generate pseudo-periodic disk change arrival distribution.

This operation should not be left 'on' for more than a single run (maximum once per week). It causes CFEngine to perform an extensive disk scan noting the schedule of changes between files. This can be used for a number of analyses including optimum backup schedule computation.

Type: boolean

Default value: false

Example:

     body volume example
     {
       scan_arrivals => "true";
     }

access

Access promises are conditional promises made by resources living on the server.

The promiser is the name of the resource affected and is interpreted to be a path, unless a different resource_type is specified. Access is then granted to hosts listed in admit_ips, admit_keys and admit_hostnames, or denied using the counterparts deny_ips, deny_keys and deny_hostnames.

You layer the access policy by denying all access and then allowing it only to selected clients, then denying to an even more restricted set.

bundle server access_rules()
{
access:

  "/source/directory"
    comment => "Access to file transfer",
    admit_ips   => { "192.168.0.1/24" };
}

For file copy requests, the file becomes transferable to the remote client according to the conditions specified in the access promise. Use ifencrypted to grant access only if the transfer is encrypted in the "classic" CFEngine protocol (the TLS protocol is always encrypted).

When access is granted to a directory, the promise is automatically made about all of its contents and sub-directories.

Use the maproot attribute (like its NFS counterpart) to control which hosts can see file objects not owned by the server process owner.

File resources are specified using an absolute filepath, but can set a shortcut through which clients can access the resource using a logical name, without having any detailed knowledge of the filesystem layout on the server. Specifically in access promises about files, a special variable context connection is available with variables ip, key and hostname, containing information about the connection through which access is attempted.

   "/var/cfengine/cmdb/$(connection.key).json"
      shortcut   => "me.json",
      admit_keys => { "$(connection.key)" };

In this example, requesting the file me.json will transfer the file stored on the server under the name /var/cfengine/cmdb/SHA=....json to the requesting host, where it will be received as me.json. Note that the usage of the $(connection.*) variables is strictly limited to literal strings within the promiser and admit/deny lists; they cannot be passed to functions or stored in other variables.

With CFEngine Enteprise, access promises can be made about additional query data for reporting and orchestration.

  # Grant orchestration communication

  "did.*"
          comment => "Access to class context (enterprise)",
    resource_type => "context",
        admit_ips => { "127.0.0.1" };


  "value of my test_scalar, can expand variables here - $(sys.host)"
          comment => "Grant access to the string in quotes, by name test_scalar",
           handle => "test_scalar",
    resource_type => "literal",
        admit_ips => { "127.0.0.1" };

  "XYZ"
          comment => "Grant access to contents of persistent scalar variable XYZ",
    resource_type => "variable",
        admit_ips => { "127.0.0.1" };

  # Client grants access to CFEngine hub access

  "delta"
               comment => "Grant access to cfengine hub to collect report deltas",
         resource_type => "query",
    report_data_select => default_data_select_host,
                 admit_ips => { "127.0.0.1"  };
  "full"
               comment => "Grant access to cfengine hub to collect full report dump",
         resource_type => "query",
    report_data_select => default_data_select_host,
             admit_ips => { "127.0.0.1"  };

  policy_server::

  "collect_calls"
          comment => "Grant access to cfengine client to request the collection of its reports",
    resource_type => "query",
        admit_ips => { "10.1.2.*" };


}

Using the built-in report_data_select body default_data_select_host:

body report_data_select default_data_select_host
{
      metatags_include => { "inventory", "report" };
      metatags_exclude => { "noreport" };
      monitoring_include => { "" };
}

The access promise allows overlapping promises to be made, and these are kept on a first-come-first-served basis. Thus file objects (promisers) should be listed in order of most-specific file first. In this way, specific promises will override less specific ones.


Attributes
admit_hostnames

Description: A list of hostnames or domains that should have access to the object.

Type: slist

Allowed input range: (arbitrary string)

Note: The host trying to access the object is identified using a reverse DNS lookup on the connecting IP. This introduces latency for every incoming connection. If possible, avoid this penalty by leaving admit_hostnames empty and only specifying numeric addresses and subnets in admit_ips.

To admit an entire domain, start the string with a dot .. This includes every hostname ending with the domain, but not a machine named after the domain itself.

For example, here we'll admit the entire domain .cfengine.com and the host www.cfengine3.com. A machine named cfengine.com would be refused access because it's not in the cfengine.com domain.

access:

   "/path/file"
   admit_hostname => { ".cfengine.com", "www.cfengine3.com" };

See also: deny_hostnames, admit_ips, admit_keys

History: Introduced in CFEngine 3.6.0

admit_ips

Description: A list of IP addresses that should have access to the object.

Subnets are specified using CIDR notation. For example, here we'll admit one host, then a subnet, then everyone:

access:

   "/path/file"
   admit_ips => {"192.168.0.1", "192.168.0.0/24", "0.0.0.0/0"};

Type: slist

Allowed input range: (arbitrary string)

See also: deny_ips, admit_hostnames, admit_keys

History: Introduced in CFEngine 3.6.0

admit_keys

Description: A list of RSA keys of hosts that should have access to the object.

For example, here we'll admit the fictitious SHA key abcdef:

access:

   "/path/file"
   admit_keys => {"SHA=abcdef"};

In Community, MD5 keys are used, so similarly we can admit the fictitious MD5 key abcdef:

access:

   "/path/file"
   admit_keys => {"MD5=abcdef"};

Type: slist

Allowed input range: (arbitrary string)

See also: deny_keys, admit_hostnames, admit_ips

History: Introduced in CFEngine 3.6.0

deny_hostnames

Description: A list of hostnames that should be denied access to the object.

This overrides the grants in admit_hostnames, admit_ips and admit_keys.

To deny an entire domain, start the string with a dot .. This includes every hostname ending with the domain, but not a machine named after the domain itself.

For example, here we'll deny the entire domain .cfengine.com and the host www.cfengine3.com. A machine named cfengine.com would be allowed access (unless it's denied by other promises) because it's not in the cfengine.com domain.

access:

   "/path/file"
   deny_hostname => { ".cfengine.com", "www.cfengine3.com" };

Type: slist

Allowed input range: (arbitrary string)

See also: admit_hostnames, deny_ips, deny_keys

History: Introduced in CFEngine 3.6.0

deny_ips

Description: A list of IP addresses that should be denied access to the object.

Subnets are specified using CIDR notation.

This overrides the grants in admit_hostnames, admit_ips and admit_keys.

For example, here we'll deny one host, then a subnet, then everyone:

access:

   "/path/file"
   deny_ips => {"192.168.0.1", "192.168.0.0/24", "0.0.0.0/0"};

Type: slist

Allowed input range: (arbitrary string)

See also: admit_ips, deny_hostnames, deny_keys

History: Introduced in CFEngine 3.6.0

deny_keys

Description: A list of RSA keys of hosts that should be denied access to the object.

This overrides the grants in admit_hostnames, admit_ips and admit_keys.

Type: slist

Allowed input range: (arbitrary string)

For example, here we'll deny the fictitious SHA key abcdef:

access:

   "/path/file"
   deny_keys => {"SHA=abcdef"};

In Community, MD5 keys are used, so similarly we can deny the fictitious MD5 key abcdef:

access:

   "/path/file"
   deny_keys => {"MD5=abcdef"};

See also: admit_keys, deny_hostnames, deny_ips

History: Introduced in CFEngine 3.6.0

admit

Description: The admit slist contains host names or IP addresses to grant access to file objects.

Admit promises grant access to file objects on the server. Arguments may be IP addresses or hostnames, provided DNS name resolution is active. In order to reach this stage, a client must first have passed all of the standard connection tests in the control body.

The lists may contain network addresses in CIDR notation or regular expressions to match the IP address or name of the connecting host.

Type: slist

Allowed input range: (arbitrary string)

Example:

access:

  "/home/mark/LapTop"

    admit   => { "127.0.0.1", "192.168.0.1/24", ".*\.domain\.tld"  };

Notes:

admit will be deprecated in CFEngine 3.7 in favor of admit_ips, admit_hostnames, and admit_keys.

deny

Description: The deny slist contains host names or IP addresses to deny access to file objects.

Denial is for special exceptions. A better strategy is always to grant on a need to know basis. A security policy based on exceptions is a weak one.

Type: slist

Allowed input range: (arbitrary string)

Example:

bundle server access_rules()
{
access:

  "/path"

    admit   => { ".*\.example\.org" },
    deny    => { "badhost_1\.example\.org", "badhost_1\.example\.org" };
}

Notes: Only regular expressions or exact matches are allowed in this list, as non-specific matches are too greedy for denial.

deny will be deprecated in CFEngine 3.7 in favor of deny_ips, deny_hostnames, and deny_keys.

maproot

Description: The maproot slist contains host names or IP addresses to grant full read-privilege on the server.

Normally users authenticated by the server are granted access only to files owned by them and no-one else. Even if the cf-serverd process runs with root privileges on the server side of a client-server connection, the client is not automatically granted access to download files owned by non-privileged users. If maproot is true then remote root users are granted access to all files.

A typical case where mapping is important is in making backups of many user files.

Type: slist

Allowed input range: (arbitrary string)

Example:

access:

 "/home"

     admit_hostnames => { "backup_host.example.org" },
 ifencrypted => "true",

     # Backup needs to have access to all users

     maproot => { "backup_host.example.org" };

Notes:

On Windows, cf-serverd, maproot is required to read files if the connecting user does not own the file on the server.

ifencrypted

Description: The ifencrypted menu option determines whether the current file access promise is conditional on the connection from the client being encrypted.

This option has no effect with the TLS CFEngine protocol, where encryption is always enabled.

If this flag is true a client cannot access the file object unless its connection is encrypted.

Type: boolean

Default value: false

Example:

access:

   "/path/file"

    admit_hostnames => { ".*\.example\.org" },
    ifencrypted => "true";
report_data_select

This body is only available in CFEngine Enterprise.

Description: The report_data_select body restricts which data is included for query resources, and allows filtering of data reported to the CFEngine Enterprise server.

Use this body template to control the content of reports collected by the CFEngine Enterprise server, and to strip unwanted data (e.g. temporary variables) from reporting.

By default, no filtering is applied. If include and exclude rules are combined, then the exclude statement is applied to the subset from the include statement.

If more than one report_data_select body applies to the same host, all of them are applied.

Usage of this body is only allowed in conjunction with using resource_type => "query", as this is the resource type that is being affected.

Type: body report_data_select

Example:

body report_data_select report_data
{
    metatags_include => { "inventory", "compliance" };
    promise_handle_exclude => { "_.*" };
    monitoring_exclude => { "mem_.*swap" };
}

Example:

Here are the built-in report_data_select bodies default_data_select_host() and default_data_select_policy_hub():

body report_data_select default_data_select_host
{
      metatags_include => { "inventory", "report" };
      metatags_exclude => { "noreport" };
      monitoring_include => { "" };
}
body report_data_select default_data_select_policy_hub
{
      metatags_include => { "inventory", "report" };
      metatags_exclude => { "noreport" };
      monitoring_include => { "" };
}

History: Introduced in Enterprise 3.5.0

metatags_exclude

Description: List of anchored regular expressions matching metatags to exclude from reporting.

Classes and variables with metatags matching any entry of that list will not be reported to the CFEngine Enterprise server.

When combined with metatags_include, this list is applied to the selected subset.

Type: slist

Allowed input range: .*

See also: metatags_include, promise_handle_exclude, monitoring_exclude

History: Introduced in CFEngine 3.6.0

metatags_include

Description: List of anchored regular expressions matching metatags to include in reporting.

Classes and variables with metatags matching any entry of that list will be reported to the CFENgine Enterprise server.

When combined with metatags_exclude, the exclude list is applied to the subset from this list.

Type: slist

Allowed input range: .*

See also: metatags_exclude, promise_handle_include, monitoring_include

History: Introduced in CFEngine 3.6.0

promise_handle_exclude

Description: List of anchored regular expressions matching promise handles to exclude from reporting.

Information about promises with handles that match any entry in that list will not be reported to the CFEngine Enterprise server.

When combined with promise_handle_include, this list is applied to the selected subset.

Type: slist

Allowed input range: .*

See also: promise_handle_include, metatags_exclude, monitoring_exclude

History: Introduced in CFEngine 3.6.0

promise_handle_include

Description: List of anchored regular expressions matching promise handles to include in reporting.

Information about promises with handles that match any entry in that list will be reported to the CFEngine Enterprise server.

When combined with promise_handle_exclude, the exclude list is applied to the subset from this list.

Type: slist

Allowed input range: .*

See also: promise_handle_exclude, metatags_include, monitoring_include

History: Introduced in CFEngine 3.6.0

monitoring_include

Description: List of anchored regular expressions matching monitoring objects to include in reporting.

Monitoring objects with names matching any entry in that list will be reported to the CFEngine Enterprise server.

When combined with monitoring_exclude, the exclude list is applied to the subset from this list.

Type: slist

Allowed input range: .*

See also: monitoring_exclude, promise_handle_include, metatags_include

History: Introduced in Enterprise 3.5.0

monitoring_exclude

Description: List of anchored regular expressions matching monitoring objects to exclude from reporting.

Monitoring objects with names matching any entry in that list will not be reported to the CFEngine Enterprise server.

When combined with monitoring_include, this list is applied to the selected subset.

Type: slist

Allowed input range: .*

See also: monitoring_include, promise_handle_exclude, metatags_exclude

History: Introduced in Enterprise 3.5.0

classes_include

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.

See also: metatags_include, metatags_exclude

classes_exclude

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.

See also: metatags_include, metatags_exclude

variables_include

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.

See also: metatags_include, metatags_exclude

variables_exclude

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.

See also: metatags_include, metatags_exclude

promise_notkept_log_include

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.

See also: promise_handle_exclude

promise_notkept_log_exclude

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.

See also: promise_handle_exclude

promise_repaired_log_include

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.

See also: promise_handle_exclude

promise_repaired_log_exclude

Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.

See also: promise_handle_exclude

resource_type

Description: The resource_type is the type of object being granted access.

By default, access to resources granted by the server are files. However, sometimes it is useful to cache literal strings, hints and data on the server for easy access (e.g. the contents of variables or hashed passwords). In the case of literal data, the promise handle serves as the reference identifier for queries. Queries are instigated by function calls by any agent.

Type: (menu option)

Allowed input range:

  • path
  • literal
  • context
  • query
  • variable

If the resource type is literal, CFEngine will grant access to a literal data string. This string is defined either by the promiser itself, but the name of the variable is the identifier given by the promise handle of the access promise, since the promiser string might be complex.

If the resource type is variable then the promiser is the name of a persistent scalar variable defined on the server-host. Currently persistent scalars are only used internally by Enterprise CFEngine to hold enumerated classes for orchestration purposes.

If you want to send the value of a policy defined variable in the server host (which for some reason is not available directly through policy on the client, e.g. because they have different policies), then you could use the following construction:

access:

  "$(variable_name)"

         handle => "variable_name",
  resource_type => "literal";

If the resource type is context, the promiser is treated as a regular expression to match persistent classes defined on the server host. If these are matched by the request from the client, they will be transmitted (See remoteclassesmatching()).

The term query may also be used in CFEngine Enterprise to query the server for data from embedded databases. This is currently for internal use only, and is used to grant access to report 'menus'. If the promiser of a query request is called collect_calls, this grants access to server peering collect-call tunneling (see also call_collect_interval).

Example:

bundle server access_rules()
{
access:

  "value of my test_scalar, can expand variables here - $(sys.host)"
    handle => "test_scalar",
    comment => "Grant access to contents of test_scalar VAR",
    resource_type => "literal",
    admit_ips => { "127.0.0.1" };

  "XYZ"
    resource_type => "variable",
    handle => "XYZ",
    admit_ips => { "127.0.0.1" };


  # On the policy hub

  "collect_calls"
     resource_type => "query",
     admit_ips => { "127.0.0.1" };

  # On the isolated client in the field


 "delta"
    comment => "Grant access to cfengine hub to collect report deltas",
    resource_type => "query",
    admit_ips   => { "127.0.0.1"  };

 "full"
          comment => "Grant access to cfengine hub to collect full report dump",
    resource_type => "query",
        admit_ips => { "127.0.0.1"  };
}
shortcut

Description: For file promisers, the server will give access to the file under its shortcut name.

Type: string

Allowed input range: (arbitrary string)

Example:

  "/var/cfengine/cmdb/$(connection.key).json"
    shortcut   => "me.json",
    admit_keys => { "$(connection.key)" };

In this example, requesting the file me.json will transfer the file stored on the server under the name /var/cfengine/cmdb/SHA=....json to the requesting host, where it will be received as me.json.

History: Introduced in CFEngine 3.6.0


processes

Process promises refer to items in the system process table, i.e., a command in some state of execution (with a Process Control Block). Promiser objects are patterns that are unanchored, meaning that they match line fragments in the system process table.

      processes:

        "regex contained in process line"

            process_select = process_filter_body,
            restart_class = "activation class for process",
            ..;

Note: Process table formats differ between operating systems, and the use of simple patterns such as program-names is recommended. For more sophisticated matches, users should use the*process_select*feature.* For example, on many systems, the process pattern "^cp" may not match any processes, even though "cp" is running. This is because the process table entry may list "/bin/cp". However, the process pattern "cp" will also match a process containing "scp", so take care not to oversimplify your patterns (the PCRE pattern anchors "\b" and "\B" may prove very useful to you).

To restart a process, you must use a class to activate and then describe a command in that class.

    commands:

      restart_me::

       "/path/executable" ... ;

This rationalizes complex restart-commands and avoids unnecessary overlap between processes and commands.

The process_stop is also arguably a command, but it should be an ephemeral command that does not lead to a persistent process. It is intended only for commands of the form /etc/inetd service stop, not for processes that persist. Processes are restarted at the end of a bundle's execution, but stop commands are executed immediately.

    bundle agent example
    {
    processes:

     ".*"

        process_count   => anyprocs,
        process_select  => proc_finder;

    reports:

     any_procs::

       "Found processes out of range";
    }

    body process_select proc_finder
    {
      # Processes started between 5.5 hours and 20 minutes ago
      stime_range => irange(ago(0,0,0,5,30,0),ago(0,0,0,0,20,0));
      process_result => "stime";
    }

    body process_count anyprocs
    {
      match_range => "0,0";
      out_of_range_define => { "any_procs" };
    }
Commands and Processes

CFEngine distinguishes between processes and commands so that there is a clean separation between detection (promises about the process table) and certain repairs (promises to execute commands that start processes).

Command executions are about jobs, services, scripts etc. They are properties of an executable file, and the referring 'promiser' 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. For example:

  • A "PID" (which is not an executable) promises to be reminded of a signal, e.g.
    kill signal pid
  • An "command" promises to start or stop itself with a parameterized specification.
    exec command argument1 argument2 ...

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.

Platform notes

Process promises depend on the ps native tool, which by default truncates lines at 128 columns on HP-UX. It is recommended to edit the file /etc/default/ps and increase the DEFAULT_CMD_LINE_WIDTH setting to 1024 to guarantee that process promises will work smoothly on that platform.


Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

process_count

Type: body process_count

in_range_define

Description: List of classes to define if the matches are in range

Classes are defined if the processes that are found in the process table satisfy the promised process count, in other words if the promise about the number of processes matching the other criteria is kept.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body process_count example
     {
     in_range_define => { "class1", "class2" };
     }
match_range

Description: Integer range for acceptable number of matches for this process

This is a numerical range for the number of occurrences of the process in the process table. As long as it falls within the specified limits, the promise is considered kept.

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_count example
     {
     match_range => irange("10","50");
     }
out_of_range_define

Description: List of classes to define if the matches are out of range

Classes to activate remedial promises conditional on this promise failure to be kept.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body process_count example(s)
     {
     out_of_range_define => { "process_anomaly", "anomaly_$(s)"};
     }
process_select

Type: body process_select

command

Description: Regular expression matching the command/cmd field of a process

This expression should match the entire COMMAND field of the process table, not just a fragment. This field is usually the last field on the line, so it thus starts with the first non-space character and ends with the end of line.

Type: string

Allowed input range: (arbitrary string)

Example:

     body process_select example

     {
     command => "cf-.*";

     process_result => "command";
     }
pid

Description: Range of integers matching the process id of a process

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_select example
     {
     pid => irange("1","10");
     process_result => "pid";
     }
pgid

Description: Range of integers matching the parent group id of a process

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_select example
     {
     pgid => irange("1","10");
     process_result => "pgid";
     }
ppid

Description: Range of integers matching the parent process id of a process

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_select example
     {
     ppid => irange("407","511");
     process_result => "ppid";
     }
priority

Description: Range of integers matching the priority field (PRI/NI) of a process

Type: irange[int,int]

Allowed input range: -20,+20

Example:

     body process_select example
     {
     priority => irange("-5","0");
     }
process_owner

Description: List of regexes matching the user of a process

The regular expressions should match a legal user name on the system. The regex is anchored, meaning it must match the entire name.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body process_select example
     {
     process_owner => { "wwwrun", "nobody" };
     }
process_result

Description: Boolean class expression with the logical combination of process selection criteria

A logical combination of the process selection classifiers. The syntax is the same as that for class expressions. If process_result is not specified, then all set attributes in the process_select body are AND'ed together.

Type: string

Allowed input range: [(process_owner|pid|ppid||pgid|rsize|vsize|status|command|ttime|stime|tty|priority|threads)[|!.]*]*

Example:

     body process_select proc_finder(p)

     {
     process_owner  => { "avahi", "bin" };
     command        => "$(p)";
     pid            => irange("100","199");
     vsize          => irange("0","1000");
     process_result => "command.(process_owner|vsize).!pid";
     }

See also: file_result

rsize

Description: Range of integers matching the resident memory size of a process, in kilobytes

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_select
     {
     rsize => irange("4000","8000");
     }
status

Description: Regular expression matching the status field of a process

For instance, characters in the set NRSsl+... Windows processes do not have status fields.

Type: string

Allowed input range: (arbitrary string)

Example:

     body process_select example
     {
     status => "Z";
     }
stime_range

Description: Range of integers matching the start time of a process

The calculation of time from process table entries is sensitive to Daylight Savings Time (Summer/Winter Time) so calculations could be an hour off. This is for now a bug to be fixed.

Type: irange[int,int]

Allowed input range: 0,2147483647

Example:

     body process_select example
     {
     stime_range => irange(ago(0,0,0,1,0,0),now);
     }
ttime_range

Description: Range of integers matching the total elapsed time of a process.

This is total accumulated time for a process.

Type: irange[int,int]

Allowed input range: 0,2147483647

Example:

     body process_select example
     {
     ttime_range => irange(0,accumulated(0,1,0,0,0,0));
     }
tty

Description: Regular expression matching the tty field of a process

Windows processes are not regarded as attached to any terminal, so they all have tty '?'.

Type: string

Allowed input range: (arbitrary string)

Example:

     body process_select example
     {
     tty => "pts/[0-9]+";
     }
threads

Description: Range of integers matching the threads (NLWP) field of a process

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_select example
     {
     threads => irange(1,5);
     }
vsize

Description: Range of integers matching the virtual memory size of a process, in kilobytes.

On Windows, the virtual memory size is the amount of memory that cannot be shared with other processes. In Task Manager, this is called Commit Size (Windows 2008), or VM Size (Windows XP).

Type: irange[int,int]

Allowed input range: 0,99999999999

Example:

     body process_select example
     {
     vsize => irange("4000","9000");
     }
process_stop

Description: A command used to stop a running process

As an alternative to sending a termination or kill signal to a process, one may call a 'stop script' to perform a graceful shutdown.

Type: string

Allowed input range: "?(/.*)

Example:

    processes:

     "snmpd"

            process_stop => "/etc/init.d/snmp stop";
restart_class

Description: A class to be defined globally if the process is not running, so that a command: rule can be referred to restart the process

This is a signal to restart a process that should be running, if it is not running. Processes are signaled first and then restarted later, at the end of bundle execution, after all possible corrective actions have been made that could influence their execution.

Windows does not support having processes start themselves in the background, like Unix daemons usually do; as fork off a child process. Therefore, it may be useful to specify an action body that sets background to true in a commands promise that is invoked by the class set by restart_class. See the commands promise type for more information.

Type: string

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

processes:

   "cf-serverd"

     restart_class => "start_cfserverd";

commands:

  start_cfserverd::

    "/var/cfengine/bin/cf-serverd";
signals

Description: A list of menu options representing signals to be sent to a process.

Signals are presented as an ordered list to the process. On Windows, only the kill signal is supported, which terminates the process.

Type: (option list)

Allowed input range:

       hup
       int
       trap
       kill
       pipe
       cont
       abrt
       stop
       quit
       term
       child
       usr1
       usr2
       bus
       segv

Example:

    processes:

     cfservd_out_of_control::

       "cfservd"

            signals         => { "stop" , "term" },
            restart_class   => "start_cfserv";

     any::

       "snmpd"

            signals         => { "term" , "kill" };

measurements

This is an Enterprise-only feature.

By default,CFEngine's monitoring component cf-monitord records performance data about the system. These include process counts, service traffic, load average and CPU utilization and temperature when available.

CFEngine Enterprise extends this in two ways. First it adds a three year trend summary based any 'shift'-averages. Second, it adds customizable measurements promises to monitor or log very specific user data through a generic interface. The end-result is to either generate a periodic time series, like the above mentioned values, or to log the results to custom-defined reports.

Promises of type measurement are written just like all other promises within a bundle destined for the agent concerned, in this case monitor. However, it is not necessary to add them to the bundlesequence, because cf-monitord executes all bundles of type monitor.

    bundle monitor self_watch
    {
    measurements:
      # Follow a special process over time
      # using CFEngine's process cache to avoid resampling

       "/var/cfengine/state/cf_rootprocs"

          handle => "monitor_self_watch",
          stream_type => "file",
          data_type => "int",
          history_type => "weekly",
          units => "kB",
          match_value => proc_value(".*cf-monitord.*",
             "root\s+[0-9.]+\s+[0-9.]+\s+[0-9.]+\s+[0-9.]+\s+([0-9]+).*");
    }

    body match_value proc_value(x,y)
    {
      select_line_matching => "$(x)";
      extraction_regex => "$(y)";
    }

It is important to specify a promise handle for measurement promises, as the names defined in the handle are used to determine the name of the log file or variable to which data will be reported. Log files are created under WORKDIR/state. Data that have no history type are stored in a special variable context called mon, analogous to the system variables in sys. Thus the values may be used in other promises in the form $(mon.handle).

    bundle monitor watch_diskspace
    {
     measurements:
      # Discover disk device information
      "/bin/df"

          handle => "free_diskspace_watch",
          stream_type => "pipe",
          data_type => "slist",
          history_type => "static",
          units => "device",
          match_value => file_systems;
          # Update this as often as possible

    }

    body match_value file_systems
    {
      select_line_matching => "/.*";
      extraction_regex => "(.*)";
    }

The general pattern of these promises is to decide whether the source of the information is either a file or pipe, determine the data type (integer, string etc.), specify a pattern to match the result in the file stream and then specify what to do with the result afterwards.


Attributes
stream_type

Description: The datatype being collected.

CFEngine treats all input using a stream abstraction. The preferred interface is files, since they can be read without incurring the cost of a process. However pipes from executed commands may also be invoked.

Type: (menu option)

Allowed input range:

     pipe
     file

Example:

stream_type => "pipe";
data_type

Description: The datatype being collected.

When CFEngine observes data, such as the attached partitions in the example above, the datatype determines how that data will be handled. Integer and real values, counters etc., are recorded as time-series if the history type is 'weekly', or as single values otherwise. If multiple items are matched by an observation (e.g. several lines in a file match the given regular expression), then these can be made into a list by choosing slist, else the first matching item will be selected.

Type: (menu option)

Allowed input range:

    counter
    int
    real
    string
    slist

Example:

  "/bin/df"

      handle => "free_disk_watch",
      stream_type => "pipe",

      data_type => "slist",

      history_type => "static",
      units => "device",
      match_value => file_systems,
      action => sample_min(10,15);
history_type

Description: Whether the data can be seen as a time-series or just an isolated value

Type: (menu option)

Allowed input range:

  • scalar

A single value, with compressed statistics is retained. The value of the data is not expected to change much for the lifetime of the daemon (and so will be sampled less often by cf-monitord).

  • static

A synonym for 'scalar'.

  • log

The measured value is logged as an infinite time-series in \$(sys.workdir)/state.

  • weekly

A standard CFEngine two-dimensional time average (over a weekly period) is retained.

Example:

 "/proc/meminfo"

      handle => "free_memory_watch",
      stream_type => "file",
      data_type => "int",
      history_type => "weekly",
      units => "kB",
      match_value => free_memory;
units

Description: The engineering dimensions of this value or a note about its intent used in plots

This is an arbitrary string used in documentation only.

Type: string

Allowed input range: (arbitrary string)

Example:

   "/var/cfengine/state/cf_rootprocs"

      handle => "monitor_self_watch",
      stream_type => "file",
      data_type => "int",
      history_type => "weekly",
      units => "kB",
      match_value => proc_value(".*cf-monitord.*",

         "root\s+[0-9.]+\s+[0-9.]+\s+[0-9.]+\s+[0-9.]+\s+([0-9]+).*");
match_value

Type: body match_value

select_line_matching

Description: Regular expression for matching line location

The expression is anchored, meaning it must match a whole line, and not a fragment within a line.

This attribute is mutually exclusive of select_line_number.

Type: string

Allowed input range: .*

Example:

     # Editing

     body location example
     {
     select_line_matching => "Expression match.* whole line";
     }

     # Measurement promises

     body match_value example
     {
     select_line_matching => "Expression match.* whole line";
     }
select_line_number

Description: Read from the n-th line of the output (fixed format)

This is mutually exclusive of select_line_matching.

Type: int

Allowed input range: 0,99999999999

Example:

     body match_value find_line
     {
     select_line_number => "2";
     }

Notes:

extraction_regex

Description: Regular expression that should contain a single back-reference for extracting a value.

A single parenthesized back-reference should be given to lift the value to be measured out of the text stream. The regular expression is unanchored, meaning it may match a partial string

Type: string

Allowed input range: (arbitrary string)

Example:

     body match_value free_memory
     {
     select_line_matching => "MemFree:.*";
     extraction_regex => "MemFree:\s+([0-9]+).*";
     }
track_growing_file

Description: If true, CFEngine remembers the position to which is last read when opening the file, and resets to the start if the file has since been truncated

This option applies only to file based input streams. If this is true, CFEngine treats the file as if it were a log file, growing continuously. Thus the monitor reads all new entries since the last sampling time on each invocation. In this way, the monitor does not count lines in the log file redundantly.

This makes a log pattern promise equivalent to something like tail -f logfile | grep pattern in Unix parlance.

Type: boolean

Example:

     bundle monitor watch
     {
     measurements:

        "/home/mark/tmp/file"

              handle => "line_counter",
         stream_type => "file",
           data_type => "counter",
         match_value => scan_log("MYLINE.*"),
        history_type => "log",
              action => sample_rate("0");

     }

     #

     body match_value scan_log(x)
     {
     select_line_matching => "^$(x)$";
     track_growing_file => "true";
     }

     #

     body action sample_rate(x)
     {
     ifelapsed => "$(x)";
     expireafter => "10";
     }
select_multiline_policy

Description: Regular expression for matching line location

This option governs how CFEngine handles multiple matching lines in the input stream. It can average or sum values if they are integer or real, or use first or last representative samples. If non-numerical data types are used only the first match is used.

Type: (menu option)

Allowed input range:

    average
    sum
    first
    last

Example:

     body match_value myvalue(xxx)
     {
      select_line_matching => ".*$(xxx).*";
      extraction_regex => "root\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+).*";
      select_multiline_policy => "sum";
     }

History: Was introduced in 3.4.0 (2012)


meta

Meta-data promises have no internal function. They are intended to be used to represent arbitrary information about promise bundles. Formally, meta promises are implemented as variables, and the values map to a variable context called bundlename_meta. The values can be used as variables and will appear in CFEngine Enterprise variable reports.

    bundle agent example
    {
    meta:

      "bundle_version" string => "1.2.3";
      "works_with_cfengine" slist => { "3.4.0", "3.5.0" };

    reports:

      "Not a local variable: $(bundle_version)";
      "Meta data (variable): $(example_meta.bundle_version)";

    }

The value of meta data can be of the types string or slist or data.


bundle edit_xml

The use of XML documents in systems configuration is widespread. XML documents represent data that is complex and can be structured in various ways. The XML based editing offers a powerful environment for editing hierarchical and structured XML datasets.

XML editing promises are made in a bundle edit_xml, which is then used in the edit_xml attribute of a files promise. The promiser of the files promise needs to be the XML document that is being edited. Within an edit_xml bundle, various promise types are available to create new or manipulate existing XML documents.


Common Attributes

The following attributes are available in all edit_xml promise types.

build_xpath

Description: Builds an XPath within the XML file

Please note that when build_xpath is defined as an attribute within an edit_xml promise body, the tree described by the specified XPath will be verified and built BEFORE other edit_xml promises within same promise body. Therefore, the file will not be empty during the execution of such promises.

Type: string

Allowed input range: (arbitrary string)

select_xpath

Description: Select the XPath node in the XML file to edit

Edits to the XML document take place within the selected node. This attribute is not used when inserting XML content into an empty file.

Type: string

Allowed input range: (arbitrary string)

build_xpath

This promise type assures that a balanced XML tree, described by the given XPath, will be present within the XML file. If the document is empty, the default promise is to build the XML tree within the document. If the document is not empty, the default promise is to verify the given XPath, and if necessary, locate an insertion node and build the necessary portion of XML tree within selected node. The insertion node is selected as the last unique node that is described by the XPath and also found within the document. The promise object referred to is a literal string representation of an XPath.

    bundle edit_xml example
    {
    build_xpath:
       "/Server/Service/Engine/Host";
    }

Note that typically, only a single XPath is built in each build_xpath promise. You may of course have multiple promises that each build an XPath. The supported syntax used in building an XPath is currently limited to a simple and compact format, as shown in the above example. The XPath must begin with '/', as it is verified and built using an absolute path, from the root node of the document.

The resulting document can then be further modified using insert_tree, set_text, set_attribute etc promises. Using predicate statements to set attributes or text values directly via build_xpath can lead to non-convergent behavior, and is discouraged.

insert_tree

This promise type assures that a balanced XML tree, containing the matching subtree, will be present in the specified node within the XML file. If the subtree is not found, the default promise is to insert the tree into the specified node. The specified node is determined by body-attributes. The promise object referred to is a literal string representation of a balanced XML tree.

    bundle edit_xml example
    {
    insert_tree:

      "<Host name=\"cfe_host\"><Alias>cfe_alias</Alias></Host>"
        select_xpath => "/Server/Service/Engine";
    }

Note that typically, only a single tree, within a single specified node, is inserted in each insert_tree promise. You may of course have multiple promises that each insert a tree.

delete_tree

This promise type assures that a balanced XML tree, containing the matching subtree, will not be present in the specified node within the XML file. If the subtree is found, the default promise is to remove the containing tree from within the specified node. The specified node is determined by body-attributes. The promise object referred to is a literal string representation of a balanced XML subtree.

    bundle edit_xml example
    {
    delete_tree:

      "<Host name=\"cfe_host\"></Host>"
        select_xpath => "/Server/Service/Engine";
    }

Note that typically, only a single tree, within a single specified node, is deleted in each delete_tree promise. You may of course have multiple promises that each delete a tree.

insert_text

This proimse type assures that a value string, containing the matching substring, will be present in the specified node within the XML file. If the substring is not found, the default promise is to append the substring to the end of the existing value string, within the specified node. The specified node is determined by body-attributes. The promise object referred to is a literal string of text.

    bundle edit_xml example
    {
    insert_text:

      "text content to be appended to existing text, including whitespace, within specified node"
        select_xpath => "/Server/Service/Engine/Host/Alias";
    }

Note that typically only a single value string, within a single specified node, is inserted in each insert_text promise. You may of course have multiple promises that each insert a value string.

set_text

This promise type assures that a matching value string will be present in the specified node within the XML file. If the existing value string does not exactly match, the default promise is to replace the existing value string, within the specified node. The specified node is determined by body-attributes. The promise object referred to is a literal string of text.

    bundle edit_xml example
    {
    set_text:
      "text content to replace existing text, including whitespace, within selected node"

        select_xpath => "/Server/Service/Engine/Host/Alias";
    }

Note that typically only a single value string, within a single selected node, is set in each set_text promise. You may of course have multiple promises that each set a value string.

delete_text

This promise type assures that a value string, containing the matching substring, will not be present in the specified node within the XML file. If the substring is found, the default promise is to remove the existing value string, from within the specified node. The specified node is determined by body-attributes. The promise object referred to is a literal string of text.

    bundle edit_xml example
    {
    delete_text:

      "text content to match, as a substring, of text to be deleted from specified node"
        select_xpath => "/Server/Service/Engine/Host/Alias";
    }

Note that typically, only a single value string, within a single specified node, is deleted in each delete_text promise. You may of course have multiple promises that each delete a value string.

set_attribute

This promise type assures that an attribute, with the given name and value, will be present in the specified node within the XML file. If the attribute is not found, the default promise is to insert the attribute, into the specified node. If the attribute is already present, the default promise is to insure that the attribute value is set to the given value. The specified node and attribute value are each determined by body-attributes. The promise object referred to is a literal string representation of the name of the attribute to be set.

    bundle edit_xml example
    {
    set_attribute:
      "name"

        attribute_value => "cfe_host",
           select_xpath => "/Server/Service/Engine/Host";
    }

Note that typically only a single attribute, within a single selected node, is set in each set_attribute promise. You may of course have multiple promises that each set an attribute.

Attributes
attribute_value

Description: Value of the attribute to be inserted into the XPath node of the XML file

Type: string

Allowed input range: (arbitrary string)

delete_attribute

This promise type assures that an attribute, with the given name, will not be present in the specified node within the XML file. If the attribute is found, the default promise is to remove the attribute, from within the specified node. The specified node is determined by body-attributes. The promise object referred to is a literal string representation of the name of the attribute to be deleted.

    bundle edit_xml example
    {
    delete_attribute:
      "attribute name"
        select_xpath => "/Server/Service/Engine/Host";
    }

Note that typically, only a single attribute, within a single specified node, is deleted in each delete_attribute promise. You may of course have multiple promises that each delete an attribute.


guest_environments

Guest environment promises describe enclosed computing environments that can host physical and virtual machines, Solaris zones, grids, clouds or other enclosures, including embedded systems. CFEngine will support the convergent maintenance of such inner environments in a fixed location, with interfaces to an external environment.

CFEngine currently seeks to add convergence properties to existing interfaces for automatic self-healing of guest environments. The current implementation integrates with libvirt, supporting host virtualization for Xen, KVM, VMWare, etc. Thus CFEngine, running on a virtual host, can maintain the state and deployment of virtual guest machines defined within the libvirt framework. Guest environment promises are not meant to manage what goes on within the virtual guests. For that purpose you should run CFEngine directly on the virtual machine, as if it were any other machine.

 site1::

  "unique_name1"

       environment_resources => myresources("2GB","512MB"),
       environment_interface => mymachine("hostname"),
            environment_type => "xen",
            environment_state => "running",
            environment_host => "atlas";

  "unique_name2"

            environment_type => "xen_network",
           environment_state => "create",
            environment_host => "atlas";

CFEngine currently provides a convergent interface to libvirt.


Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

environment_host

Description: environment_host is a class indicating which physical node will execute this guest machine

The promise will only apply to the machine with this class set. Thus, CFEngine must be running locally on the hypervisor for the promise to take effect.

Type: string

Allowed input range: [a-zA-Z0-9_]+

Example:

guest_environments:

 linux::

 "host1"
                 comment => "Keep this vm suspended",
   environment_resources => myresources,
        environment_type => "kvm",
       environment_state => "suspended",
        environment_host => "ubuntu";

This attribute is required.

History: this feature was introduced in Nova 2.0.0 (2010), Community 3.3.0 (2012)

environment_interface

Type: body environment_interface

env_addresses

Description: env_addresses is the IP addresses of the environment's network interfaces

The IP addresses of the virtual machine can be overridden here at run time.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body environment_interface vnet(primary)
     {
     env_name      => "$(this.promiser)";
     env_addresses => { "$(primary)" };

     host1::

       env_network => "default_vnet1";

     host2::

       env_network => "default_vnet2";

     }
env_name

Description: env_name is the hostname of the virtual environment.

The 'hostname' of a virtual guest may or may not be the same as the identifier used as 'promiser' by the virtualization manager.

Type: string

Allowed input range: (arbitrary string)

Example:

     body environment_interface vnet(primary)
     {
     env_name      => "$(this.promiser)";
     env_addresses => { "$(primary)" };

     host1::
       env_network => "default_vnet1";

     host2::
       env_network => "default_vnet2";
     }
env_network

Description: The hostname of the virtual network

Type: string

Allowed input range: (arbitrary string)

Example:

    body environment_interface vnet(primary)
    {
    env_name      => "$(this.promiser)";
    env_addresses => { "$(primary)" };

    host1::
      env_network => "default_vnet1";

    host2::
      env_network => "default_vnet2";
    }
environment_resources

Type: body enviornment_resources

env_cpus

Description: env_cpus represents the number of virtual CPUs in the environment.

The maximum number of cores or processors in the physical environment will set a natural limit on this value.

Type: int

Allowed input range: 0,99999999999

Example:

     body environment_resources my_environment
     {
     env_cpus => "2";
     env_memory => "512"; # in KB
     env_disk => "1024";  # in MB
     }

Notes: This attribute conflicts with env_spec.

env_memory

Description: env_memory represents the amount of primary storage (RAM) in the virtual environment (in KB).

The maximum amount of memory in the physical environment will set a natural limit on this value.

Type: int

Allowed input range: 0,99999999999

Example:

     body environment_resources my_environment
     {
     env_cpus => "2";
     env_memory => "512"; # in KB
     env_disk => "1024";  # in MB
     }

Notes: This attribute conflicts with env_spec.

env_disk

Description: env_disk represents the amount of secondary storage (DISK) in the virtual environment (in KB).

This parameter is currently unsupported, for future extension.

Type: int

Allowed input range: 0,99999999999

Example:

     body environment_resources my_environment
     {
     env_cpus => "2";
     env_memory => "512"; # in KB
     env_disk => "1024";  # in MB
     }

Notes: This parameter is currently unsupported, for future extension.

This attribute conflicts with env_spec.

env_baseline

Description: The env_baseline string represents a path to an image with which to baseline the virtual environment.

Type: string

Allowed input range: "?(/.*)

Example:

     env_baseline => "/path/to/image";

Notes: This function is for future development.

env_spec

Description: A env_spec string contains a technology specific set of promises for the virtual instance.

This is the preferred way to specify the resources of an environment on creation; in other words, when environment_state is create.

Type: string

Allowed input range: .*

Example:

     body environment_resources virt_xml(host)
     {
     env_spec =>

     "<domain type='xen'>
       <name>$(host)/name>
       <os>
         <type>linux/type>
         <kernel>/var/lib/xen/install/vmlinuz-ubuntu10.4-x86_64/kernel>
         <initrd>/var/lib/xen/install/initrd-vmlinuz-ubuntu10.4-x86_64/initrd>
         <cmdline> kickstart=http://example.com/myguest.ks /cmdline>
       </os>
       <memory>131072/memory>
       <vcpu>1/vcpu>
       <devices>
         <disk type='file'>
           <source file='/var/lib/xen/images/$(host).img'/>
           <target dev='sda1'/>
         </disk>
         <interface type='bridge'>
           <source bridge='xenbr0'/>
           <mac address='aa:00:00:00:00:11'/>
           <script path='/etc/xen/scripts/vif-bridge'/>
         </interface>
         <graphics type='vnc' port='-1'/>
         <console tty='/dev/pts/5'/>
       </devices>
     </domain>
     ";
     }

Notes:

This attribute conflicts with env_cpus, env_memory and env_disk.

History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010)

environment_state

Description: The environment_state defines the desired dynamic state of the specified environment.

Type: (menu option)

Allowed input range:

The guest machine is allocated, installed and left in a running state.

The guest machine is shut down and deallocated, but no files are removed.

  • running

The guest machine is in a running state, if it previously exists.

  • suspended

The guest exists in a suspended state or a shutdown state. If the guest is running, it is suspended; otherwise it is ignored.

  • down

The guest machine is shut down, but not deallocated.

Example:

guest_environments:

 linux::

 "bishwa-kvm1"
                 comment => "Keep this vm suspended",
   environment_resources => myresources,
        environment_type => "kvm",
       environment_state => "suspended",
        environment_host => "ubuntu";
environment_type

Description: environment_type defines the virtual environment type.

The currently supported types are those supported by libvirt. More will be added in the future.

Type: (menu option)

Allowed input range:

    xen
    kvm
    esx
    vbox
    test
    xen_net
    kvm_net
    esx_net
    test_net
    zone
    ec2
    eucalyptus

Example:

bundle agent my_vm_cloud
{
guest_environments:

 scope::

   "vguest1"

       environment_resources => my_environment_template,
       environment_interface => vnet("eth0,192.168.1.100/24"),
       environment_type      => "test",
       environment_state     => "create",
       environment_host      => "atlas";

   "vguest2"

       environment_resources => my_environment_template,
       environment_interface => vnet("eth0,192.168.1.101/24"),
       environment_type      => "test",
       environment_state     => "delete",
       environment_host      => "atlas";
}

bundle edit_line

Line based editing is a simple model for editing files. Before XML, and later JSON, most configuration files were line based. The line-based editing offers a powerful environment for model-based editing and templating.

File editing in CFEngine 3

File editing is not just a single kind of promise but a whole range of 'promises within files'. It is therefore not merely a body to a single kind of promise, but a bundle of sub-promises. After all, inside each file are new objects that can make promises, quite separate from files' external attributes.

A typical file editing stanza has the elements in the following example:

body common control
{
  version => "1.2.3";
  bundlesequence  => { "outerbundle"  };
}

bundle agent outerbundle
{
files:

  "/home/mark/tmp/cf3_test"
       create    => "true",     # Like autocreate in cf2
       edit_line => inner_bundle;
}

bundle edit_line inner_bundle
{
  vars:

    "who" string => "SysAdmin John"; # private variable in bundle

  insert_lines:
    "/* This file is maintained by CFEngine (see $(who) for details) */",
    location => first_line;

  replace_patterns:
   # replace shell comments with C comments

   "#(.*)"
      replace_with => C_comment,
     select_region => MySection("New section");

  reports:
      "This is file $(edit.filename)";
}

body replace_with C_comment
{
  replace_value => "/* $(match.1) */"; # backreference
  occurrences => "all";          # first, last all
}

body select_region MySection(x)
{
  select_start => "\[$(x)\]";
  select_end => "\[.*\]";
}

body location first_line
{
  before_after => "before";
  first_last => "first";
  select_line_matching => ".*";
}

There are several things to notice:

  • The line-editing promises are all convergent promises about patterns within the file. They have bodies, just like other attributes do and these allow us to make simple templates about file editing while extending the power of the basic primitives.
  • All file edits specified in a single edit_line bundle are handled "atomically". CFEngine edits files like this:

    • CFEngine makes a copy of the file you you want to edit.
    • CFEngine makes all the edits in the copy of the file. The filename is the same as your original file with the extension .cf-after-edit appended.
    • After all edits are complete (the delete_lines, field_edits, insert_lines, and finally replace_patterns promises), CFEngine checks to see if the new file is the same as the original one. If there are no differences, the promises have converged, so it deletes the copy, and the original is left completely unmodified.
    • If there are any differences, CFEngine makes a copy of your original file with the extension .cf-before-edit (so you always have the most recent backup available), and then renames the edited version to your original filename.

    Because file rename is an atomic operation (guaranteed by the operating system), any application program will either see the old version of the file or the new one. There is no "window of opportunity" where a partially edited file can be seen (unless an application intentionally looks for the .cf-after-edit file). Problems during editing (such as disk-full or permission errors) are likewise detected, and CFEngine will not rename a partial file over your original.

  • All pattern matching is through Perl Compatible Regular Expressions

  • Editing takes place within a marked region (which defaults to the whole file if not otherwise specified).

  • Search/replace functions now allow back-references.

  • The line edit model now contains a field or column model for dealing with tabular files such as Unix passwd and group files. We can now apply powerful convergent editing operations to single fields inside a table, to append, order and delete items from lists inside fields.

  • The special variable $(edit.filename) contains the name of the file being edited within an edit bundle.

  • On Windows, a text file may be stored stored either with CRLF line endings (Windows style), or LF line endings (Unix style). CFEngine will respect the existing line ending type and make modifications using the same type. New files will get CRLF line ending type.

In the example above, back references are used to allow conversion of comments from shell-style to C-style.

Another example of files promises is to look for changes in files. The following example reports on all recent changes to files in a directory by maintaining the most recent version of the md5 hash of the file contents. Similar checks can be used to examine metadata or both the contents and metadata, as well as using different difference checks. The Community Edition only reports that changes were found, but Enterprise versions of CFEngine can also report on what exactly the significant changes were.

bundle agent example
{
files:

  "/home/mark/tmp" -> "Security team"

       changes      => lay_a_tripwire,
       depth_search => recurse("inf"),
       action       => background;
}

body changes lay_a_tripwire
{
  hash           => "md5";
  report_changes => "content";
  update         => "yes";
}
Scope and lifetime of the select_region

The region selected with select_region exists during the lifetime of the promise. This means that once a promise has been started the selected region will be used regardless of what the changes are.

There is a down side to this, promise lifetime is shorter than expected. For instance let's look at the following code example:

bundle agent init
{
vars:
    "states" slist => { "actual", "expected" };

    "actual" string =>
"header
header
BEGIN
One potato
Two potato
Three potatoe
Four
END
trailer
trailer";

    "expected" string =>
"header
header
One potato
Two potato
Four
trailer
trailer";

files:
    "testfile.$(states)"
      create => "true",
      edit_line => init_insert("$(init.$(states))"),
      edit_defaults => init_empty;
}

bundle edit_line init_insert(str)
{
insert_lines:
    "$(str)";
}

body edit_defaults init_empty
{
  empty_file_before_editing => "true";
}

#######################################################

bundle agent test
{
vars:
    "tstr" slist => { "BEGIN", "    Three potatoe", "END" };

files:
    "testfile.actual"
      edit_line => test_delete("$(test.tstr)");
}

bundle edit_line test_delete(str)
{
delete_lines:
    "$(str)"
      select_region => test_select;
}

body select_region test_select
{
  select_start => "BEGIN";
  select_end => "END";
  include_start_delimiter => "true";
  include_end_delimiter => "true";
}

The code generates two files, testfile.actual and testfile.expected. The idea is that both files will be equal after the promise is run, since the transformations applied to testfile.actual will convert it into testfile.equal.

However due to the lifetime of promises, this is not true. The attribute select_region lives as long as the promise that created it lives and it will be recreated on the next incarnation.

Notice that tstr is a slist that is used as a parameter for edit_line, which uses it to select the strings that will be removed. The select_region body specifies that the select_start attribute is "BEGIN", which holds true only for the first invocation of the promise because during that iteration it will be removed. Once it is removed the select_region body will never be able to match select_start again.

In the previous example, it is easy to think that the select_region will be kept during the whole iteration of the slist. This is not true, each element in the slist will trigger its own invocation of the promise, therefore select_region will only match the first iteration.

The solution to this problem is simple: if the marker for a region needs to be removed, then it cannot be used as a marker. In the example above it is enough to change the markers from "BEGIN" to "header" and from "END" to "trailer" to obtain the desired result.


Attributes
select_region

Type: body select_region

Description: Restrict edit_line promise to specific section

Restrict edits to a specific region of a file based on select_start and select_end regular expressions. If the beginning and ending regular expressions match more than one region only the first region will be selected for editing.

include_start_delimiter

Description: Whether to include the section delimiter

In a sectioned file, the line that marks the opening of a section is not normally included in the defined region (that is, it is recognized as a delimiter, but it is not included as one of the lines available for editing). Setting this option to true makes it included.

Type: boolean

Default value: false

Example:

     body select_region MySection(x)
     {
       select_start => "\[$(x)\]";
       select_end => "\[.*\]";
       include_start_delimiter => "true";
     }

Input file:

     [My section]
     one
     two
     three

In this example, the section does not normally include the line [My section]. By setting include_start_delimiter to true it would be possible for example, to delete the entire section, including the section header. If however include_start_delimiter is false, the contents of the section could be deleted, but the header would be unaffected by any delete_lines promises. See the next section on include_start_delimiter for further details.

History: This attribute was introduced in CFEngine version 3.0.5 (2010)

include_end_delimiter

Description: Whether to include the section delimiter

In a sectioned file, the line that marks the end of a section is not normally included in the defined region; that is, it is recognized as a delimiter, but it is not included as one of the lines available for editing. Setting this option to true makes it included.

Type: boolean

Default value: false

Example:

     body select_region BracketSection(x)
     {
     select_start => "$(x) \{";
     select_end => "}";
     include_end_delimiter => "true";
     }

Input file:

     /var/log/mail.log {
         monthly
         missingok
         notifempty
         rotate 7
         }

The section does not normally include the line containing }. By setting include_end_delimiter to true it would be possible for example, to delete the entire section, including the section trailer. If however include_end_delimiter is false, the contents of the section could be deleted, but the header would be unaffected by any delete_lines promises.

The use of include_start_delimiter and include_end_delimiter depend on the type of sections you are dealing with, and what you want to do with them. Note that sections can be bounded at both the start and end (as in the example above) or just at the start (as in the sample shown in include_start_delimiter).

History: This attribute was introduced in CFEngine version 3.0.5 (2010)

select_start

Description: Regular expression matching start of edit region

See also select_end. These delimiters mark out the region of a file to be edited.

Type: string

Allowed input range: .*

Example:

     body select_region example(x)
     {
       select_start => "\[$(x)\]";
       select_end => "\[.*\]";
     }
select_end

Description: Regular expression matches end of edit region from start

See also select_start. These delimiters mark out the region of a file to be edited.

Type: string

Allowed input range: .*

Example:

     body select_region example(x)
     {
     select_start => "\[$(x)\]";
     select_end => "\[.*\]";
     }

If you want to match from a starting location to the end of the file (even if there are other lines matching select_start intervening), then just omit the select_end promise and the selected region will run to the end of the file.


field_edits

Certain types of text files are tabular in nature, with field separators (e.g. : or ,). The passwd and group files are classic examples of tabular files, but there are many ways to use this feature. For example, editing a string:

    VARIABLE="one two three"

View this line as a tabular line separated by " and with sub-separator given by the space.

Field editing allows us to edit tabular files in a unique way, adding and removing data from addressable fields.

    bundle agent example
    {
      vars:

       "userset" slist => { "one-x", "two-x", "three-x" };

      files:

        "/tmp/passwd"

             create    => "true",
             edit_line => SetUserParam("mark","6","/set/this/shell");

        "/tmp/group"

             create    => "true",
             edit_line => AppendUserParam("root","4","@(userset)");
    }

The promise in this example assumes a parameterizable model for editing the fields of such files.

    bundle edit_line SetUserParam(user,field,val)
    {
      field_edits:

       "$(user):.*"

          # Set field of the file to parameter

          edit_field => col(":","$(field)","$(val)","set");
    }

    bundle edit_line AppendUserParam(user,field,allusers)
    {
      vars:

        "val" slist => { @(allusers) };

      field_edits:

       "$(user):.*"

          # Set field of the file to parameter

          edit_field => col(":","$(field)","$(val)","alphanum");
    }

First you match the line with a regular expression. The regular expression must match the entire line; that is, it is anchored.

    body edit_field col(split,col,newval,method)
    {
      field_separator => "$(split)";
      select_field    => "$(col)";
      value_separator  => ",";
      field_value     => "$(newval)";
      field_operation => "$(method)";
      extend_fields => "true";
    }

Then a field_edits body describes the separators for fields and one level of sub-fields, along with policies for editing these fields, ordering the items within them.


Attributes
edit_field

Type: body edit_field

Example:

     body edit_field col(split, col, newval, method)
     {
       field_separator    => "$(split)";
       select_field       => "$(col)";
       value_separator    => ",";
       field_value        => "$(newval)";
       field_operation    => "$(method)";
       extend_fields      => "true";
       allow_blank_fields => "true";
       start_fields_from_zero => "true";
     }
allow_blank_fields

Description: Setting allow_blank_fields defines how blank fields in a line are handled.

When editing a file using the field or column model, blank fields, especially at the start and end, are generally discarded. If allow_blank_fields is set to true, CFEngine will retain the blank fields and print the appropriate number of field separators.

Type: boolean

Default value: false

Example:

     body edit_field example
     {
     allow_blank_fields => "true";
     }
extend_fields

Description: Setting extend_fields can add new fields, to avoid triggering an error.

If a user specifies a field that does not exist, because there are not so many fields, this allows the number of fields to be extended. Without this setting, CFEngine will issue an error if a non-existent field is referenced. Blank fields in a tabular file can be eliminated or kept depending in this setting. If in doubt, set this to true.

Type: boolean

Default value: false

Example:

     body edit_field example
     {
     extend_fields => "true";
     }
field_operation

Description: Menu option policy for editing subfields.

The method by which to edit a field in multi-field/column editing of tabular files.

Type: (menu option)

Allowed input range:

  • append

Append the specified value to the end of the field/column, separating (potentially) multiple values with value_separator

  • prepend

Prepend the specified value at the beginning of the field/column, separating (potentially) multiple values with value_separator

  • alphanum

Insert the specified value into the field/column, keeping all the values (separated by value_separator) in alphanumerically sorted order

  • set

Replace the entire field/column with the specified value.

Delete the specified value (if present) in the specified field/column.

Default value: append

Example:

     body edit_field example
     {
     field_operation => "append";
     }
field_separator

Description: The regular expression used to separate fields in a line.

Most tabular files are separated by simple characters, but by allowing a general regular expression one can make creative use of this model to edit all kinds of line-based text files.

Type: string

Allowed input range: .*

Default value: none

Example:

     body edit_field example
     {
     field_separator => ":";
     }
field_value

Description: Set a field to a constant value.

For example, reset the value to a constant default, empty the field, or set it fixed list.

Type: string

Allowed input range: .*

Example:

     body edit_field example(s)
     {
     field_value => "$(s)";
     }
select_field

Description: Sets the index of the field required, see also start_fields_from_zero.

Type: int

Allowed input range: 0,99999999

Example:

     body field_edits example
     {
     select_field => "5";
     }
start_fields_from_zero

Description: The numbering of fields is a matter for consistency and convention. Arrays are usually thought to start with first index equal to zero (0), but the first column in a file would normally be 1. By setting this option, you can tell CFEngine that the first column should be understood as number 0 instead, for consistency with other array functions.

Type: boolean

History: Version 3.1.0b1,Nova 2.0.0b1 (2010)

value_separator

Description: Defines the character separator for subfields inside the selected field.

For example, elements in the group file are separated by a colon (':'), but the lists of users in these fields are separated by a comma (',').

Type: string

Allowed input range: ^.$

Default value: none

Example:

     body field_edit example
     {
     value_separator => ",";
     }

insert_lines

This promise is part of the line-editing model. It inserts lines into the file at a specified location. The location is determined by body-attributes. The promise object referred to can be a literal line or a file-reference from which to read lines.

      insert_lines:

        "literal line or file reference"

           location => location_body,
           ...;

By parameterizing the editing bundle, one can make generic and reusable editing bundles.

Note: When inserting multiple lines anchored to a particular place in a file, be careful with your intuition. If your intention is to insert a set of lines in a given order after a marker, then the following is incorrect:

    bundle edit_line x
    {
    insert_lines:

      "line one" location => myloc;
      "line two" location => myloc;
    }

    body location myloc
    {
      select_line_matching => "# Right here.*";
      before_after => "after";
    }

This will reverse the order of the lines and will not converge, since the anchoring after the marker applies independently for each new line. This is not a bug, but an error of logic.

What was probably intended was to add multiple ordered lines after the marker, which should be a single correlated promise.

    bundle edit_line x
    {
    insert_lines:

     "line one$(const.n)line two" location => myloc;
    }

Or:

    bundle edit_line x
    {
    insert_lines:

      "line one
    line two"
      location => myloc;
    }

Attributes
expand_scalars

Description: Expand any unexpanded variables

This is a way of incorporating templates with variable expansion into file operations. Variables should be named and scoped appropriately for the bundle in which this promise is made. In other words, you should qualify the variables with the bundle in which they are defined. For example:

    $(bundle.variable)
    $(sys.host)
    $(mon.www_in)

Type: boolean

Default value: false

Example:

bundle agent testbundle
{
files:

  "/home/mark/tmp/file_based_on_template"

       create    => "true",
       edit_line => ExpandMeFrom("/tmp/source_template");
}

bundle edit_line ExpandMeFrom(template)
{
insert_lines:
   "$(template)"

          insert_type => "file",
       expand_scalars => "true";
}
insert_type

Description: Type of object the promiser string refers to

The default is to treat the promiser as a literal string of convergent lines.

Type: (menu option)

Allowed input range:

Treat the promiser as a literal string of convergent lines.

  • file

The string should be interpreted as a filename from which to import lines.

  • preserve_block

The default behavior assumes that multi-line entries are not ordered specifically. They should be treated as a collection of lines of text, and not as a single unbroken object.

If the option preserve_block is used, then CFEngine will not break up multiple lines into individual, non-ordered objects, so that the block of text will be preserved. Even if some of the lines in the block already exist, they will be added again as a coherent block. Thus if you suspect that some stray / conflicting lines might be present they should be cleaned up with delete_lines first.

  • preserve_all_lines

Disables idempotency during the insertion of a block of text so that multiple identical lines may be inserted.

This means that the text will be inserted to the file even if it is already present. To avoid that the file grows, use this together with empty_file_before_editing.

  • file_preserve_block

Interpret the string as a filename, and assume preserve_block semantics. This was added in CFEngine 3.5.x.

Default value: literal

Example:

    bundle edit_line lynryd_skynyrd
    {
     vars:
        "keepers" slist => { "Won't you give me", "Gimme three steps" };

     insert_lines:

         "And you'll never see me no more"
           insert_type => "literal";    # the default

         "/song/lyrics"
           insert_type => "file",  # read selected lines from /song/lyrics
           insert_select => keep("@{keepers}");
    }

    body insert_select keep(s)
    {
    insert_if_startwith_from_list => { "@(s)" };
    }

This will ensure that the following lines are inserted into the promised file:

    And you'll never see me no more
    Gimme three steps, Mister
    Gimme three steps towards the door
    Gimme three steps
insert_select

Type: body insert_select

insert_if_startwith_from_list

Description: Insert line if it starts with a string in the list

The list contains literal strings to search for in the secondary file (the file being read via the insert_type attribute, not the main file being edited). If a string with matching starting characters is found, then that line from the secondary file will be inserted at the present location in the primary file.

insert_if_startswith_from_list is ignored unless insert_type is file, or the promiser is a multi-line block.

Type: slist

Allowed input range: .*

Example:

     body insert_select example
     {
     insert_if_startwith_from_list => { "find_me_1", "find_me_2" };
     }
insert_if_not_startwith_from_list

Description: Insert line if it DOES NOT start with a string in the list

The complement of insert_if_startwith_from_list. If the start of a line does not match one of the strings, that line is inserted into the file being edited.

insert_if_not_startswith_from_list is ignored unless insert_type is file or the promiser is a multi-line block.

Type: slist

Allowed input range: .*

Example:

     body insert_select example
     {
     insert_if_not_startwith_from_list => { "find_me_1", "find_me_2" };
     }
insert_if_match_from_list

Description: Insert line if it fully matches a regex in the list

The list contains literal strings to search for in the secondary file (the file being read via the insert_type attribute, not the main file being edited). If the regex matches a complete line of the file, that line from the secondary file will be inserted at the present location in the primary file. That is, the regex's in the list are anchored.

insert_if_match_from_list is ignored unless insert_type is file, or the promiser is a multi-line block.

Type: slist

Allowed input range: .*

Example:

     body insert_select example
     {
     insert_if_match_from_list => { ".*find_.*_1.*", ".*find_.*_2.*" };
     }
insert_if_not_match_from_list

Description: Insert line if it DOES NOT fully match a regex in the list

The complement of insert_if_match_from_list. If the line does not match a line in the secondary file, it is inserted into the file being edited.

insert_if_not_match_from_list is ignored unless insert_type is file, or the promiser is a multi-line block.

Type: slist

Allowed input range: .*

Example:

     body insert_select example
     {
     insert_if_not_match_from_list => { ".*find_.*_1.*", ".*find_.*_2.*" };
     }
insert_if_contains_from_list

Description: Insert line if a regex in the list match a line fragment.

The list contains literal strings to search for in the secondary file; in other words, the file being read via the insert_type attribute, not the main file being edited. If the string is found in a line of the file, that line from the secondary file will be inserted at the present location in the primary file.

insert_if_contains_from_list is ignored unless insert_type is file, or the promiser is a multi-line block.

Type: slist

Allowed input range: .*

Example:

     body insert_select example
     {
     insert_if_contains_from_list => { "find_me_1", "find_me_2" };
     }
insert_if_not_contains_from_list

Description: Insert line if a regex in the list DOES NOT match a line fragment.

The complement of insert_if_contains_from_list. If the line is not found in the secondary file, it is inserted into the file being edited.

insert_if_not_contains_from_list is ignored unless insert_type is file, or the promiser is a multi-line block.

Type: slist

Allowed input range: .*

Example:

     body insert_select example
     {
     insert_if_not_contains_from_list => { "find_me_1", "find_me_2" };
     }
location

Type: body location

before_after

Description: Menu option, point cursor before of after matched line

Determines whether an edit will occur before or after the currently matched line.

Type: (menu option)

Allowed input range:

    before
    after

Default value: after

Example:

     body location append
     {
     before_after => "before";
     }
first_last

Description: Choose first or last occurrence of match in file.

In multiple matches, decide whether the first or last occurrence of the matching pattern in the case affected by the change. In principle this could be generalized to more cases but this seems like a fragile quality to evaluate, and only these two cases are deemed of reproducible significance.

Type: (menu option)

Allowed input range:

    first
    last

Default value: last

Example:

     body location example
     {
     first_last => "last";
     }
select_line_matching

Description: Regular expression for matching file line location

The expression must match a whole line, not a fragment within a line; that is, it is anchored.

This attribute is mutually exclusive of select_line_number.

Type: string

Allowed input range: .*

Example:

     # Editing

     body location example
     {
     select_line_matching => "Expression match.* whole line";
     }

     # Measurement promises
     body match_value example
     {
     select_line_matching => "Expression match.* whole line";
     }
whitespace_policy

Description: Criteria for matching and recognizing existing lines

The white space matching policy applies only to insert_lines, as a convenience. It works by rewriting the insert string as a regular expression when matching lines (that is, when determining if the line is already in the file), but leaving the string as specified when actually inserting it.

Simply put, the 'does this line exist' test will be changed to a regexp match. The line being tested will optionally have \s* prepended or appended if ignore_leading or ignore_trailing is specified, and if ignore_imbedded is used then all embedded white spaces are replaced with \s+. Since whitespace_policy is additive you may specify more than one.

Any regular expression meta-characters that exist in your input line will be escaped. In this way, it is possible to safely insert a line such as authpriv.* /var/log/something into a syslog config file.

Type: (option list)

Allowed input range:

    ignore_leading
    ignore_trailing
    ignore_embedded
    exact_match

Default value: exact_match

Example:

bundle edit_line Insert(service, filename)
{
insert_lines:

  "$(service).* $(filename)"

      whitespace_policy => { "ignore_trailing", "ignore_embedded" };
}

History: This attribute was introduced in CFEngine version 3.0.5 (2010)


replace_patterns

This promise refers to arbitrary text patterns in a file. The pattern is expressed as a PCRE regular expression.

       replace_patterns:

        "search pattern"

           replace_with => replace_body,
           ...;

In replace_patterns promises, the regular expression may match a line fragment, that is, it is unanchored.

    bundle edit_line upgrade_cfexecd
    {
      replace_patterns:

        "cfexecd" replace_with => value("cf-execd");
    }

    body replace_with value(x)  # defined in cfengine_stdlib.cf
    {
    replace_value => "$(x)";
    occurrences => "all";
    }

This is a straightforward search and replace function. Only the portion of the line that matches the pattern in the promise will be replaced; the remainder of the line will not be affected. You can also use PCRE look-behind and look-ahead patterns to restrict the lines upon which the pattern will match.


Attributes
replace_with

Type: body replace_with

occurrences

Description: Defines which occurrences should be replaced.

Using "first" is generally unwise, as it will change a different matching string each time the promise is executed, and may not "catch up" with whatever external action is altering the text the promise applies to.

Type: (menu option)

Allowed input range:

  • all

Replace all occurrence.

  • first

Replace only the first occurrence. Note: this is non-convergent.

Default value: all

Example:

     body replace_with example
     {
     occurrences => "first";        # Warning! Using "first" is non-convergent
     }
replace_value

Description: Value used to replace regular expression matches in search

Type: string

Allowed input range: .*

Example:

     body replace_with example(s)
     {
     replace_value => "$(s)";
     }

delete_lines

This promise assures that certain lines exactly matching regular expression patterns will not be present in a text file. If the lines are found, the default promise is to remove them (this behavior may be modified with further pattern matching in delete_select and/or changed with not_matching).

    bundle edit_line example
    {
    delete_lines:

      "olduser:.*";

    }

Note that typically, only a single line is specified in each delete_lines promise. However, you may of course have multiple promises that each delete a line.

It is also possible to specify multi-line delete_lines promises. However, these promises will only delete those lines if all the lines are present in the file in exactly the same order as specified in the promise (with no intervening lines). That is, all the lines must match as a unit for the delete_lines promise to be kept.

If the promiser contains multiple lines, then CFEngine assumes that all of the lines must exist as a contiguous block in order to be deletes. This gives preserve_block semantics to any multiline delete_lines promise.


Attributes
delete_select

Type: body delete_select

delete_if_startwith_from_list

Description: Delete lines from a file if they begin with the sub-strings listed.

Note that this determination is made only on promised lines (that is, this attribute modifies the selection criteria, it does not make the initial selection).

Type: slist

Allowed input range: .*

Example:

     bundle edit_line alpha
     {
     delete_lines:
         ".*alpha.*"
        delete_select => starters;
     }

     body delete_select starters
     {
         delete_if_startwith_from_list => { "begin", "start", "init" };
     }

If the file contains the following lines, then this promise initially selects the four lines containing alpha, but is moderated by the delete_select attribute.

     start alpha igniter
     start beta igniter
     init alpha burner
     init beta burner
     stop beta igniter
     stop alpha igniter
     stop alpha burner

Thus, the promise will delete only the first and third lines of the file:

delete_if_not_startwith_from_list

Description: Delete lines from a file unless they start with the sub-strings in the list given.

Note that this determination is made only on promised lines. In other words, this attribute modifies the selection criteria, it does not make the initial selection.

Type: slist

Allowed input range: .*

Example:

     body delete_select example(s)
     {
     delete_if_not_startwith_from_list => { @(s) };
     }
delete_if_match_from_list

Description: Delete lines from a file if the lines completely match any of the anchored regular expressions listed.

Note that this attribute modifies the selection criteria, it does not make the initial selection, and the match determination is made only on promised lines.

Type: slist

Allowed input range: .*

Example:

     body delete_select example(s)
     {
     delete_if_match_from_list => { @(s) };
     }
delete_if_not_match_from_list

Description: Delete lines from a file unless the lines completely match any of the anchored regular expressions listed.

Note that this attribute modifies the selection criteria, it does not make the initial selection, and the match determination is made only on promised lines.

Type: slist

Allowed input range: .*

Example:

     body delete_select example(s)
     {
     delete_if_not_match_from_list => { @(s) };
     }
delete_if_contains_from_list

Description: Delete lines from a file if they contain the sub-strings listed.

Note that this attribute modifies the selection criteria, it does not make the initial selection, and the match determination is made only on promised lines.

Type: slist

Allowed input range: .*

Example:

     body delete_select example(s)
     {
     delete_if_contains_from_list => { @(s) };
     }
delete_if_not_contains_from_list

Description: Delete lines from the file which do not contain the sub-strings listed.

Note that this attribute modifies the selection criteria, it does not make the initial selection, and the match determination is made only on promised lines.

Type: slist

Allowed input range: .*

Example:

     body delete_select discard(s)
     {
     delete_if_not_contains_from_list => { "substring1", "substring2" };
     }
not_matching

Description: When this option is true, it negates the pattern match of the promised lines.

This makes no sense for multi-line deletions, and is therefore disallowed. Either a multi-line promiser matches and it should be removed (i.e. not_matching is false), or it does not match the whole thing and the ordered lines have no meaning anymore as an entity. In this case, the lines can be separately stated.

Note that this does not negate any condition expressed in delete_select. It only negates the match of the initially promised lines.

Type: boolean

Default value: false

Example:

    delete_lines:

      # edit /etc/passwd - account names that are not "mark" or "root"

      "(mark|root):.*" not_matching => "true";

services

A service is a set of zero or more processes. It can be zero if the service is not currently running. Services run in the background, and do not require user intervention.

Service promises may be viewed as an abstraction of process and commands promises. An important distinguisher is however that a single service may consist of multiple processes. Additionally, services are registered in the operating system in some way, and get a unique name. Unlike processes and commands promises, this makes it possible to use the same name both when it is running and not.

Some operating systems are bundled with a lot of unused services that are running as default. At the same time, faulty or inherently insecure services are often the cause of security issues. With CFEngine, one can create promises stating the services that should be stopped and disabled.

The operating system may start a service at boot time, or it can be started by CFEngine. Either way, CFEngine will ensure that the service maintains the correct state (started, stopped, or disabled). On some operating systems, CFEngine also allows services to be started on demand, when they are needed. This is implemented though the inetd or xinetd daemon on Unix. Windows does not support this.

CFEngine also allows for the concept of dependencies between services, and can automatically start or stop these, if desired. Parameters can be passed to services that are started by CFEngine.

    bundle agent example
    {
    services:

      "Dhcp"
        service_policy => "start",
        service_dependencies => { "Alerter", "W32Time" },
        service_method => winmethod;
    }

    body service_method winmethod
    {
      service_type => "windows";
      service_args => "--netmask=255.255.0.0";
      service_autostart_policy => "none";
      service_dependence_chain => "start_parent_services";
    }

Note: Services promises for Windows are only available in CFEngine Enterprise. Windows Vista/Server 2008 and later introduced new complications to the service security policy. Therefore, when testing services promises from the command line, CFEngine may not be given proper access rights, which gives errors like "Access is denied". However, when running through the CFEngine Enterprise Executor service, typical for on production machines, CFEngine has sufficient rights.

Services of type generic promises are implemented for all operating systems and are merely as a convenient front-end to processes and commands. If nothing else is specified, CFEngine looks for an special reserved agent bundle called

    bundle agent standard_services(service,state)
    {
    ...
    }

This bundle is called with two parameters: the name of the service and a start/stop state variable. The CFEngine standard library defines many common services for standard operating systems for convenience. If no service_bundle is defined in a service_method body, then CFEngine assumes the standard_services bundle to be the default source of action for the services. This is executed just like a methods promise on the service bundle, so this is merely a front-end.

The standard bundle can be replaced with another, as follows:

    bundle agent test
    {
    vars:

     "mail" slist => { "spamassassin", "postfix" };

    services:

      "www" service_policy => "start",
            service_method => service_test;

      "$(mail)" service_policy => "stop",
            service_method => service_test;
    }

    body service_method service_test
    {
      service_bundle => non_standard_services("$(this.promiser)","$(this.service_policy)");
    }

    bundle agent non_standard_services(service,state)
    {
    reports:

      !done::

        "Test service promise for \"$(service)\" -> $(state)";
    }

Note that the special variables $(this.promiser) and $(this.service_policy) may be used to fill in the service and state parameters from the promise definition. The $(this.service_policy) variable is only defined for services promises.

History: This promise type was introduced in CFEngine 3.3.0 (2012).


Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

service_policy

Description: Policy for CFEngine service status.

If set to start, CFEngine will keep the service in a running state, while stop means that the service is kept in a stopped state. disable implies stop, and ensures that the service can not be started directly, but needs to be enabled somehow first (e.g. by changing file permissions).

Type: (menu option)

Allowed input range:

    start
    stop
    disable
    restart
    reload

Example:

services:

  "Telnet"
     service_policy => "disable";
service_dependencies

Description: A list of services on which the named service abstraction depends

A list of services that must be running before the service can be started. These dependencies can be started automatically by CFEngine if they are not running see service_dependence_chain. However, the dependencies will never be implicitly stopped by CFEngine. Specifying dependencies is optional.

Note that the operating system may keep an additional list of dependencies for a given service, defined during installation of the service. CFEngine requires these dependencies to be running as well before starting the service. The complete list of dependencies is thus the union of service_dependencies and the internal operating system list.

Type: slist

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

services:

  "ftp"
    service_policy => "start",
    service_dependencies => { "network", "logging" };
service_method

Type: body service_method

service_args

Description: Parameters for starting the service as command

These arguments will only be passed if CFEngine starts the service. Thus, set service_autostart_policy to none to ensure that the arguments are always passed.

Escaped quotes can be used to pass an argument containing spaces as a single argument, e.g. -f \"file name.conf\". Passing arguments is optional.

Type: string

Allowed input range: (arbitrary string)

Example:

     body service_method example
     {
       service_args => "-f filename.conf --some-argument";
     }
service_autostart_policy

Description: Should the service be started automatically by the OS

Defaults to none, which means that the service is not registered for automatic startup by the operating system in any way. It must be none if service_policy is not start. boot_time means the service is started at boot time, while on_demand means that the service is dispatched once it is being used.

Type: (menu option)

Allowed input range:

    none
    boot_time
    on_demand

Example:

     body service_method example
     {
       service_autostart_policy => "boot_time";
     }

Notes: on_demand is not supported by Windows, and is implemented through inetd or xinetd on Unix.

service_bundle

Type: bundle agent

service_dependence_chain

Description: How to handle dependencies and dependent services

The service dependencies include both the dependencies defined by the operating system and in service_dependencies, as described there.

Defaults to ignore, which means that CFEngine will never start or stop dependencies or dependent services, but fail if dependencies are not satisfied. start_parent_services means that all dependencies of the service will be started if they are not already running. When stopping a service, stop_child_services means that other services that depend on this service will be stopped also. all_related means both start_parent_services and stop_child_services.

Note that this setting also affects dependencies of dependencies and so on.

For example, consider the case where service A depends on B, which depends on C. If we want to start B, we must first make sure A is running. If start_parent_services or all_related is set, CFEngine will start A, if it is not running. On the other hand, if we want to stop B, C needs to be stopped first. stop_child_services or all_related means that CFEngine will stop C, if it is running.

Type: (menu option)

Allowed input range:

    ignore
    start_parent_services
    stop_child_services
    all_related

Example:

     body service_method example
     {
       service_dependence_chain => "start_parent_services";
     }
service_type

Description: Service abstraction type

Type: (menu option)

Allowed input range:

    windows
    generic

Example:

     body service_method example
     {
       service_type => "windows";
     }

Notes: On Windows this defaults to, and must be windows. Unix systems can however have multiple means of registering services, but the choice must be available on the given system.


users

User promises are promises made about local users on a host. They express which users should be present on a system, and which attributes and group memberships the users should have.

Every user promise has at least one attribute, policy, which describes whether or not the user should be present on the system. Other attributes are optional; they allow you to specify UID, home directory, login shell, group membership, description, and password. Platform native tools are used to create/modify/delete users (C api on Windows, and useradd usermod userdel on Unix, Linux and similar platforms).

A bundle can be associated with a user promise, such as when a user is created in order to do housekeeping tasks in his/her home directory, like putting default configuration files in place, installing encryption keys, and storing a login picture.

Note: This promise type does not create or delete groups (not even a users primary group). The groups the user is promised to be in need to be managed separately.

History: Introduced in CFEngine 3.6.0

Example:

      users:
         "jsmith"
            policy => "present",
            description => "John Smith",
            home_dir => "/remote/home/jsmith",
            group_primary => "users",
            groups_secondary => { "printers", "webadmin" },
            shell => "/bin/bash";

Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

description

Description: The description string sets the description associated with a user.

The exact use of this string depends on the operating system, but most systems treat it as the full name of the user and therefore display it on graphical login terminals.

Type: string

Allowed input range: (arbitrary string)

Example:

      users:
         "jsmith"
            policy => "present",
            description => "John Smith";
group_primary

Description: The group_primary attribute sets the user's primary group.

Note: On Windows, no difference exists between primary and secondary groups so specifying either one works.

Type: string

Allowed input range: (arbitrary string)

Example:

      users:
         "jsmith"
            policy => "present",
            group_primary => "users";
groups_secondary

Description: The groups_secondary attributes sets the user's secondary group membership(s), in addition to his/her primary group.

Note: On Windows, no difference exists between primary and secondary groups so specifying either one works.

Type: slist

Allowed input range: .*

Example:

      users:
         "jsmith"
            policy => "present",
            groups_secondary => { "site_a", "tester" };
home_bundle

Description: The home_bundle attribute specifies a bundle that is evaluated when the user is created.

If the user already exists, the bundle is not evaluated.

The name of the promised user is not passed to the bundle directly, but you can specify a bundle with parameters in order to pass it in.

Note that this attribute does not set the home directory in the user database. For that, you must use the home_dir attribute.

Type: bundle

Example:

bundle agent main
{
  vars:
      "users" slist => { "jack", "john" };
      "skel" string => "/etc/skel";

  users:
    !windows::
      "$(users)"
        policy => "present",
        home_dir => "/home/$(users)",
        home_bundle => home_skel($(users), $(skel));
}
bundle agent home_skel(user, skel)
{
  files:
    "/home/$(user)/."
      create => "true",
      copy_from => seed_cp($(skel)),
      depth_search => recurse("inf");
}

This example uses implicit looping to create the two users, "jack" and "john." Each has his respective home directory that is created by the files promise.

home_bundle_inherit

Description: The home_bundle_inherit attribute specifies if classes set in the current bundle are inherited by the bundle specified in the home_bundle attribute.

Type: boolean

Example:

   bundle agent main
   {
      vars:
         "user" string => "jack";
      classes:
         "should_have_home_dir" expression => regcmp("j.*", "$(user)");
      users:
         "$(user)"
            policy => "present",
            home_dir => "/home/$(user)",
            home_bundle => setup_home_dir("$(user)"),
            home_bundle_inherit => "true";
   }

   bundle agent setup_home_dir(user)
   {
      files:
         should_have_home_dir::
            "/home/$(user)/."
               create => "true";
   }

The user "jack" will have his home directory created, since his username starts with "j".

home_dir

Description: The home_dir attribute associates a user with the given home directory.

Note that this attribute does not create the directory. For that you must use the home_bundle attribute. This just sets the home directory in the user database.

Type: string

Allowed input range: "?(/.*)

Example:

      users:
         "jsmith"
            policy => "present",
            home_dir => "/home/j/jsmith";
password

Description: The password attribute specifies a password body that contains information about a user's password.

Type: body password

Example:

    body password user_password
    {
        format => "hash";
          data => "jiJSlLSkZuVLE"; # "CFEngine"
    }
format

Description: Specifies the format of the given password data.

If the value is "hash," then the data attribute is expected to contain a string with a password in hashed format. Note that CFEngine does not validate that the given hash format is supported by the platform. The system administrator must verify this. However, CFEngine continues to run even in the event of an unsupported password format, so it can always be corrected by updating the policy.

If the value is "plaintext," then the data attribute contains the password in plain text.

Note: On Windows, only the "plaintext" password type is supported, due to a lack of support from the operating system for setting hashed passwords.

Type: (menu option)

Allowed input range:

  • plaintext
  • hash

Example:

    body password user_password
    {
        format => "plaintext";
          data => "CFEngine";
    }
data

Description: Specifies the password data.

The format of the password data depends on the format attribute.

Type: string

Allowed input range: (arbitrary string)

Example:

    body password user_password
    {
        format => "plaintext";
          data => "CFEngine";
    }
policy

Description: The policy attribute specifies what state the user account has on the system.

If the policy is present, the user is present and active on the system. Note that an unset password might still prevent the user from logging in.

If the policy is locked, and the user does not exist, it is created with password authentication disabled. If the user account already exists its password digest is prepended with a "!", disabling password authentication. Note that only logins via the PAM framework are prevented. This includes normal console logins and SSH logins on most systems.

If the policy is absent, the user does not exist on the system. Note that if a user previously existed, his/her files are not automatically removed. You must create a separate files promise for this.

Note: When CFEngine locks an account it does two things, it disables the login password, and it sets the account expiration date far in the past. The expiration date is to prevent key based SSH logins. However, on Solaris it is not possible to set the account expiration date in this way, hence SSH logins may still work there after an account is locked and additional steps may be required.

Type: (menu option)

Allowed input range:

  • present
  • absent
  • locked

Example:

      users:
         "jsmith"
            policy => "locked";
shell

Description: The shell attribute specifies the user's login shell.

Type: string

Allowed input range: "?(/.*)

Example:

      users:
         "jsmith"
            shell => "/bin/bash";
uid

Description: The uid attribute specifies the user's UID number.

Note that if the UID of an existing user is changed, the files owned by that user do not automatically change ownership. You must create a separate files promise for this.

Type: int

Allowed input range: -99999999999,99999999999

Example:

      users:
         "jsmith"
            uid => "1357";

files

Files promises are an umbrella for attributes of files. Operations fall basically into three categories: create, delete and edit.

    files:

      "/path/file_object"

          perms = perms_body,
          ... ;

Prior to version 3, file promises were scattered into many different types, including files, tidy, copy, and links. File handling in CFEngine 3 uses regular expressions everywhere for pattern matching. The old 'wildcard/globbing' expressions \* and ? are deprecated, and everything is based consistently on Perl Compatible Regular Expressions.

There is a natural ordering in file processing that obviates the need for the actionsequence. For example, the trick of using multiple actionsequence items with different classes.

    actionsequence = ( ... files.one  ..  files.two )

can now be handled more elegantly using bundles. The natural ordering uses that fact that some operations are mutually exclusive and that some operations do not make sense in reverse order. For example, editing a file and then copying onto it would be nonsense. Similarly, you cannot both remove a file and rename it.

File copying

Copying is 'backwards'. Instead of the default object being source and the option being the destination, in CFEngine 3 the destination is paramount and the source is an option. This is because the model of voluntary cooperation tells us that it is the object that is changed, which is the agent making the promise. One cannot force change onto a destination with CFEngine, one can only invite change from a source.

Normal ordering of promise attributes

CFEngine has no 'action sequence'. Ordering of operations has, in most cases, a natural ordering that is assumed by the agent. For example, 'delete then create' (normal ordering) makes sense, but 'create then delete' does not. This sort of principle can be extended to deal with all aspects of file promises.

The diagram below shows the ordering. Notice that the same ordering applies regardless of file type (plain-file or directory). Note also that file editing is done "atomically".

The normal ordering of file operators in CFEngine 3

The pseudo-code for this logic is shown in the diagram and below:

 for each file promise-object
    {
    if (depth_search)
      do
        DepthSearch (HandleLeaf)
      else
        (HandleLeaf)
      done
    }

 HandleLeaf()
   {
   Does leaf-file exist?

     NO:  create
     YES: rename,delete,touch,

     do
      for all servers in {localhost, @(servers)}
         {
         if (server-will-provide)
            do
              if (depth_search)
                 embedded source-depth-search (use file source)
                 break
              else
                 (use file source)
                 break
              done
            done
         }
     done

   Do all links (always local)

   Check Permissions

   Do edits
   }
Depth searches (aka 'recursion') during searches

Recursion is called "depth-search", and CFEngine uses the 'globbing' symbols with standard regular expressions:

/one/.*/two/thr.*/four

When searching for hidden files (files with names starting with a '.') or files with specific extensions, you should take care to escape the dot (e.g., \.cshrc or .*\.txt) when you wish it to mean a literal character and not the any character interpretation provided by regular expression interpretation.

When doing a recursive search, the files '.' and '..' are never included in the matched files, even if the regular expression in the leaf_name specifically allows them.

The filename /dir/ect/ory/. is a special case used with the create attribute to indicate the directory named /dir/ect/ory and not any of the files under it. If you really want to specify a regular expression that matches any single-character filename, use /dir/ect/ory/[\w\W] as your promise regular expression (you can't use /dir/ect/ory/[^/], see below for an explanation.

Depth search refers to a search for file objects that starts from the one or more matched base-paths as shown in the example above.

Filenames and regular expressions

CFEngine allows regular expressions within filenames, but only after first doing some sanity checking to prevent some readily avoidable problems. The biggest rule you need to know about filenames and regular expressions is that all regular expressions in filenames are bounded by directory separators, and that each component expression is anchored between the directory separators. In other words, CFEngine splits up any file paths into its component parts, and then it evaluates any regular expressions at a component-level.

What this means is that the path /tmp/gar.* will only match filenames like /tmp/gar, /tmp/garbage and /tmp/garden. It will not match filename like /tmp/gar/baz; because even though the .* in a regular expression means "zero or more of any character", CFEngine restricts that to mean "zero or more of any character in a path component".

Correspondingly, CFEngine also restricts where you can use the / character. For example, you cannot use it in a character class like [^/] or in a parenthesized or repeated regular expression component.

This means that regular expressions that include "optional directory components" will not work. You cannot have a files promise to tidy the directory (/usr)?/tmp. Instead, you need to be more verbose and specify /usr/tmp|/tmp. Potentially more efficient would be a declarative approach. First, create an slist that contains both the strings /tmp and /usr/tmp and then allow CFEngine to iterate over the list.

This also means that the path /tmp/.*/something will match files such as /tmp/abc/something or /tmp/xyzzy/something. However, even though the pattern .* means "zero or more of any character (except /)", CFEngine matches files bounded by directory separators. So even though the pathname /tmp//something is technically the same as the pathname /tmp/something, the regular expression /tmp/.*/something will not match on the case of /tmp//something (or /tmp/something).

Promises involving regular expressions

CFEngine can only keep (or repair, or fail to keep) a promise on files which actually exist. If you make a promise based on a wildcard match, then the promise is only ever attempted if the match succeeds. However, if you make a promise containing a recursive search that includes a wildcard match, then the promise can be kept or repaired, provided that the directory specified in the promise exists. Consider the following two examples, which assume that there first exist files named /tmp/gar, /tmp/garbage and /tmp/garden. Initially, the two promises look like they should do the same thing; but there is a subtle difference:

bundle agent foobaz
{
files:
 "/tmp/gar.*"
    delete => tidy,
    classes => if_ok("done");
}

body classes if_ok(x)
{
  promise_repaired => { "$(x)" };
  promise_kept => { "$(x)" };
}
bundle agent foobaz
{
  files:
    "/tmp"
      delete => tidy,
      depth_search => recurse("0"),
      file_select => gars,
      classes => if_ok("done");
}

body file_select gars
{
leaf_name => { "gar.*" };
file_result => "leaf_name";
}

body classes if_ok(x)
{
  promise_repaired => { "$(x)" };
  promise_kept => { "$(x)" };
}

In the first example, when the configuration containing this promise is first executed, any file starting with "gar" that exists in the /tmp directory will be removed, and the done class will be set. However, when the configuration is executed a second time, the pattern /tmp/gar.* will not match any files, and that promise will not even be attempted (and, consequently the done class will not be set).

In the second example, when the configuration containing this promise is first executed, any file starting with "gar" that exists in the /tmp directory will also be removed, and the done class will also be set. The second time the configuration is executed, however, the promise on the /tmp directory will still be executed (because /tmp of course still exists), and the done class will be set, because all files matching the file_select attribute have been deleted from that directory.

Local and remote searches

There are two distinct kinds of depth search:

  • A local search over promiser agents.
  • A remote search over provider agents.

When we are copying or linking to a file source, it is the search over the remote source that drives the content of a promise (the promise is a promise to use what the remote source provides). In general, the sources are on a different device to the images that make the promises. For all other promises, we search over existing local objects.

If we specify depth search together with copy of a directory, then the implied remote source search is assumed, and it is made after the search over local base-path objects has been made. If you mix complex promise body operations in a single promise, this could lead to confusion about the resulting behavior, and a warning is issued. In general it is not recommended to mix searches without a full understanding of the consequences, but this might occasionally be useful.

Depth search is not allowed with edit_line promises.


Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

acl

Type: body acl

Please note that until https://dev.cfengine.com/issues/4862 is fixed (as of 3.6.0 it's not), you need to specify a perms body or only the base directory will be considered. As a workaround, use the following perms body if you are not specifying one already, as suggested in that ticket:

body perms null_perms_body {
    ## Workaround for https://dev.cfengine.com/issues/4862
    ## Bug #4862: Recursive ACLs not working by default only with perms
    ##
    ## Dummy perms body is used as otherwise ACLs are not applied recursively
    rxdirs => "true";
}
aces

Description: Native settings for access control entry are defined by 'aces'. POSIX ACL are available in CFEngine Community starting with 3.4.0. NTFS ACL are available in with CFEngine Enterprise.

Type: slist

Allowed input range: ((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)

Form of the permissions is as follows:

                aces = {
                        "user:uid:mode[:perm_type]", ...,
                        "group:gid:mode[:perm_type]", ...,
                        "all:mode[:perm_type]"
                        };
  • user

indicates that the line applies to a user specified by the user identifier uid. mode is the permission mode string.

  • group

Indicates that the line applies to a group specified by the group identifier gid. mode The permission mode string.

  • all

Indicates that the line applies to every user. mode is the permission mode string.

A valid user identifier for the system and cannot be empty. However, uid can be set to * as a synonym for the entity that owns the file system object (e.g. user:*:r).

  • gid

A valid group identifier for the system and cannot be empty. However, in some ACL types, gid can be set to * to indicate a special group (e.g. in POSIX this refers to the file group).

One or more strings op|perms|(nperms); a concatenation of op, perms and optionally (nperms) separated with commas (e.g. +rx,-w(s) ). mode is parsed from left to right.

  • op

Specifies the operation on any existing permissions, if the defined ACE already exists. op can be =, empty, + or -. = or empty sets the permissions to the ACE as stated. + adds and - removes the permissions from any existing ACE.

  • nperms (optional)

Specifies file system specific (native) permissions. Only valid if acl_type is defined. nperms will only be enforced if the file object is stored on a file system supporting the ACL type set in acl_type. For example, nperms will be ignored if acl_type:ntfs and the object is stored on a file system not supporting NTFS ACLs. Valid values for nperms varies with different ACL types, and is defined in subsequent sections.

  • perm_type (optional)

Can be set to either allow or deny, and defaults to allow. deny is only valid if acl_type is set to an ACL type that support deny permissions. A deny ACE will only be enforced if the file object is stored on a file system supporting the acl type set in acl_type.

  • gperms (generic permissions)

A concatenation of zero or more of the characters shown in the table below. If left empty, none of the permissions are set.

Flag Description Semantics on file Semantics on directory
r Read Read data, permissions, attributes Read directory contents, permissions, attributes
w Write Write data Create, delete, rename subobjects
x Execute Execute file Access subobjects

Note that the r permission is not necessary to read an object's permissions and attributes in all file systems. For example, in POSIX, having x on its containing directory is sufficient.

Example:

     body acl template

     {
     acl_method => "overwrite";
     acl_type => "posix";
     acl_default => "access";

     aces => {
             "user:*:r(wwx),-r:allow",
             "group:*:+rw:allow",
             "mask:x:allow",
             "all:r"
             };
     }
acl_default

Description: The access control list type for the affected file system is determined by acl_default.

Directories have ACLs associated with them, but they also have the ability to inherit an ACL to sub-objects created within them. POSIX calls the former ACL type "access ACL" and the latter "default ACL", and we will use the same terminology.

The constraint acl_default gives control over the default ACL of directories. The default ACL can be left unchanged (nochange), empty (clear), or be explicitly specified (specify). In addition, the default ACL can be set equal to the directory's access ACL (access). This has the effect that child objects of the directory gets the same access ACL as the directory.

Type: (menu option)

Allowed input range:

    nochange
    access
    specify
    clear

Example:

     body acl template

     {
     acl_method => "overwrite";
     acl_type => "posix";
     acl_default => "access";

     aces => {
             "user:*:rwx:allow",
             "group:*:+rw:allow",
             "mask:rx:allow",
             "all:r"
             };
     }

History: Was introduced in 3.5. Replaces the now deprecated acl_directory_inherit.

acl_inherit

Description: Defines whether the object inherits its ACL from its parent.

Type: (menu option)

Allowed input range:

  • true
  • false
  • yes
  • no
  • on
  • off
  • nochange

Notes: This attribute has an effect only on Windows.

acl_method

Description: The acl_method menu option defines the editing method for an access control list.

When defining an ACL, we can either use an existing ACL as the starting point, or state all entries of the ACL. If we just care about one entry, say that the superuser has full access, the method constraint can be set to append, which is the default. This has the effect that all the existing ACL entries that are not mentioned will be left unchanged. On the other hand, if method is set to overwrite, the resulting ACL will only contain the mentioned entries. When doing this, it is important to check that all the required ACL entries are set. For example, owning user, group and all in POSIX ACLs.

Type: (menu option)

Allowed input range:

    append
    overwrite

Example:

     body acl template

     {
     acl_method => "overwrite";
     acl_type => "posix";
     aces => { "user:*:rw:allow", "group:*:+r:allow", "all:"};
     }
acl_type

Description: The acl_type menu option defines the access control list type for the affected file system.

ACLs are supported on multiple platforms, which may have different sets of available permission flags. By using the constraint acl_type, we can specify which platform, or ACL API, we are targeting with the ACL.

The default, generic, is designed to work on all supported platforms. However, if very specific permission flags are required, like Take Ownership on the NTFS platform, we must set acl_type to indicate the target platform. Currently, the supported values are posix and ntfs.

Type: (menu option)

Allowed input range:

    generic
    posix
    ntfs

Example:

     body acl template

     {
     acl_type => "ntfs";
     aces => { "user:Administrator:rwx(po)", "user:Auditor:r(o)"};
     }
specify_default_aces

Description: The slist specify_default_aces specifies the native settings for access control entry.

specify_default_aces (optional) is a list of access control entries that are set on child objects. It is also parsed from left to right and allows multiple entries with same entity-type and id. Only valid if acl_default is set to specify.

This is an ACL which makes explicit setting for the acl inherited by new objects within a directory. It is included for those implementations that do not have a clear inheritance policy.

Type: slist

Allowed input range: ((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)

Example:

     body acl template
     {
     specify_default_aces => {  "all:r" };
     }
changes

Type: body changes

hash

Description: The hash menu option defines the ash files for change detection.

The best option cross correlates the best two available algorithms known in the OpenSSL library.

Type: (menu option)

Allowed input range:

    md5
    sha1
    sha224
    sha256
    sha384
    sha512
    best

Example:

     body changes example
     {
     hash => "md5";
     }
report_changes

Description: Specify criteria for change warnings using the report_changes menu option.

Files can change in permissions and contents, i.e. external or internal attributes. If all is chosen all attributes are checked.

Type: (menu option)

Allowed input range:

    all
    stats
    content
    none

Example:

     body changes example
     {
     report_changes => "content";
     }
update_hashes

Description: Use of update_hashes determines whether hash values should be updated immediately after a change.

If this is positive, file hashes should be updated as soon as a change is registered so that multiple warnings are not given about a single change. This applies to addition and removal too.

Type: boolean

Example:

     body changes example
     {
     update_hashes => "true";
     }
report_diffs

This feature requires CFEngine Enterprise.

Description: Setting report_diffs determines whether to generate reports summarizing the major differences between individual text files.

If true, CFEngine will log a 'diff' summary of major changes to the files. It is not permitted to combine this promise with a depth search, since this would consume a dangerous amount of resources and would lead to unreadable reports.

The feature is intended as a informational summary, not as a version control function suitable for transaction control. If you want to do versioning on system files, you should keep a single repository for them and use CFEngine to synchronize changes from the repository source. Repositories should not be used to attempt to capture random changes of the system.

Limitations: Diffs will not be reported for files that are larger than 80MB in size. Diffs will not be reported if the number of lines between the first and last change exceed 4500. Diffs for binary files are not generated. Files are considered binary files if control character 0-32 excluding 9, 10, 13, and 32, or 127 are found in the file.

Type: boolean

Example:

     body changes example
     {
     report_diffs => "true";
     }
copy_from

Type: body copy_from

source

Description: The source string represents the reference source file from which to copy. For remote copies this refers to the file name on the remote server.

Type: string

Allowed input range: .+

Example:

     body copy_from example
     {
     source => "/path/to/source";
     }
servers

Description: The servers slist names servers in order of preference from which to copy. The servers are tried in order until one of them succeeds.

Type: slist

Allowed input range: [A-Za-z0-9_.:-]+

Example:

     body copy_from example
     {
     servers => { "primary.example.org", "secondary.example.org",
                      "tertiary.other.domain" };
     }
collapse_destination_dir

Description: Use collapse_destination_dir to flatten the directory hierarchy during copy. All the files will end up in the root destination directory.

Under normal operations, recursive copies cause CFEngine to track subdirectories of files. So, for instance, if we copy recursively from src to dest, then src/subdir/file will map to dest/subdir/file.

By setting this option to true, the promiser destination directory promises to aggregate files searched from all subdirectories into itself; in other words, a single destination directory. So src/subdir/file will map to dest/file for any subdir.

Type: boolean

Example:

     body copy_from mycopy(from,server)
     {
     source      => "$(from)";
     servers     => { "$(server)" };
     collapse_destination_dir => "true";
     }
compare

Description: The menu option policy compare is used for comparing source and image file attributes.

The default copy method is mtime (modification time) or ctime (change time), meaning that the source file is copied to the destination (promiser) file, if the source file has been modified (content, permissions, ownership, moved to a different file system) more recently than the destination. Note this is special behavior when no comparison is specified as generally only a single comparison can be used.

Type: (menu option)

Allowed input range:

CFEngine copies the file if the modification time of the source file is more recent than that of the promised file

CFEngine copies the file if the creation time of the source file is more recent than that of the promised file

CFEngine copies the file if the modification time or creation time of the source file is more recent than that of the promised file. If the times are equal, a byte-for-bye comparison is done on the files to determine if it needs to be copied.

  • exists

CFEngine copies the file if the promised file does not already exist.

  • binary

CFEngine copies the file if they are both plain files and a byte-for-byte comparison determines that they are different. If both are not plain files, CFEngine reverts to comparing the mtime and ctime of the files. If the source file is on a different machine (e.g. network copy), then hash is used instead to reduce network bandwidth.

CFEngine copies the file if they are both plain files and a message digest comparison indicates that the files are different. In Enterprise versions of CFEngine version 3.1.0 and later, SHA256 is used as a message digest hash to conform with FIPS; in older Enterprise versions of CFEngine and all Community versions, MD5 is used.

  • digest a synonym for hash

Default value: mtime or ctime differs

Example:

     body copy_from example
     {
     compare => "digest";
     }
copy_backup

Description: Menu option policy for file backup/version control

Determines whether a backup of the previous version is kept on the system. This should be viewed in connection with the system repository, since a defined repository affects the location at which the backup is stored.

Type: (menu option)

Allowed input range:

    true
    false
    timestamp

Default value: true

Example:

     body copy_from example
     {
     copy_backup => "timestamp";
     }
encrypt

Description: The encrypt menu option policy describes whether to use encrypted data stream to connect to remote hosts.

Client connections are encrypted with using a Blowfish randomly generated session key. The initial connection is encrypted using the public/private keys for the client and server hosts.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     servers  => { "remote-host.example.org" };
     encrypt => "true";
     }
check_root

Description: The check_root menu option policy checks permissions on the root directory when copying files recursively by depth_search.

This flag determines whether the permissions of the root directory should be set from the root of the source. The default is to check only copied file objects and subdirectories within this root (false).

Type: boolean

Example:

     body copy_from example
     {
     check_root => "true";
     }

Description: The copylink_patterns slist of patterns are matching files that should be copied instead of linked.

The matches are performed on the last node of the filename; in other words, the file without its path. As Windows does not support symbolic links, this feature is not available there.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body copy_from example
     {
     copylink_patterns => { "special_node1", "other_node.*" };
     }
copy_size

Description: The integers specified in copy_size determines the range for the size of files that may be copied.

The use of the irange function is optional. Ranges may also be specified as comma separated numbers.

Type: irange[int,int]

Allowed input range: 0,inf

Default value: any size range

Example:

     body copy_from example
     {
     copy_size => irange("0","50000");
     }
findertype

Description: The findertype menu option policy describes the default finder type on MacOSX.

This applies only to the Mac OS X variants.

Type: (menu option)

Allowed input range:

    MacOSX

Example:

     body copy_from example
     {
     findertype => "MacOSX";
     }
linkcopy_patterns

Description: The linkcopy_patterns contains patterns for matching files that should be replaced with symbolic links.

The pattern matches the last node filename; in other words, without the absolute path. Windows only supports hard links.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body copy_from mycopy(from)
     {
     source            => "$(from)";
     linkcopy_patterns => { ".*" };
     }

See Also: link_type.

Description: The link_type menu option policy contains the type of links to use when copying.

Users are advised to be wary of 'hard links' (see Unix manual pages for the ln command). The behavior of non-symbolic links is often precarious and unpredictable. However, hard links are the only supported type by Windows.

Note that symlink is synonymous with absolute links, which are different from relative links. Although all of these are symbolic links, the nomenclature here is defined such that symlink and absolute are equivalent. When verifying a link, choosing 'relative' means that the link must be relative to the source, so relative and absolute links are mutually exclusive.

Type: (menu option)

Allowed input range:

    symlink
    hardlink
    relative
    absolute

Default value: symlink

Example:

     body copy_from example
     {
     link_type => "symlink";
     source => "/tmp/source";
     }
force_update

Description: The force_update menu option policy instructs whether to always force copy update.

Warning: this is a non-convergent operation. Although the end point might stabilize in content, the operation will never quiesce. Use of this feature is not recommended except in exceptional circumstances since it creates a busy-dependency. If the copy is a network copy, the system will be disturbed by network disruptions.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     force_update => "true";
     }
force_ipv4

Description: The force_ipv4 menu option policy can determine whether to use ipv4 on an ipv6 enabled network.

IPv6 should be harmless to most users unless you have a partially or mis-configured setup.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     force_ipv4 => "true";
     }
portnumber

Description: Setting portnumber determines the port number to connect to on a server host.

The standard or registered port number is tcp/5308. CFEngine does not presently use its registered udp port with the same number, but this could change in the future.

Type: int

Allowed input range: 1,65535

Example:

     body copy_from example
     {
     portnumber => "5308";
     }
preserve

Description: Setting the preserve menu option policy determines whether to preserve file permissions on copied files.

This ensures that the destination file (promiser) gets the same file permissions as the source. For local copies, all attributes are preserved, including ACLs and SELinux security contexts. For remote copies, only Unix mode is preserved.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     preserve => "true";
     }

History: Version 3.1.0b3,Nova 2.0.0b1 (2010)

protocol_version

Description: Defines the protocol to use for the outgoing connection in this copy operation.

Type: (menu option)

Allowed input range:

  • 0
  • undefined
  • 1
  • classic
  • 2
  • latest

Default value: classic

Note: The value here will override the setting from body common control.

See also: protocol_version in body common, allowlegacyconnects

History: Introduced in CFEngine 3.6.0

purge

Description: The purge menu option policy instructs on whether to purge files on client that do not match files on server when a depth_search is used.

Purging files is a potentially dangerous matter during a file copy it implies that any promiser (destination) file which is not matched by a source will be deleted. Since there is no source, this means the file will be irretrievable. Great care should be exercised when using this feature.

Note that purging will also delete backup files generated during the file copying if copy_backup is set to true.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     purge => "true";
     }
stealth

Description: Setting the stealth menu option policy determines whether to preserve time stamps on copied files. This preserves file access and modification times on the promiser files.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     stealth => "true";
     }
timeout

Description: The integer set in timeout is the value for the connection timeout, in seconds.

Type: int

Allowed input range: 1,3600

Default Value: default_timeoutcf-agent#default_timeout

Example:

     body copy_from example
     {
     timeout => "10";
     }
trustkey

Description: The trustkey menu option policy determines whether to trust public keys from a remote server, if previously unknown.

If the server's public key has not already been trusted, trustkey provides automated key-exchange.

Note that, as a simple security precaution, trustkey should normally be set to false. Even though the risks to the client low, it is a good security practice to avoid key exchange with a server one is not one hundred percent sure about. On the server-side however, trust is often granted to many clients or to a whole network in which possibly unauthorized parties might be able to obtain an IP address. Thus the trust issue is most important on the server side.

As soon as a public key has been exchanged, the trust option has no effect. A machine that has been trusted remains trusted until its key is manually revoked by a system administrator. Keys are stored in WORKDIR/ppkeys.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     trustkey => "true";
     }
type_check

Description: The type_check menu option policy compares file types before copying.

File types at source and destination should normally match in order for updates to overwrite them. This option allows this checking to be switched off.

Type: boolean

Example:

     body copy_from example
     {
     type_check => "false";
     }
verify

Description: The verify menu option policy instructs whether to verify transferred file by hashing after copy.

Warning: This is a highly resource intensive option, and is not recommended for large file transfers.

Type: boolean

Default value: false

Example:

     body copy_from example
     {
     verify => "true";
     }
create

Description: true/false whether to create non-existing file

Directories are created by using the /. to signify a directory type. Note that, if no permissions are specified, mode 600 is chosen for a file, and mode 755 is chosen for a directory. If you cannot accept these defaults, you should specify permissions.

Note that technically, /. is a regular expression. However, it is used as a special case meaning "directory". See filenames and regular expressions for a more complete discussion.

Type: boolean

Default value: false

Example:

files:

  "/path/plain_file"

     create =>   "true";

  "/path/dir/."

     create =>   "true";

Note: In general, you should not use create with copy_from or link_from in files promises. These latter attributes automatically create the promised file, and using create may actually prevent the copy or link promise from being kept (since create acts first, which may affect file comparison or linking operations).

delete

Type: body delete

Description: Menu option policy for dealing with symbolic links to directories during deletion

Links to directories are normally removed just like any other link or file objects. By keeping directory links, you preserve the logical directory structure of the file system, so that a link to a directory is not removed but is treated as a directory to be descended into.

The value keep instructs CFEngine not to remove directory links. The values delete and tidy are synonymous, and instruct CFEngine to remove directory links.

Type: (menu option)

Allowed input range:

    delete
    tidy
    keep

Example:

     body delete example
     {
     dirlinks => "keep";
     }

Default value (only if body is present): dirlinks = delete

The default value only has significance if there is a delete body present. If there is no delete body then files (and directory links) are not deleted.

rmdirs

Description: true/false whether to delete empty directories during recursive deletion

Type: boolean

Example:

     body delete example
     {
     rmdirs => "true";
     }

Note the parent directory of a search is not deleted in recursive deletions. You must code a separate promise to delete the single parent object.

     bundle agent cleanup
     {
     files:

       # This will not delete the parent

       "/home/mark/tmp/testcopy"

         delete => tidyfiles,
         file_select => changed_within_1_year,
         depth_search => recurse("inf");

       # Now delete the parent.

       "/home/mark/tmp/testcopy"
         delete => tidyfiles;
     }

     body delete tidyfiles
     {
     dirlinks => "delete";
     rmdirs   => "true";
     }

     body file_select changed_within_1_year
     {
     mtime     => irange(ago(1,0,0,0,0,0),now);
     file_result => "mtime";
     }

Default value (only if body is present): rmdirs = true

The default value only has significance if there is a delete body present. If there is no delete body then files (and directories) are not deleted.

Type: body depth_search

depth

Description: Maximum depth level for search

When searching recursively from a directory, the parent directory is only the anchor point and is not part of the search. To alter the parent, a separate non-recursive promise should be made.

Type: int

Allowed input range: 0,99999999999

Note that the value inf may be used for an unlimited value.

Example:

     body depth_search example
     {
     depth => "inf";
     }
exclude_dirs

Description: List of regexes of directory names NOT to include in depth search

Directory names are treated specially when searching recursively through a file system.

Type: slist

Allowed input range: .*

Example:

     body depth_search
     {
     # no dot directories
     exclude_dirs => { "\..*" };
     }
include_basedir

Description: true/false include the start/root dir of the search results

When checking files recursively (with depth_search) the promiser is a directory. This parameter determines whether that initial directory should be considered part of the promise or simply a boundary that marks the edge of the search. If true, the promiser directory will also promise the same attributes as the files inside it.

Type: boolean

Example:

     body depth_search example
     {
     include_basedir => "true";
     }
include_dirs

Description: List of regexes of directory names to include in depth search

This is the complement of exclude_dirs.

Type: slist

Allowed input range: .*

Example:

     body depth_search example
     {
     include_dirs => { "subdir1", "subdir2", "pattern.*" };
     }

Description: true/false remove links that point to nowhere

A value of true determines that links pointing to files that do not exist should be deleted; or kept if set to false.

Type: boolean

Default value: false

Example:

     body depth_search example
     {
     rmdeadlinks => "true";
     }

Description: true/false traverse symbolic links to directories

If this is true, cf-agent will treat symbolic links to directories as if they were directories. Normally this is considered a potentially dangerous assumption and links are not traversed.

Type: boolean

Default value: false

Example:

     body depth_search example
     {
     traverse_links => "true";
     }
xdev

Description: true/false exclude directories that are on different devices

Type: boolean

Default value: false

Example:

     body depth_search example
     {
     xdev => "true";
     }
edit_defaults

Type: body edit_defaults

edit_backup

Description: Menu option for backup policy on edit changes

Type: (menu option)

Allowed input range:

    true
    false
    timestamp
    rotate

Default value: true

Example:

     body edit_defaults example
     {
     edit_backup => "timestamp";
     }
empty_file_before_editing

Description: Baseline memory model of file to zero/empty before commencing promised edits.

Emptying a file before reconstructing its contents according to a fixed recipe allows an ordered procedure to be convergent.

Type: boolean

Default value: false

Example:

     body edit_defaults example
     {
     empty_file_before_editing => "true";
     }
inherit

Description: If true this causes the sub-bundle to inherit the private classes of its parent

Type: boolean

Example:

     bundle agent name
     {
     methods:

       "group name" usebundle => my_method,
                      inherit => "true";
     }

     body edit_defaults example
     {
     inherit => "true";
     }

History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)

Default value: false

Notes: The inherit constraint can be added to the CFEngine code in two places: for edit_defaults and in methods promises. If set to true, it causes the child-bundle named in the promise to inherit only the classes of the parent bundle. Inheriting the variables is unnecessary as the child can always access the parent's variables by a qualified reference using its bundle name. For example, $(bundle.variable).

max_file_size

Description: Do not edit files bigger than this number of bytes

max_file_size is a local, per-file sanity check to make sure the file editing is sensible. If this is set to zero, the check is disabled and any size may be edited. The default value of max_file_size is determined by the global control body setting whose default value is 100k.

Type: int

Allowed input range: 0,99999999999

Example:

     body edit_defaults example
     {
     max_file_size => "50K";
     }
recognize_join

Description: Join together lines that end with a backslash, up to 4kB limit

If set to true, this option allows CFEngine to process line based files with backslash continuation. The default is to not process continuation backslashes.

Back slash lines will only be concatenated if the file requires editing, and will not be restored. Restoration of the backslashes is not possible in a meaningful and convergent fashion.

Type: boolean

Default value: false

Example:

     files:

       "/tmp/test_insert"
                 create => "true",
              edit_line => Insert("$(insert.v)"),
          edit_defaults => join;
     }

     #

     body edit_defaults join
     {
     recognize_join => "true";
     }
rotate

Description: How many backups to store if 'rotate' edit_backup strategy is selected. Defaults to 1

Used for log rotation. If the file is named foo and the rotate attribute is set to 4, as above, then initially foo is copied to foo.1 and the old file foo is zeroed out. In other words, the inode of the original logfile does not change, but the original logfile will be empty after the rotation is complete.

The next time the promise is executed, foo.1 will be renamed foo.2, foo is again copied to foo.1 and the old file foo is again zeroed out.

A promise may typically be executed as guarded by time-based or file-size-based classes. Each time the promise is executed the files are copied/zeroed or rotated (as above) until there are rotate numbered files, plus the one "main" file. In the example above, the file foo.3 will be renamed foo.4, but the old version of the file foo.4 will be deleted (that is, it "falls off the end" of the rotation).

Type: int

Allowed input range: 0,99

Example:

     body edit_defaults example
     {
     edit_backup => "rotate";
     rotate => "4";
     }
edit_line

Type: bundle edit_line

edit_template

Description: The name of a Mustache or native-CFEngine template file to expand

The default native-CFEngine template format (selected when template_method is cfengine or unspecified) uses inline tags to mark regions and classes. Each line represents an insert_lines promise, unless the promises are grouped into a block using:

    [%CFEngine BEGIN %]
    ...
    [%CFEngine END %]

Variables, scalars and list variables are expanded within each promise based on the current scope of the calling promise. If lines are grouped into a block, the whole block is repeated when lists are expanded (see the Special Topics Guide on editing).

If a class-context modified is used:

[%CFEngine class-expression:: %]

then the lines that follow are only inserted if the context matches the agent's current context. This allows conditional insertion.

Type: string

Allowed input range: "?(/.*)

Example:

    #This is a template file /templates/input.tmpl

    These lines apply to anyone

    [%CFEngine solaris.Monday:: %]
    Everything after here applies only to solaris on Mondays
    until overridden...

    [%CFEngine linux:: %]
    Everything after here now applies now to linux only.

    [%CFEngine BEGIN %]
    This is a block of text
    That contains list variables: $(some.list)
    With text before and after.
    [%CFEngine END %]

    nameserver $(some.list)

For example:

    [%CFEngine any:: %]
    VirtualHost $(sys.ipv4[eth0]):80>
            ServerAdmin             $(stage_file.params[apache_mail_address][1])
            DocumentRoot            /var/www/htdocs
            ServerName              $(stage_file.params[apache_server_name][1])
            AddHandler              cgi-script cgi
            ErrorLog                /var/log/httpd/error.log
            AddType                 application/x-x509-ca-cert .crt
            AddType                 application/x-pkcs7-crl    .crl
            SSLEngine               off
            CustomLog               /var/log/httpd/access.log
    /VirtualHost>

    [%CFEngine webservers_prod:: %]
    [%CFEngine BEGIN %]
    VirtualHost $(sys.ipv4[$(bundle.interfaces)]):443>
            ServerAdmin             $(stage_file.params[apache_mail_address][1])
            DocumentRoot            /var/www/htdocs
            ServerName              $(stage_file.params[apache_server_name][1])
            AddHandler              cgi-script cgi
            ErrorLog                /var/log/httpd/error.log
            AddType                 application/x-x509-ca-cert .crt
            AddType                 application/x-pkcs7-crl    .crl
            SSLEngine               on
            SSLCertificateFile      $(stage_file.params[apache_ssl_crt][1])
            SSLCertificateKeyFile   $(stage_file.params[apache_ssl_key][1])
            CustomLog               /var/log/httpd/access.log
    /VirtualHost>
    [%CFEngine END %]

The Mustache template format works differently. When you specify template_method to be mustache, none of the variables or classes in the promise's context will come through. Instead, you pass a data variable (a "data container") to the promise's template_data attribute. You can use mergedata(), readjson(), and parsejson() to generate data variables.

If you don't specify a template_data container with Mustache templates, the output of the function datastate() is used instead, so you can then use classes.x as a boolean trigger based on class x and vars.bundlename.y to get the value of variable y in bundle bundlename. The advantage of specifying template_data however, is that variable references become shorter, and that you can change the data source without changing the Mustache template.

The full specification for Mustache templates is at http://mustache.github.io/

Example:

Save this in test_mustache.cf, for example.

body common control
{
    bundlesequence => { test_mustache };
}

bundle agent test_mustache
{
  files:
      "/tmp/myfile.txt"
      create => "true",
      edit_template => "$(this.promise_filename).mustache",
      template_method => "mustache",
      template_data => parsejson('
{
 "x": 100,
 "boolean": false,
 "list":
  [
   { "k": 789, "v": 0 },
   { "k": null, "v": true },
   { "k": -1, "v": -2 }
  ]
}');
}

Simply, the data container's top-level keys will be used. So this template (saved in test_mustache.cf.mustache if you follow the example):

x is {{x}}

{{#boolean}}The boolean is true{{/boolean}}
{{^boolean}}The boolean is false{{/boolean}}

{{#list}}{{k}}={{v}}, {{/list}}

Will produce this text in /tmp/myfile.txt when you run cf-agent -f ./test_mustache.cf:

x is 100


The boolean is false

789=0, =true, -1=-2,

Example:

This is an example using the datastate() capability mentioned earlier. Save this in test_datastate_mustache.cf, for example.

body common control
{
      bundlesequence => { holder, test_datastate_mustache };
}

bundle common holder
{
  classes:
      "holderclass" expression => "any"; # will be global

  vars:
      "s" string => "Hello!";
      "d" data => parsejson('[4,5,6]');
      "list" slist => { "element1", "element2" };
}

bundle agent test_datastate_mustache
{
  files:
      "/tmp/myfile.txt"
      create => "true",
      edit_template => "$(this.promise_filename).mustache",
      template_method => "mustache";
}

Then this template (saved in test_datastate_mustache.cf.mustache if you follow the example):

{{#classes.holderclass}}The holderclass is defined{{/classes.holderclass}}
{{^classes.holderclass}}The holderclass is not defined{{/classes.holderclass}}

{{#vars.holder.list}}element = {{.}}, {{/vars.holder.list}}

holder.s = {{vars.holder.s}}

Will produce this text in /tmp/myfile.txt when you run cf-agent -f ./test_datastate_mustache.cf:

The holderclass is defined


element = element1, element = element2,

holder.s = Hello!

History: Was introduced in 3.3.0, Nova 2.2.0 (2012). Mustache templates were introduced in 3.6.0.

See also: template_method, template_data, readjson(), parsejson(), mergedata(), data

edit_xml

Type: bundle edit_xml

file_select

Type: body file_select

leaf_name

Description: List of regexes that match an acceptable name

This pattern matches only the node name of the file, not its path.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body file_select example
     {
     leaf_name => { "S[0-9]+[a-zA-Z]+", "K[0-9]+[a-zA-Z]+" };
     file_result => "leaf_name";
     }
path_name

Description: List of pathnames to match acceptable target

Path name and leaf name can be conveniently tested for separately by use of appropriate regular expressions.

Type: slist

Allowed input range: "?(/.*)

Example:

     body file_select example
     {
     leaf_name => { "prog.pid", "prog.log" };
     path_name => { "/etc/.*", "/var/run/.*" };

     file_result => "leaf_name.path_name"
     }
search_mode

Description: A list of mode masks for acceptable file permissions

The mode may be specified in symbolic or numerical form with + and - constraints. Concatenation ug+s implies u OR g, and u+s,g+s implies u AND g.

Type: slist

Allowed input range: [0-7augorwxst,+-]+

Example:

     bundle agent testbundle
     {
     files:

       "/home/mark/tmp/testcopy"

         file_select => by_modes,
         transformer => "/bin/echo DETECTED $(this.promiser)",
         depth_search => recurse("inf");

     }

     body file_select by_modes
     {
     search_mode => { "711" , "666" };
     file_result => "mode";
     }

     body depth_search recurse(d)
     {
     depth => "$(d)";
     }
search_size

Type: irange[int,int]

Allowed input range: 0,inf

Description: Integer range of file sizes in bytes

Example:

     body file_select example
     {
     search_size => irange("0","20k");
     file_result => "size";
     }
search_owners

Description: List of acceptable user names or ids for the file, or regexes to match

A list of anchored regular expressions any of which must match the entire userid.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body file_select example
     {
     search_owners => { "mark", "jeang", "student_.*" };
     file_result => "owner";
     }

Notes: Windows does not have user ids, only names.

search_groups

Description: List of acceptable group names or ids for the file, or regexes to match

A list of anchored regular expressions, any of which must match the entire group.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body file_select example
     {
     search_groups => { "users", "special_.*" };
     file_result => "group";
     }

Notes: On Windows, files do not have group associations.

search_bsdflags

Description: String of flags for bsd file system flags expected set

Extra BSD file system flags (these have no effect on non-BSD versions of CFEngine). See the manual page for chflags for more details.

Type: slist

Allowed input range: [+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+

Example:

     body file_select xyz
     {
     search_bsdflags => "archived|dump";
     file_result => "bsdflags";
     }
ctime

Description: Range of change times (ctime) for acceptable files

The file's change time refers to both modification of content and attributes, such as permissions. On Windows, ctime refers to creation time.

Type: irange[int,int]

Allowed input range: 0,2147483647

Example:

     body files_select example
     {
     ctime => irange(ago(1,0,0,0,0,0),now);
     file_result => "ctime";
     }
mtime

Description: Range of modification times (mtime) for acceptable files

The file's modification time refers to both modification of content but not other attributes, such as permissions.

Type: irange[int,int]

Allowed input range: 0,2147483647

Example:

     body files_select example
     {
     # Files modified more than one year ago (i.e., not in mtime range)
     mtime => irange(ago(1,0,0,0,0,0),now);
     file_result => "!mtime";
     }
atime

Description: Range of access times (atime) for acceptable files

A range of times during which a file was accessed can be specified in a file_select body.

Type: irange[int,int]

Allowed input range: 0,2147483647

Example:

     body file_select used_recently
     {
     # files accessed within the last hour
     atime     => irange(ago(0,0,0,1,0,0),now);
     file_result => "atime";
     }


     body file_select not_used_much
     {
     # files not accessed since 00:00 1st Jan 2000 (in the local timezime)
     atime     => irange(on(2000,1,1,0,0,0),now);
     file_result => "!atime";
     }
exec_regex

Description: Matches file if this regular expression matches any full line returned by the command

The regular expression must be used in conjunction with the exec_program test. In this way the program must both return exit status 0 and its output must match the regular expression. The entire output must be matched.

Type: string

Allowed input range: .*

Example:

     body file_select example
     {
     exec_regex => "SPECIAL_LINE: .*";
     exec_program => "/path/test_program $(this.promiser)";
     file_result => "exec_program.exec_regex";
     }
exec_program

Description: Execute this command on each file and match if the exit status is zero

This is part of the customizable file search criteria. If the user-defined program returns exit status 0, the file is considered matched.

Type: string

Allowed input range: "?(/.*)

Example:

     body file_select example
     {
     exec_program => "/path/test_program $(this.promiser)";
     file_result => "exec_program";
     }
file_types

Description: List of acceptable file types from menu choices

File types vary in details between operating systems. The main POSIX types are provided here as menu options, with reg being a synonym for plain. In both cases this means not one of the "special" file types.

Type: (option list)

Allowed input range:

    plain
    reg
    symlink
    dir
    socket
    fifo
    door
    char
    block

Example:

     body file_select filter
     {
     file_types => { "plain","symlink" };

     file_result => "file_types";
     }
issymlinkto

Description: List of regular expressions to match file objects

If the file is a symbolic link that points to files matched by one of these expressions, the file will be selected.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body file_select example
     {
     issymlinkto => { "/etc/[^/]*", "/etc/init\.d/[a-z0-9]*" };
     }

Notes: Windows does not support symbolic links, so this attribute is not applicable on that platform.

file_result

Description: Logical expression combining classes defined by file search criteria

The syntax is the same as for a class expression, since the file selection is a classification of the file-search in the same way that system classes are a classification of the abstract host-search. That is, you may specify a boolean expression involving any of the file-matching components.

Type: string

Allowed input range: [!*(leaf_name|path_name|file_types|mode|size|owner|group|atime|ctime|mtime|issymlinkto|exec_regex|exec_program|bsdflags)[|.]*]*

Example:

     body file_select year_or_less
     {
     mtime       => irange(ago(1,0,0,0,0,0),now);
     file_result => "mtime";
     }

     body file_select my_pdf_files_morethan1dayold
     {
     mtime         => irange(ago(0,0,1,0,0,0),now);
     leaf_name     => { ".*\.pdf" , ".*\.fdf" };
     search_owners => { "mark" };

     file_result => "owner.leaf_name.!mtime";
     }

You may specify arbitrarily complex file-matching parameters, such as what is shown above, "is owned by mark, has the extension '.pdf' or '.fdf', and whose modification time is not between 1 day ago and now"; that is, it is older than 1 day.

See also: process_result

Type: body link_from

copy_patterns

Description: A set of patterns that should be copied and synchronized instead of linked

During the linking of files, it is sometimes useful to buffer changes with an actual copy, especially if the link is to an ephemeral file system. This list of patterns matches files that arise during a linking policy. A positive match means that the file should be copied and updated by modification time.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body link_from example
     {
     copy_patterns =>  { "special_node1", "/path/special_node2" };
     }

Description: true/false whether to link all directory's children to source originals

If the promiser is a directory, instead of copying the children, link them to the source.

Type: boolean

Default value: false

Example:

     body link_from example
     {
     link_children => "true";
     }

Description: The type of link used to alias the file

This determines what kind of link should be used to link files. Users are advised to be wary of 'hard links' (see Unix manual pages for the ln command). The behavior of non-symbolic links is often precarious and unpredictable.

Note that symlink is synonymous with absolute links, which are different from relative links. Although all of these are symbolic links, the nomenclature here is defined such that symlink and absolute are equivalent . When verifying a link, choosing 'relative' means that the link must be relative to the source, so relative and absolute links are mutually exclusive.

Type: (menu option)

Allowed input range:

    symlink
    hardlink
    relative
    absolute

Default value: symlink

Example:

     body link_from example
     {
     link_type => "symlink";
     source => "/tmp/source";
     }

Notes: On Windows, hard links are the only supported type.

source

Description: The source file to which the link should point

For remote copies this refers to the file name on the remote server.

Type: string

Allowed input range: .+

Example:

     body link_from example
     {
     source => "/path/to/source";
     }
when_linking_children

Description: Policy for overriding existing files when linking directories of children

The options refer to what happens if the directory already exists, and is already partially populated with files. If the directory being copied from contains a file with the same name as that of a link to be created, it must be decided whether to override the existing destination object with a link, or simply omit the automatic linkage for files that already exist. The latter case can be used to make a copy of one directory with certain fields overridden.

Type: (menu option)

Allowed input range:

    override_file
    if_no_such_file

Example:

     body link_from example
     {
     when_linking_children => "if_no_such_file";
     }
when_no_source

Description: Behavior when the source file to link to does not exist

This describes how CFEngine should respond to an attempt to create a link to a file that does not exist. The options are to force the creation to a file that does not (yet) exist, delete any existing link, or do nothing.

Type: (menu option)

Allowed input range:

    force
    delete
    nop

Default value: nop

Example:

     body link_from example
     {
     when_no_source => "force";
     }
move_obstructions

Description: true/false whether to move obstructions to file-object creation

If we have promised to make file X a link, but it already exists as a file, or vice-versa, or if a file is blocking the creation of a directory, then normally CFEngine will report an error. If this is set, existing objects will be moved aside to allow the system to heal without intervention. Files and directories are saved/renamed, but symbolic links are deleted.

Note that symbolic links for directories are treated as directories, not links. This behavior can be discussed, but the aim is to err on the side of caution.

Type: boolean

Default value: false

Example:

    files:

      "/tmp/testcopy"

        copy_from    => mycopy("/tmp/source"),
        move_obstructions => "true",
        depth_search => recurse("inf");

Notes: Some operating systems (Solaris) use symbolic links in path names. Copying to a directory could then result in renaming of the important link, if the behavior is different.

pathtype

Description: Menu option for interpreting promiser file object

By default, CFEngine makes an educated guess as to whether the promise pathname involves a regular expression or not. This guesswork is needed due to cross-platform differences in filename interpretation.

If CFEngine guesses (or is told) that the pathname uses a regular expression pattern, it will undertake a file search to find possible matches. This can consume significant resources, and so the guess option will always try to optimize this. Guesswork is, however, imperfect, so you have the option to declare your intention.

Type: (menu option)

Allowed input range:

    literal
    regex
    guess

If the keyword literal is invoked, a path will be treated as a literal string regardless of what characters it contains. If it is declared regex, it will be treated as a pattern to match.

Note that CFEngine splits the promiser up into path links before matching, so that each link in the path chain is matched separately. Thus it it meaningless to have a / in a regular expression, as the comparison will never see this character.

Default value: guess

Example:

files:

   "/var/lib\d"
      pathtype => "guess",  # best guess (default)
         perms => system;

   "/var/lib\d"
      pathtype => "regex",  # force regex interpretation
         perms => system;

   "/var/.*/lib"

      pathtype => "literal",    # force literal interpretation
         perms => system;

In these examples, at least one case implies an iteration over all files/directories matching the regular expression, while the last case means a single literal object with a name composed of dots and stars.

Notes: On Windows paths using regex must use the forward slash (/) as path separator, since the backward slash has a special meaning in a regular expression. Literal paths may also use backslash (\) as a path separator.

perms

Type: body perms

bsdflags

Description: List of menu options for BSD file system flags to set

Type: slist

Allowed input range: [+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+

Example:

     body perms example
     {
     bsdflags => { "uappnd","uchg","uunlnk","nodump",
                   "opaque","sappnd","schg","sunlnk" };
     }

Notes: The BSD Unices (FreeBSD, OpenBSD, NetBSD) and MacOSX have additional file system flags which can be set. Refer to the BSD chflags documentation for this.

groups

Description: List of acceptable groups of group ids, first is change target

The first named group in the list is the default that will be configured if the file does not match an element of the list. The reserved word none may be used to match files that are not owned by a registered group.

Type: slist

Allowed input range: [a-zA-Z0-9_$.-]+

Example:

     body perms example
     {
     groups => { "users", "administrators" };
     }

Notes: On Windows, files do not have file groups associated with them, and thus this attribute is ignored. ACLs may be used in place for this.

mode

Description: File permissions

The mode string may be symbolic or numerical, like chmod.

Type: string

Allowed input range: [0-7augorwxst,+-]+

Example:

     body perms example
     {
     mode => "a+rx,o+w";
     }

See also: rxdirs

Notes: This is ignored on Windows, as the permission model uses ACLs.

owners

Description: List of acceptable owners or user ids, first is change target

The first user is the reference value that CFEngine will set the file to if none of the list items matches the true state of the file. The reserved word none may be used to match files that are not owned by a registered user.

Type: slist

Allowed input range: [a-zA-Z0-9_$.-]+

Example:

     body perms example
     {
     owners => { "mark", "wwwrun", "jeang" };
     }

Notes: On Windows, users can only take ownership of files, never give it. Thus, the first user in the list should be the user running the CFEngine process (usually Administrator). Additionally, some groups may be owners on Windows (such as the Administrators group).

rxdirs

Description: true/false add execute flag for directories if read flag is set

Default behavior is to set the x flag on directories automatically if the r flag is specified in mode.

Type: boolean

Example:

     body perms rxdirs
     {
     rxdirs => "false";
     }

See also: mode

Notes: This is ignored on Windows, as the permission model uses ACLs.

rename

Type: body rename

disable

Description: true/false automatically rename and remove permissions

Disabling a file means making it unusable. For executables this means preventing execution, for an information file it means making the file unreadable.

Type: boolean

Default value: false

Example:

     body rename example
     {
     disable => "true";
     disable_suffix => ".nuked";
     }
disable_mode

Description: The permissions to set when a file is disabled

To disable an executable it is not enough to rename it, you should also remove the executable flag.

Type: string

Allowed input range: [0-7augorwxst,+-]+

Example:

     body rename example
     {
     disable_mode => "0600";
     }
disable_suffix

Description: The suffix to add to files when disabling

To disable files in a particular manner, use this string suffix.

Type: string

Allowed input range: (arbitrary string)

Default value: .cfdisabled

Example:

     body rename example
     {
     disable => "true";
     disable_suffix => ".nuked";
     }
newname

Description: The desired name for the current file

Type: string

Allowed input range: (arbitrary string)

Example:

     body rename example(s)
     {
     newname => "$(s)";
     }
rotate

Description: Maximum number of file rotations to keep

Used for log rotation. If the file is named foo and the rotate attribute is set to 4, as above, then initially foo is copied to foo.1 and the old file foo is zeroed out (that is, the inode of the original logfile does not change, but the original log file will be empty after the rotation is complete).

The next time the promise is executed, foo.1 will be renamed foo.2, foo is again copied to foo.1 and the old file foo is again zeroed out.

Each time the promise is executed (and typically, the promise would be executed as guarded by time-based or file-size-based classes), the files are copied/zeroed or rotated as above until there are rotate numbered files plus the one "main" file.

Type: int

Allowed input range: 0,99

Example:

     body rename example
     {
     rotate => "4";
     }

In the example above, the file foo.3 will be renamed foo.4, but the old version of the file foo.4 will be deleted (that is, it "falls off the end" of the rotation).

repository

Description: Name of a repository for versioning

A local repository for this object, overrides the default.

Note that when a repository is specified, the files are stored using the canonified directory name of the original file, concatenated with the name of the file. So, for example, /usr/local/etc/postfix.conf would ordinarily be stored in an alternative repository as _usr_local_etc_postfix.conf.cfsaved.

Type: string

Allowed input range: "?(/.*)

Example:

    files:

     "/path/file"

       copy_from => source,
       repository => "/var/cfengine/repository";
template_data

Description: The data to be passed to the template (Mustache only)

Type: data

Allowed input range: (arbitrary string)

Example:

    files:

     "/path/file"
     ...
     edit_template => "mytemplate.mustache",
     template_data => parsejson('{"message":"hello"}'),
     template_method => "mustache";

If this is omitted, the result of the datastate() function call is used instead. See edit_template for how you can use the data state in Mustache.

See also: edit_template, template_method, datastate()

template_method

Description: The template type.

By default cfengine requests the native CFEngine template implementation, but you can use mustache as well.

Type: (menu option)

Allowed input range:

  • cfengine
  • mustache

Default value: cfengine

    files:

     "/path/file"
     ...
     edit_template => "mytemplate.mustache",
     template_data => parsejson('{"message":"hello"}'),
     template_method => "mustache";

See also: edit_template, template_data, datastate()

touch

Description: true/false whether to touch time stamps on file

Type: boolean

Example:

    files:

     "/path/file"

       touch => "true";
transformer

Description: Command (with full path) used to transform current file (no shell wrapper used)

A command to execute, usually for the promised file to transform it to something else (but possibly to create the promised file based on a different origin file).

The promiser file must exist in order to effect the transformer.

Note also that if you use the $(this.promiser) variable or other variable in this command, and the file object contains spaces, then you should quote the variable. For example:

    transformer => "/usr/bin/gzip \"$(this.promiser)\"",

Note also that the transformer does not actually need to change the file. You can, for example, simply report on the existence of files with:

    transformer => "/bin/echo I found a file named $(this.promiser)",

The file streams stdout and stderr are redirected by CFEngine, and will not appear in any output unless you run cf-agent with the -v switch.

It is possible to set classes based on the return code of a transformer-command in a very flexible way. See the kept_returncodes, repaired_returncodes and failed_returncodes attributes.

Finally, you should note that the command is not run in a shell. This means that you cannot perform file redirection or create pipelines.

Type: string

Allowed input range: "?(/.*)

Example:

These examples show both types of promises.

    files:
      "/home/mark/tmp/testcopy"

        file_select => pdf_files,
        transformer => "/usr/bin/gzip $(this.promiser)",
        depth_search => recurse("inf");

In the first example, the promise is made on the file that we wish to transform. If the promised file exists, the transformer will change the file to a compressed version (and the next time CFEngine runs, the promised file will no longer exist, because it now has the .gz extension).

     classes:
        "do_update" expression => isnewerthan("/etc/postfix/alias",
                                              "/etc/postfix/alias.cdb");

     files:
        "/etc/postfix/alias.cdb"
           create => "true",        # Must have this!
           transformer => "/usr/sbin/postalias /etc/postfix/alias",
           ifvarclass => "do_update";

In the second example, the promise is made on the file resulting from the transformation (and the promise is conditional on the original file being newer than the result file). In this case, we must specify create = true. If we do not, then if the promised file is removed the transformer will not be executed.


packages

NOTE: CFEngine 3.6 introduces bundles package_absent, package_present, package_latest, package_specific_present, package_specific_absent, and package_specific_latest that provide a higher-level abstraction for working with packages. This is the recommended way to make promises about packages. The bundles can be found in the file packages.cf in masterfiles.

CFEngine supports a generic approach to integration with native operating support for packaging. Package promises allow CFEngine to make promises regarding the state of software packages conditionally, given the assumption that a native package manager will perform the actual manipulations. Since no agent can make unconditional promises about another, this is the best that can be achieved.

 vars:

  "match_package" slist => {
                           "apache2",
                           "apache2-mod_php5",
                           "apache2-prefork",
                           "php5"
                           };
 packages:

    "$(match_package)"

         package_policy => "add",
         package_method => yum;

Packages are treated as black-boxes with three labels:

  • A package name
  • A version string
  • An architecture name

Package managers are treated as black boxes that may support some or all of the following promise types:

  • List installed packages
  • Add packages
  • Delete packages
  • Reinstall (repair) packages
  • Update packages
  • Patch packages
  • Verify packages

If these services are promised by a package manager, cf-agent promises to use the service and encapsulate it within the overall CFEngine framework. It is possible to set classes based on the return code of a package-manager command in a very flexible way. See the kept_returncodes, repaired_returncodes and failed_returncodes attributes.

Domain knowledge

CFEngine does not maintain operating system specific expert knowledge internally, rather it uses a generic model for dealing with promises about packages (which depend on the behavior of an external package manager). The approach is to define package system details in body-constraints that can be written once and for all, for each package system.

Package promises are like commands promises in the sense that CFEngine promises nothing about the outcome of executing a command. All it can promise is to interface with it, starting it and using the results in good faith. Packages are basically 'outsourced', to invoke IT parlance.

Behavior

A package promise consists of a name, a version and an architecture, (n,v,a), and behavior to be promised about packages that match criteria based on these. The components (n,v,a) can be determined in one of two different ways:

  • They may be specified independently, e.g.
     packages:

       "mypackage"

          package_policy => "add",
          package_method => rpm,
          package_select => ">=",
          package_architectures => { "x86_64", "i586" },
          package_version => "1.2.3";
  • They may be extracted from a package identifier (promiser) or filename, using pattern matching. For example, a promiser 7-Zip-4.50-x86_64.msi and a package_method containing the following:
      package_name_regex => "^(\S+)-(\d+\.?)+";
      package_version_regex => "^\S+-((\d+\.?)+)";
      package_arch_regex => "^\S+-[\d\.]+-(.*).msi";

When scanning a list of installed packages different managers present the information (n,v,a) in quite different forms and pattern extraction is necessary. When making a promise about a specific package, the CFEngine user may choose one or the other model.

Smart and dumb package systems

Package managers vary enormously in their capabilities and in the kinds of promises they make. There are broadly two types:

  • Smart package systems that resolve dependencies and require only a symbolic package name.
  • Dumb package managers that do not resolve dependencies and need filename input.

Normal ordering for packages is the following:

  • Delete
  • Add
  • Update
  • Patch
Promise repair logic

Identified package matched by name, but not version

Command Dumb manager Smart manager
add unable Never
delete unable Attempt deletion
reinstall unable Attempt delete/add
upgrade unable Upgrade if capable
patch unable Patch if capable

Package not installed

Command Dumb manager Smart manager
add Attempt to install named Install any version
delete unable unable
reinstall Attempt to install named unable
upgrade unable unable
patch unable unable
    bundle agent packages
    {
    vars:

     # Test the simplest case -- leave everything to the yum smart manager

     "match_package" slist => {
                              "apache2",
                              "apache2-mod_php5",
                              "apache2-prefork",
                              "php5"
                              };
    packages:

      "$(match_package)"

         package_policy => "add",
         package_method => yum;

    }

Packages promises can be very simple if the package manager is of the smart variety that handles details for you. If you need to specify architecture and version numbers of packages, this adds some complexity, but the options are flexible and designed for maximal adaptability.

Patching

Some package systems also support the idea of 'patches'. These might be formally different objects to packages. A patch might contain material for several packages and be numbered differently. When you select patching-policy the package name (promiser) can be a regular expression that will match possible patch names, otherwise identifying specific patches can be cumbersome.

Note that patching is a subtle business. There is no simple way using the patch settings to install 'all new system patches'.

If we specify the name of a patch, then CFEngine will try to see if it exists and/or is installed. If it exists in the pending list, it will be installed. If it exists in the installed list it will not be installed. Now consider the pattern .*. This will match any installed package, so CFEngine will assume the relevant patch has been installed already. On the other hand, the pattern no match will not match an installed patch, but it will not match a named patch either.

Some systems provide a command to do this, which can be specified without specific patch arguments. If so, that command can be called periodically under commands. The main purposes of patching body items are:

  • To install specific named patches in a controlled manner.
  • To generate reports of available and installed patches during system reporting.
Installers without package/patch arguments

CFEngine supports the syntax $ at the end of a command to mean that no package name arguments should be used or appended after the dollar sign. This is because some commands require a list of packages, while others require an empty list. The default behavior is to try to append the name of one or more packages to the command, depending on whether the policy is for individual or bulk installation.

Default package method

As of core 3.3.0, if no package_method is defined, CFEngine will look for a method called generic. Such a method is defined in the standard library for supported operating systems.

Platform notes

Currently, packages promises do not work on HP-UX because CFEngine does not come with package bodies for that platform.

Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

package_architectures

Description: Select the architecture for package selection

It is possible to specify a list of packages of different architectures if it is desirable to install multiple architectures on the host. If no value is specified, CFEngine makes no promise about the result; the package manager's behavior prevails.

Type: slist

Allowed input range: (arbitrary string)

Example:

packages:

  "$(exact_package)"

     package_policy => "add",
     package_method => rpm,
     package_architectures => { "x86_64" };
package_method

Type: body package_method

package_add_command

Description: Command to install a package to the system

This command should install a package when appended with the package reference id, formed using the package_name_convention, using the model of (name,version,architecture). If package_file_repositories is specified, the package reference id will include the full path to a repository containing the package.

Package managers generally expect the name of a package to be passed as a parameter. However, in some cases we do not need to pass the name of a particular package to the command. Ending the command string with $ prevents CFEngine from appending the package name to the string.

Type: string

Allowed input range: .+

Example:

     body package_method rpm
     {
     package_add_command => "/bin/rpm -i ";
     }
package_arch_regex

Description: Regular expression with one back-reference to extract package architecture string

This is for use when extracting architecture from the name of the promiser, when the architecture is not specified using the package_architectures list. It is an unanchored regular expression that contains exactly one parenthesized back-reference which marks the location in the promiser at which the architecture is specified.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm

     {
     package_list_arch_regex    => "[^.]+\.([^.]+)";
     }

Notes: If no architecture is specified for thegiven package manager, then do not define this.

package_changes

Description: Defines whether to group packages into a single aggregate command.

This indicates whether the package manager is capable of handling package operations with multiple arguments. If this is set to bulk then multiple arguments will be passed to the package commands. If set to individual packages will be handled one by one. This might add a significant overhead to the operations, and also affect the ability of the operating system's package manager to handle dependencies.

Type: (menu option)

Allowed input range:

      individual
      bulk

Example:

     body package_method rpm

     {
     package_changes => "bulk";
     }
package_delete_command

Description: Command to remove a package from the system

The command that deletes a package from the system when appended with the package reference identifier specified by package_name_convention.

Package managers generally expect the name of a package to be passed as a parameter. However, in some cases we do not need to pass the name of a particular package to the command. Ending the command string with $ prevents CFEngine from appending the package name to the string.

Type: string

Allowed input range: .+

Example:

     body package_method rpm
     {
     package_delete_command => "/bin/rpm -e --nodeps";
     }
package_delete_convention

Description: This is how the package manager expects the package to be referred to in the deletion part of a package update, e.g. $(name)

This attribute is used when package_policy is delete, or package_policy is update and package_file_repositories is set and package_update_command is not set. It is then used to set the pattern for naming the package in the way expected by the package manager during the deletion of existing packages.

Three special variables are defined from the extracted data, in a private context for use: $(name), $(version) and $(arch). version and arch is the version and architecture (if package_list_arch_regex is given) of the already installed package. Additionally, if package_file_repositories is defined, $(firstrepo) can be prepended to expand the first repository containing the package. For example: $(firstrepo)$(name)-$(version)-$(arch).msi.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method freebsd

     {
     package_file_repositories => { "/path/to/packages" };
     package_name_convention => "$(name)-$(version).tbz";
     package_delete_convention => "$(name)-$(version)";
     }

Notes: If this is not defined, it defaults to the value of package_name_convention.

package_file_repositories

Description: A list of machine-local directories to search for packages

If specified, CFEngine will assume that the package installation occurs by filename and will search the named paths for a package matching the pattern package_name_convention. If found the name will be prefixed to the package name in the package commands.

Type: slist

Allowed input range: (arbitrary string)

Example:

     body package_method filebased
     {
     package_file_repositories => { "/package/repos1", "/packages/repos2" };
     }
package_installed_regex

Description: Regular expression which matches packages that are already installed

This regular expression must match complete lines in the output of the list command that are actually installed packages. If all the lines match, then the regex can be set of .*, however most package systems output prefix lines and a variety of human padding that needs to be ignored.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method yum
     {
     package_installed_regex => ".*installed.*";
     }
package_default_arch_command

Description: Command to detect the default packages' architecture

This command allows CFEngine to detect default architecture of packages managed by package manager. As an example, multiarch-enabled dpkg only lists architectures explicitly for multiarch-enabled packages.

In case this command is not provided, CFEngine treats all packages without explicit architecture set as belonging to implicit default architecture.

Type: string

Allowed input range: "?(/.*)

Example:

     body package_method dpkg
     {
       package_default_arch_command => "/usr/bin/dpkg --print-architecture";

       # ...
     }

History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)

package_list_arch_regex

Description: Regular expression with one back-reference to extract package architecture string

An unanchored regular expression that contains exactly one parenthesized back reference that marks the location in the listed package at which the architecture is specified.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm
     {
     package_list_arch_regex    => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*";
     }

Notes: If no architecture is specified for the given package manager, then do not define this regex.

package_list_command

Description: Command to obtain a list of available packages

This command should provide a complete list of the packages installed on the system. It might also list packages that are not installed. Those should be filtered out using the package_installed_regex.

Package managers generally expect the name of a package to be passed as a parameter. However, in some cases we do not need to pass the name of a particular package to the command. Ending the command string with $ prevents CFEngine from appending the package name to the string.

Type: string

Allowed input range: .+

Example:

     body package_method rpm

     {
     package_list_command => "/bin/rpm -qa --queryformat \"%{name} %{version}-%{release}\n\"";
     }
package_list_name_regex

Description: Regular expression with one back-reference to extract package name string

An unanchored regular expression that contains exactly one parenthesized back reference which marks the name of the package from the package listing.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm

     {
     package_list_name_regex    => "([^\s]+).*";
     }
package_list_update_command

Description: Command to update the list of available packages (if any)

Not all package managers update their list information from source automatically. This command allows a separate update command to be executed at intervals determined by package_list_update_ifelapsed.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method xyz
     {
     debian|ubuntu::

     package_list_update_command => "/usr/bin/apt-get update";
     package_list_update_ifelapsed => "240";        # 4 hours
     }
package_list_update_ifelapsed

Description: The ifelapsed locking time in between updates of the package list

Type: int

Allowed input range: -99999999999,9999999999

Example:

     body package_method xyz
     {
     debian|ubuntu::

     package_list_update_command => "/usr/bin/apt-get update";
     package_list_update_ifelapsed => "240";        # 4 hours
     }
package_list_version_regex

Description: Regular expression with one back-reference to extract package version string

This unanchored regular expression should contain exactly one parenthesized back-reference that marks the version string of packages listed as installed.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm

     {
     package_list_version_regex => "[^\s]+ ([^.]+).*";
     }
package_name_convention

Description: This is how the package manager expects the package to be referred to, e.g. $(name).$(arch)

This sets the pattern for naming the package in the way expected by the package manager. Three special variables are defined from the extracted data, in a private context for use: $(name), $(version) and $(arch). Additionally, if package_file_repositories is defined, $(firstrepo) can be prepended to expand the first repository containing the package. For example: $(firstrepo)$(name)-$(version)-$(arch).msi.

When package_policy is update, and package_file_repositories is specified, package_delete_convention may be used to specify a different convention for the delete command.

If this is not defined, it defaults to the value $(name).

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm
     {
     package_name_convention => "$(name).$(arch).rpm";
     }
package_name_regex

Description: Regular expression with one back-reference to extract package name string

This unanchored regular expression is only used when the promiser contains not only the name of the package, but its version and architecture also. In that case, this expression should contain a single parenthesized back-reference to extract the name of the package from the string.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm
     {
     package_name_regex => "([^\s]).*";
     }
package_noverify_regex

Description: Regular expression to match verification failure output

Ananchored regular expression to match output from a package verification command. If the output string matches this expression, the package is deemed broken.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method xyz

     {
     package_noverify_regex => "Package .* is not installed.*";
     package_verify_command => "/usr/bin/dpkg -s";
     }
package_noverify_returncode

Description: Integer return code indicating package verification failure

For use if a package verification command uses the return code as the signal for a failed package verification.

Type: int

Allowed input range: -99999999999,9999999999

Example:

     body package_method xyz
     {
     package_noverify_returncode => "-1";
     package_verify_command => "/bin/rpm -V";
     }
package_patch_arch_regex

Description: Anchored regular expression with one back-reference to extract update architecture string

A few package managers keep a separate notion of patches, as opposed to package updates. OpenSuSE, for example, is one of these. This provides an analogous command struct to the packages for patch updates.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method zypper
     {
     package_patch_arch_regex => "";
     }
package_patch_command

Description: Command to update to the latest patch release of an installed package

If the package manager supports patching, this command should patch a named package. If only patching of all packages is supported then consider running that as a batch operation in commands. Alternatively one can end the command string with a $ symbol, which CFEngine will interpret as an instruction to not append package names.

Type: string

Allowed input range: .+

Example:

     body package_method zypper

     {
     package_patch_command => "/usr/bin/zypper -non-interactive patch";
     }
package_patch_installed_regex

Description: Anchored regular expression which matches packages that are already installed

A few package managers keep a separate notion of patches, as opposed to package updates. OpenSuSE, for example, is one of these. This provide an analogous command struct to the packages for patch updates.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method zypper
     {
     package_patch_installed_regex => ".*(Installed|Not Applicable).*";
     }
package_patch_list_command

Description: Command to obtain a list of available patches or updates

This command, if it exists at all, is presumed to generate a list of patches that are available on the system, in a format analogous to (but not necessarily the same as) the package-list command. Patches might formally be available in the package manager's view, but if they have already been installed, CFEngine will ignore them.

Package managers generally expect the name of a package to be passed as a parameter. However, in some cases we do not need to pass the name of a particular package to the command. Ending the command string with $ prevents CFEngine from appending the package name to the string.

Type: string

Allowed input range: .+

Example:

      package_patch_list_command => "/usr/bin/zypper patches";
package_patch_name_regex

Description: Unanchored regular expression with one back-reference to extract update name string.

A few package managers keep a separate notion of patches, as opposed to package updates. OpenSuSE, for example, is one of these. This provides an analogous command struct to the packages for patch updates.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method zypper
     {
     package_patch_name_regex    => "[^|]+\|\s+([^\s]+).*";
     }
package_patch_version_regex

Description: Unanchored regular expression with one back-reference to extract update version string.

A few package managers keep a separate notion of patches, as opposed to package updates. OpenSuSE, for example, is one of these. This provides an analogous command struct to the packages for patch updates.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method zypper
     {
     package_patch_version_regex => "[^|]+\|[^|]+\|\s+([^\s]+).*";
     }
package_update_command

Description: Command to update to the latest version a currently installed package

If supported this should be a command that updates the version of a single currently installed package. If only bulk updates are supported, consider running this as a single command under commands. The package reference id is appended, with the pattern of package_name_convention.

When package_file_repositories is specified, the package reference id will include the full path to a repository containing the package. If package_policy is update, and this command is not specified, the package_delete_command and package_add_command will be executed to carry out the update.

Type: string

Allowed input range: .+

Example:

     body package_method zypper
     {
     package_update_command => "/usr/bin/zypper -non-interactive update";
     }
package_verify_command

Description: Command to verify the correctness of an installed package

If available, this is a command to verify an already installed package. It is required only when package_policy is verify.

The outcome of the command is compared with package_noverify_returncode or package_noverify_regex, one of which has to be set when using this command. If the package is not installed, the command will not be run the promise gets flagged as not kept before the verify command executes.

In order for the promise to be considered kept, the package must be installed, and the verify command must be successful according to package_noverify_returncode xor package_noverify_regex.

Package managers generally expect the name of a package to be passed as a parameter. However, in some cases we do not need to pass the name of a particular package to the command. Ending the command string with $ prevents CFEngine from appending the package name to the string.

Type: string

Allowed input range: .+

Example:

     body package_method rpm

     {
     package_verify_command => "/bin/rpm -V";
     package_noverify_returncode => "-1";
     }
package_version_regex

Description: Regular expression with one back-reference to extract package version string

If the version of a package is not specified separately using package_version, then this should be an unanchored regular expression that contains exactly one parenthesized back-reference that matches the version string in the promiser.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method rpm
     {
     package_version_regex => "[^\s]+ ([^.]+).*";
     }
package_multiline_start

Description: Regular expression which matches the start of a new package in multiline output

This pattern is used in determining when a new package record begins. It is used when package managers (like the Solaris package manager) use multi-line output formats. This pattern matches the first line of a new record.

Type: string

Allowed input range: (arbitrary string)

Example:

     body package_method solaris (pkgname, spoolfile, adminfile)
     {
     package_changes => "individual";
     package_list_command => "/usr/bin/pkginfo -l";
     package_multiline_start    =>  "\s*PKGINST:\s+[^\s]+";
     ...
     }
package_commands_useshell

Description: Whether to use shell for commands in this body

Type: boolean

Default value: true

History: Was introduced in 3.4.0, Nova 2.3.0 (2012)

package_version_less_command

Description: Command to check whether first supplied package version is less than second one

This attribute allows overriding of the built-in CFEngine algorithm for version comparison, by calling an external command to check whether the first passed version is less than another.

The built-in algorithm does a good approximation of version comparison, but different packaging systems differ in corner cases (e.g Debian treats symbol ~ less than any other symbol and even less than empty string), so some sort of override is necessary.

Variables v1 and v2 are substituted with the first and second version to be compared. Command should return code 0 if v1 is less than v2 and non-zero otherwise.

Note that if package_version_equal_command is not specified, but package_version_less_command is, then equality will be tested by issuing less comparison twice (v1 equals to v2 if v1 is not less than v2, and v2 is not less than v1).

Type: string

Allowed input range: .+

Example:

     body package_method deb
     {
     ...
     package_version_less_command => "dpkg --compare-versions ${v1} lt ${v2}";
     }

History: Was introduced in 3.4.0 (2012)

package_version_equal_command

Description: Command to check whether first supplied package version is equal to second one

This attribute allows overriding of the built-in CFEngine algorithm for version comparison by calling an external command to check whether the passed versions are the same. Some package managers consider textually different versions to be the same (e.g. optional epoch component, so 0:1.0-1 and 1.0-1 versions are the same), and rules for comparing vary from package manager to package manager, so override is necessary.

Variables v1 and v2 are substituted with the versions to be compared. Command should return code 0 if versions are equal and non-zero otherwise.

Note that if package_version_equal_command is not specified, but package_version_less_command is, then equality will be tested by issuing less comparison twice (v1 equals to v2 if v1 is not less than v2, and v2 is not less than v1).

Type: string

Allowed input range: .+

Example:

     body package_method deb
     {
     ...
     package_version_equal_command => "dpkg --compare-versions ${v1} eq ${v2}";
     }

Notes:

History: Was introduced in 3.4.0 (2012)

package_policy

Description: Criteria for package installation/upgrade on the current system

Type: (menu option)

Allowed input range:

  • add

Ensure that a package is present (this is the default setting from 3.3.0).

Ensure that a package is not present.

  • reinstall

Delete then add package (warning, non-convergent).

  • update

Update the package if an update is available (manager dependent).

  • addupdate

Equivalent to add if the package is not installed, and update if it is installed. Note: This attribute requires the specification of package_version and package_select in order to select the proper version to update to if available. See Also package_latest package_specific_latest in the standard library.

  • patch

Install one or more patches if available (manager dependent).

Verify the correctness of the package (manager dependent). The promise is kept if the package is installed correctly, not kept otherwise. Requires setting package_verify_command.

Default value: verify

Example:

packages:

  "$(match_package)"

     package_policy => "add",
     package_method => xyz;
package_select

Description: Selects which comparison operator to use with package_version.

This selects the operator used to compare available packages. If an available package is found to satisfy the version requirement, it may be selected for install (if package_policy is add, update or addupdate). To select the right package, imagine the available package being on the left hand side of the operator, and the value in package_version being on the right.

Note that in the case of deleting a package, you must specify an exact version.

If package_policy is update or addupdate, CFEngine will always try to keep the most recent package installed that satisfies the version requirement. For example, if package_select is < and package_version is 3.0.0, you may still match updates to 2.x.x series, like: 2.2.1, 2.2.2, 2.3.0, because they all satisfy the version requirement.

Type: (menu option)

Allowed input range:

     <
     >
     ==
     !=
     >=
     <=

Example:

packages:

  "$(exact_package)"

     package_policy => "add",
     package_method => xyz,
     package_select => ">=",
     package_architectures => { "x86_64" },
     package_version => "1.2.3-456";
package_version

Description: Version reference point for determining promised version

Used for specifying the targeted package version when the version is written separately from the name of the command.

Type: string

Allowed input range: (arbitrary string)

Example:

packages:

  "mypackage"

     package_policy => "add",
     package_method => rpm,
     package_select => "==",
     package_version => "1.2.3";

reports

Reports promises simply print messages. Outputting a message without qualification can be a dangerous operation. In a large installation it could unleash an avalanche of messaging.

    reports:

    "literal string or file refererence",
       printfile = printfile_body,
       ...;

Messages outputted from report promises are prefixed with the letter R to distinguish them from other output, for example from commands.

    bundle agent report
    {
    reports:

       "/etc/passwd except $(const.n)"

        # printfile => pr("/etc/passwd","5");

         showstate => { "otherprocs", "rootprocs" };
    }

Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

friend_pattern

Description: Regular expression to keep selected hosts from the friends report list

This regular expression should match hosts we want to exclude from friend reports.

Type: string

Allowed input range: (arbitrary string)

Example:

reports:
   "Friend status report"
           lastseen => "0",
     friend_pattern => "host1|host2|.*\.domain\.tld";
intermittency

Description: Real number threshold [0,1] of intermittency about current peers, report above

Type: real

Allowed input range: 0,1

Default value: intermittency => "0"

printfile

Description: Outputs the content of a file to standard output

Type: body printfile

file_to_print

Description: Path name to the file that is to be sent to standard output

Include part of a file in a report.

Type: string

Allowed input range: "?(/.*)

number_of_lines

Description: Integer maximum number of lines to print from selected file

Type: int

Allowed input range: 0,99999999999

Example:

     bundle agent example
     {
     reports:
         "$(sys.date) - current message of the day:"
            printfile => "motd";
     }

     body printfile motd
     {
         file_to_print   => "/etc/motd";
         number_of_lines => "10";
     }
report_to_file

Description: The path and filename to which output should be appended

Append the output of the report to the named file instead of standard output. If the file cannot be opened for writing then the report defaults to the standard output.

Type: string

Allowed input range: "?(/.*)

Example:

    bundle agent test
    {
    reports:
       "$(sys.date),This is a report from $(sys.host)"

           report_to_file => "/tmp/test_log";
    }
bundle_return_value_index

Description: The promiser is to be interpreted as a literal value that the caller can accept as a result for this bundle; in other words, a return value with array index defined by this attribute.

Return values are limited to scalars.

Type: string

Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

    body common control
    {
    bundlesequence => { "test" };
    }

    bundle agent test
    {
    methods:

       "any" usebundle => child,
        useresult => "my_return_var";


    reports:
        "My return was: \"$(my_return_var[1])\" and \"$(my_return_var[2])\"";

    }

    bundle agent child
    {
    reports:
       # Map these indices into the useresult namespace

       "this is a return value"
          bundle_return_value_index => "1";

       "this is another return value"
          bundle_return_value_index => "2";

    }
lastseen

Description: Integer time threshold in hours since current peers were last seen, report absence

After this time has passed, CFEngine will begin to warn about the host being overdue. After the lastseenexpireafter expiry time, hosts will be purged from this host's database.

Type: int

Allowed input range: 0,99999999999

Default value: lastseen => 168 # one week

Example:

    reports:
      "Comment"

        lastseen => "10";
showstate

Deprecated: This attribute is kept for source compatibility, and has no effect. Deprecated in CFEngine 3.5.


commands

Commands and processes are separated cleanly. Restarting of processes must be coded as a separate command. This stricter type separation allows for more careful conflict analysis to be carried out.

     commands:

       "/path/to/command args"

                  args = "more args",
                  contain = contain_body,
                  module = true/false;

Output from commands executed here is quoted inline, but prefixed with the letter Q to distinguish it from other output; for example, from reports, which is prefixed with the letter R.

It is possible to set classes based on the return code of a commands-promise in a very flexible way. See the kept_returncodes, repaired_returncodes and failed_returncodes attributes.

bundle agent example

{
commands:

  "/bin/sleep 10"
     action  => background;

  "/bin/sleep"
     args => "20",
     action  => background;

}

When referring to executables the full path to the executable must be used. When reffereing to executables whose paths contain spaces, you should quote the entire program string separately so that CFEngine knows the name of the executable file. For example:

      commands:

       windows::

        "\"c:\Program Files\my name with space\" arg1 arg2";

       linux::

        "\"/usr/bin/funny command name\" -a -b -c";

Note: Some unices leave a hanging pipe on restart (they never manage to detect the end of file condition). This occurs on POSIX.1 and SVR4 popen calls which use wait4. For some reason they fail to find and end-of-file for an exiting child process and go into a deadlock trying to read from an already dead process. This leaves a zombie behind (the parent daemon process which forked and was supposed to exit) though the child continues. A way around this is to use a wrapper script which prints the line cfengine-die to STDOUT after restarting the process. This causes cfengine to close the pipe forcibly and continue.


Attributes
Common Attributes

Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:

action
classes
comment
depends_on
handle
ifvarclass
meta

args

Description: Allows to separate the arguments to the command from the command itself.

Sometimes it is convenient to separate command and arguments. The final arguments are the concatenation with one space.

Type: string

Allowed input range: (arbitrary string)

commands:

  "/bin/echo one"

   args => "two three";

So in the example above the command would be:

 /bin/echo one two three
contain

Description: Allows running the command in a 'sandbox'.

Command containment allows you to make a `sandbox' around a command, to run it as a non-privileged user inside an isolated directory tree.

Type: body contain

Example:

    body contain example
    {
        useshell => "noshell";
           umask => "077";
      exec_owner => "mysql_user";
      exec_group => "nogroup";
    exec_timeout => "60";
           chdir => "/working/path";
          chroot => "/private/path";
    }
useshell

Description: Specifies which shell to use when executing the command.

The default is to not use a shell when executing commands. Use of a shell has both resource and security consequences. A shell consumes an extra process and inherits environment variables, reads commands from files and performs other actions beyond the control of CFEngine.

If one does not need shell functionality such as piping through multiple commands then it is best to manage without it. In the Windows version of CFEngine Enterprise, the command is run in the cmd Command Prompt if this attribute is set to useshell, or in the PowerShell if the attribute is set to powershell.

Type: (menu option)

Allowed input range:

    useshell
    noshell
    powershell

For compatibility, the boolean values are also supported, and map to useshell and noshell, respectively.

Default value: noshell

Example:

     body contain example
     {
     useshell => "useshell";
     }
umask

Description: Sets the internal umask for the process.

Default value for the mask is 077. On Windows, umask is not supported and is thus ignored by Windows versions of CFEngine.

Type: (menu option)

Allowed input range:

    0
    77
    22
    27
    72
    002
    077
    022
    027
    072

Example:

     body contain example
     {
     umask => "077";
     }
exec_owner

Description: Specifies the user under which the command executes.

This is part of the restriction of privilege for child processes when running cf-agent as the root user, or a user with privileges.

Windows requires the clear text password for the user account to run under. Keeping this in CFEngine policies could be a security hazard. Therefore, this option is not yet implemented on Windows versions of CFEngine.

Type: string

Allowed input range: (arbitrary string)

Example:

     body contain example
     {
     exec_owner => "mysql_user";
     }
exec_group

Description: Associates the command with a group.

This is part of the restriction of privilege for child processes when running cf-agent as the root group, or a group with privileges. It is ignored on Windows, as processes do not have any groups associated with them.

Type: string

Allowed input range: (arbitrary string)

Example:

     body contain example
     {
     exec_group => "nogroup";
     }
exec_timeout

Description: Attempt to time-out after this number of seconds.

This cannot be guaranteed as not all commands are willing to be interrupted in case of failure.

Type: int

Allowed input range: 1,3600

Example:

     body contain example
     {
     exec_timeout => "30";
     }

See Also: body action expireafter, body agent control expireafter, body executor control agent_expireafter

chdir

Description: Run the command with a working directory.

This attribute has the effect of placing the running command into a current working directory equal to the parameter given; in other words, it works like the cd shell command.

Type: string

Allowed input range: "?(/.*)

Example:

     body contain example

     {
     chdir => "/containment/directory";
     }
chroot

Description: Specify the path that will be the root directory for the process.

The path of the directory will be experienced as the top-most root directory for the process. In security parlance, this creates a 'sandbox' for the process. Windows does not support this feature.

Type: string

Allowed input range: "?(/.*)

Example:

     body contain example

     {
     chroot => "/private/path";
     }
preview

Description: This is the preview command when running in dry-run mode (with -n).

Previewing shell scripts during a dry-run is a potentially misleading activity. It should only be used on scripts that make no changes to the system. It is CFEngine best practice to never write change-functionality into user-written scripts except as a last resort. CFEngine can apply its safety checks to user defined scripts.

Type: boolean

Default value: false

Example:

     body contain example
     {
     preview => "true";
     }
no_output

Description: Allows to discard all output from the command.

Setting this attribute to true is equivalent to piping standard output and error to /dev/null.

Type: boolean

Default value: false

Example:

     body contain example
     {
     no_output => "true";
     }
module

Description: Set variables and classes based on command output.

CFEngine modules are commands that support a simple protocol in order to set additional variables and classes on execution from user defined code. Modules are intended for use as system probes rather than additional configuration promises. Such a module may be written in any language.

This attribute determines whether or not to expect the CFEngine module protocol. If true, the module protocol is supported for this command:

  • lines which begin with a ^ are protocol extensions
    • ^context=xyz sets the module context to xyz instead of the default
    • ^meta=a,b,c sets the class and variable tags for any following definitions to a, b, and c
  • lines which begin with a + are treated as classes to be defined (like -D)
  • lines which begin with a - are treated as classes to be undefined (like -N)
  • lines which begin with = are scalar variables to be defined
  • lines which begin with = and include [] are array variables to be defined
  • lines which begin with @ are lists.
  • lines which begin with % are data containers. The value needs to be valid JSON and will be decoded.

These variables end up in a context that has the same name as the module, unless the ^context extension is used.

All the variables and classes will have at least the tag source=module in addition to any tags you may set.

Any other lines of output are cited by cf-agent as being erroneous, so you should normally make your module completely silent.

Type: boolean

Default value: false

Example:

Here is an example module written in shell:

     #!/bin/sh
     /bin/echo "@mylist= { \"one\", \"two\", \"three\" }"
     /bin/echo "=myscalar= scalar val"
     /bin/echo "=myarray[key]= array key val"
     /bin/echo "%mydata=[1,2,3]"
     /bin/echo "+module_class"

And here is an example using it:

    body common control
    {
    bundlesequence  => { def, modtest };
    }

    bundle agent def
    {
    commands:

      "$(sys.workdir)/modules/module_name"
        module => "true";

    reports:

      # Each module forms a private context with its name as id
      module_class::

        "Module set variable $(module_name.myscalar)";
    }


    bundle agent modtest
    {
    vars:

      "mylist" slist => { @(module_name.mylist) };

    reports:

      module_class::

        "Module set variable $(mylist)";
    }

Here is an example module written in Perl:

     #!/usr/bin/perl
     #
     # module:myplugin
     #

       # lots of computation....

     if (special-condition)
        {
        print "+specialclass";
        }

If your module is simple and is best expressed as a shell command, then we suggest that you expose the class being defined in the command being executed (making it easier to see what classes are used when reading the promises file). For example, the promises could read as follows (the two echo commands are to ensure that the shell always exits with a successful execution of a command):

    bundle agent sendmail
    {
    commands:
      # This next module checks a specific failure mode of dcc, namely
      # more than 3 error states since the last time we ran cf-agent
      is_mailhost::
            "/bin/test `/usr/bin/tail -100 /var/log/maillog | /usr/bin/grep 'Milter (dcc): to error state' | /usr/bin/wc -l` -gt 3  echo '+start_dccm' || echo
    ''"
        contain => shell_command,
        module => "true";

        start_dccm::
          "/var/dcc/libexec/start-dccm"
              contain => not_paranoid;
    }

    body contain shell_command
    {
        useshell    => "useshell";
    }

    body contain not_paranoid
    {
        useshell    => "no";
        exec_owner  => "root";
        umask       => "22";
    }

Modules inherit the environment variables from cf-agent and accept arguments, just as a regular command does.


Functions

Functions are parameterized "RVALUES". Parameter values need to be of the type and range as documented for each functions. Some functions are documented with a ..., in which case they take an arbitrary amount of parameters.

They can return scalar and list values:

    vars:
      "random" int => randomint("0", "100");
      "list" slist => readstringlist("/tmp/listofstring", "#.*", "\s", 10, 400);

In addition, functions with return type boolean evaluate to true or false. The class on the left-hand side is set if the function evaluates to true. If the function evaluates to false, then the class remains unchanged.

    bundle agent test
    {
    vars:
      "five" int => "5";
      "seven" " int => "7";
    classes:
      "ok" expression => islessthan("$(five)","$(seven)");

    reports:

      ok::
        "$(five) is smaller than $(seven)";

     !ok::
        "$(seven) is smaller than $(five)";

    }

Underneath, CFEngine functions that return boolean will actually return a context expression like any or !any which will then be deemed true or false by the CFEngine evaluator. Note the truth of a context expression or the result of a function call may change during evaluation, but a class, once defined, will stay defined.

Functions that return a boolean can thus sometimes be used in places where a string is accepted as well, but this behavior is not clearly defined or supported. Use at your own discretion.

Function caching

During convergence, CFEngine's evaluation model will evaluate functions multiple times, which can be a performance concern.

Some system functions are particularly expensive:

As of 3.6.0, the new cache_system_functions body common parameter is set to true by default and CFEngine's evaluator will use it. Although you can override it to false, in practice you should almost never need to do so. The effect of having it true (the default) is that the expensive system functions will be run just once and then their result will be cached.

Note that caching is per-process so results will not be cached between runs of e.g. cf-agent and cf-promises.

List of all functions

There are a large number of functions built into CFEngine. The following tables might make it easier for you to find the function you need.

Functions by Category
files communication utils system io data
accessedbefore() host2ip() classesmatching() getenv() countlinesmatching() accumulated()
changedbefore() hostrange() classmatch() getuid() data_readstringarray() ago()
dirname() hostsseen() countclassesmatching() getusers() data_readstringarrayidx() and()
diskfree() hostswithclass() datastate() groupexists() parseintarray() bundlesmatching()
fileexists() hubknowledge() execresult() hostinnetgroup() parsejson() canonify()
filesexist() ip2host() getclassmetatags() now() parserealarray() canonifyuniquely()
filesize() iprange() getvariablemetatags() packagesmatching() parsestringarray() classify()
filestat() ldaparray() isvariable() packageupdatesmatching() parsestringarrayidx() concat()
findfiles() ldaplist() returnszero() registryvalue() readfile() difference()
isdir() ldapvalue() splayclass() userexists() readintarray() escape()
isexecutable() peerleader() usemodule() readintlist() eval()
islink() peerleaders() variablesmatching() readjson() every()
isnewerthan() peers() readrealarray() filter()
isplain() readtcp() readreallist() format()
laterthan() regldap() readstringarray() getfields()
lsdir() remoteclassesmatching() readstringarrayidx() getgid()
translatepath() remotescalar() readstringlist() getindices()
selectservers() regline() getvalues()
grep()
hash()
hashmatch()
ifelse()
intersection()
irange()
isgreaterthan()
islessthan()
join()
lastnode()
length()
makerule()
maparray()
maplist()
max()
mean()
mergedata()
min()
none()
not()
nth()
on()
or()
product()
randomint()
regarray()
regcmp()
regextract()
reglist()
reverse()
rrange()
shuffle()
some()
sort()
splitstring()
storejson()
strcmp()
strftime()
string_downcase()
string_head()
string_length()
string_reverse()
string_split()
string_tail()
string_upcase()
sublist()
sum()
unique()
variance()
Functions by Return Type
real (i,r)range string int (i,r,s)list data class
mean() irange() and() accumulated() bundlesmatching() data_readstringarray() accessedbefore()
product() rrange() canonify() ago() classesmatching() data_readstringarrayidx() changedbefore()
sum() canonifyuniquely() countclassesmatching() difference() datastate() classify()
variance() concat() countlinesmatching() filter() mergedata() classmatch()
dirname() diskfree() findfiles() packagesmatching() every()
escape() filesize() getclassmetatags() packageupdatesmatching() fileexists()
eval() getfields() getindices() parsejson() filesexist()
execresult() getgid() getusers() readjson() groupexists()
filestat() getuid() getvalues() hashmatch()
format() length() getvariablemetatags() hostinnetgroup()
getenv() now() grep() hostrange()
hash() on() hostsseen() iprange()
host2ip() parseintarray() hostswithclass() isdir()
hubknowledge() parserealarray() intersection() isexecutable()
ifelse() parsestringarray() ldaplist() isgreaterthan()
ip2host() parsestringarrayidx() lsdir() islessthan()
join() randomint() maparray() islink()
lastnode() readintarray() maplist() isnewerthan()
ldapvalue() readrealarray() peerleaders() isplain()
max() readstringarray() peers() isvariable()
min() readstringarrayidx() readintlist() laterthan()
not() selectservers() readreallist() ldaparray()
nth() string_length() readstringlist() makerule()
or() reverse() none()
peerleader() shuffle() regarray()
readfile() sort() regcmp()
readtcp() splitstring() regextract()
registryvalue() string_split() regldap()
remotescalar() sublist() regline()
storejson() unique() reglist()
strftime() variablesmatching() remoteclassesmatching()
string_downcase() returnszero()
string_head() some()
string_reverse() splayclass()
string_tail() strcmp()
string_upcase() usemodule()
translatepath() userexists()

usemodule

Prototype: usemodule(module, args)

Return type: boolean

Description: Execute CFEngine module script module with args, and return whether successful.

The module script is expected to be located in the registered modules directory, WORKDIR/modules.

Arguments:

  • module: string, in the range: .*
  • args: string, in the range: .*

Example:

bundle agent test
{
  classes:

      # returns $(user)

      "done" expression => usemodule("getusers","");

  commands:

      "/bin/echo" args => "test $(user)";
}

readstringarrayidx

Prototype: readstringarrayidx(array, filename, comment, split, maxentries, maxbytes)

Return type: int

Description: Populates the two-dimensional array array with up to maxentries fields from the first maxbytes bytes of file filename.

One dimension is separated by the regex split, the other by the lines in the file. The array arguments are both integer indexes, allowing for non-identifiers at first field (e.g. duplicates or names with spaces), unlike readstringarray.

The comment field will strip out unwanted patterns from the file being read, leaving unstripped characters to be split into fields. Using the empty string ("") indicates no comments.

Returns an integer number of keys in the array (i.e., the number of lines matched). If you only want the fields in the first matching line (e.g., to mimic the behavior of the getpwnam(3) on the file /etc/passwd), use getfields(), instead.

Arguments:

  • array: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • filename: string, in the range: "?(/.*)
  • comment: string, in the range: .*
  • split: string, in the range: .*
  • maxentries: int, in the range: 0,99999999999
  • maxbytes: int, in the range: 0,99999999999

Example:

    vars:

      "dim_array"

         int =>  readstringarrayidx("array_name","/tmp/array","\s*#[^\n]*",":",10,4000);

Input example:

     at spaced:x:25:25:Batch jobs daemon:/var/spool/atjobs:/bin/bash
     duplicate:x:103:105:User for Avahi:/var/run/avahi-daemon:/bin/false    # Disallow login
     beagleindex:x:104:106:User for Beagle indexing:/var/cache/beagle:/bin/bash
     duplicate:x:1:1:bin:/bin:/bin/bash
     # Daemon has the default shell
     daemon:x:2:2:Daemon:/sbin:

Results in a systematically indexed map of the file:

     array_name[0][0]       at spaced
     array_name[0][1]       x
     array_name[0][2]       25
     array_name[0][3]       25
     array_name[0][4]       Batch jobs daemon
     array_name[0][5]       /var/spool/atjobs
     array_name[0][6]       /bin/bash
     array_name[1][0]       duplicate
     array_name[1][1]       x
     array_name[1][2]       103
     array_name[1][3]       105
     array_name[1][4]       User for Avahi
     array_name[1][5]       /var/run/avahi-daemon
     array_name[1][6]       /bin/false
     ...

canonifyuniquely

Prototype: canonifyuniquely(text)

Return type: string

Description: Convert an arbitrary string text into a unique legal class name.

This function turns arbitrary text into class data, appending the SHA-1 hash for uniqueness. It is exactly equivalent to concat(canonify($(string)), "_", hash($(string),"sha1"); for a given $(string) but is much more convenient to write and remember.

A common use case is when you need unique array keys for each file in a list, but files in the list may have the same name when canonify-ed.

Arguments:

  • text: string, in the range: .*

Example:

    commands:

       "/var/cfengine/bin/$(component)"

           ifvarclass => canonifyuniquely("start_$(component)");

See also: canonify()).


strcmp

Prototype: strcmp(string1, string2)

Return type: boolean

Description: Returns whether the two strings string1 and string2 match exactly.

Arguments:

  • string1: string, in the range: .*
  • string2: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "same" expression => strcmp("test","test");

  reports:

    same::

      "Strings are equal";

    !same::

      "Strings are not equal";
}

Output:

R: Strings are equal

and

Prototype: and(...)

Return type: string

Description: Returns any if all arguments evaluate to true and !any if any argument evaluates to false.

Arguments: A list of classes, class expressions, or functions that return classes.

Example:

    commands:
      "/usr/bin/generate_config $(config)"
        ifvarclass => and( "generating_configs",
                           not(fileexists("/etc/config/$(config)"))
                         );

Notes: Introduced primarily for use with ifvarclass, if, and unless promise attributes.

See Also: and, or, not

History: Was introduced in 3.2.0, Nova 2.1.0 (2011)


"parse[int|real|string]array"

Prototype: parseintarray(array, input, comment, split, maxentries, maxbytes)
Prototype: parserealarray(array, input, comment, split, maxentries, maxbytes)
Prototype: parsestringarray(array, input, comment, split, maxentries, maxbytes)

Return type: int

Description: Parses up to maxentries values from the first maxbytes bytes in string input and populates array. Returns the dimension.

These functions mirror the exact behavior of their read[int|real|string]array() counterparts, but read data from a variable instead of a file. By making data readable from a variable, data driven policies can be kept inline.

Arguments:

  • array : Array identifier to populate, in the range [a-zA-Z0-9_$(){}\[\].:]+
  • input : A string to parse for input data, in the range "?(/.*)
  • comment : Unanchored regex matching comments, in the range .*
  • split : Unanchored regex to split data, in the range .*
  • maxentries : Maximum number of entries to read, in the range 0,99999999999
  • maxbytes : Maximum bytes to read, in the range 0,99999999999

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test(f)
{
  vars:
      # Define data inline for convenience
      "table"   string =>
      "1:2
          3:4
          5:6";

      "dim" int => parseintarray(
                                  "items",
                  "$(table)",
                  "\s*#[^\n]*",
                  ":",
                  "1000",
                  "200000"
      );

      "keys" slist => getindices("items");
      "sorted_keys" slist => sort(keys, "int");

  reports:
      "$(sorted_keys)";
}

Output:

R: 1
R: 3
R: 5

History: Was introduced in version 3.1.5a1, Nova 2.1.0 (2011)


peers

Prototype: peers(filename, regex, groupsize)

Return type: slist

Description: Returns the current host's partition peers (excluding it).

So given groupsize 3 and the file

a
b
c
# this is a comment d
e

The peers of host b will be a and c.

Given a list of host names in filename, one per line, and excluding comment lines starting with the unanchored regular expression regex, CFEngine partitions the host list into groups of up to groupsize. Each group's peer leader is the first host in the group.

The current host (unqualified or fully qualified) should belong to this file if it is expected to interact with the others. The function returns an empty list otherwise.

Arguments:

  • filename: string, in the range: "?(/.*)
  • regex: regular expression, in the range: .*
  • groupsize: int, in the range: 2,64

groupsize must be between 2 and 64 to avoid nonsensical promises.

Example:

Prepare:

echo alpha > /tmp/cfe_hostlist
echo beta >> /tmp/cfe_hostlist
echo gamma >> /tmp/cfe_hostlist
echo "Set HOSTNAME appropriately beforehand"
echo "$HOSTNAME" >> /tmp/cfe_hostlist
echo "Delta Delta Delta may I help ya help ya help ya"
echo delta1 >> /tmp/cfe_hostlist
echo delta2 >> /tmp/cfe_hostlist
echo delta3 >> /tmp/cfe_hostlist
echo may1.I.help.ya >> /tmp/cfe_hostlist
echo may2.I.help.ya >> /tmp/cfe_hostlist
echo may3.I.help.ya >> /tmp/cfe_hostlist

Run:

body common control
{
      bundlesequence => { "peers" };
}

bundle agent peers
{
  vars:

      "mygroup" slist => peers("/tmp/cfe_hostlist","#.*",4);

      "myleader" string => peerleader("/tmp/cfe_hostlist","#.*",4);

      "all_leaders" slist => peerleaders("/tmp/cfe_hostlist","#.*",4);

  reports:

      # note that the current host name is fourth in the host list, so
      # its peer group is the first 4-host group, minus the host itself.
      "/tmp/cfe_hostlist mypeer $(mygroup)";
      # note that the current host name is fourth in the host list, so
      # the peer leader is "alpha"
      "/tmp/cfe_hostlist myleader $(myleader)";
      "/tmp/cfe_hostlist another leader $(all_leaders)";
}

Output:

R: /tmp/cfe_hostlist mypeer alpha
R: /tmp/cfe_hostlist mypeer beta
R: /tmp/cfe_hostlist mypeer gamma
R: /tmp/cfe_hostlist myleader alpha
R: /tmp/cfe_hostlist another leader alpha
R: /tmp/cfe_hostlist another leader delta1
R: /tmp/cfe_hostlist another leader may2.I.help.ya

sort

Prototype: sort(list, mode)

Return type: slist

Description: Returns list sorted according to mode.

Lexicographical, integer, real, IP, and MAC address sorting is supported currently. The example below will show each sorting mode in action.

Note IPv6 addresses can not use uppercase hexadecimal characters (A-Z) but must use lowercase (a-z) instead.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • mode: one of
    • lex
    • int
    • real
    • IP
    • ip
    • MAC
    • mac

Example:

body common control
{
      bundlesequence => { test };
}

bundle agent test
{
  vars:
      "a" slist => { "b", "c", "a" };
      "b" slist => { "100", "9", "10", "8.23" };
      "c" slist => { };
      "d" slist => { "", "a", "", "b" };
      "e" slist => { "a", "1", "b" };

      "ips" slist => { "100.200.100.0", "1.2.3.4", "9.7.5.1", "9", "9.7", "9.7.5", "", "-1", "where are the IP addresses?" };
      "ipv6" slist => { "FE80:0000:0000:0000:0202:B3FF:FE1E:8329",
                        "FE80::0202:B3FF:FE1E:8329",
                        "::1",
                        # the following should all be parsed as the same address and sorted together
                        "2001:db8:0:0:1:0:0:1",
                        "2001:0db8:0:0:1:0:0:1",
                        "2001:db8::1:0:0:1",
                        "2001:db8::0:1:0:0:1",
                        "2001:0db8::1:0:0:1",
                        "2001:db8:0:0:1::1",
                        "2001:db8:0000:0:1::1",
                        "2001:DB8:0:0:1::1", # note uppercase IPv6 addresses are invalid
                        # examples from https://www.ripe.net/lir-services/new-lir/ipv6_reference_card.pdf
                        "8000:63bf:3fff:fdd2",
                        "::ffff:192.0.2.47",
                        "fdf8:f53b:82e4::53",
                        "fe80::200:5aee:feaa:20a2",
                        "2001:0000:4136:e378:",
                        "8000:63bf:3fff:fdd2",
                        "2001:0002:6c::430",
                        "2001:10:240:ab::a",
                        "2002:cb0a:3cdd:1::1",
                        "2001:db8:8:4::2",
                        "ff01:0:0:0:0:0:0:2",
                        "-1", "where are the IP addresses?" };

      "macs" slist => { "00:14:BF:F7:23:1D", "0:14:BF:F7:23:1D", ":14:BF:F7:23:1D", "00:014:BF:0F7:23:01D",
                        "00:14:BF:F7:23:1D", "0:14:BF:F7:23:1D", ":14:BF:F7:23:1D", "00:014:BF:0F7:23:01D",
                        "01:14:BF:F7:23:1D", "1:14:BF:F7:23:1D",
                        "01:14:BF:F7:23:2D", "1:14:BF:F7:23:2D",
                        "-1", "where are the MAC addresses?" };

      "ja" string => join(",", "a");
      "jb" string => join(",", "b");
      "jc" string => join(",", "c");
      "jd" string => join(",", "d");
      "je" string => join(",", "e");

      "jips" string => join(",", "ips");
      "jipv6" string => join(",", "ipv6");
      "jmacs" string => join(",", "macs");

      "sa" slist => sort("a", "lex");
      "sb" slist => sort("b", "lex");
      "sc" slist => sort("c", "lex");
      "sd" slist => sort("d", "lex");
      "se" slist => sort("e", "lex");

      "sb_int" slist => sort("b", "int");
      "sb_real" slist => sort("b", "real");

      "sips" slist => sort("ips", "ip");
      "sipv6" slist => sort("ipv6", "ip");
      "smacs" slist => sort("macs", "mac");


      "jsa" string => join(",", "sa");
      "jsb" string => join(",", "sb");
      "jsc" string => join(",", "sc");
      "jsd" string => join(",", "sd");
      "jse" string => join(",", "se");

      "jsb_int" string => join(",", "sb_int");
      "jsb_real" string => join(",", "sb_real");

      "jsips" string => join(",", "sips");
      "jsipv6" string => join(",", "sipv6");
      "jsmacs" string => join(",", "smacs");

  reports:
      "sorted lexicographically '$(ja)' => '$(jsa)'";
      "sorted lexicographically '$(jb)' => '$(jsb)'";
      "sorted lexicographically '$(jc)' => '$(jsc)'";
      "sorted lexicographically '$(jd)' => '$(jsd)'";
      "sorted lexicographically '$(je)' => '$(jse)'";

      "sorted integers '$(jb)' => '$(jsb_int)'";
      "sorted reals '$(jb)' => '$(jsb_real)'";

      "sorted IPs '$(jips)' => '$(jsips)'";
      "sorted IPv6s '$(jipv6)' => '$(jsipv6)'";
      "sorted MACs '$(jmacs)' => '$(jsmacs)'";
}

Output:

2013-09-05T14:05:04-0400   notice: R: sorted lexicographically 'b,c,a' => 'a,b,c'
2013-09-05T14:05:04-0400   notice: R: sorted lexicographically '100,9,10,8.23' => '10,100,8.23,9'
2013-09-05T14:05:04-0400   notice: R: sorted lexicographically '' => ''
2013-09-05T14:05:04-0400   notice: R: sorted lexicographically ',a,,b' => ',,a,b'
2013-09-05T14:05:04-0400   notice: R: sorted lexicographically 'a,1,b' => '1,a,b'
2013-09-05T14:05:04-0400   notice: R: sorted integers '100,9,10,8.23' => '8.23,9,10,100'
2013-09-05T14:05:04-0400   notice: R: sorted reals '100,9,10,8.23' => '8.23,9,10,100'
2013-09-05T14:05:04-0400   notice: R: sorted IPs '100.200.100.0,1.2.3.4,9.7.5.1,9,9.7,9.7.5,,-1,where are the IP addresses?' => ',-1,9,9.7,9.7.5,where are the IP addresses?,1.2.3.4,9.7.5.1,100.200.100.0'
2013-09-05T14:05:04-0400   notice: R: sorted IPv6s 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329,FE80::0202:B3FF:FE1E:8329,::1,2001:db8:0:0:1:0:0:1,2001:0db8:0:0:1:0:0:1,2001:db8::1:0:0:1,2001:db8::0:1:0:0:1,2001:0db8::1:0:0:1,2001:db8:0:0:1::1,2001:db8:0000:0:1::1,2001:DB8:0:0:1::1,8000:63bf:3fff:fdd2,::ffff:192.0.2.47,fdf8:f53b:82e4::53,fe80::200:5aee:feaa:20a2,2001:0000:4136:e378:,8000:63bf:3fff:fdd2,2001:0002:6c::430,2001:10:240:ab::a,2002:cb0a:3cdd:1::1,2001:db8:8:4::2,ff01:0:0:0:0:0:0:2,-1,where are the IP addresses?' => '-1,2001:0000:4136:e378:,2001:DB8:0:0:1::1,8000:63bf:3fff:fdd2,8000:63bf:3fff:fdd2,::ffff:192.0.2.47,FE80:0000:0000:0000:0202:B3FF:FE1E:8329,FE80::0202:B3FF:FE1E:8329,where are the IP addresses?,::1,2001:0002:6c::430,2001:10:240:ab::a,2001:db8:0000:0:1::1,2001:db8:0:0:1::1,2001:0db8::1:0:0:1,2001:db8::0:1:0:0:1,2001:db8::1:0:0:1,2001:0db8:0:0:1:0:0:1,2001:db8:0:0:1:0:0:1,2001:db8:8:4::2,2002:cb0a:3cdd:1::1,fdf8:f53b:82e4::53,fe80::200:5aee:feaa:20a2,ff01:0:0:0:0:0:0:2'
2013-09-05T14:05:04-0400   notice: R: sorted MACs '00:14:BF:F7:23:1D,0:14:BF:F7:23:1D,:14:BF:F7:23:1D,00:014:BF:0F7:23:01D,00:14:BF:F7:23:1D,0:14:BF:F7:23:1D,:14:BF:F7:23:1D,00:014:BF:0F7:23:01D,01:14:BF:F7:23:1D,1:14:BF:F7:23:1D,01:14:BF:F7:23:2D,1:14:BF:F7:23:2D,-1,where are the MAC addresses?' => '-1,:14:BF:F7:23:1D,:14:BF:F7:23:1D,where are the MAC addresses?,00:014:BF:0F7:23:01D,0:14:BF:F7:23:1D,00:14:BF:F7:23:1D,00:014:BF:0F7:23:01D,0:14:BF:F7:23:1D,00:14:BF:F7:23:1D,1:14:BF:F7:23:1D,01:14:BF:F7:23:1D,1:14:BF:F7:23:2D,01:14:BF:F7:23:2D'

See also: shuffle().


concat

Prototype: concat(...)

Return type: string

Description: Concatenates all arguments into a string.

Example:

    commands:
      "/usr/bin/generate_config $(config)"
        ifvarclass => concat("have_config_", canonify("$(config)"));

History: Was introduced in 3.2.0, Nova 2.1.0 (2011)


ago

Prototype: ago(years, months, days, hours, minutes, seconds)

Return type: int

Description: Convert a time relative to now to an integer system representation.

The ago function measures time relative to now. Arguments are applied in order, so that ago(0,18,55,27,0,0) means "18 months, 55 days, and 27 hours ago". However, you are strongly encouraged to keep your usage of ago sensible and readable, e.g., ago(0,0,120,0,0,0) or ago(0,0,0,72,0,0).

Arguments:

  • years, in the range 0,1000

Years of run time. For convenience in conversion, a year of runtime is always 365 days (one year equals 31,536,000 seconds).

  • month, in the range 0,1000

Months of run time. For convenience in conversion, a month of runtime is always equal to 30 days of runtime (one month equals 2,592,000 seconds).

  • days, in the range 0,1000

Days of runtime (one day equals 86,400 seconds)

  • hours, in the range 0,1000

Hours of runtime

  • minutes, in the range 0,1000

Minutes of runtime 0-59

  • seconds, in the range 0,40000

Seconds of runtime

Example:

body common control
{
      bundlesequence => { "testbundle" };
}

bundle agent testbundle
{
  processes:

      ".*"

      process_count   => anyprocs,
      process_select  => proc_finder;

  reports:

    any_procs::

      "Found processes out of range";
}


body process_select proc_finder

{
      # Processes started between 100 years + 5.5 hours and 20 minutes ago
      stime_range => irange(ago(100,0,0,5,30,0),ago(0,0,0,0,20,0));
      process_result => "stime";
}

body process_count anyprocs

{
      match_range => "0,0";
      out_of_range_define => { "any_procs" };
}

Output:

R: Found processes out of range

some

Prototype: some(regex, list)

Return type: boolean

Description: Return whether any element of list matches the Unanchored regular expression regex.

list can be a data container or a regular list.

Arguments:

  • regex: regular expression, in the range: .*
  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

some() will return as soon as any element matches.

It's convenient to set a class to not => some(".*", mylist) in order to check if mylist is empty. Since some() returns as soon as possible, that is better than using length() or every() or none() which all must traverse the entire list.

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  classes:

      # This is an easy way to check if a list is empty, better than
      # expression => strcmp(length(x), "0")

      # Note that if you use length() or none() or every() they will
      # go through all the elements!!! some() returns as soon as any
      # element matches.
      "empty_x" not => some(".*", x);
      "empty_y" not => some(".*", y);

      "some11" expression => some("long string", test1);
      "some12" expression => some("none", test1);
      "some21" expression => some("long string", test2);
      "some22" expression => some("none", test2);

  vars:
      "x" slist => { "a", "b" };
      "y" slist => { };

      "test1" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",
      };


      "test2" data => parsejson('[1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",]');

  reports:
    empty_x::
      "x has no elements";
    empty_y::
      "y has no elements";

    any::
      "The test1 list is $(test1)";
    some11::
      "some() test1 1 passed";
    !some11::
      "some() test1 1 failed";
    some12::
      "some() test1 2 failed";
    !some12::
      "some() test1 2 passed";

      "The test2 list is $(test2)";
    some21::
      "some() test2 1 passed";
    !some21::
      "some() test2 1 failed";
    some22::
      "some() test2 2 failed";
    !some22::
      "some() test2 2 passed";

}

Output:

R: y has no elements
R: The test1 list is 1
R: The test1 list is 2
R: The test1 list is 3
R: The test1 list is one
R: The test1 list is two
R: The test1 list is three
R: The test1 list is long string
R: The test1 list is four
R: The test1 list is fix
R: The test1 list is six
R: some() test1 1 passed
R: some() test1 2 passed
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: some() test2 1 passed
R: some() test2 2 passed

See also: filter(), every(), and none().


"on"

Prototype: on(year, month, day, hour, minute, second)

Return type: int

Description: Returns the specified date/time in integer system representation.

The specified date/time is an absolute date in the local timezone.

Arguments:

  • year: int, in the range: 1970,3000
  • month: int, in the range: 0,1000
  • day: int, in the range: 0,1000
  • hour: int, in the range: 0,1000
  • minute: int, in the range: 0,1000
  • second: int, in the range: 0,1000

Example:

    body file_select zero_age
    {
      mtime       => irange(on(2000,1,1,0,0,0),now);
      file_result => "mtime";
    }

Notes: In process matching, dates could be wrong by an hour depending on Daylight Savings Time / Summer Time. This is a known bug to be fixed.


filestat

Prototype: filestat(filename, field)

Return type: string

Description: Returns the requested file field field for the file object filename.

If the file object does not exist, the function call fails and the variable does not expand.

Arguments:

  • filename : the file or directory name to inspect, in the range: "?(/.*)
  • field : the requested field, with the following allowed values:
    • size : size in bytes
    • gid : group ID
    • uid : owner ID
    • ino : inode number
    • nlink : number of hard links
    • ctime : creation time in Unix epoch format
    • atime : last access time in Unix epoch format
    • mtime : last modification time in Unix epoch format
    • mode : file mode as a decimal number
    • modeoct : file mode as an octal number, e.g. 10777
    • permstr : permission string, e.g. -rwx---rwx (not available on Windows)
    • permoct : permissions as an octal number, e.g. 644 (not available on Windows)
    • type : file type (not available on Windows): block device,character device, directory, FIFO/pipe, symlink, regular file, socket, or unknown
    • devno : device number (drive letter on Windows, e.g. C:)
    • dev_minor : minor device number (not available on Windows)
    • dev_major : major device number (not available on Windows)
    • basename : the file name minus the directory
    • dirname : the directory portion of the file name
    • linktarget : if the file is a symlink, its final target. The target is chased up to 32 levels of recursion. On Windows, this returns the file name itself.
    • linktarget_shallow : if the file is a symlink, its first target. On Windows, this returns the file name itself.

Example:

Prepare:

echo 1234567890 > FILE.txt
chmod 0755 FILE.txt
chown 0 FILE.txt
chgrp 0 FILE.txt

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "file" string => "$(this.promise_filename).txt";
  methods:
      "fileinfo" usebundle => fileinfo("$(file)");
}
bundle agent fileinfo(f)
{
  vars:
      # use the full list if you want to see all the attributes!
      # "fields" slist => splitstring("size,gid,uid,ino,nlink,ctime,atime,mtime,mode,modeoct,permstr,permoct,type,devno,dev_minor,dev_major,basename,dirname,linktarget,linktarget_shallow", ",", 999);

      # ino (inode number), ctime (creation time),
      # devno/dev_minor/dev_major (device numbers) were omitted but
      # they are all integers

      "fields" slist => splitstring("size,gid,uid,nlink,mode,modeoct,permstr,permoct,type,basename", ",", 999);

      "stat[$(f)][$(fields)]" string => filestat($(f), $(fields));

  reports:
      "$(this.bundle): file $(stat[$(f)][basename]) has $(fields) = $(stat[$(f)][$(fields)])";
}

Output:

R: fileinfo: file filestat.cf.txt has size = 11
R: fileinfo: file filestat.cf.txt has gid = 0
R: fileinfo: file filestat.cf.txt has uid = 0
R: fileinfo: file filestat.cf.txt has nlink = 1
R: fileinfo: file filestat.cf.txt has mode = 33261
R: fileinfo: file filestat.cf.txt has modeoct = 100755
R: fileinfo: file filestat.cf.txt has permstr = -rwxr-xr-x
R: fileinfo: file filestat.cf.txt has permoct = 755
R: fileinfo: file filestat.cf.txt has type = regular file
R: fileinfo: file filestat.cf.txt has basename = filestat.cf.txt

Notes:

The list of fields may be extended as needed by CFEngine.

History: Was introduced in version 3.5.0,Enterprise 3.1 (2013). linktarget and linktarget_shallow were added in version 3.6.

See also: lastnode(), dirname(), splitstring().


execresult

Prototype: execresult(command, shell)

Return type: string

The return value is cached.

Description: Execute command and return output as string.

If the command is not found, the result will be the empty string.

The shell argument decides whether a shell will be used to encapsulate the command. This is necessary in order to combine commands with pipes etc, but remember that each command requires a new process that reads in files beyond CFEngine's control. Thus using a shell is both a performance hog and a potential security issue.

Arguments:

  • command: string, in the range: .+
  • shell: one of
    • noshell
    • useshell
    • powershell

Example:

Prepare:

rm -rf /tmp/testhere
mkdir -p /tmp/testhere
touch /tmp/testhere/a
touch /tmp/testhere/b
touch /tmp/testhere/c
touch /tmp/testhere/d
touch /tmp/testhere/e

Run:

body common control
{
      bundlesequence  => { "example" };
}

bundle agent example
{
  vars:
      "my_result" string => execresult("/bin/ls /tmp/testhere","noshell");

  reports:
      "/bin/ls /tmp/testhere returned '$(my_result)'";
}

Output:

R: /bin/ls /tmp/testhere returned 'a
b
c
d
e'

Notes: you should never use this function to execute commands that make changes to the system, or perform lengthy computations. Such an operation is beyond CFEngine's ability to guarantee convergence, and on multiple passes and during syntax verification these function calls are executed, resulting in system changes that are covert. Calls to execresult should be for discovery and information extraction only. Effectively calls to this function will be also repeatedly executed by cf-promises when it does syntax checking, which is highly undesirable if the command is expensive. Consider using commands promises instead, which have locking and are not evaluated by cf-promises.

Limitations: Only 8192 bytes (8K) of data can be returned from execresult. Lines that exceed 8192 bytes are truncated.

See also: returnszero().

Change: policy change in CFEngine 3.0.5. Previously newlines were changed for spaces, now newlines are preserved.


remoteclassesmatching

This function is only available in CFEngine Enterprise.

Prototype: remoteclassesmatching(regex, server, encrypt, prefix)

Return type: boolean

Description: Reads persistent classes matching regular expression regex from a remote CFEngine server server and adds them into local context with prefix prefix.

The return value is true (sets the class) if communication with the server was successful and classes were populated in the current bundle.

This function contacts a remote cf-serverd and requests access to defined persistent classes on that system. Access must be granted by making an access promise with resource_type set to context.

Arguments:

  • regex: regular expression, in the range: .*
  • server: string, in the range: .*
  • encrypt: one of
    • true
    • false
    • yes
    • no
    • on
    • off
  • prefix: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

   "succeeded" expression => remoteclassesmatching("regex","server","yes","myprefix");

Notes: Note that this function assumes that you have already performed a successful key exchange between systems, (e.g. using either a remote copy or cf-runagent connection). It contains no mechanism for trust establishment and will fail if there is no trust relationship pre-established.

See also: hubknowledge(), remotescalar(), hostswithclass()


filesize

Prototype: filesize(filename)

Return type: int

Description: Returns the size of the file filename in bytes.

If the file object does not exist, the function call fails and the variable does not expand.

Arguments:

  • filename: string, in the range: "?(/.*)

Example:

Run:

body common control
{
      bundlesequence => { example };
}

bundle agent example
{
  vars:
      # my own size!
      "exists" int => filesize("$(this.promise_filename)");
      "nexists" int => filesize("/etc/passwdx");

  reports:
      "File size $(exists)";
      "Does not exist: $(nexists)";
}

Output:

R: File size 301
R: Does not exist: $(nexists)

History: Was introduced in version 3.1.3, Nova 2.0.2 (2010).


splayclass

Prototype: splayclass(input, policy)

Return type: boolean

Description: Returns whether input's time-slot has arrived, according to a policy.

The function returns true if the system clock lies within a scheduled time-interval that maps to a hash of input (which may be any arbitrary string). Different strings will hash to different time intervals, and thus one can map different tasks to time-intervals.

This function may be used to distribute a task, typically on multiple hosts, in time over a day or an hourly period, depending on the policy (that must be either daily or hourly). This is useful for copying resources to multiple hosts from a single server, (e.g. large software updates), when simultaneous scheduling would lead to a bottleneck and/or server overload.

The function is similar to the splaytime feature in cf-execd, except that it allows you to base the decision on any string-criterion on a given host.

Arguments:

  • input: string, in the range: .*
  • policy: one of
    • daily
    • hourly

The variation in input determines how effectively CFEngine will be able to distribute tasks. CFEngine instances with the same input will yield a true result at the same time, and different input will yield a true result at different times. Thus tasks could be scheduled according to group names for predictability, or according to IP addresses for distribution across the policy interval.

The times at which the splayclass will be defined depends on the policy. If it is hourly then the class will be defined for a 5-minute interval every hour. If the policy daily, then the class will be defined for one 5-minute interval every day. This means that splayclass assumes that you are running CFEngine with the default schedule of "every 5 minutes". If you change the executor schedule control variable, you may prevent the splayclass from ever being defined (that is, if the hashed 5-minute interval that is selected by the splayclass is a time when you have told CFEngine not to run).

Example:

    bundle agent example
    {
    classes:

      "my_turn" expression => splayclass("$(sys.host)$(sys.ipv4)","daily");

    reports:

      my_turn::

        "Load balanced class activated";
    }

ip2host

Prototype: ip2host(ip)

Return type: string

The return value is cached.

Description: Returns the primary name-service host name for the IP address ip.

Uses whatever configured name service is used by the resolver library to translate an IP address to a hostname. IPv6 addresses will also resolve, if supported by the resolver library.

Note that DNS lookups may take time and thus cause CFEngine agents to wait for responses, slowing their progress significantly.

Arguments:

  • ip: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "reverse_lookup" };
}

bundle agent reverse_lookup
{
  vars:
      "local4" string => ip2host("127.0.0.1");

      # this will be localhost on some systems, ip6-localhost on others...
      "local6" string => ip2host("::1");

  reports:
    _cfe_output_testing::
      "we got local4" ifvarclass => isvariable("local4");

    !_cfe_output_testing::
      "local4 is $(local4)";
      "local6 is $(local6)";
}

Output:

R: we got local4

History: Was introduced in version 3.1.3, Nova 2.0.2 (2010)


storejson

Prototype: storejson(data_container)

Return type: string

Description: Converts a data container to a JSON string.

Arguments:

  • data_container: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

   vars:

      "loadthis"
         data =>  readjson("/tmp/data.json", 4000);
      "andback"
         string =>  storejson(loadthis);
  reports:
      "Converted /tmp/data.json to '$(andback)'";

See also: readjson() and data documentation.


unique

Prototype: unique(list)

Return type: slist

Description: Returns list of unique elements from list.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",
      };

      "test_str" string => join(",", "test");
      "test_unique" slist => unique("test");
      "unique_str" string => join(",", "test_unique");

  reports:
      "The test list is $(test_str)";
      "The unique elements of the test list: $(unique_str)";
}

Output:

R: The test list is 1,2,3,one,two,three,long string,four,fix,six,one,two,three
R: The unique elements of the test list: 1,2,3,one,two,three,long string,four,fix,six

getvalues

Prototype: getvalues(varref)

Return type: slist

Description: Returns the list of values in varref which can be the name of an array or container.

If the array contains list values, then all of the list elements are flattened into a single list to make the return value a list.

If the data container contains non-scalar values (e.g. nested containers) they are skipped. The special values true, false, and null are serialized to their string representations. Numerical values are serialized to their string representations.

You can specify a path inside the container. For example, below you'll look at the values of d[k], not at the top level of d.

Make sure you specify the correct scope when supplying the name of the variable.

Arguments:

  • varref: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "v[index_1]" string => "value_1";
      "v[index_2]" string => "value_2";

      "values" slist => getvalues("v");

      # works with data containers too
      "d" data => parsejson('{ "k": [ 1, 2, 3, "a", "b", "c" ] }');
      "cvalues" slist => getvalues("d[k]");

  reports:
      "Found values: $(values)";
      "Found container values: $(cvalues)";

}

Output:

R: Found values: value_1
R: Found values: value_2
R: Found container values: 1
R: Found container values: 2
R: Found container values: 3
R: Found container values: a
R: Found container values: b
R: Found container values: c

Notes:


intersection

Prototype: intersection(list1, list2)

Return type: slist

Description: Returns the unique elements in list1 that are also in list2.

Arguments:

  • list1: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • list2: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "a" slist => { 1,2,3,"x" };
      "b" slist => { "x" };

      "mylist1" slist => { "a", "b" };
      "mylist2" slist => { "a", "b" };
      "$(mylist1)_str" string => join(",", $(mylist1));

      "int_$(mylist1)_$(mylist2)" slist => intersection($(mylist1), $(mylist2));
      "int_$(mylist1)_$(mylist2)_str" string => join(",", "int_$(mylist1)_$(mylist2)");

  reports:
      "The intersection of list '$($(mylist1)_str)' with '$($(mylist2)_str)' is '$(int_$(mylist1)_$(mylist2)_str)'";
}

Output:

R: The intersection of list '1,2,3,x' with '1,2,3,x' is '1,2,3,x'
R: The intersection of list '1,2,3,x' with 'x' is 'x'
R: The intersection of list 'x' with '1,2,3,x' is 'x'
R: The intersection of list 'x' with 'x' is 'x'

See also: difference().


regarray

Prototype: regarray(array, regex)

Return type: boolean

Description: Returns whether array contains elements matching the anchoredregular expression regex.

Arguments:

  • array: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • regex: regular expression, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "myarray[0]" string => "bla1";
      "myarray[1]" string => "bla2";
      "myarray[3]" string => "bla";

  classes:

      "ok" expression => regarray("myarray","b.*2");

  reports:

    ok::

      "Found in list";

    !ok::

      "Not found in list";

}

Output:

R: Found in list

dirname

Prototype: dirname(path)

Return type: string

Description: Return the parent directory name for given path.

This function returns the directory name for path. If path is a directory, then the name of its parent directory is returned.

Arguments:

  • path: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "apache_dir" string => dirname("/etc/apache2/httpd.conf");
  reports:
      "apache conf dir = $(apache_dir)";
}

Output:

R: apache conf dir = /etc/apache2

Notes:

History: Was introduced in 3.3.0, Nova 2.2.0 (2011)

See also: lastnode(), filestat(), splitstring().


string_tail

Prototype: string_tail(data, max)

Return type: string

Description: Returns the last max bytes of data.

Arguments:

  • data: string, in the range: .*
  • max: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "end" string => string_tail("abc", "1"); # will contain "c"
  reports:
      "end of abc = $(end)";

}

Output:

R: end of abc = c

History: Introduced in CFEngine 3.6

See also: string_head(), string_length(), string_reverse().


countlinesmatching

Prototype: countlinesmatching(regex, filename)

Return type: int

Description: Count the number of lines in file filename matching regex.

This function matches lines in the named file, using an anchored regular expression that should match the whole line, and returns the number of lines matched.

Arguments:

  • regex: regular expression, in the range: .*
  • filename: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      # typically there is only one root user
      "no" int => countlinesmatching("root:.*","/etc/passwd");

  reports:
      "Found $(no) lines matching";
}

Output:

R: Found 1 lines matching

returnszero

Prototype: returnszero(command, shell)

Return type: boolean

The return value is cached.

Description: Runs command and returns whether it has returned with exit status zero.

This is the complement of execresult(), but it returns a class result rather than the output of the command.

Arguments:

  • command: string, in the range: .+
  • shell: one of
    • noshell
    • useshell
    • powershell

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "my_result" expression => returnszero("/usr/local/bin/mycommand","noshell");

  reports:

    !my_result::

      "Command failed";

}

Output:

2014-02-10T17:43:55-0500    error: /default/example/classes: Proposed executable file '/usr/local/bin/mycommand' doesn't exist
2014-02-10T17:43:55-0500    error: /default/example/classes: returnszero '/usr/local/bin/mycommand' is assumed to be executable but isn't
2014-02-10T17:43:55-0500    error: /default/example/classes: Proposed executable file '/usr/local/bin/mycommand' doesn't exist
2014-02-10T17:43:55-0500    error: /default/example/classes: returnszero '/usr/local/bin/mycommand' is assumed to be executable but isn't
R: Command failed

Notes: you should never use this function to execute commands that make changes to the system, or perform lengthy computations. Such an operation is beyond CFEngine's ability to guarantee convergence, and on multiple passes and during syntax verification these function calls are executed, resulting in system changes that are covert. Calls to execresult should be for discovery and information extraction only. Effectively calls to this function will be also repeatedly executed by cf-promises when it does syntax checking, which is highly undesirable if the command is expensive. Consider using commands promises instead, which have locking and are not evaluated by cf-promises.

See also: execresult().


ldaparray

This function is only available in CFEngine Enterprise.

Prototype: ldaparray(array, uri, dn, filter, scope, security)

Return type: boolean

Description: Fills array with the entire LDAP record, and returns whether there was a match for the search.

This function retrieves an entire record with all elements and populates an associative array with the entries. It returns a class that is true if there was a match for the search, and false if nothing was retrieved.

Arguments:

  • array: string, in the range: .*
  • uri: string, in the range: .*
  • dn: string, in the range: .*
  • filter: string, in the range: .*
  • scope: one of
    • subtree
    • onelevel
    • base
  • security: one of
    • none
    • ssl
    • sasl

dn specifies the distinguished name, an ldap formatted name built from components, e.g. "dc=cfengine,dc=com". filter is an ldap search, e.g. "(sn=User)". Which security values are supported depends on machine and server capabilities.

Example:

classes:

   "gotdata" expression => ldaparray(
                                    "myarray",
                                    "ldap://ldap.example.org",
                                    "dc=cfengine,dc=com",
                                    "(uid=mark)",
                                    "subtree",
                                    "none");

ldapvalue

This function is only available in CFEngine Enterprise.

Prototype: ldapvalue(uri, dn, filter, record, scope, security)

Return type: string

The return value is cached.

Description: Returns the first matching named value from ldap.

This function retrieves a single field from a single LDAP record identified by the search parameters. The first matching value it taken.

Arguments:

  • uri: string, in the range: .*
  • dn: string, in the range: .*
  • filter: string, in the range: .*
  • record: string, in the range: .*
  • scope: one of
    • subtree
    • onelevel
    • base
  • security: one of
    • none
    • ssl
    • sasl

dn specifies the distinguished name, an ldap formatted name built from components, e.g. "dc=cfengine,dc=com". filter is an ldap search, e.g. "(sn=User)", and record is the name of the single record to be retrieved, e.g. uid. Which security values are supported depends on machine and server capabilities.

Example:

vars:

   # Get the first matching value for "uid" in schema

  "value" string => ldapvalue(
                             "ldap://ldap.example.org",
                             "dc=cfengine,dc=com",
                             "(sn=User)",
                             "uid",
                             "subtree",
                             "none"
                             );

isplain

Prototype: isplain(filename)

Return type: boolean

Description: Returns whether the named object filename is a plain/regular file.

Arguments:

  • filename: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "fileisplain" expression => isplain("/etc/passwd");
      "dirisnotplain" not => isplain("/");

  reports:

    fileisplain::
      "/etc/passwd is plain..";

    dirisnotplain::
      "/ is not plain..";

}

Output:

R: /etc/passwd is plain..
R: / is not plain..

bundlesmatching

Prototype: bundlesmatching(name, tag1, tag2, ...)

Return type: slist

Description: Return the list of defined bundles matching name and any tags given. Both bundlename and tags are regular expressions. name is required, tags are optional.

This function searches for the given unanchored name and tag1,tag2`,... regular expression in the list of currently defined bundles.

Every bundle is prefixed with the namespace, usually default:.

When any tags are given, only the bundles with those tags are returned. Bundle tags are set a tags variable within a meta promise; see the example below.

This function, used together with the findfiles function, allows you to do dynamic inputs and a dynamic bundle call chain. The dynamic chain is constrained by an explicit regular expression to avoid accidental or intentional running of unwanted bundles.

Arguments:

  • name: string, in the range: .*

Example:

body common control
{
      bundlesequence => { mefirst };
}

bundle common g
{
  vars:
      "todo" slist => bundlesmatching("default:run.*");
}

bundle agent mefirst
{
  methods:
      # note this is a dynamic bundle sequence!
      "" usebundle => $(g.todo);
}

bundle agent run_deprecated
{
  meta:
      "tags" slist => { "deprecated" };
}

bundle agent run_123_456
{
  vars:
      "bundles" slist => bundlesmatching(".*");
      "deprecated_bundles" slist => bundlesmatching(".*", "deprecated");
      "no_bundles" slist => bundlesmatching("891");
  reports:
      "bundles = $(bundles)";
      "deprecated bundles = $(deprecated_bundles)";
      "no bundles = $(no_bundles)";
}

Output:

R: bundles = default:run_123_456
R: bundles = default:run_deprecated
R: bundles = default:mefirst
R: bundles = default:g
R: deprecated bundles = default:run_deprecated

See also: findfiles().


sum

Prototype: sum(list)

Return type: real

Description: Return the sum of the reals in list.

This function might be used for simple ring computation. Of course, you could easily combine sum with readstringarray or readreallist etc., to collect summary information from a source external to CFEngine.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "adds_to_six" ilist => { "1", "2", "3" };
      "six" real => sum("adds_to_six");
      "adds_to_zero" rlist => { "1.0", "2", "-3e0" };
      "zero" real => sum("adds_to_zero");

  reports:
      "six is $(six), zero is $(zero)";
}

Output:

R: six is 6.000000, zero is 0.000000

Because $(six) and $(zero) are both real numbers, the report that is generated will be:

six is 6.000000, zero is 0.000000

Notes:

History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010)


format

Prototype: format(string, ...)

Return type: string

Description: Applies sprintf-style formatting to a given string.

This function will format numbers (o, x, d and f) or strings (s) but not potentially dangerous things like individual characters or pointer offsets.

The %S specifier is special and non-standard. When you use it on a slist or a data container, the data will be packed into a one-line string you can put in a log message, for instance.

This function will fail if it doesn't have enough arguments; if any format specifier contains the modifiers hLqjzt; or if any format specifier is not one of doxfsS.

Example:

body common control
{
      bundlesequence => { "run" };
}

bundle agent run
{
  vars:
      "v" string => "2.5.6";
      "vlist" slist => splitstring($(v), "\.", 3);
      "padded" string => format("%04d%04d%04d", nth("vlist", 0), nth("vlist", 1), nth("vlist", 2));
      "a" string => format("%10.10s", "x");
      "b" string => format("%-10.10s", "x");
      "c" string => format("%04d", 1);
      "d" string => format("%07.2f", 1);
      "e" string => format("hello my name is %s %s", "Inigo", "Montoya");

      "container" data => parsejson('{ "x": "y", "z": true }');

      "packed" string => format("slist = %S, container = %S", vlist, container);

  reports:
      "version $(v) => padded $(padded)";
      "%10.10s on 'x' => '$(a)'";
      "%-10.10s on 'x' => '$(b)'";
      "%04d on '1' => '$(c)'";
      "%07.2f on '1' => '$(d)'";
      "you killed my father... => '$(e)'";
      "$(packed)";
}

Output:

R: version 2.5.6 => padded 000200050006
R: %10.10s on 'x' => '         x'
R: %-10.10s on 'x' => 'x         '
R: %04d on '1' => '0001'
R: %07.2f on '1' => '0001.00'
R: you killed my father... => 'hello my name is Inigo Montoya'
R: slist = { "2", "5", "6" }, container = {"x":"y","z":true}

Note: the underlying sprintf system call may behave differently on some platforms for some formats. Test carefully. For example, the format %08s will use spaces to fill the string up to 8 characters on libc platforms, but on Darwin (Mac OS X) it will use zeroes. According to SUSv4 the behavior is undefined for this specific case.


regcmp

Prototype: regcmp(regex, string)

Return type: boolean

Description: Returns whether the anchored regular expression regex matches the string.

Arguments:

  • regex: regular expression, in the range: .*
  • string: string, in the range: .*

Example:

body common control
{
      bundlesequence => { subtest("mark") };
}

bundle agent subtest(user)
{
  classes:

      "invalid" not => regcmp("[a-z]{4}","$(user)");

  reports:

    !invalid::

      "User name $(user) is valid at exactly 4 letters";

    invalid::

      "User name $(user) is invalid";
}

Output:

R: User name mark is valid at exactly 4 letters

If the string contains multiple lines, then it is necessary to code these explicitly, as regular expressions do not normally match the end of line as a regular character (they only match end of string). You can do this using either standard regular expression syntax or using the additional features of PCRE (where (?ms) changes the way that ., ^ and $ behave), e.g.


classmatch

Prototype: classmatch(regex)

Return type: boolean

Description: Tests whether regex matches any currently set class.

Returns true if the anchored regular expression matches any currently defined class, otherwise returns false.

Arguments:

  • text: string, in the range: .*

Example:

body common control
{
      bundlesequence  => { "example" };
}

bundle agent example
{
  classes:

      "do_it" and => { classmatch("cfengine_3.*"), "any" };

  reports:

    do_it::

      "Host matches pattern";
}

Output:

R: Host matches pattern

data_readstringarrayidx

Prototype: data_readstringarrayidx(filename, comment, split, maxentries, maxbytes)

Return type: data

Description: Returns a data container (array) with up to maxentries fields from the first maxbytes bytes of file filename.

One dimension is separated by the regex split, the other by the lines in the file. The array arguments are both integer indexes, allowing for non-identifiers at first field (e.g. duplicates or names with spaces), unlike data_readstringarray().

The comment field will strip out unwanted patterns from the file being read, leaving unstripped characters to be split into fields. Using the empty string ("") indicates no comments.

Arguments:

  • filename: string, in the range: "?(/.*)
  • comment: string, in the range: .*
  • split: string, in the range: .*
  • maxentries: int, in the range: 0,99999999999
  • maxbytes: int, in the range: 0,99999999999

Example:

Prepare:

echo a,b,c > /tmp/cfe_array
echo "# This is a comment" >> /tmp/cfe_array
echo d,e,f >> /tmp/cfe_array
echo g,h,i >> /tmp/cfe_array
echo "# This is another comment" >> /tmp/cfe_array
echo j,k,l >> /tmp/cfe_array

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      # The comment regex warrents an explination:
      # # matches the character # literally
      # [^\n]* match a single character not including the newline character
      # between zero and unlimited times, as many times as possible
      "bykey" data => data_readstringarray("/tmp/cfe_array","#[^\n]*",",",10,400);
      "byint" data => data_readstringarrayidx("/tmp/cfe_array","#[^\n]*",",",10,400);

      "bykey_str" string => format("%S", bykey);
      "byint_str" string => format("%S", byint);
  reports:
      "By key: $(bykey_str)";
      "specific element by key a, offset 0: '$(bykey[a][0])'";
      "By int offset: $(byint_str)";
      "specific element by int offset 2, 0: '$(byint[2][0])'";

}

Output:

R: By key: {"a":["b","c"],"d":["e","f"],"g":["h","i"],"j":["k","l"]}
R: specific element by key a, offset 0: 'b'
R: By int offset: [["a","b","c"],["d","e","f"],["g","h","i"],["j","k","l"]]
R: specific element by int offset 2, 0: 'g'

See also: data_readstringarray(), data


classify

Prototype: classify(text)

Return type: boolean

Description: Returns whether the canonicalization of text is a currently set class.

This is useful for transforming variables into classes.

Arguments:

  • text: string, in the range: .*

Example:

    classes:

     "i_am_the_policy_host" expression => classify("master.example.org");

See also: canonify()


isnewerthan

Prototype: isnewerthan(newer, older)

Return type: boolean

Description: Returns whether the file newer is newer (modified later) than the file older.

This function compares the modification time (mtime) of the files, referring to changes of content only.

Arguments:

  • newer: string, in the range: "?(/.*)
  • older: string, in the range: "?(/.*)

Example:

Prepare:

touch -t '200102031234.56' /tmp/earlier
touch -t '200202031234.56' /tmp/later

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "do_it" and => { isnewerthan("/tmp/later","/tmp/earlier"), "cfengine" };

  reports:

    do_it::

      "/tmp/later is older than /tmp/earlier";
}

Output:

R: /tmp/later is older than /tmp/earlier

lsdir

Prototype: lsdir(path, regex, include_base)

Return type: slist

Description: Returns a list of files in the directory path matching the regular expression regex.

If include_base is true, full paths are returned, otherwise only names relative to the directory are returned.

Arguments:

  • path: string, in the range: .+
  • regex: regular expression, in the range: .*
  • include_base: one of
    • true
    • false
    • yes
    • no
    • on
    • off

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "listfiles" slist => lsdir("/etc", "(p.sswd|gr[ou]+p)", "true");
      "sorted_listfiles" slist => sort(listfiles, "lex");

  reports:
      "files in list: $(sorted_listfiles)";
}

Output:

R: files in list: /etc/group
R: files in list: /etc/passwd

Notes:

History: Was introduced in 3.3.0, Nova 2.2.0 (2011)


lastnode

Prototype: lastnode(string, separator)

Return type: string

Description: Returns the part of string after the last separator.

This function returns the final node in a chain, given a regular expression to split on. This is mainly useful for finding leaf-names of files, from a fully qualified path name.

Arguments:

  • string: string, in the range: .*
  • separator: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "yes" };
}

bundle agent yes
{
  vars:

      "path1" string => "/one/two/last1";
      "path2" string => "one:two:last2";
      "path4" string => "/one/two/";

      "last1" string => lastnode("$(path1)","/");
      "last2" string => lastnode("$(path2)",":");

      "last3" string => lastnode("$(path2)","/");
      "last4" string => lastnode("$(path4)","/");

  reports:
      "Last / node in / path '$(path1)' = '$(last1)'";
      "Last : node in : path '$(path2)' = '$(last2)'";
      "Last / node in : path '$(path2)' = '$(last3)'";
      "Last / node in /-terminated path '$(path4)' = '$(last4)'";

}

Output:

R: Last / node in / path '/one/two/last1' = 'last1'
R: Last : node in : path 'one:two:last2' = 'last2'
R: Last / node in : path 'one:two:last2' = 'one:two:last2'
R: Last / node in /-terminated path '/one/two/' = ''

See also: filestat(), dirname(), splitstring().


string_length

Prototype: string_length(data)

Return type: int

Description: Returns the byte length of data.

Arguments:

  • data: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "length" int =>  string_length("abc"); # will contain "3"
  reports:
      "length of string abc = $(length)";
}

Output:

R: length of string abc = 3

History: Introduced in CFEngine 3.6

See also: string_head(), string_tail(), string_reverse().


hubknowledge

This function is only available in CFEngine Enterprise.

Prototype: hubknowledge(id)

Return type: string

The return value is cached.

Description: Read global knowledge from the CFEngine Database host by id.

This function allows for is intended for use in distributed orchestration. If the identifier matches a persistent scalar variable (such as is used to count distributed processes in CFEngine Enterprise) then this will be returned preferentially. If no such variable is found, then the server will look for a literal string in a server bundle with a handle that matches the requested object.

It is recommended that you use this function sparingly, using classes as guards, as it contributes to network traffic and depends on the network for its function. Unlike remotescalar(), the result of hubknowledge() is not stored locally.

This function behaves similarly to the remotescalar() function, except that it always gets its information from the CFEngine Enterprise Database by an encrypted connection. It is designed for spreading globally calibrated information about a CFEngine system back to the client machines. The data available through this channel are generated automatically by discovery, unlike remotescalar which accesses user defined data.

Arguments:

  • id: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

    vars:

      guard::

       "global_number" string => hubknowledge("number_variable");

See also: remotescalar(), remoteclassesmatching(), hostswithclass()


userexists

Prototype: userexists(user)

Return type: boolean

Description: Return whether user name or numerical id exists on this host.

Checks whether the user is in the password database for the current host. The argument must be a user name or user id.

Arguments:

  • user: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "ok" expression => userexists("root");

  reports:

    ok::

      "Root exists";

    !ok::

      "Root does not exist";
}

Output:

R: Root exists

selectservers

Prototype: selectservers(hostlist, port, query, regex, maxbytes, array)

Return type: int

Description: Returns the number of tcp servers from hostlist which respond with a reply matching regex to a query send to port, and populates array with their names.

The regular expression is anchored. If query is empty, then no reply checking is performed (any server reply is deemed to be satisfactory), otherwise at most maxbytes bytes are read from the server and matched.

This function allows discovery of all the TCP ports that are active and functioning from an ordered list, and builds an array of their names. This allows maintaining a list of pretested failover alternatives.

Arguments:

  • hostlist: string, in the range: @[(][a-zA-Z0-9_$(){}\[\].:]+[)]
  • port: string, in the range: .*
  • query: string, in the range: .*
  • regex: regular expression, in the range: .*
  • maxbyes: int, in the range: 0,99999999999
  • array: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

    bundle agent example
    {
    vars:

     "hosts" slist => { "slogans.iu.hio.no", "eternity.iu.hio.no", "nexus.iu.hio.no" };
     "fhosts" slist => { "www.cfengine.com", "www.cfengine.org" };

     "up_servers" int =>  selectservers("@(hosts)","80","","","100","alive_servers");
     "has_favicon" int =>
            selectservers(
                "@(hosts)", "80",
            "GET /favicon.ico HTTP/1.0$(const.n)Host: www.cfengine.com$(const.n)$(const.n)",
            "(?s).*OK.*",
            "200", "favicon_servers");

    classes:

      "someone_alive" expression => isgreaterthan("$(up_servers)","0");

      "has_favicon" expression => isgreaterthan("$(has_favicon)","0");

    reports:
        "Number of active servers $(up_servers)";

      someone_alive::
        "First server $(alive_servers[0]) fails over to $(alive_servers[1])";

      has_favicon::
        "At least $(favicon_servers[0]) has a favicon.ico";

    }

If there is a multi-line response from the server, special care must be taken to ensure that newlines are matched, too. Note the use of (?s) in the example, which allows . to also match newlines in the multi-line HTTP response.


filesexist

Prototype: filesexist(list)

Return type: boolean

Description: Returns whether all the files in list can be accessed.

All files must exist, and the user must have access permissions to them for this function to return true.

Arguments:

  • list: string, in the range: @[(][a-zA-Z0-9_$(){}\[\].:]+[)]

Example:

body common control

{
      bundlesequence  => { "example" };
}

bundle agent example

{
  vars:

      "mylist" slist => { "/tmp/a", "/tmp/b", "/tmp/c" };

  classes:

      "exists" expression => filesexist("@(mylist)");

  reports:

    exists::

      "All files exist";

    !exists::

      "Not all files exist";
}

Output:

R: Not all files exist

nth

Prototype: nth(list_or_container, position_or_key)

Return type: string

Description: Returns the element of list_or_container at zero-based position_or_key.

If an invalid position (below 0 or above the size of the list minus 1) or missing key is requested, this function does not return a valid value.

list_or_container can be an slist or a data container. If it's a slist, the offset is simply the position in the list. If it's a data container, the meaning of the position_or_key depends on its top-level contents: for a list like [1,2,3,4] you will get the list element at position_or_key. For a key-value map like { a: 100, b: 200 }, a position_or_key of a returns 100.

Arguments:

  • list_or_container: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • position_or_key: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",
      };
      "test_str" string => format("%S", test);

      "test2" data => parsejson("[1, 2, 3, null]");
      "test2_str" string => format("%S", test2);

      "test3" data => parsejson('{ "x": true, "y": "z" }');
      "test3_str" string => format("%S", test3);

      "nth" slist => { 1, 2, 6, 10, 11, 1000 };
      "nth2" slist => getindices(test2);
      "nth3" slist => getindices(test3);

      "access[$(nth)]" string => nth(test, $(nth));
      "access[0]" string => nth(test, 0);

      "access2[$(nth2)]" string => nth(test2, $(nth2));
      "access3[$(nth3)]" string => nth(test3, $(nth3));

  reports:
      "The test list is $(test_str)";
      "element #$(nth) of the test list: $(access[$(nth)])";
      "element #0 of the test list: $(access[0])";

      "The test2 data container is $(test2_str)";
      "element #$(nth2) of the test2 data container: $(access2[$(nth2)])";

      "The test3 data container is $(test3_str)";
      "element #$(nth3) of the test3 data container: $(access3[$(nth3)])";
}

Output:

R: The test list is { "1", "2", "3", "one", "two", "three", "long string", "four", "fix", "six", "one", "two", "three" }
R: element #1 of the test list: 2
R: element #2 of the test list: 3
R: element #6 of the test list: long string
R: element #10 of the test list: one
R: element #11 of the test list: two
R: element #1000 of the test list: $(access[1000])
R: element #0 of the test list: 1
R: The test2 data container is [1,2,3,null]
R: element #0 of the test2 data container: 1
R: element #1 of the test2 data container: 2
R: element #2 of the test2 data container: 3
R: element #3 of the test2 data container: null
R: The test3 data container is {"x":true,"y":"z"}
R: element #x of the test3 data container: true
R: element #y of the test3 data container: z

See also: length().


string_split

Prototype: string_split(string, regex, maxent)

Return type: slist

Description: Splits string into at most maxent substrings wherever regex occurs, and returns the list with those strings.

The regular expression is unanchored.

If the maximum number of substrings is insufficient to accommodate all the entries, the generated slist will have maxent items and the last one will contain the rest of the string starting with the maxent-1-th delimiter. This is standard behavior in many languages like Perl or Ruby, and different from the splitstring() behavior.

Arguments:

  • string: string, in the range: .*
  • regex: regular expression, in the range: .*
  • maxent: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:

      "split1" slist => string_split("one:two:three", ":", "10");
      "split2" slist => string_split("one:two:three", ":", "1");
      "split3" slist => string_split("alpha:xyz:beta", "xyz", "10");

  reports:

      "split1: $(split1)";  # will list "one", "two", and "three"
      "split2: $(split2)";  # will list "one:two:three"
      "split3: $(split3)";  # will list "alpha:" and ":beta"

}

Output:

R: split1: one
R: split1: two
R: split1: three
R: split2: one:two:three
R: split3: alpha:
R: split3: :beta

History: Introduced in CFEngine 3.6; deprecates splitstring().

See also: splitstring()


randomint

Prototype: randomint(lower, upper)

Return type: int

Description: Returns a random integer between lower and up to but not including upper.

The limits must be integer values and the resulting numbers are based on the entropy of the md5 algorithm.

The upper limit is excluded from the range. Thus randomint(0, 100) will return 100 possible values, not 101.

The function will be re-evaluated on each pass if it is not restricted with a context class expression as shown in the example.

Arguments:

  • lower: int, in the range: -99999999999,99999999999
  • upper: int, in the range: -99999999999,99999999999

Example:

body common control
{
      bundlesequence => { "randomint_example" };
}

bundle agent randomint_example
{
  vars:
      "low"    string => "4";
      "high"   string => "60";

      "random"    int => randomint($(low), $(high));
  classes:
      "isabove" expression => isgreaterthan($(random), 3);

  reports:
    isabove::
      "The generated random number was above 3";
}

Output:

R: The generated random number was above 3

getfields

Prototype: getfields(regex, filename, split, array_lval)

Return type: int

Description: Fill array_lval with fields in the lines from file filename that match regex, split on split.

The function returns the number of lines matched. This function is most useful when you want only the first matching line (e.g., to mimic the behavior of the getpwnam(3) on the file /etc/passwd). If you want to examine all lines, use readstringarray() instead.

Arguments:

  • regex : Regular expression to match line, in the range .*

A regular expression matching one or more lines. The regular expression is anchored, meaning it must match the entire line.

  • filename : Filename to read, in the range "?(/.*)

The name of the file to be examined.

  • split : Regular expression to split fields, in the range .*

A regex pattern that is used to parse the field separator(s) to split up the file into items

  • array_lval : Return array name, in the range .*

The base name of the array that returns the values.

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "no" int => getfields("root:.*","/etc/passwd",":","userdata");

  reports:
      "Found $(no) lines matching";
      "root's handle = $(userdata[1])";
      "root's passwd = ... forget it!";
      "root's uid = $(userdata[3])";
      # uncomment this if you want to see the HOMEDIR field
      #"root's homedir = $(userdata[6])";
      # uncomment this if you want to see the GID field
      #"root's gid = $(userdata[4])";
      # uncomment this if you want to see the GECOS field
      #"root's name = $(userdata[5])";

}

Output:

R: Found 1 lines matching
R: root's handle = root
R: root's passwd = ... forget it!
R: root's uid = 0

Notes: This function matches lines (using a regular expression) in the named file, and splits the first matched line into fields (using a second regular expression), placing these into a named array whose elements are array[1],array[2],... This is useful for examining user data in the Unix password or group files.


hostsseen

Prototype: hostsseen(horizon, seen, field)

Return type: slist

Description: Returns a list with the information field of hosts that were seen or not seen within the last horizon hours.

Finds a list of hosts seen by a CFEngine remote connection on the current host within the number of hours specified in horizon. The argument seen may be lastseen or notseen, the latter selecting all hosts not observed to have connected within the specified time.

Arguments:

  • horizon: int, in the range: 0,99999999999
  • seen: one of
    • lastseen
    • notseen
  • field: one of
    • name
    • address

Example:

bundle agent test
{
vars:

  "myhosts" slist => { hostsseen("inf","lastseen","address") };

reports:
  "Found client/peer: $(myhosts)";
}

regline

Prototype: regline(regex, filename)

Return type: boolean

Description: Returns whether the anchored regular expression regex matches a line in file filename.

Note that regex must match an entire line of the file in order to give a true result.

Arguments:

  • regex: regular expression, in the range: .*
  • filename: string, in the range: .*

Example:

    bundle agent example
    {
    files:

      "/tmp/testfile" edit_line => test;
    }

    bundle edit_line test
    {
    classes:

        "ok" expression => regline(".*XYZ.*","$(edit.filename)");

    reports:

     ok::

       "File $(edit.filename) has a line with \"XYZ\" in it";

    }

This function is useful for edit_line applications, where one might want to set a class for detecting the presence of a string that does not exactly match one being inserted. For example:

    bundle edit_line upgrade_cfexecd
      {
      classes:

        # Check there is not already a crontab line, not identical to
        # the one proposed below...

        "exec_fix" not => regline(".*cf-execd.*","$(edit.filename)");

      insert_lines:

        exec_fix::

         "0,5,10,15,20,25,30,35,40,45,50,55 * * * * /var/cfengine/bin/cf-execd -F";

      reports:

        exec_fix::

         "Added a 5 minute schedule to crontabs";
      }

none

Prototype: none(regex, list)

Return type: boolean

Description: Returns whether no element in list matches the regular expression regex.

list can be a data container or a regular list.

Arguments:

  • regex: regular expression, in the range: .*
  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

The regular expression is unanchored.

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:
      "none11" expression => none("jebadiah", test1);
      "none12" expression => none("2", test1);
      "none21" expression => none("jebadiah", test2);
      "none22" expression => none("2", test2);

  vars:
      "test1" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",
      };


      "test2" data => parsejson('[1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",]');

  reports:
      "The test1 list is $(test1)";
    none11::
      "none() test1 1 passed";
    !none11::
      "none() test1 1 failed";
    none12::
      "none() test1 2 failed";
    !none12::
      "none() test1 2 passed";

      "The test2 list is $(test2)";
    none21::
      "none() test2 1 passed";
    !none21::
      "none() test2 1 failed";
    none22::
      "none() test2 2 failed";
    !none22::
      "none() test2 2 passed";
}

Output:

R: The test1 list is 1
R: The test1 list is 2
R: The test1 list is 3
R: The test1 list is one
R: The test1 list is two
R: The test1 list is three
R: The test1 list is long string
R: The test1 list is four
R: The test1 list is fix
R: The test1 list is six
R: none() test1 1 passed
R: none() test1 2 passed
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: none() test2 1 passed
R: none() test2 2 passed

See also: filter(), every(), and some().


min

Prototype: min(list, sortmode)

Return type: string

Description: Return the minimum of the items in list according to sortmode (same sort modes as in sort()).

list can be a data container or a regular list.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • sortmode: one of
    • lex
    • int
    • real
    • IP
    • ip
    • MAC
    • mac

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      # the behavior will be the same whether you use a data container or a list
      # "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
      "mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
      "mylist_str" string => format("%S", mylist);

      "max_int" string => max(mylist, "int");
      "max_lex" string => max(mylist, "lex");
      "max_ip" string => max(mylist, "ip");

      "min_int" string => min(mylist, "int");
      "min_lex" string => min(mylist, "lex");
      "min_ip" string => min(mylist, "ip");

      "mean" real => mean(mylist);
      "variance" real => variance(mylist);

  reports:
      "my list is $(mylist_str)";

      "mean is $(mean)";
      "variance is $(variance) (use eval() to get the standard deviation)";

      "max int is $(max_int)";
      "max IP is $(max_ip)";
      "max lexicographically is $(max_lex)";

      "min int is $(min_int)";
      "min IP is $(min_ip)";
      "min lexicographically is $(min_lex)";
}

Output:

R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1

Notes:

History: Was introduced in version 3.6.0 (2014)

See also: sort(), variance(), sum(), max(), mean()


hostrange

Prototype: hostrange(prefix, range)

Return type: boolean

Description: Returns whether the current host lies in the range of enumerated hostnames specified with prefix.

This is a pattern matching function for non-regular (enumerated) expressions.

Arguments:

  • prefix: string, in the range: .*
  • range: string, in the range: .*

Example:

bundle agent example
{
classes:

  "compute_nodes" expression => hostrange("cpu-","01-32");

reports:

  compute_nodes::

    "No computer is a cluster";

}

host2ip

Prototype: host2ip(hostname)

Return type: string

The return value is cached.

Description: Returns the primary name-service IP address for the named host hostname.

Uses whatever configured name service is used by the resolver library to translate hostname into an IP address. It will return an IPv6 address by preference if such an address exists. This function uses the standard lookup procedure for a name, so it mimics internal processes and can therefore be used not only to cache multiple lookups in the configuration, but to debug the behavior of the resolver.

Arguments:

  • hostname: string, in the range: .*

Example:

    bundle server control
    {
      allowconnects         => { escape(host2ip("www.example.com")) };
    }

History: This function was introduced in CFEngine version 3.0.4 (2010)


changedbefore

Prototype: changedbefore(newer, older)

Return type: boolean

Description: Compares the ctime fields of two files.

Returns true if newer was changed before older, otherwise returns false.

Change times include both file permissions and file contents. Comparisons like this are normally used for updating files (like the 'make' command).

Arguments:

  • newer: string, in the range: "?(/.*)
  • older: string, in the range: "?(/.*)

Example:

    body common control
    {
      bundlesequence  => { "example" };
    }

    bundle agent example
    {
      classes:

        "do_it" and => { changedbefore("/tmp/earlier","/tmp/later"), "linux" };

      reports:

        do_it::

          "The derived file needs updating";
    }

or

Prototype: or(...)

Return type: string

Description: Returns any if any argument evaluates to true and !any if any argument evaluates to false.

Arguments: A list of classes, class expressions, or functions that return classes.

Example:

    commands:
      "/usr/bin/generate_config $(config)"
        ifvarclass => or( "force_configs",
                          not(fileexists("/etc/config/$(config)"))
                        );

Notes: Introduced primarily for use with ifvarclass, if, and unless promise attributes.

See Also: and, or, not

History: Was introduced in 3.2.0, Nova 2.1.0 (2011)


not

Prototype: not(expression)

Return type: string

Description: Returns any if all arguments evaluate to false and !any if any argument evaluates to true.

Arguments:

  • expression: string, in the range: .*

Argument Descriptions:

  • expression - Class, class expression, or function that returns a class

Example:

commands:
  "/usr/bin/generate_config $(config)"
    ifvarclass => not( fileexists("/etc/config/$(config)") );

Notes: Introduced primarily for use with ifvarclass, if, and unless promise attributes.

See Also: and, or, not

History: Was introduced in 3.2.0, Nova 2.1.0 (2011)


groupexists

Prototype: groupexists(group)

Return type: boolean

Description: Returns whether a group group exists on this host.

The group may be specified by name or identifier.

Arguments:

  • group: string, in the range: .*

Example:

body common control

{
      bundlesequence  => { "example" };
}


bundle agent example
{
  classes:
      "gname" expression => groupexists("sys");
      "gid"   expression => groupexists("0");

  reports:
    gname::
      "Group exists by name";
    gid::
      "Group exists by id";
}

Output:

R: Group exists by name
R: Group exists by id

fileexists

Prototype: fileexists(filename)

Return type: boolean

Description: Returns whether the file filename can be accessed.

The file must exist, and the user must have access permissions to the file for this function to return true.

Arguments:

  • filename: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence  => { "example" };
}

bundle agent example
{
  classes:
      # this.promise_filename has the currently-executed file, so it
      # better exist!
      "exists" expression => fileexists($(this.promise_filename));
      "exists_etc_passwd" expression => fileexists("/etc/passwd");

  reports:

    exists::

      "I exist!  I mean, file exists!";
}

Output:

R: I exist!  I mean, file exists!

diskfree

Prototype: diskfree(path)

Return type: int

Descriptions: Return the free space (in KB) available on the current partition of path.

If path is not found, this function returns 0.

Arguments:

  • path: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:
      "has_space" expression => isgreaterthan($(free), 0);

  vars:
      "free" int => diskfree("/tmp");

  reports:
    has_space::
      "The filesystem has free space";
    !has_space::
      "The filesystem has NO free space";
}

Output:

R: The filesystem has free space

read[int|real|string]list

Prototype: readintlist(filename, comment, split, maxentries, maxbytes)
Prototype: readreallist(filename, comment, split, maxentries, maxbytes)
Prototype: readstringlist(filename, comment, split, maxentries, maxbytes)

Return type: ilist, rlist or slist

Description: Splits the file filename into separated values and returns the list.

The comment regex will strip out unwanted patterns from the file being read, leaving unstripped characters to be split into fields. Using the empty string ("") indicates no comments.

Arguments:

  • filename : File name to read, in the range "?(/.*)
  • comment : Unanchored regex matching comments, in the range .*
  • split : Unanchored regex to split data, in the range .*
  • maxentries : Maximum number of entries to read, in the range 0,99999999999
  • maxbytes : Maximum bytes to read, in the range 0,99999999999

Example:

Prepare:

echo 1 > /tmp/cfe_list_ints
echo 2 >> /tmp/cfe_list_ints
echo 3 >> /tmp/cfe_list_ints
echo 1.1 > /tmp/cfe_list_reals
echo 2.2 >> /tmp/cfe_list_reals
echo 3 >> /tmp/cfe_list_reals
echo alpha > /tmp/cfe_list_strings
echo beta >> /tmp/cfe_list_strings
echo gamma >> /tmp/cfe_list_strings

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "integers" ilist => readintlist("/tmp/cfe_list_ints","#.*","[\n]",10,400);
      "strings" slist => readstringlist("/tmp/cfe_list_strings", "#.*", "\s", 10, 400);
      "reals" rlist => readreallist("/tmp/cfe_list_reals","#.*","[\n]",10,400);

  reports:

      "integers in /tmp/cfe_list_ints: $(integers)";
      "strings in /tmp/cfe_list_strings: $(strings)";
      "reals in /tmp/cfe_list_reals: $(reals)";

}

Output:

R: integers in /tmp/cfe_list_ints: 1
R: integers in /tmp/cfe_list_ints: 2
R: integers in /tmp/cfe_list_ints: 3
R: strings in /tmp/cfe_list_strings: alpha
R: strings in /tmp/cfe_list_strings: beta
R: strings in /tmp/cfe_list_strings: gamma
R: reals in /tmp/cfe_list_reals: 1.1
R: reals in /tmp/cfe_list_reals: 2.2
R: reals in /tmp/cfe_list_reals: 3

ldaplist

This function is only available in CFEngine Enterprise.

Prototype: ldaplist(uri, dn, filter, record, scope, security)

Return type: slist

The return value is cached.

Description: Returns a list with all named values from multiple ldap records.

This function retrieves a single field from all matching LDAP records identified by the search parameters.

Arguments:

  • uri: string, in the range: .*
  • dn: string, in the range: .*
  • filter: string, in the range: .*
  • record: string, in the range: .*
  • scope: one of
    • subtree
    • onelevel
    • base
  • security: one of
    • none
    • ssl
    • sasl

dn specifies the distinguished name, an ldap formatted name built from components, e.g. "dc=cfengine,dc=com". filter is an ldap search, e.g. "(sn=User)", and record is the name of the single record to be retrieved, e.g. uid. Which security values are supported depends on machine and server capabilities.

Example:

vars:

   # Get all matching values for "uid" - should be a single record match

  "list" slist =>  ldaplist(
                           "ldap://ldap.example.org",
                           "dc=cfengine,dc=com",
                           "(sn=User)",
                           "uid",
                           "subtree",
                           "none"
                           );

every

Prototype: every(regex, list)

Return type: boolean

Description: Returns whether every element in the variable list matches the unanchored regex.

Arguments:

  • regex : Regular expression to find, in the range .*

  • list : The name of the list variable to check, in the range [a-zA-Z0-9_$(){}\[\].:]+. It can be a data container or a regular list.

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test

{
  classes:
      "every_dot_star" expression => every(".*", test);
      "every_dot" expression => every(".", test);
      "every_number" expression => every("[0-9]", test);

      "every2_dot_star" expression => every(".*", test2);
      "every2_dot" expression => every(".", test2);
      "every2_number" expression => every("[0-9]", test2);

  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",
      };

      "test2" data => parsejson('[1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",]');

  reports:
      "The test list is $(test)";

    every_dot_star::
      "every() test passed: every element matches '.*'";
    !every_dot_star::
      "every() test failed: not every element matches '.*'";
    every_number::
      "every() test failed: every element matches '[0-9]'";
    !every_number::
      "every() test passed: not every element matches '[0-9]'";
    every_dot::
      "every() test failed: every element matches '.'";
    !every_dot::
      "every() test passed: not every element matches '.'";

      "The test2 list is $(test2)";
    every2_dot_star::
      "every() test2 passed: every element matches '.*'";
    !every2_dot_star::
      "every() test2 failed: not every element matches '.*'";
    every2_number::
      "every() test2 failed: every element matches '[0-9]'";
    !every2_number::
      "every() test2 passed: not every element matches '[0-9]'";
    every2_dot::
      "every() test2 failed: every element matches '.'";
    !every2_dot::
      "every() test2 passed: not every element matches '.'";
}

Output:

R: The test list is 1
R: The test list is 2
R: The test list is 3
R: The test list is one
R: The test list is two
R: The test list is three
R: The test list is long string
R: The test list is four
R: The test list is fix
R: The test list is six
R: every() test passed: every element matches '.*'
R: every() test passed: not every element matches '[0-9]'
R: every() test passed: not every element matches '.'
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: every() test2 passed: every element matches '.*'
R: every() test2 passed: not every element matches '[0-9]'
R: every() test2 passed: not every element matches '.'

See also: filter(), some(), and none().


data_readstringarray

Prototype: data_readstringarray(filename, comment, split, maxentries, maxbytes)

Return type: data

Description: Returns a data container (map) with up to maxentries-1 fields from the first maxbytes bytes of file filename. The first field becomes the key in the map.

One dimension is separated by the regex split, the other by the lines in the file. The array key (the first field) must be unique; if you need to allow duplicate lines use data_readstringarrayidx().

The comment field is a multiline regular expression and will strip out unwanted patterns from the file being read, leaving unstripped characters to be split into fields. Using the empty string ("") indicates no comments.

Arguments:

  • filename: string, in the range: "?(/.*)
  • comment: string, in the range: .*
  • split: string, in the range: .*
  • maxentries: int, in the range: 0,99999999999
  • maxbytes: int, in the range: 0,99999999999

Example:

Prepare:

echo a,b,c > /tmp/cfe_array
echo "# This is a comment" >> /tmp/cfe_array
echo d,e,f >> /tmp/cfe_array
echo g,h,i >> /tmp/cfe_array
echo "# This is another comment" >> /tmp/cfe_array
echo j,k,l >> /tmp/cfe_array

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      # The comment regex warrents an explination:
      # # matches the character # literally
      # [^\n]* match a single character not including the newline character
      # between zero and unlimited times, as many times as possible
      "bykey" data => data_readstringarray("/tmp/cfe_array","#[^\n]*",",",10,400);
      "byint" data => data_readstringarrayidx("/tmp/cfe_array","#[^\n]*",",",10,400);

      "bykey_str" string => format("%S", bykey);
      "byint_str" string => format("%S", byint);
  reports:
      "By key: $(bykey_str)";
      "specific element by key a, offset 0: '$(bykey[a][0])'";
      "By int offset: $(byint_str)";
      "specific element by int offset 2, 0: '$(byint[2][0])'";

}

Output:

R: By key: {"a":["b","c"],"d":["e","f"],"g":["h","i"],"j":["k","l"]}
R: specific element by key a, offset 0: 'b'
R: By int offset: [["a","b","c"],["d","e","f"],["g","h","i"],["j","k","l"]]
R: specific element by int offset 2, 0: 'g'

See also: data_readstringarrayidx(), data


getuid

Prototype: getuid(username)

Return type: int

Description: Return the integer user id of the named user on this host

If the named user is not registered the variable will not be defined.

Arguments:

  • username: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "uid" int => getuid("root");

  reports:
      "root's uid is $(uid)";
}

Output:

R: root's uid is 0

Notes: On Windows, which does not support user ids, the variable will not be defined.


escape

Prototype: escape(text)

Return type: string

Description: Escape regular expression characters in text.

This function is useful for making inputs readable when a regular expression is required, but the literal string contains special characters. The function simply 'escapes' all the regular expression characters, so that you do not have to.

Arguments:

  • path: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "ip" string => "10.123.321.250";
      "escaped" string => escape($(ip));

  reports:
      "escaped $(ip) = $(escaped)";
}

Output:

R: escaped 10.123.321.250 = 10\.123\.321\.250

In this example, the string "192.168.2.1" is "escaped" to be equivalent to "192\.168\.2\.1", because without the backslashes, the regular expression "192.168.2.1" will also match the IP ranges "192.168.201", "192.168.231", etc (since the dot character means "match any character" when used in a regular expression).

Notes:

History: This function was introduced in CFEngine version 3.0.4 (2010)


grep

Prototype: grep(regex, list)

Return type: slist

Description: Returns the sub-list if items in list matching the anchored regular expression regex.

list can be a data container or a regular list.

Arguments:

  • regex: regular expression, in the range: .*
  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:

      "mylist" slist => { "One", "Two", "Three", "Four", "Five" };
      "Tlist" slist => grep("T.*","mylist");
      "empty_list" slist => grep("ive","mylist");

      "datalist" data => parsejson('[1,2,3, "Tab", "chive"]');
      "data_Tlist" slist => grep("T.*","datalist");
      "data_empty_list" slist => grep("ive","datalist");

      "todo" slist => { "mylist", "Tlist", "empty_list", "datalist", "data_Tlist", "data_empty_list" };
      "$(todo)_str" string => format("%S", $(todo));

  reports:
      "$(todo): $($(todo)_str)";
}

Output:

R: mylist: { "One", "Two", "Three", "Four", "Five" }
R: Tlist: { "Two", "Three" }
R: empty_list: { --empty-list-- }
R: datalist: [1,2,3,"Tab","chive"]
R: data_Tlist: { "Tab" }
R: data_empty_list: { --empty-list-- }

shuffle

Prototype: shuffle(list, seed)

Return type: slist

Description: Return list shuffled with seed.

The same seed will produce the same shuffle every time. For a random shuffle, provide a random seed with the randomint function.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • seed: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "mylist" slist => { "b", "c", "a" };
      "seeds" slist => { "xx", "yy", "zz" };

      "shuffled_$(seeds)" slist => shuffle(mylist, $(seeds));

      "joined_$(seeds)" string => join(",", "shuffled_$(seeds)");

  reports:
      "shuffled RANDOMLY by $(seeds) = '$(joined_$(seeds))'";
}

Output:

R: shuffled RANDOMLY by xx = 'b,a,c'
R: shuffled RANDOMLY by yy = 'a,c,b'
R: shuffled RANDOMLY by zz = 'c,b,a'

See also: sort().


registryvalue

Prototype: registryvalue(key, valueid)

Return type: string

Description: Returns the value of valueid in the Windows registry key key.

This function applies only to Windows-based systems. The value is parsed as a string.

Arguments:

  • key: string, in the range: .*
  • valueid: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "reg" };
}

bundle agent reg
{
  vars:
    windows::
      "value" string => registryvalue("HKEY_LOCAL_MACHINE\SOFTWARE\CFEngine AS\CFEngine","value3");
    !windows::
      "value" string => "Sorry, no registry data is available";

  reports:
      "Value extracted: $(value)";

}

Output:

R: Value extracted: Sorry, no registry data is available

Notes: Currently values of type REG_SZ (string), REG_EXPAND_SZ (expandable string) and REG_DWORD (double word) are supported.


difference

Prototype: difference(list1, list2)

Return type: slist

Description: Returns the unique elements in list1 that are not in list2.

Arguments:

  • list1: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • list2: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "a" slist => { 1,2,3,"x" };
      "b" slist => { "x" };

      # normal usage
      "diff_between_a_and_b" slist => difference(a, b);
      "diff_between_a_and_b_str" string => join(",", diff_between_a_and_b);

      # NOTE: advanced usage!
      "mylist1" slist => { "a", "b" };
      "mylist2" slist => { "a", "b" };
      "$(mylist1)_str" string => join(",", $(mylist1));

      # Here we're going to really call difference(a,a) then difference(a,b) then difference(b,a) then difference(b,b)
      # We create a new variable for each difference!!!
      "diff_$(mylist1)_$(mylist2)" slist => difference($(mylist1), $(mylist2));
      "diff_$(mylist1)_$(mylist2)_str" string => join(",", "diff_$(mylist1)_$(mylist2)");

  reports:
      # normal usage
      "The difference between lists a and b is '$(diff_between_a_and_b_str)'";

      # NOTE: advanced usage results!
      "The difference of list '$($(mylist1)_str)' with '$($(mylist2)_str)' is '$(diff_$(mylist1)_$(mylist2)_str)'";
}

Output:

R: The difference between lists a and b is '1,2,3'
R: The difference of list '1,2,3,x' with '1,2,3,x' is ''
R: The difference of list '1,2,3,x' with 'x' is '1,2,3'
R: The difference of list 'x' with '1,2,3,x' is ''
R: The difference of list 'x' with 'x' is ''

See also: intersection().


remotescalar

This function is only available in CFEngine Enterprise.

Prototype: remotescalar(id, server, encrypt)

Return type: string

The return value is cached.

Description: Returns a scalar value identified by id from a remote CFEngine server. Communication is encrytped depending on encrypt.

If the identifier matches a persistent scalar variable then this will be returned preferentially. If no such variable is found, then the server will look for a literal string in a server bundle with a handle that matches the requested object.

The remote system's cf-serverd must accept the query for the requested variable from the host that is requesting it. Access must be granted by making an access promise with resource_type set to literal.

CFEngine stores the value of this function on the calling host, so that, if the network is unavailable, the last known value will be used. Hence use of this function is fault tolerant. Care should be taken in attempting to access remote variables that are not available, as the repeated connections needed to resolve the absence of a value can lead to undesirable behavior. As a general rule, users are recommended to refrain from relying on the availability of network resources.

Arguments:

  • id: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • server: string, in the range: .*
  • encrypt: one of
    • true
    • false
    • yes
    • no
    • on
    • off

Example:

    vars:

     "remote" string => remotescalar("test_scalar","127.0.0.1","yes");
    bundle server access
    {
    access:
      "value of my test_scalar, can expand variables here - $(sys.host)"
        handle => "test_scalar",
        comment => "Grant access to contents of test_scalar VAR",
        resource_type => "literal",
        admit => { "127.0.0.1" };
    }

Notes: Note that this function assumes that you have already performed a successful key exchange between systems, (e.g. using either a remote copy or cf-runagent connection). It contains no mechanism for trust establishment and will fail if there is no trust relationship established in advance.

See also: hubknowledge(), remoteclassesmatching(), hostswithclass()


packagesmatching

Prototype: packagesmatching(package_regex, version_regex, arch_regex, method_regex)

Return type: data

Description: Return a data container with the list of installed packages matching the parameters.

This function searches for the anchored regular expressions in the list of currently installed packages.

The return is a data container with a list of package descriptions, looking like this:

[
  {
    "name": "bfoobar",
    "version": "1",
    "arch": "besm6",
    "method": "printf"
  }
]

The following code extracts just the package names, then looks for some desired packages, and finally reports if they are installed.

body common control

{
      bundlesequence => { "missing_packages" };
}


bundle agent missing_packages
{
  vars:
    "desired" slist => { "mypackage1", "mypackage2" };

    "installed" data => packagesmatching(".*",".*",".*",".*");
    "installed_indices" slist => getindices(installed);
    "installed_name[$(installed_indices)]" string => "$(installed[$(installed_indices)][name])";
    "installed_names" slist => getvalues("installed_name");

    "missing_list" slist => difference(desired,installed_names);

  reports:
    "Missing packages = $(missing_list)";
    "Installed packages = $(installed_names)";
    "Desired packages = $(desired)";
}

This policy can be found in /var/cfengine/share/doc/examples/packagesmatching.cf and downloaded directly from github.

Arguments:

  • package_regex: string, in the range: .*
  • version_regex: string, in the range: .*
  • arch_regex: string, in the range: .*
  • method_regex: string, in the range: .*

Example:

      "all_packages" data => packagesmatching(".*", ".*", ".*", ".*");

History: Introduced in CFEngine 3.6

See also: packageupdatesmatching().


readtcp

Prototype: readtcp(hostnameip, port, sendstring, maxbytes)

Return type: string

The return value is cached.

Description: Connects to tcp port of hostnameip, sends sendstring, reads at most maxbytes from the response and returns those.

If the send string is empty, no data are sent or received from the socket. Then the function only tests whether the TCP port is alive and returns an empty string.

Not all Unix TCP read operations respond to signals for interruption, so poorly formed requests can block the cf-agent process. Always test TCP connections fully before deploying.

Arguments:

  • host: string, in the range: .*
  • port: string, in the range: .*
  • sendstring: string, in the range: .*
  • maxbytes: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "my80" string => readtcp("myserver.com","80","GET /index.html HTTP/1.1$(const.r)$(const.n)Host: myserver.com$(const.r)$(const.n)$(const.r)$(const.n)",20);

  classes:

      "server_ok" expression => regcmp("[^\n]*200 OK.*\n.*","$(my80)");

  reports:

    server_ok::

      "Server is alive";

    !server_ok::

      "Server is not responding - got $(my80)";
}

Output:

R: Server is alive

Notes: Note that on some systems the timeout mechanism does not seem to successfully interrupt the waiting system calls so this might hang if you send an incorrect query string. This should not happen, but the cause has yet to be diagnosed.


mergedata

Prototype: mergedata(one, two, etc)

Return type: data

Description: Returns the merger of any named data containers or lists.

The returned data container will have the keys from each of the named data containers.

If all data containers are JSON arrays, they are merged into a single array, as you'd expect from merging two arrays.

If any of the data containers are JSON objects, all the containers are treated as JSON objects (for arrays, the key is the element's offset).

If any list (slist, ilist, or rlist) is named, it's first converted to a JSON array, then merged as above.

If any CFEngine "classic" array (array[key]) is named, it's first converted to a JSON object, then merged as above.

mergedata() is thus a convenient way, together with getindices() and getvalues(), to bridge the gap between data container and the traditional list and array data types in CFEngine.

Example:

body common control
{
      bundlesequence => { "test", "test2" };
}

bundle agent test
{
  vars:
      "d1" data => parsejson('{ "a": [1,2,3], "b": [] }');
      "d2" data => parsejson('{ "b": [4,5,6] }');
      "d3" data => parsejson('[4,5,6]');
      "list1" slist => { "element1", "element2" };
      "array1[mykey]" slist => { "array_element1", "array_element2" };
      "array2[otherkey]" string => "hello";

      "merged_d1_d2" data => mergedata("d1", "d2");
      "merged_d1_d3" data => mergedata("d1", "d3");
      "merged_d3_list1" data => mergedata("d3", "list1");

      "merged_d1_array1" data => mergedata("d1", "array1");
      "merged_d2_array2" data => mergedata("d2", "array2");

      "merged_d1_d2_str" string => format("merging %S with %S produced %S", d1, d2, merged_d1_d2);
      "merged_d1_d3_str" string => format("merging %S with %S produced %S", d1, d3, merged_d1_d3);
      "merged_d3_list1_str" string => format("merging %S with %S produced %S", d3, list1, merged_d3_list1);

      "merged_d1_array1_str" string => format("merging %S with %s produced %S", d1, array1, merged_d1_array1);
      "merged_d2_array2_str" string => format("merging %S with %s produced %S", d2, array2, merged_d2_array2);
  reports:
      "$(merged_d1_d2_str)";
      "$(merged_d1_d3_str)";
      "$(merged_d3_list1_str)";
      "$(merged_d1_array1_str)";
      "$(merged_d2_array2_str)";
}

bundle agent test2
{
  vars:
      "a"       data  => parsejson('{ "a": "1" }'), meta => { "mymerge" };
      "b"       data  => parsejson('{ "b": "2" }'), meta => { "mymerge" };
      "c"       data  => parsejson('{ "c": "3" }'), meta => { "mymerge" };
      "d"       data  => parsejson('{ "d": "4" }'), meta => { "mymerge" };
      "todo"    slist => variablesmatching(".*", "mymerge");

  methods:
      "go" usebundle => cmerge(@(todo)); # a, b, c, d

  reports:
      "$(this.bundle): merged containers with cmerge = $(cmerge.all_str)";
}

bundle agent cmerge(varlist)
{
  vars:
      "all"     data => parsejson('[]'),            policy => "free";
      "all"     data => mergedata(all, $(varlist)), policy => "free";
      "all_str" string => format("%S", all),        policy => "free";
}

Output:

R: merging {"a":[1,2,3],"b":[]} with {"b":[4,5,6]} produced {"a":[1,2,3],"b":[4,5,6]}
R: merging {"a":[1,2,3],"b":[]} with [4,5,6] produced {"a":[1,2,3],"b":[],"0":4,"1":5,"2":6}
R: merging [4,5,6] with { "element1", "element2" } produced [4,5,6,"element1","element2"]
R: merging {"a":[1,2,3],"b":[]} with array1 produced {"a":[1,2,3],"b":[],"mykey":["array_element1","array_element2"]}
R: merging {"b":[4,5,6]} with array2 produced {"b":[4,5,6],"otherkey":"hello"}
R: test2: merged containers with cmerge = {"a":"1","b":"2","c":"3","d":"4"}

See also: getindices(), getvalues(), readjson(), parsejson(), and data documentation.


hostinnetgroup

Prototype: hostinnetgroup(netgroup)

Return type: boolean

Description: True if the current host is in the named netgroup.

Arguments:

  • netgroup: string, in the range: .*

Example:

    classes:

      "ingroup" expression => hostinnetgroup("my_net_group");

iprange

Prototype: iprange(range)

Return type: boolean

Description: Returns whether the current host lies in the range of IP addresses specified.

Pattern matching based on IP addresses.

Arguments:

  • range: string, in the range: .*

Example:

bundle agent example
{
classes:

  "dmz_1" expression => iprange("128.39.89.10-15");
  "lab_1" expression => iprange("128.39.74.1/23");

reports:

  dmz_1::

    "DMZ 1 subnet";

  lab_1::

    "Lab 1 subnet";
}

variance

Prototype: variance(list)

Return type: real

Description: Return the variance of the numbers in list.

list can be a data container or a regular list.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Use the eval() function to easily get the standard deviation (square root of the variance).

This is not part of a full statistical package but a convenience function.

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      # the behavior will be the same whether you use a data container or a list
      # "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
      "mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
      "mylist_str" string => format("%S", mylist);

      "max_int" string => max(mylist, "int");
      "max_lex" string => max(mylist, "lex");
      "max_ip" string => max(mylist, "ip");

      "min_int" string => min(mylist, "int");
      "min_lex" string => min(mylist, "lex");
      "min_ip" string => min(mylist, "ip");

      "mean" real => mean(mylist);
      "variance" real => variance(mylist);

  reports:
      "my list is $(mylist_str)";

      "mean is $(mean)";
      "variance is $(variance) (use eval() to get the standard deviation)";

      "max int is $(max_int)";
      "max IP is $(max_ip)";
      "max lexicographically is $(max_lex)";

      "min int is $(min_int)";
      "min IP is $(min_ip)";
      "min lexicographically is $(min_lex)";
}

Output:

R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1

Notes:

History: Was introduced in version 3.6.0 (2014)

See also: sort(), mean(), sum(), max(), min()


packageupdatesmatching

Prototype: packageupdatesmatching(package_regex, version_regex, arch_regex, method_regex)

Return type: data

Description: Return a data container with the list of available packages matching the parameters.

This function searches for the anchored regular expressions in the list of currently available packages.

The return is a data container with a list of package descriptions, looking like this:

Arguments:

  • package_regex: string, in the range: .*
  • version_regex: string, in the range: .*
  • arch_regex: string, in the range: .*
  • method_regex: string, in the range: .*

Example:

      "all_packages" data => packageupdatesmatching(".*", ".*", ".*", ".*");

History: Introduced in CFEngine 3.6

See also: packagesmatching().


string_head

Prototype: string_head(data, max)

Return type: string

Description: Returns the first max bytes of data.

Arguments:

  • data: string, in the range: .*
  • max: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "start" string => string_head("abc", "1"); # will contain "a"
  reports:
      "start of abc = $(start)";

}

Output:

R: start of abc = a

History: Introduced in CFEngine 3.6

See also: string_tail(), string_length(), string_reverse().


islessthan

Prototype: islessthan(value1, value2)

Return type: boolean

Description: Returns whether value1 is less than value2.

The comparison is made numerically if possible. If the values are strings, the comparison is lexical (based on C's strcmp()).

Arguments:

  • value1: string, in the range: .*
  • value2: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  classes:

      "ok" expression => islessthan("0","1");

  reports:

    ok::

      "Assertion is true";

    !ok::

      "Assertion is false";

}

Output:

R: Assertion is true

See also: isgreaterthan().


countclassesmatching

Prototype: countclassesmatching(regex)

Return type: int

Description: Count the number of defined classes matching regex.

This function matches classes, using an anchored regular expression that should match the whole line. The function returns the number of classes matched.

Arguments:

  • regex: regular expression, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      # this is anchored, so you need .* to match multiple things
      "num" int => countclassesmatching("cfengine");

  reports:
      "Found $(num) classes matching";
}

Output:

R: Found 1 classes matching

Prototype: islink(filename)

Return type: boolean

Description: Returns whether the named object filename is a symbolic link.

The link node must both exist and be a symbolic link. Hard links cannot be detected using this function.

Arguments:

  • filename: string, in the range: "?(/.*)

Example:

Prepare:

ln -fs /tmp/cfe_testhere.txt /tmp/link

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "islink" expression => islink("/tmp/link");

  reports:

    islink::

      "It's a link.";

}

Output:

R: It's a link.

ifelse

Prototype: ifelse(...)

Return type: string

Description: Evaluate each pair of arguments up to the last one as a (class, value) tuple, returning value if class is set.

If none are set, returns the last argument.

Arguments:

The ifelse function is like a multi-level if-else statement.It was inspired by Oracle's DECODE function. It must have an odd number of arguments (from 1 to N). The last argument is the default value, like the else clause in standard programming languages. Every pair of arguments before the last one are evaluated as a pair. If the first one evaluates true (as if you had used it in a class expression, so it can be more than just a class name, it's a whole context like Tuesday.linux.!verbose) then the second one is returned.

Generally, if ifelse were called with arguments (a1, a2, b1, b2, c), the behavior expressed as pseudo-code is:

    if a1 then return a2
    else-if b1 then return b2
    else return c

(But again, note that any odd number of arguments is supported.)

The ifelse function is extremely useful when you want to avoid explicitly stating the negative of all the expected cases; this problem is commonly seen like so:

    class1.class2::
      "myvar" string => "x";

    class3.!class2::
      "myvar" string => "y";

    !((class1.class2)||class3.!class2)::
      "myvar" string => "z";

That's hard to read and error-prone (do you know how class2 will affect the default case?). Here's the alternative with ifelse:

    "myvar" string => ifelse("class1.class2", "x",
                             "class3.!class2", "y",
                             "z");

Example:

bundle agent example
{
  classes:
      "myclass" expression => "any";
      "myclass2" expression => "any";
      "secondpass" expression => "any";
  vars:
      # we need to use the secondpass class because on the first pass,
      # myclass and myclass2 are not defined yet

    secondpass::

      # result: { "1", "single string parameter", "hardclass OK", "bundle class OK", "5 parameters OK" }

      "mylist" slist => {
                          ifelse(1),
                          ifelse("single string parameter"),
                          ifelse("cfengine", "hardclass OK", "hardclass broken"),
                          ifelse("myclass.myclass2", "bundle class OK", "bundle class broken"),
                          ifelse("this is not true", "5 parameters broken",
                                 "this is also not true", "5 parameters broken 2",
                                 "5 parameters OK"),
                        };

  reports:
      "ifelse result list: $(mylist)";
}

datastate

Prototype: datastate()

Return type: data

Description: Returns the current evaluation data state.

The returned data container will have the keys classes and vars.

Under classes you'll find a map with the class name as the key and true as the value. Namespaced classes will be prefixed as usual.

Under vars you'll find a map with the bundle name as the key (namespaced if necessary). Under the bundle name you'll find another map with the variable name as the key. The value is converted to a data container (JSON format) if necessary. The example should make it clearer.

Mustache templates (see template_method), if not given a template_data, will use the output of datastate() as their input.

Example:

body common control
{
      bundlesequence => { holder, test };
}

bundle common holder
{
  classes:
      "holderclass" expression => "any"; # will be global

  vars:
      "s" string => "Hello!";
      "d" data => parsejson('[4,5,6]');
      "list" slist => { "element1", "element2" };
}

bundle agent test
{
  vars:
      "state" data => datastate();

      # all the variables in bundle "holder" defined as of the execution of datastate() will be here
      "holderstate" string => format("%S", "state[vars][holder]");
      # all the classes defined as of the execution of datastate() will be here
      "allclasses" slist => getindices("state[classes]");

  classes:
      "have_holderclass" expression => some("holderclass", allclasses);

  reports:
      "holder vars = $(holderstate)";
    have_holderclass::
      "I have the holder class";
}

Output:

R: holder vars = {"list":["element1","element2"],"s":"Hello!","d":[4,5,6]}
R: I have the holder class

See also: getindices(), classesmatching(), variablesmatching(), mergedata(), template_method, mustache


getclassmetatags

Prototype: getclassmetatags(classname)

Return type: slist

Description: Returns the list of meta tags for class classname.

Arguments:

  • classname: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:
      "c" expression => "any", meta => { "mytag" };

  vars:
      "ctags" slist => getclassmetatags("c");

  reports:
      "Found tags: $(ctags)";

}

Output:

R: Found tags: source=promise
R: Found tags: mytag

Notes:

See also: getvariablemetatags()


strftime

Prototype: strftime(mode, template, time)

Return type: string

Description: Interprets a time and date format string at a particular point in GMT or local time using Unix epoch time.

Arguments:

  • mode: one of
    • gmtime
    • localtime
  • template: string, in the range: .*
  • time: int, in the range: 0,99999999999

The mode is either gmtime (to get GMT times and dates) or localtime (to get times and dates according to the local timezone, usually specified by the TZ environment variable).

The conversion specifications that can appear in the format template are specialized for printing components of the date and time according to the system locale.

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "time" int => "1234567890";
      "at_time" string => strftime("localtime", "%Y-%m-%d %T", $(time));
      "then" string => strftime("localtime", "%Y-%m-%d %T", 0);

      "gmt_at_time" string => strftime("gmtime", "%Y-%m-%d %T", $(time));
      "gmt_then" string => strftime("gmtime", "%Y-%m-%d %T", 0);

  reports:
      # this will be different depending on your time zone
      # "time $(time); at_time $(at_time); then $(then)";

      # this will be the same in every time zone
      "time $(time); GMT at_time $(gmt_at_time); GMT then $(gmt_then)";
}

Output:

R: time 1234567890; GMT at_time 2009-02-13 23:31:30; GMT then 1970-01-01 00:00:00

Notes: Note that strftime is a standard C function and you should consult its reference to be sure of the specifiers it allows. The below is from the documentation of the standard strftime implementation in the glibc manual at http://www.gnu.org/software/libc/manual/html_node/Formatting-Calendar-Time.html#Formatting-Calendar-Time

Ordinary characters appearing in the template are copied to the output. Conversion specifiers are introduced by a % character and end with a format specifier taken from the following list. The whole % sequence is replaced in the output string as follows:

  • %a

The abbreviated weekday name according to the current locale.

  • %A

The full weekday name according to the current locale.

  • %b

The abbreviated month name according to the current locale.

  • %B

The full month name according to the current locale.

Using %B together with %d produces grammatically incorrect results for some locales.

  • %c

The preferred calendar time representation for the current locale.

  • %C

The century of the year. This is equivalent to the greatest integer not greater than the year divided by 100.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %d

The day of the month as a decimal number (range 01 through 31).

  • %D

The date using the format %m/%d/%y.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %e

The day of the month like with %d, but padded with blank (range 1 through 31).

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %F

The date using the format %Y-%m-%d. This is the form specified in the ISO 8601 standard and is the preferred form for all uses.

This format was first standardized by ISO C99 and by POSIX.1-2001.

  • %g

The year corresponding to the ISO week number, but without the century (range 00 through 99). This has the same format and value as %y, except that if the ISO week number (see %V) belongs to the previous or next year, that year is used instead.

This format was first standardized by ISO C99 and by POSIX.1-2001.

  • %G

The year corresponding to the ISO week number. This has the same format and value as %Y, except that if the ISO week number (see %V) belongs to the previous or next year, that year is used instead.

This format was first standardized by ISO C99 and by POSIX.1-2001 but was previously available as a GNU extension.

  • %h

The abbreviated month name according to the current locale. The action is the same as for %b.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %H

The hour as a decimal number, using a 24-hour clock (range 00 through 23).

  • %I

The hour as a decimal number, using a 12-hour clock (range 01 through 12).

  • %j

The day of the year as a decimal number (range 001 through 366).

  • %k

The hour as a decimal number, using a 24-hour clock like %H, but padded with blank (range 0 through 23).

This format is a GNU extension.

  • %l

The hour as a decimal number, using a 12-hour clock like %I, but padded with blank (range 1 through 12).

This format is a GNU extension.

  • %m

The month as a decimal number (range 01 through 12).

  • %M

The minute as a decimal number (range 00 through 59).

  • %n

A single \n (newline) character.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %p

Either AM or PM, according to the given time value; or the corresponding strings for the current locale. Noon is treated as PM and midnight as AM. In most locales AM/PM format is not supported, in such cases %p yields an empty string.

  • %P

Either am or pm, according to the given time value; or the corresponding strings for the current locale, printed in lowercase characters. Noon is treated as pm and midnight as am. In most locales AM/PM format is not supported, in such cases %P yields an empty string.

This format is a GNU extension.

  • %r

The complete calendar time using the AM/PM format of the current locale.

This format was first standardized by POSIX.2-1992 and by ISO C99. In the POSIX locale, this format is equivalent to %I:%M:%S %p.

  • %R

The hour and minute in decimal numbers using the format %H:%M.

This format was first standardized by ISO C99 and by POSIX.1-2001 but was previously available as a GNU extension.

  • %s

The number of seconds since the epoch, i.e., since 1970-01-01 00:00:00 UTC. Leap seconds are not counted unless leap second support is available.

This format is a GNU extension.

  • %S

The seconds as a decimal number (range 00 through 60).

  • %t

A single \t (tabulator) character.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %T

The time of day using decimal numbers using the format %H:%M:%S.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %u

The day of the week as a decimal number (range 1 through 7), Monday being 1.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %U

The week number of the current year as a decimal number (range 00 through 53), starting with the first Sunday as the first day of the first week. Days preceding the first Sunday in the year are considered to be in week 00.

  • %V

The ISO 8601:1988 week number as a decimal number (range 01 through 53). ISO weeks start with Monday and end with Sunday. Week 01 of a year is the first week which has the majority of its days in that year; this is equivalent to the week containing the year's first Thursday, and it is also equivalent to the week containing January 4. Week 01 of a year can contain days from the previous year. The week before week 01 of a year is the last week (52 or 53) of the previous year even if it contains days from the new year.

This format was first standardized by POSIX.2-1992 and by ISO C99.

  • %w

The day of the week as a decimal number (range 0 through 6), Sunday being 0.

  • %W

The week number of the current year as a decimal number (range 00 through 53), starting with the first Monday as the first day of the first week. All days preceding the first Monday in the year are considered to be in week 00.

  • %x

The preferred date representation for the current locale.

  • %X

The preferred time of day representation for the current locale.

  • %y

The year without a century as a decimal number (range 00 through 99). This is equivalent to the year modulo 100.

  • %Y

The year as a decimal number, using the Gregorian calendar. Years before the year 1 are numbered 0, -1, and so on.

  • %z

RFC 822/*ISO 8601:1988* style numeric time zone (e.g., -0600 or +0100), or nothing if no time zone is determinable.

This format was first standardized by ISO C99 and by POSIX.1-2001 but was previously available as a GNU extension.

In the POSIX locale, a full RFC 822 timestamp is generated by the format %a, %d %b %Y %H:%M:%S %z (or the equivalent %a, %d %b %Y %T %z).

  • %Z

The time zone abbreviation (empty if the time zone can't be determined).

  • %%

A literal % character.

According to POSIX.1 every call to strftime checks the contents of the environment variable TZ before any output is produced.


sublist

Prototype: sublist(list, head_or_tail, max_elements)

Return type: slist

Description: Returns list of up to max_elements of list, obtained from head or tail depending on head_or_tail.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • head_or_tail: one of
    • head
    • tail
  • max_elements: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
      };

      "test_head9999" slist => sublist("test", "head", 9999);
      "test_head1" slist => sublist("test", "head", 1);
      "test_head0" slist => sublist("test", "head", 0);

      "test_tail9999" slist => sublist("test", "tail", 9999);
      "test_tail10" slist => sublist("test", "tail", 10);
      "test_tail2" slist => sublist("test", "tail", 2);
      "test_tail1" slist => sublist("test", "tail", 1);
      "test_tail0" slist => sublist("test", "tail", 0);

  reports:
      "The test list is $(test)";
      "This line should not appear: $(test_head0)";
      "The head(1) of the test list is $(test_head1)";
      "The head(9999) of the test list is $(test_head9999)";
      "This line should not appear: $(test_tail0)";
      "The tail(1) of the test list is $(test_tail1)";
      "The tail(10) of the test list is $(test_tail10)";
      "The tail(2) of the test list is $(test_tail2)";
      "The tail(9999) of the test list is $(test_tail9999)";
}

Output:

R: The test list is 1
R: The test list is 2
R: The test list is 3
R: The test list is one
R: The test list is two
R: The test list is three
R: The test list is long string
R: The test list is four
R: The test list is fix
R: The test list is six
R: The head(1) of the test list is 1
R: The head(9999) of the test list is 1
R: The head(9999) of the test list is 2
R: The head(9999) of the test list is 3
R: The head(9999) of the test list is one
R: The head(9999) of the test list is two
R: The head(9999) of the test list is three
R: The head(9999) of the test list is long string
R: The head(9999) of the test list is four
R: The head(9999) of the test list is fix
R: The head(9999) of the test list is six
R: The tail(1) of the test list is six
R: The tail(10) of the test list is 1
R: The tail(10) of the test list is 2
R: The tail(10) of the test list is 3
R: The tail(10) of the test list is one
R: The tail(10) of the test list is two
R: The tail(10) of the test list is three
R: The tail(10) of the test list is long string
R: The tail(10) of the test list is four
R: The tail(10) of the test list is fix
R: The tail(10) of the test list is six
R: The tail(2) of the test list is fix
R: The tail(2) of the test list is six
R: The tail(9999) of the test list is 1
R: The tail(9999) of the test list is 2
R: The tail(9999) of the test list is 3
R: The tail(9999) of the test list is one
R: The tail(9999) of the test list is two
R: The tail(9999) of the test list is three
R: The tail(9999) of the test list is long string
R: The tail(9999) of the test list is four
R: The tail(9999) of the test list is fix
R: The tail(9999) of the test list is six

Notes:


reglist

Prototype: reglist(list, regex)

Return type: boolean

Description: Returns whether the anchored regular expression regex matches any item in list.

Arguments:

  • list: string, in the range: @[(][a-zA-Z0-9_$(){}\[\].:]+[)]
  • regex: regular expression, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "nameservers" slist => {
                               "128.39.89.10",
                               "128.39.74.16",
                               "192.168.1.103",
                               "10.132.51.66"
      };
  classes:

      "am_name_server" expression => reglist(@(nameservers), "127\.0\.0\.1");
  reports:
    am_name_server::
      "127.0.0.1 is currently set as a nameserver";
    !am_name_server::
      "127.0.0.1 is NOT currently set as a nameserver";
}

Output:

R: 127.0.0.1 is NOT currently set as a nameserver

In the example above, the IP address in $(sys.ipv4[eth0]) must be escaped, so that the (.) characters in the IP address are not interpreted as the regular expression "match any" characters.


reverse

Prototype: reverse(list)

Return type: slist

Description: Reverses a list.

This is a simple function to reverse a list.

Arguments:

  • list : The name of the list variable to check, in the range [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "one", "two", "three",
      };

      "reversed" slist => reverse("test");

  reports:
      "Original list is $(test)";
      "The reversed list is $(reversed)";
}

Output:

R: Original list is 1
R: Original list is 2
R: Original list is 3
R: Original list is one
R: Original list is two
R: Original list is three
R: Original list is long string
R: The reversed list is three
R: The reversed list is two
R: The reversed list is one
R: The reversed list is long string
R: The reversed list is 3
R: The reversed list is 2
R: The reversed list is 1

See also: filter(), grep(), every(), some(), and none().


accumulated

Prototype: accumulated(years, months, days, hours, minutes, seconds)

Return type: int

Description: Convert an accumulated amount of time into a system representation.

The accumulated function measures total accumulated runtime. Arguments are applied additively, so that accumulated(0,0,2,27,90,0) means "2 days, 27 hours and 90 minutes of runtime" ". However, you are strongly encouraged to keep your usage of accumulated sensible and readable; for example, accumulated(0,0,0,48,0,0) or accumulated(0,0,0,0,90,0).

Arguments:

  • years, in the range 0,1000

Years of run time. For convenience in conversion, a year of runtime is always 365 days (one year equals 31,536,000 seconds).

  • month, in the range 0,1000

Months of run time. For convenience in conversion, a month of runtime is always equal to 30 days of runtime (one month equals 2,592,000 seconds).

  • days, in the range 0,1000

Days of runtime (one day equals 86,400 seconds)

  • hours, in the range 0,1000

Hours of runtime

  • minutes, in the range 0,1000

Minutes of runtime 0-59

  • seconds, in the range 0,40000

Seconds of runtime

Example:

    bundle agent testbundle
    {
      processes:

       ".*"

          process_count   => anyprocs,
          process_select  => proc_finder;

      reports:

       any_procs::

         "Found processes in range";
    }

    body process_select proc_finder
    {
      ttime_range => irange(accumulated(0,0,0,0,2,0),accumulated(0,0,0,0,20,0));
      process_result => "ttime";
    }

    body process_count anyprocs
    {
      match_range => "0,0";
      out_of_range_define => { "any_procs" };
    }

In the example we look for processes that have accumulated between 2 and 20 minutes of total run time.


peerleader

Prototype: peerleader(filename, regex, groupsize)

Return type: string

Description: Returns the current host's partition peer leader.

So given groupsize 3 and the file

a
b
c
# this is a comment d
e

The peer leader of host b will be host a.

Given a list of host names in filename, one per line, and excluding comment lines starting with the unanchored regular expression regex, CFEngine partitions the host list into groups of up to groupsize. Each group's peer leader is the first host in the group.

The current host (unqualified or fully qualified) should belong to this file if it is expected to interact with the others. The function fails otherwise.

If the current host name (fully qualified or unqualified) is the peer leader, the string localhost is used instead of the host name.

Arguments:

  • filename: string, in the range: "?(/.*)
  • regex: regular expression, in the range: .*
  • groupsize: int, in the range: 2,64

groupsize must be between 2 and 64 to avoid nonsensical promises.

Example:

Prepare:

echo alpha > /tmp/cfe_hostlist
echo beta >> /tmp/cfe_hostlist
echo gamma >> /tmp/cfe_hostlist
echo "Set HOSTNAME appropriately beforehand"
echo "$HOSTNAME" >> /tmp/cfe_hostlist
echo "Delta Delta Delta may I help ya help ya help ya"
echo delta1 >> /tmp/cfe_hostlist
echo delta2 >> /tmp/cfe_hostlist
echo delta3 >> /tmp/cfe_hostlist
echo may1.I.help.ya >> /tmp/cfe_hostlist
echo may2.I.help.ya >> /tmp/cfe_hostlist
echo may3.I.help.ya >> /tmp/cfe_hostlist

Run:

body common control
{
      bundlesequence => { "peers" };
}

bundle agent peers
{
  vars:

      "mygroup" slist => peers("/tmp/cfe_hostlist","#.*",4);

      "myleader" string => peerleader("/tmp/cfe_hostlist","#.*",4);

      "all_leaders" slist => peerleaders("/tmp/cfe_hostlist","#.*",4);

  reports:

      # note that the current host name is fourth in the host list, so
      # its peer group is the first 4-host group, minus the host itself.
      "/tmp/cfe_hostlist mypeer $(mygroup)";
      # note that the current host name is fourth in the host list, so
      # the peer leader is "alpha"
      "/tmp/cfe_hostlist myleader $(myleader)";
      "/tmp/cfe_hostlist another leader $(all_leaders)";
}

Output:

R: /tmp/cfe_hostlist mypeer alpha
R: /tmp/cfe_hostlist mypeer beta
R: /tmp/cfe_hostlist mypeer gamma
R: /tmp/cfe_hostlist myleader alpha
R: /tmp/cfe_hostlist another leader alpha
R: /tmp/cfe_hostlist another leader delta1
R: /tmp/cfe_hostlist another leader may2.I.help.ya

hash

Prototype: hash(input, algorithm)

Return type: string

Description: Return the hash of input using the hash algorithm.

Hash functions are extremely sensitive to input. You should not expect to get the same answer from this function as you would from every other tool, since it depends on how whitespace and end of file characters are handled.

Arguments:

  • input: string, in the range: .*
  • algorithm: one of
    • md5
    • sha1
    • sha256
    • sha384
    • sha512

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example

{
  vars:

      "md5" string => hash("Cfengine is not cryptic","md5");
      "sha256" string => hash("Cfengine is not cryptic","sha256");
      "sha384" string => hash("Cfengine is not cryptic","sha384");
      "sha512" string => hash("Cfengine is not cryptic","sha512");

  reports:

      "Hashed to: md5 $(md5)";
      "Hashed to: sha256 $(sha256)";
      "Hashed to: sha384 $(sha384)";
      "Hashed to: sha512 $(sha512)";

}

Output:

R: Hashed to: md5 2036af0ee58d6d9dffcc6507af92664f
R: Hashed to: sha256 e2fb1927976bfe1ea3987c1a731c75e8ac1453d22a21811dc352db5e62d3f73c
R: Hashed to: sha384 b348c0b83ccd9ee12673f5daaba3ee5f49c42906540936bb16cf9d2001ed502b8c56f6e36b8389ab596febb529aab17f
R: Hashed to: sha512 29ce0883afbe7740bb2a016735499ae5a0a9b067539018ce6bb2c309a7e885c2d7da64744956e9f151bc72ec8dc19f85efd85eb0a73cbf1e829a15ac9ac35358

regldap

This function is only available in CFEngine Enterprise.

Prototype: regldap(uri, dn, filter, record, scope, regex, security)

Return type: boolean

The return value is cached.

Description: Returns whether the regular expression regex matches a value item in the LDAP search.

This function retrieves a single field from all matching LDAP records identified by the search parameters and compares it to the regular expression regex.

Arguments:

  • uri: string, in the range: .*
  • dn: string, in the range: .*
  • filter: string, in the range: .*
  • record: string, in the range: .*
  • scope: one of
    • subtree
    • onelevel
    • base
  • regex: regular expression, in the range: .*
  • security: one of
    • none
    • ssl
    • sasl

dn specifies the distinguished name, an ldap formatted name built from components, e.g. "dc=cfengine,dc=com". filter is an ldap search, e.g. "(sn=User)", and record is the name of the single record to be retrieved and matched against regex, e.g. uid. Which security values are supported depends on machine and server capabilities.

Example:

classes:

   "found" expression => regldap(
                                "ldap://ldap.example.org",
                                "dc=cfengine,dc=com",
                                "(sn=User)",
                                "uid",
                                "subtree",
                                "jon.*",
                                "none"
                                );

eval

Prototype: eval(expression, mode, options)

Return type: string

Description: Returns expression evaluated according to mode and options. Currently only the math mode with infix option is supported for evaluating traditional math expressions.

All the math is done with the C double type internally. The results are returned as a string

The supported infix mathematical syntax, in order of precedence, is:

  • ( and ) parentheses for grouping expressions
  • ^ operator for exponentiation
  • * and / operators for multiplication and division
  • % operators for modulo operation
  • + and - operators for addition and subtraction
  • == "close enough" operator to tell if two expressions evaluate to the same number, with a tiny margin to tolerate floating point errors. It returns 1 or 0.

The numbers can be in any format acceptable to the C scanf function with the %lf format specifier, followed by the k, m, g, t, or p SI units. So e.g. -100 and 2.34m are valid numbers.

In addition, the following constants are recognized:

  • e: 2.7182818284590452354
  • log2e: 1.4426950408889634074
  • log10e: 0.43429448190325182765
  • ln2: 0.69314718055994530942
  • ln10: 2.30258509299404568402
  • pi: 3.14159265358979323846
  • pi_2: 1.57079632679489661923 (pi over 2)
  • pi_4: 0.78539816339744830962 (pi over 4)
  • 1_pi: 0.31830988618379067154 (1 over pi)
  • 2_pi: 0.63661977236758134308 (2 over pi)
  • 2_sqrtpi: 1.12837916709551257390 (2 over square root of pi)
  • sqrt2: 1.41421356237309504880 (square root of 2)
  • sqrt1_2: 0.70710678118654752440 (square root of 1/2)

The following functions can be used, with parentheses:

  • ceil and floor: the next highest or the previous highest integer
  • log10, log2, log
  • sqrt
  • sin, cos, tan, asin, acos, atan
  • abs: absolute value
  • step: 0 if the argument is negative, 1 otherwise

Arguments:

  • mode: string, in the range: .*
  • options: one of
    • math
  • expression: one of
    • infix

Example:

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "values[0]" string => "x"; # bad
      "values[1]" string => "+ 200"; # bad
      "values[2]" string => "200 + 100";
      "values[3]" string => "200 - 100";
      "values[4]" string => "- - -"; # bad
      "values[5]" string => "2 + 3 - 1";
      "values[6]" string => ""; # 0
      "values[7]" string => "3 / 0"; # inf but not an error
      "values[8]" string => "3^3";
      "values[9]" string => "-1^2.1"; # -nan but not an error
      "values[10]" string => "sin(20)";
      "values[11]" string => "cos(20)";
      "values[19]" string => "20 % 3"; # remainder
      "values[20]" string => "sqrt(0.2)";
      "values[21]" string => "ceil(3.5)";
      "values[22]" string => "floor(3.4)";
      "values[23]" string => "abs(-3.4)";
      "values[24]" string => "-3.4 == -3.4";
      "values[25]" string => "-3.400000 == -3.400001";
      "values[26]" string => "e";
      "values[27]" string => "pi";
      "values[28]" string => "100m"; # 100 million
      "values[29]" string => "100k"; # 100 thousand

      "indices" slist => getindices("values");

      "eval[$(indices)]" string => eval("$(values[$(indices)])", "math", "infix");

  reports:
      "math/infix eval('$(values[$(indices)])') = '$(eval[$(indices)])'";
}

Output:

2013-09-14T08:34:16-0400     info: eval error: expression could not be parsed (input 'x')
2013-09-14T08:34:16-0400     info: eval error: expression could not be parsed (input '+ 200')
2013-09-14T08:34:16-0400     info: eval error: expression could not be parsed (input '- - -')
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('x') = ''
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('+ 200') = ''
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('floor(3.4)') = '3.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('sqrt(0.2)') = '0.447214'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('2 + 3 - 1') = '4.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('sin(20)') = '0.912945'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('200 - 100') = '100.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('20 % 3') = '2.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('ceil(3.5)') = '4.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('200 + 100') = '300.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('pi') = '3.141593'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('- - -') = ''
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('-3.400000 == -3.400001') = '0.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('3 / 0') = 'inf'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('e') = '2.718282'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('cos(20)') = '0.408082'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('') = '0.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('-1^2.1') = '-nan'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('abs(-3.4)') = '3.400000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('-3.4 == -3.4') = '1.000000'
2013-09-14T08:34:16-0400   notice: R: math/prefix eval('3^3') = '27.000000'

makerule

Prototype: makerule(target, sources)

Return type: boolean

Description: Evaluates whether a target file needs to be built or rebuilt from one or more sources files.

The function is provided to emulate the semantics of the Unix make program.

In a traditional Makefile, rules take the form

 target: source1 source2 ..
 (tab) commands

The top line evaluates to a predicate for executing a number of commands, which is true if the target file does not exist, or if any of the sources dependencies in the list has been changed since the target was last built.

The makerule function emulates the same semantics and sets a class if the target needs to be built or rebuit, i.e. if the top line of an equivalent makefile is true.

Arguments:

  • target: string, in the range: "?(/.*)
  • sources: string, in the range: .*

The sources argument may be either a scalar or a list reference. If the sources argument is the name of a list variable, then the entire list of sources is used to determine whether the target needs rebuilding.

Example:

classes:

 "build_me" expressions => makerule("/tmp/target", "/tmp/source.c");

commands:

   build_me::

      "/usr/bin/gcc -o /tmp/target /tmp/source.c";

hostswithclass

This function is only available in CFEngine Enterprise.

Prototype: hostswithclass(class, field)

Return type: slist

Description: Returns a list from the CFEngine Database with the information field of hosts on which classs is set.

On CFEngine Enterprise, this function can be used to return a list of hostnames or ip-addresses of hosts that have a given class set. Note that this function only works locally on the hub, but allows the hub to construct custom configuration files for (classes of) hosts.

Arguments:

  • class: string, in the range: [a-zA-Z0-9_]+
  • field: one of
    • name
    • address

Example:

    bundle agent debian_hosts
    {
    vars:

      am_policy_hub::
        "host_list" slist => hostswithclass( "debian", "name" );

    files:
      am_policy_hub::
        "/tmp/master_config.cfg"
             edit_line => insert_lines("host=$(host_list)"),
                create => "true";
    }

History: Was introduced in 3.3.0, Nova 2.2.0 (2012)

See also: hubknowledge(), remotescalar(), remoteclassesmatching()


isdir

Prototype: isdir(filename)

Return type: boolean

Description: Returns whether the named object filename is a directory.

The CFEngine process must have access to filename in order for this to work.

Arguments:

  • filename: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "isdir" expression => isdir("/");

  reports:

    isdir::

      "Directory exists..";

}

Output:

R: Directory exists..

isgreaterthan

Prototype: isgreaterthan(value1, value2)

Return type: boolean

Description: Returns whether value1 is greater than value2.

The comparison is made numerically if possible. If the values are strings, the comparison is lexical (based on C's strcmp()).

Arguments:

  • value1: string, in the range: .*
  • value2: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "ok" expression => isgreaterthan("1","0");

  reports:

    ok::

      "Assertion is true";

    !ok::

      "Assertion is false";

}

Output:

R: Assertion is true

filter

Prototype: filter(filter, list, is_regex, invert, max_return)

Return type: slist

Description: Transforms a list or data container into a list subset thereof.

This is a generic filtering function that returns a list of up to max_return elements in list that match the filtering rules specified in filter, is_regex and invert.

Arguments:

  • filter : Anchored regular expression or static string to find, in the range .*
  • list : The name of the list variable or data container to check, in the range [a-zA-Z0-9_$(){}\[\].:]+
  • is_regex_ : Boolean

Treat filter as a regular expression or as a static string.

  • invert : Boolean

Invert filter.

  • max_return : Maximum number of elements to return in the range 0,999999999

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "one", "two", "three",
      };

      "test2" data => parsejson('[1,2,3, "ab", "c"]');

      "test_filtergrep" slist => filter("[0-9]", test, "true", "false", 999);
      "test_exact1" slist => filter("one", test, "false", "false", 999);
      "test_exact2" slist => filter(".", test, "false", "false", 999);
      "test_invert" slist => filter("[0-9]", test, "true", "true", 999);
      "test_max2" slist => filter(".*", test, "true", "false", 2);
      "test_max0" slist => filter(".*", test, "true", "false", 0);
      "test_grep" slist => grep("[0-9]", test);

      "test2_filtergrep" slist => filter("[0-9]", test2, "true", "false", 999);
      "test2_exact1" slist => filter("one", test2, "false", "false", 999);
      "test2_exact2" slist => filter(".", test2, "false", "false", 999);
      "test2_invert" slist => filter("[0-9]", test2, "true", "true", 999);
      "test2_max2" slist => filter(".*", test2, "true", "false", 2);
      "test2_max0" slist => filter(".*", test2, "true", "false", 0);
      "test2_grep" slist => grep("[0-9]", test2);

      "todo" slist => { "test", "test2", "test_filtergrep", "test_exact1",
                        "test_exact2", "test_invert", "test_max2",
                        "test_max0", "test_grep", "test2_filtergrep",
                        "test2_exact1", "test2_exact2",
                        "test2_invert", "test2_max2", "test2_max0",
                        "test2_grep"};

      "$(todo)_str" string => format("%S", $(todo));
      "tests" slist => { "test", "test2" };

  reports:
      "The $(tests) list is $($(tests)_str)";
      "The grepped list (only single digits from $(tests)) is $($(tests)_grep_str)";
      "The filter-grepped list (only single digits from $(tests)) is $($(tests)_grep_str)";
      "The filter-exact list, looking for only 'one' in $(tests), is $($(tests)_exact1_str)";
      "This list should be empty, the '.' is not literally in the list $(tests): $($(tests)_exact2_str)";
      "The filter-invert list, looking for non-digits in $(tests), is $($(tests)_invert_str)";
      "The filter-bound list, matching at most 2 items from the whole list $(tests), is $($(tests)_max2_str)";
      "This list should be empty because 0 elements of $(tests) were requested: $($(tests)_max0_str)";
}

Output:

R: The test list is { "1", "2", "3", "one", "two", "three", "long string", "one", "two", "three" }
R: The test2 list is [1,2,3,"ab","c"]
R: The grepped list (only single digits from test) is { "1", "2", "3" }
R: The grepped list (only single digits from test2) is { "1", "2", "3" }
R: The filter-grepped list (only single digits from test) is { "1", "2", "3" }
R: The filter-grepped list (only single digits from test2) is { "1", "2", "3" }
R: The filter-exact list, looking for only 'one' in test, is { "one", "one" }
R: The filter-exact list, looking for only 'one' in test2, is { --empty-list-- }
R: This list should be empty, the '.' is not literally in the list test: { --empty-list-- }
R: This list should be empty, the '.' is not literally in the list test2: { --empty-list-- }
R: The filter-invert list, looking for non-digits in test, is { "one", "two", "three", "long string", "one", "two", "three" }
R: The filter-invert list, looking for non-digits in test2, is { "ab", "c" }
R: The filter-bound list, matching at most 2 items from the whole list test, is { "1", "2" }
R: The filter-bound list, matching at most 2 items from the whole list test2, is { "1", "2" }
R: This list should be empty because 0 elements of test were requested: { --empty-list-- }
R: This list should be empty because 0 elements of test2 were requested: { --empty-list-- }

See also: grep(), every(), some(), and none().


hashmatch

Prototype: hashmatch(filename, algorithm, hash)

Return type: boolean

Description: Compute the hash of file filename using the hash algorithm and test if it matches hash.

This function may be used to determine whether a system has a particular version of a binary file (e.g. software patch).

Arguments:

  • filename: string, in the range: "?(/.*)
  • algorithm: one of
    • md5
    • sha1
    • sha256
    • sha384
    • sha512
  • hash: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

hash is an ASCII representation of the hash for comparison.

Example:

bundle agent example
{
classes:

  "matches" expression => hashmatch("/etc/passwd","md5","c5068b7c2b1707f8939b283a2758a691");

reports:

  matches::

    "File has correct version";

}

maparray

Prototype: maparray(pattern, array)

Return type: slist

Description: Returns a list with each array element modified by a pattern.

The $(this.k) and $(this.v) variables expand to the key and value of the array element, similar to the way this is available for maplist.

If a value in the array is an slist, you'll get one result for each value (implicit looping).

The order of the array keys is not guaranteed. Use the sort function if you need order in the resulting output.

Arguments:

  • pattern: string, in the range: .*
  • array: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "run" };
}

bundle agent run
{
  vars:
      "static[2]" string => "lookup 2";
      "static[two]" string => "lookup two";
      "static[big]" string => "lookup big";
      "static[small]" string => "lookup small";

      "todo[1]" string => "2";
      "todo[one]" string => "two";
      "todo[3999]" slist => { "big", "small" };
      "map" slist => maparray("key='$(this.k)', static lookup = '$(static[$(this.v)])', value='$(this.v)'", "todo");

  reports:
      "mapped array: $(map)";
}

Output:

R: mapped array: key='3999', static lookup = 'lookup big', value='big'
R: mapped array: key='3999', static lookup = 'lookup small', value='small'
R: mapped array: key='one', static lookup = 'lookup two', value='two'
R: mapped array: key='1', static lookup = 'lookup 2', value='2'

max

Prototype: max(list, sortmode)

Return type: string

Description: Return the maximum of the items in list according to sortmode (same sort modes as in sort()).

list can be a data container or a regular list.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • sortmode: one of
    • lex
    • int
    • real
    • IP
    • ip
    • MAC
    • mac

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      # the behavior will be the same whether you use a data container or a list
      # "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
      "mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
      "mylist_str" string => format("%S", mylist);

      "max_int" string => max(mylist, "int");
      "max_lex" string => max(mylist, "lex");
      "max_ip" string => max(mylist, "ip");

      "min_int" string => min(mylist, "int");
      "min_lex" string => min(mylist, "lex");
      "min_ip" string => min(mylist, "ip");

      "mean" real => mean(mylist);
      "variance" real => variance(mylist);

  reports:
      "my list is $(mylist_str)";

      "mean is $(mean)";
      "variance is $(variance) (use eval() to get the standard deviation)";

      "max int is $(max_int)";
      "max IP is $(max_ip)";
      "max lexicographically is $(max_lex)";

      "min int is $(min_int)";
      "min IP is $(min_ip)";
      "min lexicographically is $(min_lex)";
}

Output:

R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1

Notes:

History: Was introduced in version 3.6.0 (2014)

See also: sort(), variance(), sum(), mean(), min()


laterthan

Prototype: laterthan(year, month, day, hour, minute, second)

Return type: boolean

Description: Returns whether the current time is later than the given date and time.

The specified date/time is an absolute date in the local timezone. Note that, unlike some other functions, the month argument is 1-based (i.e. 1 corresponds to January).

Arguments:

  • year: int, in the range: 0,10000
  • month: int, in the range: 0,1000
  • day: int, in the range: 0,1000
  • hour: int, in the range: 0,1000
  • minute: int, in the range: 0,1000
  • second: int, in the range: 0,40000

Example:

bundle agent example
{
    classes:

      "after_deadline" expression => laterthan(2000,1,1,0,0,0);
    reports:
      after_deadline::
        "deadline has passed";
}

See also: on()


getusers

Prototype: getusers(exclude_names, exclude_ids)

Return type: slist

Description: Returns a list of all users defined, except those names in exclude_names and uids in exclude_ids

Arguments:

  • exclude_names: string, in the range: .*
  • exclude_ids: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "allusers" slist => getusers("","");
      "root_list" slist => { "root" };
      # this will get just the root users out of the full user list
      "justroot" slist => intersection(allusers, root_list);

  reports:
      "Found just the root user: $(justroot)";
}

Output:

R: Found just the root user: root

Notes: This function is currently only available on Unix-like systems.

History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010).


findfiles

Prototype: findfiles(glob1, glob2, ...)

Return type: slist

Description: Return the list of files that match any of the given glob patterns.

This function searches for the given glob patterns in the local filesystem, returning files or directories that match. Note that glob patterns are not regular expressions. They match like Unix shells:

  • * matches any filename or directory at one level, e.g. *.cf will match all files in one directory that end in .cf but it won't search across directories. */*.cf on the other hand will look two levels deep.
  • ? matches a single letter
  • [a-z] matches any letter from a to z
  • {x,y,anything} will match x or y or anything.

This function, used together with the bundlesmatching function, allows you to do dynamic inputs and a dynamic bundle call chain.

Example:

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "findtmp" slist => findfiles("/[tT][mM][pP]");
      # or find all .txt files under /tmp, up to 6 levels deep...
      # "findtmp" slist => findfiles("/tmp/**/*.txt");
  reports:
      "All files that match '/[tT][mM][pP]' = $(findtmp)";
}

Output:

R: All files that match '/[tT][mM][pP]' = /tmp

See also: bundlesmatching().


isexecutable

Prototype: isexecutable(filename)

Return type: boolean

Description: Returns whether the named object filename has execution rights for the current user.

Arguments:

  • filename: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      "yes" expression => isexecutable("/bin/ls");
  reports:
    yes::
      "/bin/ls is an executable file";
}

Output:

R: /bin/ls is an executable file

History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010)


product

Prototype: product(list)

Return type: real

Description: Returns the product of the reals in list.

This function might be used for simple ring computation. Of course, you could easily combine product with readstringarray or readreallist etc., to collect summary information from a source external to CFEngine.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:

      "series" rlist => { "1.1", "2.2", "3.3", "5.5", "7.7" };

      "prod" real => product("series");
      "sum"  real => sum("series");

  reports:
      "Product result: $(prod) > $(sum)";
}

Output:

R: Product result: 338.207100 > 19.800000

History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010)


accessedbefore

Prototype: accessedbefore(newer, older)

Return type: boolean

Description: Compares the atime fields of two files.

Return true if newer was accessed before older.

Arguments:

  • newer: string, in the range: "?(/.*)
  • older: string, in the range: "?(/.*)

Example:

Prepare:

touch -a -t '200102031234.56' /tmp/earlier
touch -a -t '200202031234.56' /tmp/later

Run:

body common control
{
      bundlesequence  => { "example" };
}

bundle agent example
{
  classes:
      "do_it" expression => accessedbefore("/tmp/earlier","/tmp/later");

  reports:
    do_it::
      "The secret changes have been accessed after the reference time";
}

Output:

R: The secret changes have been accessed after the reference time

translatepath

Prototype: translatepath(path)

Return type: string

Description: Translate separators in path from Unix style to the host's native style and returns the result.

Takes a string argument with slashes as path separators and translate these to the native format for path separators on the host. For example translatepath("a/b/c") would yield "a/b/c" on Unix platforms, but "a\b\c" on Windows.

Arguments:

  • path: string, in the range: "?(/.*)

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      "inputs_dir" string => translatepath("/a/b/c/inputs");

  reports:

    windows::
      "The path has backslashes: $(inputs_dir)";

    !windows::
      "The path has slashes: $(inputs_dir)";
}

Output:

R: The path has slashes: /a/b/c/inputs

Notes: Be careful when using this function in combination with regular expressions, since backslash is also used as escape character in regex's. For example, in the regex dir/.abc, the dot represents the regular expression "any character", while in the regex dir\.abc, the backslash-dot represents a literal dot character.


parsestringarrayidx

Prototype: parsestringarrayidx(array, input, comment, split, maxentries, maxbytes)

Return type: int

Description: Populates the two-dimensional array array with up to maxentries fields from the first maxbytes bytes of the string input.

This function mirrors the exact behavior of readstringarrayidx(), but reads data from a variable instead of a file. By making data readable from a variable, data driven policies can be kept inline.

Arguments:

  • array: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • input: string, in the range: .*
  • comment: string, in the range: .*
  • split: string, in the range: .*
  • maxentries: int, in the range: 0,99999999999
  • maxbytes: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test(f)
{
  vars:
      # Define data inline for convenience
      "table"   string => "one: a
                           two: b
                         three: c";

      #######################################

      "dim" int => parsestringarrayidx(
                    "items",
                    "$(table)",
                    "\s*#[^\n]*",
                    ":",
                    "1000",
                    "200000"
      );

      "keys" slist => getindices("items");
      "sorted_keys" slist => sort(keys, "int");

  reports:
      "item $(sorted_keys) has column 0 = $(items[$(sorted_keys)][0]) and column 1 = $(items[$(sorted_keys)][1])";
}

Output:

R: item 0 has column 0 = one and column 1 =  a
R: item 1 has column 0 =                            two and column 1 =  b
R: item 2 has column 0 =                          three and column 1 =  c

History: Was introduced in version 3.1.5, Nova 2.1.0 (2011)


canonify

Prototype: canonify(text)

Return type: string

Description: Convert an arbitrary string text into a legal class name.

This function turns arbitrary text into class data.

Arguments:

  • text: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "component" string => "/var/cfengine/bin/cf-serverd";
      "canon" string => canonify("$(component)");

  reports:
      "canonified component == $(canon)";
}

Output:

R: canonified component == _var_cfengine_bin_cf_serverd

See also: classify(), canonifyuniquely().


string_reverse

Prototype: string_reverse(data)

Return type: string

Description: Returns data reversed.

Arguments:

  • data: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "reversed"
      string =>  string_reverse("abc"); # will contain "cba"
  reports:
      "reversed abs = $(reversed)";
}

Output:

R: reversed abs = cba

History: Introduced in CFEngine 3.6

See also: string_head(), string_tail(), string_length().


variablesmatching

Prototype: variablesmatching(name, tag1, tag2, ...)

Return type: slist

Description: Return the list of variables matching name and any tags given. Both name and tags are regular expressions.

This function searches for the given anchored name and tag1, tag2, ... regular expressions in the list of currently defined variables.

When one or more tags are given, the variables with tags matching any of the given anchored regular expressions are returned (logical OR semantics). For example, if one variable has tag inventory, a second variable has tag time_based but not inventory, both are returned by variablesmatching(".*", "inventory", "time_based"). If you want logical AND semantics instead, you can make two calls to the function with one tag in each call and use the intersection function on the return values.

Variable tags are set using the meta attribute.

Arguments:

  • regex: regular expression, in the range: .*

Example:

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "all" slist => variablesmatching(".*");
      "v" slist => variablesmatching("default:sys.cf_version.*");
  reports:
      "Variables matching 'default:sys.cf_version.*' = $(v)";
}

Output:

R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_major
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_patch
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_minor

History: Introduced in CFEngine 3.6


getvariablemetatags

Prototype: getvariablemetatags(varname)

Return type: slist

Description: Returns the list of meta tags for variable varname.

Make sure you specify the correct scope when supplying the name of the variable.

Arguments:

  • varname: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "v" string => "myvalue", meta => { "mytag" };
      "vtags" slist => getvariablemetatags("example.v");

  reports:
      "Found tags: $(vtags)";

}

Output:

R: Found tags: source=promise
R: Found tags: mytag

Notes:

See also: getclassmetatags()


maplist

Prototype: maplist(pattern, list)

Return type: slist

Description: Return a list with each element in list modified by a pattern.

The $(this) variable expands to the currently processed entry from list. This is essentially like the map() function in Perl, and applies to lists.

Arguments:

  • pattern: string, in the range: .*
  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle common g
{
  vars:

      "otherlist" slist => { "x", "y", "z" };
}


bundle agent example

{
  vars:

      "oldlist" slist => { "a", "b", "c" };

      "newlist1" slist => maplist("Element ($(this))","@(g.otherlist)");
      "newlist2" slist => maplist("Element ($(this))",@(oldlist));

  reports:
      "Transform: $(newlist1)";
      "Transform: $(newlist2)";
}

Output:

R: Transform: Element (x)
R: Transform: Element (y)
R: Transform: Element (z)
R: Transform: Element (a)
R: Transform: Element (b)
R: Transform: Element (c)

History: Was introduced in 3.3.0, Nova 2.2.0 (2011)


string_downcase

Prototype: string_downcase(data)

Return type: string

Description: Returns data in lower case.

Arguments:

  • data: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "downcase" string => string_downcase("ABC"); # will contain "abc"
  reports:
      "downcased ABC = $(downcase)";
}

Output:

R: downcased ABC = abc

History: Introduced in CFEngine 3.6

See also: string_upcase().


isvariable

Prototype: isvariable(var)

Return type: boolean

Description: Returns whether a variable named var is defined.

The variable need only exist. This says nothing about its value. Use regcmp to check variable values.

Arguments:

  • var: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "bla" string => "xyz..";

  classes:

      "exists" expression => isvariable("bla");

  reports:

    exists::

      "Variable exists: \"$(bla)\"..";

}

Output:

R: Variable exists: "xyz.."..

classesmatching

Prototype: classesmatching(name, tag1, tag2, ...)

Return type: slist

Description: Return the list of set classes matching name and any tags given. Both name and tags are regular expressions. name is required, tags are optional.

This function searches for the given unanchored name and optionally tag1, tag2, ... regular expression in the list of currently set classes. The search order is hard, soft, then local to the current bundle.

When any tags are given, only the classes with those tags matching the given unanchored regular expressions are returned. Class tags are set using the meta attribute.

Arguments:

  • name: string, in the range: .*

Example:

body common control
{
      bundlesequence => { run };
}

bundle agent run
{
  vars:
      "all" slist         => classesmatching(".*");
      "c" slist           => classesmatching("cfengine");
      "c_plus_plus" slist => classesmatching("cfengine", "plus");

      # order of classes is not guaranteed
      "internal_environment_unsorted" slist =>
          classesmatching(".*", 'cfe_internal', 'source=environment');
      "internal_environment" slist =>
          sort(internal_environment_unsorted, lex);

  reports:
      # you may find this list of all classes interesting but it
      # produces different output every time, so it's commented out here
      # "All classes = '$(all)'";

      "All classes with the 'cfe_internal' and 'source=environment' tags = '$(internal_environment)'";

      "Classes matching 'cfengine' = '$(c)'";

      # this should produce no output
      "Classes matching 'cfengine' with the 'plus' tag = $(c_plus_plus)";
}

Output:

R: All classes with the 'cfe_internal' and 'source=environment' tags = '_cfe_output_testing'
R: All classes with the 'cfe_internal' and 'source=environment' tags = 'agent'
R: All classes with the 'cfe_internal' and 'source=environment' tags = 'opt_dry_run'
R: Classes matching 'cfengine' = 'cfengine'

Note: This function replaces the allclasses.txt static file available in older versions of CFEngine.

History: Introduced in CFEngine 3.6


getgid

Prototype: getgid(groupname)

Return type: int

Description: Return the integer group id of the group groupname on this host.

If the named group does not exist, the function will fail and the variable will not be defined.

Arguments:

  • groupname: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
    linux|solaris|hpux::
      "gid" int => getgid("root");
    freebsd|darwin|openbsd::
      "gid" int => getgid("wheel");
    aix::
      "gid" int => getgid("system");

  reports:
      "root's gid is $(gid)";
}

Output:

R: root's gid is 0

Notes: On Windows, which does not support group ids, the variable will not be defined.


splitstring

Prototype: splitstring(string, regex, maxent)

Return type: slist

Description: Splits string into at most maxent substrings wherever regex occurs, and returns the list with those strings.

The regular expression is unanchored.

If the maximum number of substrings is insufficient to accommodate all the entries, the final entry in the generated slist will contain the rest of the un-split string.

Arguments:

  • string: string, in the range: .*
  • regex: regular expression, in the range: .*
  • maxent: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:

      "split1" slist => splitstring("one:two:three",":","10");
      "split2" slist => splitstring("one:two:three",":","1");
      "split3" slist => splitstring("alpha:xyz:beta","xyz","10");

  reports:

      "split1: $(split1)";  # will list "one", "two", and "three"
      "split2: $(split2)";  # will list "one" and "two:three"
      "split3: $(split3)";  # will list "alpha:" and ":beta"

}

Output:

R: split1: one
R: split1: two
R: split1: three
R: split2: one
R: split3: alpha:
R: split3: :beta

History: Deprecated in CFEngine 3.6 in favor of string_split

See also: string_split()


peerleaders

Prototype: peerleaders(filename, regex, groupsize)

Return type: slist

Description: Returns a list of partition peer leaders from a file of host names.

Given a list of host names in filename, one per line, and excluding comment lines starting with the unanchored regular expression regex, CFEngine partitions the host list into groups of up to groupsize. Each group's peer leader is the first host in the group.

So given groupsize 2 and the file

a
b
c
# this is a comment d
e

The peer leaders will be a and c.

The current host name does not need to belong to this file. If it's found (fully qualified or unqualified), the string localhost is used instead of the host name.

Arguments:

  • filename: string, in the range: "?(/.*)
  • regex: regular expression, in the range: .*
  • groupsize: int, in the range: 2,64

groupsize must be between 2 and 64 to avoid nonsensical promises.

Example:

Prepare:

echo alpha > /tmp/cfe_hostlist
echo beta >> /tmp/cfe_hostlist
echo gamma >> /tmp/cfe_hostlist
echo "Set HOSTNAME appropriately beforehand"
echo "$HOSTNAME" >> /tmp/cfe_hostlist
echo "Delta Delta Delta may I help ya help ya help ya"
echo delta1 >> /tmp/cfe_hostlist
echo delta2 >> /tmp/cfe_hostlist
echo delta3 >> /tmp/cfe_hostlist
echo may1.I.help.ya >> /tmp/cfe_hostlist
echo may2.I.help.ya >> /tmp/cfe_hostlist
echo may3.I.help.ya >> /tmp/cfe_hostlist

Run:

body common control
{
      bundlesequence => { "peers" };
}

bundle agent peers
{
  vars:

      "mygroup" slist => peers("/tmp/cfe_hostlist","#.*",4);

      "myleader" string => peerleader("/tmp/cfe_hostlist","#.*",4);

      "all_leaders" slist => peerleaders("/tmp/cfe_hostlist","#.*",4);

  reports:

      # note that the current host name is fourth in the host list, so
      # its peer group is the first 4-host group, minus the host itself.
      "/tmp/cfe_hostlist mypeer $(mygroup)";
      # note that the current host name is fourth in the host list, so
      # the peer leader is "alpha"
      "/tmp/cfe_hostlist myleader $(myleader)";
      "/tmp/cfe_hostlist another leader $(all_leaders)";
}

Output:

R: /tmp/cfe_hostlist mypeer alpha
R: /tmp/cfe_hostlist mypeer beta
R: /tmp/cfe_hostlist mypeer gamma
R: /tmp/cfe_hostlist myleader alpha
R: /tmp/cfe_hostlist another leader alpha
R: /tmp/cfe_hostlist another leader delta1
R: /tmp/cfe_hostlist another leader may2.I.help.ya

readfile

Prototype: readfile(filename, maxbytes)

Return type: string

Description: Returns the first maxbytes bytes from file filename. When maxbytes is 0, the maximum possible bytes will be read from the file (but see Notes below).

Arguments:

  • filename: string, in the range: "?(/.*)
  • maxbytes: int, in the range: 0,99999999999

Example:

Prepare:

echo alpha > /tmp/cfe_hostlist
echo beta >> /tmp/cfe_hostlist
echo gamma >> /tmp/cfe_hostlist

Run:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "xxx"
      string => readfile( "/tmp/cfe_hostlist" , "5" );
  reports:
      "first 5 characters of /tmp/cfe_hostlist: $(xxx)";
}

Output:

R: first 5 characters of /tmp/cfe_hostlist: alpha

Notes:

  • To reliably read files located within /proc or /sys directories, maxsize has to be set to 0.

  • At the moment, only 4095 bytes can fit into a string variable. This limitation may be removed in the future. If this should happen, a warning will be printed.

  • If you request more bytes than CFEngine can read into a string variable (e.g. 999999999), a warning will also be printed.

  • If either because you specified a large value, or you specified 0, more bytes are read than will fit in a string, the string is truncated to the maximum.

  • On Windows, the file will be read in text mode, which means that CRLF line endings will be converted to LF line endings in the resulting variable. This can make the variable length shorter than the size of the file being read.

History: Warnings about the size limit and the special 0 value were introduced in 3.6.0


rrange

Prototype: rrange(arg1, arg2)

Return type: rrange

Description: Define a range of real numbers for CFEngine internal use.

Arguments:

  • arg1: real, in the range: -9.99999E100,9.99999E100
  • arg2: real, in the range: -9.99999E100,9.99999E100

Notes: This is not yet used.


readjson

Prototype: readjson(filename, maxbytes)

Return type: data

Description: Parses JSON data from the first maxbytes bytes of file filename and returns the result as a data variable.

Arguments:

  • filename: string, in the range: "?(/.*)
  • maxbytes: int, in the range: 0,99999999999

Example:

    vars:

      "loadthis"

         data =>  readjson("/tmp/data.json", 4000);

See also: parsejson(), storejson(), mergedata(), and data documentation.


length

Prototype: length(list)

Return type: int

Description: Returns the length of list.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test

{
  vars:
      "test" slist => {
                        1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",
      };

      "length" int => length("test");
      "test_str" string => join(",", "test");

  reports:
      "The test list is $(test_str)";
      "The test list has $(length) elements";
}

Output:

R: The test list is 1,2,3,one,two,three,long string,four,fix,six,one,two,three
R: The test list has 13 elements

See also: nth().


string_upcase

Prototype: string_upcase(data)

Return type: string

Description: Returns data in uppercase.

Arguments:

  • data: string, in the range: .*

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:
      "upcase" string => string_upcase("abc"); # will contain "ABC"
  reports:
      "upcased abc: $(upcase)";
}

Output:

R: upcased abc: ABC

History: Introduced in CFEngine 3.6

See also: string_downcase().


now

Prototype: now()

Return type: int

Description: Return the time at which this agent run started in system representation.

In order to provide an immutable environment against which to converge, this value does not change during the execution of an agent.

Example:

    body file_select zero_age
    {
      mtime       => irange(ago(1,0,0,0,0,0),now);
      file_result => "mtime";
    }

getindices

Prototype: getindices(varref)

Return type: slist

Description: Returns the list of keys in varref which can be the name of an array or container.

Make sure you specify the correct scope when supplying the name of the variable.

Note the list which getindices returns is not guaranteed to be in any specific order.

In the case of a doubly-indexed array (such as parsestringarrayidx and friends produce), the primary keys are returned; i.e. if varref[i][j] exist for various i, j and you ask for the keys of varref, you get the i values. For each such i you can then ask for getindices("varref[i]") to get a list of the j values (and so on, for higher levels of indexing).

Arguments:

  • varref: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "ps[relayhost]"                  string => "[mymailrelay]:587";
      "ps[mydomain]"                   string => "iu.hio.no";
      "ps[smtp_sasl_auth_enable]"      string => "yes";
      "ps[smtp_sasl_password_maps]"    string => "hash:/etc/postfix/sasl-passwd";
      "ps[smtp_sasl_security_options]" string => "";
      "ps[smtp_use_tls]"               string => "yes";
      "ps[default_privs]"              string => "mailman";
      "ps[inet_protocols]"             string => "all";
      "ps[inet_interfaces]"            string => "127.0.0.1";

      "parameter_name" slist => getindices("ps");

  reports:

      "Found key $(parameter_name)";
}

Output:

R: Found key inet_protocols
R: Found key mydomain
R: Found key smtp_use_tls
R: Found key smtp_sasl_password_maps
R: Found key smtp_sasl_security_options
R: Found key relayhost
R: Found key default_privs
R: Found key inet_interfaces
R: Found key smtp_sasl_auth_enable

mean

Prototype: mean(list)

Return type: real

Description: Return the mean of the numbers in list.

list can be a data container or a regular list.

Arguments:

  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:
      # the behavior will be the same whether you use a data container or a list
      # "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
      "mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
      "mylist_str" string => format("%S", mylist);

      "max_int" string => max(mylist, "int");
      "max_lex" string => max(mylist, "lex");
      "max_ip" string => max(mylist, "ip");

      "min_int" string => min(mylist, "int");
      "min_lex" string => min(mylist, "lex");
      "min_ip" string => min(mylist, "ip");

      "mean" real => mean(mylist);
      "variance" real => variance(mylist);

  reports:
      "my list is $(mylist_str)";

      "mean is $(mean)";
      "variance is $(variance) (use eval() to get the standard deviation)";

      "max int is $(max_int)";
      "max IP is $(max_ip)";
      "max lexicographically is $(max_lex)";

      "min int is $(min_int)";
      "min IP is $(min_ip)";
      "min lexicographically is $(min_lex)";
}

Output:

R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1

Notes:

History: Was introduced in version 3.6.0 (2014)

See also: sort(), variance(), sum(), max(), min()


"read[int|real|string]array"

Prototype: readintarray(array, filename, comment, split, maxentries, maxbytes)
Prototype: readrealarray(array, filename, comment, split, maxentries, maxbytes)
Prototype: readstringarray(array, filename, comment, split, maxentries, maxbytes)

Return type: int

Description: Populates array with up to maxentries values, parsed from the first maxbytes bytes in file filename.

Reads a two dimensional array from a file. One dimension is separated by the regex split, the other by the lines in the file. The first field of the lines names the first array argument.

The comment field will strip out unwanted patterns from the file being read, leaving unstripped characters to be split into fields. Using the empty string ("") indicates no comments.

Returns the number of keys in the array, i.e., the number of lines matched.

Arguments:

  • array : Array identifier to populate, in the range [a-zA-Z0-9_$(){}\[\].:]+
  • filename : File name to read, in the range "?(/.*)
  • comment : Unanchored regex matching comments, in the range .*
  • split : Unanchored regex to split lines into fields, in the range .*
  • maxentries : Maximum number of entries to read, in the range 0,99999999999
  • maxbytes : Maximum bytes to read, in the range 0,99999999999

Example:

    readintarray("array_name","/tmp/array","#[^\n]*",":",10,4000);

Input:

     1: 5:7:21:13
     2:19:8:14:14
     3:45:1:78:22
     4:64:2:98:99

Results in:

     array_name[1][0]   1
     array_name[1][1]   5
     array_name[1][2]   7
     array_name[1][3]   21
     array_name[1][4]   13
     array_name[2][0]   2
     array_name[2][1]   19
     array_name[2][2]   8
     array_name[2][3]   14
     array_name[2][4]   14
     array_name[3][0]   3
     array_name[3][1]   45
     array_name[3][2]   1
     array_name[3][3]   78
     array_name[3][4]   22
     array_name[4][0]   4
     array_name[4][1]   64
     array_name[4][2]   2
     array_name[4][3]   98
     array_name[4][4]   99
    readstringarray("array_name","/tmp/array","\s*#[^\n]*",":",10,4000);

Input:

     at:x:25:25:Batch jobs daemon:/var/spool/atjobs:/bin/bash
     avahi:x:103:105:User for Avahi:/var/run/avahi-daemon:/bin/false    # Disallow login
     beagleindex:x:104:106:User for Beagle indexing:/var/cache/beagle:/bin/bash
     bin:x:1:1:bin:/bin:/bin/bash
     # Daemon has the default shell
     daemon:x:2:2:Daemon:/sbin:

Results in a systematically indexed map of the file:

     ...
     array_name[daemon][0]   daemon
     array_name[daemon][1]   x
     array_name[daemon][2]   2
     array_name[daemon][3]   2
     array_name[daemon][4]   Daemon
     array_name[daemon][5]   /sbin
     array_name[daemon][6]   /bin/bash
     ...
     array_name[at][3]       25
     array_name[at][4]       Batch jobs daemon
     array_name[at][5]       /var/spool/atjobs
     array_name[at][6]       /bin/bash
     ...
     array_name[games][3]    100
     array_name[games][4]    Games account
     array_name[games][5]    /var/games
     array_name[games][6]    /bin/bash
     ...

getenv

Prototype: getenv(variable, maxlength)

Return type: string

Description: Return the environment variable variable, truncated at maxlength characters

Returns an empty string if the environment variable is not defined. maxlength is used to avoid unexpectedly large return values, which could lead to security issues. Choose a reasonable value based on the environment variable you are querying.

Arguments:

  • variable: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+
  • maxlength: int, in the range: 0,99999999999

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  vars:

      "myvar" string => getenv("EXAMPLE","2048");

  classes:

      "isdefined" not => strcmp("$(myvar)","");

  reports:

    isdefined::

      "The EXAMPLE environment variable is $(myvar)";

    !isdefined::

      "The environment variable EXAMPLE does not exist";

}

Output:

R: The EXAMPLE environment variable is getenv.cf

Notes:

History: This function was introduced in CFEngine version 3.0.4 (2010)


parsejson

Prototype: parsejson(json_data)

Return type: data

Description: Parses JSON data directly from an inlined string and returns the result as a data variable

Arguments:

  • json_data: string, in the range: .*

Please note that because JSON uses double quotes, it's usually most convenient to use single quotes for the string (CFEngine allows both types of quotes around a string).

Example:

    vars:

      "loadthis"

         data =>  parsejson('{ "key": "value" }');

See also: readjson(), mergedata(), and data documentation.


regextract

Prototype: regextract(regex, string, backref)

Return type: boolean

Description: Returns whether the anchored regex matches the string, and fills the array backref with back-references.

If there are any back reference matches from the regular expression, then the array will be populated with the values, in the manner:

    $(backref[0]) = entire string
    $(backref[1]) = back reference 1, etc

Arguments:

  • regex: regular expression, in the range: .*
  • string: string, in the range: .*
  • backref: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "example" };
}

bundle agent example
{
  classes:

      # Extract regex backreferences and put them in an array

      "ok" expression => regextract(
                                     "xx ([^\s]+) ([^\s]+).* xx",
                                     "xx one two three four xx",
                                     "myarray"
      );
  reports:

    ok::

      "ok - \"$(myarray[0])\" = xx + \"$(myarray[1])\" + \"$(myarray[2])\" + .. + xx";
}

Output:

R: ok - "xx one two three four xx" = xx + "one" + "two" + .. + xx

irange

Prototype: irange(arg1, arg2)

Return type: irange

Description: Define a range of integer values for CFEngine internal use.

Used for any scalar attribute which requires an integer range. You can generally interchangeably say "1,10" or irange("1","10"). However, if you want to create a range of dates or times, you must use irange if you also use the functions ago, now, accumulated, etc.

Arguments:

  • arg1: int, in the range: -99999999999,99999999999
  • arg2: int, in the range: -99999999999,99999999999

Example:

    irange("1","100");

    irange(ago(0,0,0,1,30,0), "0");

join

Prototype: join(glue, list)

Return type: string

Description: Join the items of list into a string, using the conjunction in glue.

Converts a list or data container into a scalar variable using the join string in first argument.

Arguments:

  • glue: string, in the range: .*
  • list: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

Example:

body common control
{
      bundlesequence => { "test" };
}

bundle agent test
{
  vars:

      "mylist" slist => { "one", "two", "three", "four", "five" };
      "datalist" data => parsejson('[1,2,3,
                        "one", "two", "three",
                        "long string",
                        "four", "fix", "six",
                        "one", "two", "three",]');

      "mylist_str" string => format("%S", mylist);
      "datalist_str" string => format("%S", datalist);
      "myscalar" string => join("->", mylist);
      "datascalar" string => join("->", datalist);

  reports:
      "Concatenated $(mylist_str): $(myscalar)";
      "Concatenated $(datalist_str): $(datascalar)";
}

Output:

R: Concatenated { "one", "two", "three", "four", "five" }: one->two->three->four->five
R: Concatenated [1,2,3,"one","two","three","long string","four","fix","six","one","two","three"]: 1->2->3->one->two->three->long string->four->fix->six->one->two->three

Hard and Soft Classes

Classes fall into hard (discovered) and soft (defined) types. This reference documents the hard classes that might be set by CFEngine, and soft classes used by CFEngine's default policy.

Listing Classes

To see hard classes and soft classes defined in common bundles on a particular host, run cf-promises --show-classes as a privileged user.

Example:

[root@hub masterfiles]# cf-promises --show-classes
Class name                                                   Meta tags
10_0_2_15                                                    inventory,attribute_name=none,source=agent,hardclass
127_0_0_1                                                    inventory,attribute_name=none,source=agent,hardclass
192_168_33_2                                                 inventory,attribute_name=none,source=agent,hardclass
1_cpu                                                        source=agent,derived-from=sys.cpus,hardclass
64_bit                                                       source=agent,hardclass
Afternoon                                                    time_based,source=agent,hardclass
Day22                                                        time_based,source=agent,hardclass
...

Note that some of the classes are set only if a trusted link can be established with cf-monitord, i.e. if both are running with privilege, and the /var/cfengine/state/env_data file is secure.

You can also use the built-in classesmatching() function to get a list of all the defined classes in a list, inside CFEngine policy itself. classesmatching() is especially useful because it also lets you specify tag regular expressions.

See also: The --show-vars option.

Tags

Classes and variables have tags that describe their provenance (who created them) and purpose (why were they created).

While you can provide your own tags for soft classes in policy with the meta attribute, there are some tags applied to hard classes and other special cases. This list may change in future versions of CFEngine.

  • source=agent: this hard class or variable was created by the agent in the C code. This tag is useful when you need to find classes or variables that don't match the other sources below. e.g. linux.
  • source=environment: this hard class or variable was created by the agent in the C code. It reflects something about the environment like a command-line option, e.g. -d sets debug_mode, -v sets verbose_mode, and -I sets inform_mode. Another useful option, -n, sets opt_dry_run.
  • source=bootstrap: this hard class or variable was created by the agent in the C code based on bootstrap parameters. e.g. policy_server is set based on the IP address or host name you provided when you ran cf-agent -B host-or-ip.
  • source=module: this class or variable was created through the module protocol.
  • source=persistent: this persistent class was loaded from storage.
  • source=body: this variable was created by a body with side effects.
  • source=function: this class or variable was created by a function as a side effect, e.g. see the classes that selectservers() sets or the variables that regextract() sets. These classes or variables will also have a function=FUNCTIONNAME tag.
  • source=promise: this soft class was created from policy.
  • inventory: related to the system inventory, e.g. the network interfaces
    • attribute_name=none: has no visual attribute name (ignored by Mission Portal)
    • attribute_name=X: has visual attribute name X (used by Mission Portal)
  • monitoring: related to the monitoring (cf-monitord usually).
  • time_based: based on the system date, e.g. Afternoon
  • derived-from=varname: for a class, this tells you it was derived from a variable name, e.g. if the special variable sys.fqhost is xyz, the resulting class xyz will have the tag derived-from=sys.fqhost.
  • cfe_internal: internal utility classes and variables

Enterprise only:

  • source=ldap: this soft class or variable was created from an LDAP lookup.
  • source=observation: this class or variable came from a measurements system observation and will also have the monitoring tag.
Hard Classes
  • CFEngine-specific classes
    • any: this class is always set
    • am_policy_hub, policy_server: set when the file $(workdir)/state/am_policy_hub exists. When a host is bootstrapped, if the agent detects that it is bootstrapping to itself the file is created.
    • bootstrap_mode: set when bootstrapping a host
    • inform_mode, verbose_mode, debug_mode: log verbosity levels in order of noisiness
    • opt_dry_run: set when the --dry-run option is given
    • failsafe_fallback: set when the base policy is invalid and the built-in failsafe.cf (see bootstrap.c) is invoked
    • (community, community_edition) and (enterprise, enterprise_edition): the two different CFEngine products, Community and Enterprise, can be distinguished by these mutually exclusive sets of hard classes
    • agent cf-agent, server cf-serverd, monitor cf-monitord, executor cf-execd, runagent cf-runagent, keygenerator cf-keygen, hub cf-hub, common cf-promises and others: classes that identify the current component. cf-promises is a special case because it's not an agent in the CFEngine sense, so note that using cf-promises --show-classes will not show these classes because it can't.
  • Operating System Classes (note that the presence of these classes doesn't imply platform support)
    • Operating System Architecture - arista, big_ip, debian, eos, fedora, Mandrake, Mandriva, oracle, redhat, slackware, smartmachine, smartos, solarisx86, sun4, SuSE, ubuntu, ultrix, the always-favorite unknown_ostype, etc.
    • VM or hypervisor specific: VMware, virt_guest_vz, virt_host_vz, virt_host_vz_vzps, xen, xen_dom0, xen_domu_hv, xen_domu_pv, oraclevmserver, etc.
    • On Solaris-10 systems, the zone name (in the form zone_global, zone_foo, zone_baz).
    • Windows-specific: DomainController, Win2000, WinServer, WinServer2003, WinServer2008, WinVista, WinWorkstation, WinXP
    • have_aptitude, powershell, systemd: based on the detected capabilities of the platform or the compiled-in options
    • See also: sys.arch, sys.class, sys.flavor, sys.os, sys.ostype.
  • Network Classes
  • Time Classes

    • note ALL of these have a local and a GMT version. The GMT classes are consistent the world over, in case you need global change coordination.
    • Day of the Week - Monday, Tuesday, Wednesday,...GMT_Monday, GMT_Tuesday, GMT_Wednesday,...
    • Hour of the Day in Current Time Zone - Hr00, Hr01,... Hr23 and Hr0, Hr1,... Hr23
    • Hour of the Day in GMT - GMT_Hr00, GMT_Hr01, ...GMT_Hr23 and GMT_Hr0, GMT_Hr1, ...GMT_Hr23.
    • Minutes of the Hour - Min00, Min17,... Min45,... and GMT_Min00, GMT_Min17,... GMT_Min45,...
    • Five Minute Interval of the Hour - Min00_05, Min05_10,... Min55_00 and GMT_Min00_05, GMT_Min05_10,... GMT_Min55_00. Note the second number indicates up to what minute the interval extends and does not include that minute.
    • Quarter of the Hour - Q1, Q2, Q3, Q4 and GMT_Q1, GMT_Q2, GMT_Q3, GMT_Q4
    • An expression of the current quarter hour - Hr12_Q3 and GMT_Hr12_Q3
    • Day of the Month - Day1, Day2,... Day31 and GMT_Day1, GMT_Day2,... GMT_Day31
    • Month - January, February,... December and GMT_January, GMT_February,... GMT_December
    • Year - Yr1997, Yr2004 and GMT_Yr1997, GMT_Yr2004
    • Period of the Day - Night, Morning, Afternoon, Evening and GMT_Night, GMT_Morning, GMT_Afternoon, GMT_Evening (six hour blocks starting at 00:00 hours).
    • Lifecycle Index - Lcycle_0, Lcycle_1, Lcycle_2 and GMT_Lcycle_0, GMT_Lcycle_1, GMT_Lcycle_2 (the year number modulo 3, used in long term resource memory).
    • See also: sys.cdate, sys.date.
  • The unqualified name of a particular host (e.g., www). If your system returns a fully qualified domain name for your host (e.g., www.iu.hio.no), CFEngine will also define a hard class for the fully qualified name, as well as the partially-qualified component names iu.hio.no, hio.no, and no.

  • An arbitrary user-defined string (as specified in the -D command line option, or defined in a classes promise promise or classes body, restart_class in a processes promise, etc).

  • The IP address octets of any active interface (in the form ipv4_192_0_0_1<!-- /@w -->, ipv4_192_0_0<!-- /@w -->, ipv4_192_0<!-- /@w -->, ipv4_192<!-- /@w -->), provided they are not excluded by a regular expression in the file WORKDIR/inputs/ignore_interfaces.rx.

  • The names of the active interfaces (in the form net_iface_xl0, net_iface_vr0).

  • System status and entropy information reported by cf-monitord.

Soft Classes

Soft classes can be set by using the -D or --define options wihtout having to edit the policy. Multiple classes can be defined by separating them with commas (no spaces).

$ cf-agent -Dclass

or

$ cf-agent --define class1,class2,class3

This can be especially useful when requesting a remote host to run its policy by using cf-runagent to activate policy that is normally dormant.

$ cf-runagent -Demergency_evacuation -H remoteclient

If you're using dynamic inputs this can be useful in combination with cf-promises to ensure that various input combinations syntax is validated correctly. Many people will have this run by pre-commit hooks or as part of a continuous build system like Jenkins or Bamboo.

$ cf-promises -f ./promises.cf -D prod
$ cf-promises -f ./promises.cf -D dev
./promises.cf:10:12: error: syntax error
   "global1" expression => "any";
           ^
./promises.cf:10:12: error: Check previous line, Expected ';', got '"global1"'
   "global1" expression => "any";
           ^
./promises.cf:10:23: error: Expected promiser string, got 'expression'
   "global1" expression => "any";
                      ^
./promises.cf:10:26: error: Expected ';', got '=>'
   "global1" expression => "any";
                         ^
2014-05-22T13:46:05+0000    error: There are syntax errors in policy files

Note: Classes, once defined, will stay defined either for as long as the bundle is evaluated (for classes with a bundle scope) or until the agent exits (for classes with a namespace scope). See cancel_kept, cancel_repaired, and cancel_notkept in classes body.

persistent_disable_*DAEMON*

Description: Disable a CFEngine Enterprise daemon component persistently.

DAEMON can be one of cf_execd, cf_monitord or cf_serverd.

This will stop the AGENT from starting automatically.

clear_persistent_disable_*DAEMON*

Description: Re-enable a previously disabled CFEngine Enterprise daemon component.

DAEMON can be one of cf_execd, cf_monitord or cf_serverd.


Special Variables

Variables are promises that can be defined in any promise bundle. Users can create their own variables.

To see all of the variables defined on a particular host, run

$ cf-promises --show-vars

as a privileged user. See Classes for an explanation of the tags.

CFEngine includes the following special variables:

  • const Variables defined for embedding unprintable values or values with special meanings in strings.

  • edit Variables used to access information about editing promises during their execution.

  • match Variable used in string matching.

  • mon Variables defined in a monitoring context.

  • sys Variables defined in order to automate discovery of system values.

  • this Variables used to access information about promises during their execution.


mon

The variables discovered by cf-monitord are placed in this monitoring context. Monitoring variables are expected to be changing rapidly - values are typically updated or added every 2.5 minutes.

In CFEngine Enterprise, custom defined monitoring targets also become variables in this context, named by the handle of the promise that defined them.

mon.listening_udp4_ports

Port numbers that were observed to be set up to receive connections on the host concerned.

mon.listening_tcp4_ports

Port numbers that were observed to be set up to receive connections on the host concerned.

mon.listening_udp6_ports

Port numbers that were observed to be set up to receive connections on the host concerned.

mon.listening_tcp6_ports

port numbers that were observed to be set up to receive connections on the host concerned.

mon.value_users

Users with active processes, including system users.

mon.av_users

Observational measure collected every 2.5 minutes from cf-monitord. Description: Users with active processes, including system users.

mon.dev_users

Users with active processes, including system users.

mon.value_rootprocs

Sum privileged system processes.

mon.av_rootprocs

Sum privileged system processes.

mon.dev_rootprocs

Sum privileged system processes.

mon.value_otherprocs

Sum non-privileged process.

mon.av_otherprocs

Sum non-privileged process.

mon.dev_otherprocs

Sum non-privileged process.

mon.value_diskfree

Last checked percentage Free disk on / partition.

mon.av_diskfree

Average percentage Free disk on / partition.

mon.dev_diskfree

Standard deviation of percentage Free disk on / partition.

mon.value_loadavg

Kernel load average utilization (sum over cores).

mon.av_loadavg

Kernel load average utilization (sum over cores).

mon.dev_loadavg

Kernel load average utilization (sum over cores).

mon.value_netbiosns_in

netbios name lookups (in).

mon.av_netbiosns_in

netbios name lookups (in).

mon.dev_netbiosns_in

netbios name lookups (in).

mon.value_netbiosns_out

netbios name lookups (out).

mon.av_netbiosns_out

netbios name lookups (out).

mon.dev_netbiosns_out

netbios name lookups (out).

mon.value_netbiosdgm_in

netbios name datagrams (in).

mon.av_netbiosdgm_in

netbios name datagrams (in).

mon.dev_netbiosdgm_in

netbios name datagrams (in).

mon.value_netbiosdgm_out

netbios name datagrams (out).

mon.av_netbiosdgm_out

netbios name datagrams (out).

mon.dev_netbiosdgm_out

netbios name datagrams (out).

mon.value_netbiosssn_in

Samba/netbios name sessions (in).

mon.av_netbiosssn_in

Samba/netbios name sessions (in).

mon.dev_netbiosssn_in

Samba/netbios name sessions (in).

mon.value_netbiosssn_out

Samba/netbios name sessions (out).

mon.av_netbiosssn_out

Samba/netbios name sessions (out).

mon.dev_netbiosssn_out

Samba/netbios name sessions (out).

mon.value_imap_in

imap mail client sessions (in).

mon.av_imap_in

imap mail client sessions (in).

mon.dev_imap_in

imap mail client sessions (in).

mon.value_imap_out

imap mail client sessions (out).

mon.av_imap_out

imap mail client sessions (out).

mon.dev_imap_out

imap mail client sessions (out).

mon.value_cfengine_in

cfengine connections (in).

mon.av_cfengine_in

cfengine connections (in).

mon.dev_cfengine_in

cfengine connections (in).

mon.value_cfengine_out

cfengine connections (out).

mon.av_cfengine_out

cfengine connections (out).

mon.dev_cfengine_out

cfengine connections (out).

mon.value_nfsd_in

nfs connections (in).

mon.av_nfsd_in

nfs connections (in).

mon.dev_nfsd_in

nfs connections (in).

mon.value_nfsd_out

nfs connections (out).

mon.av_nfsd_out

nfs connections (out).

mon.dev_nfsd_out

nfs connections (out).

mon.value_smtp_in

smtp connections (in).

mon.av_smtp_in

smtp connections (in).

mon.dev_smtp_in

smtp connections (in).

mon.value_smtp_out

smtp connections (out).

mon.av_smtp_out

smtp connections (out).

mon.dev_smtp_out

smtp connections (out).

mon.value_www_in

www connections (in).

mon.av_www_in

www connections (in).

mon.dev_www_in

www connections (in).

mon.value_www_out

www connections (out).

mon.av_www_out

www connections (out).

mon.dev_www_out

www connections (out).

mon.value_ftp_in

ftp connections (in).

mon.av_ftp_in

ftp connections (in).

mon.dev_ftp_in

ftp connections (in).

mon.value_ftp_out

ftp connections (out).

mon.av_ftp_out

ftp connections (out).

mon.dev_ftp_out

ftp connections (out).

mon.value_ssh_in

ssh connections (in).

mon.av_ssh_in

ssh connections (in).

mon.dev_ssh_in

ssh connections (in).

mon.value_ssh_out

ssh connections (out).

mon.av_ssh_out

ssh connections (out).

mon.dev_ssh_out

ssh connections (out).

mon.value_wwws_in

wwws connections (in).

mon.av_wwws_in

wwws connections (in).

mon.dev_wwws_in

wwws connections (in).

mon.value_wwws_out

wwws connections (out).

mon.av_wwws_out

wwws connections (out).

mon.dev_wwws_out

wwws connections (out).

mon.value_icmp_in

ICMP packets (in).

mon.av_icmp_in

ICMP packets (in).

mon.dev_icmp_in

ICMP packets (in).

mon.value_icmp_out

ICMP packets (out).

mon.av_icmp_out

ICMP packets (out).

mon.dev_icmp_out

ICMP packets (out).

mon.value_udp_in

UDP dgrams (in).

mon.av_udp_in

UDP dgrams (in).

mon.dev_udp_in

UDP dgrams (in).

mon.value_udp_out

UDP dgrams (out).

mon.av_udp_out

UDP dgrams (out).

mon.dev_udp_out

UDP dgrams (out).

mon.value_dns_in

DNS requests (in).

mon.av_dns_in

DNS requests (in).

mon.dev_dns_in

DNS requests (in).

mon.value_dns_out

DNS requests (out).

mon.av_dns_out

DNS requests (out).

mon.dev_dns_out

DNS requests (out).

mon.value_tcpsyn_in

TCP sessions (in).

mon.av_tcpsyn_in

TCP sessions (in).

mon.dev_tcpsyn_in

TCP sessions (in).

mon.value_tcpsyn_out

TCP sessions (out).

mon.av_tcpsyn_out

TCP sessions (out).

mon.dev_tcpsyn_out

TCP sessions (out).

mon.value_tcpack_in

TCP acks (in).

mon.av_tcpack_in

TCP acks (in).

mon.dev_tcpack_in

TCP acks (in).

mon.value_tcpack_out

TCP acks (out).

mon.av_tcpack_out

TCP acks (out).

mon.dev_tcpack_out

TCP acks (out).

mon.value_tcpfin_in

TCP finish (in).

mon.av_tcpfin_in

TCP finish (in).

mon.dev_tcpfin_in

TCP finish (in).

mon.value_tcpfin_out

TCP finish (out).

mon.av_tcpfin_out

TCP finish (out).

mon.dev_tcpfin_out

TCP finish (out).

mon.value_tcpmisc_in

TCP misc (in).

mon.av_tcpmisc_in

TCP misc (in).

mon.dev_tcpmisc_in

TCP misc (in).

mon.value_tcpmisc_out

TCP misc (out).

mon.av_tcpmisc_out

TCP misc (out).

mon.dev_tcpmisc_out

TCP misc (out).

mon.value_webaccess

Webserver hits.

mon.av_webaccess

Webserver hits.

mon.dev_webaccess

Webserver hits.

mon.value_weberrors

Webserver errors.

mon.av_weberrors

Webserver errors.

mon.dev_weberrors

Webserver errors.

mon.value_syslog

New log entries (Syslog).

mon.av_syslog

New log entries (Syslog).

mon.dev_syslog

New log entries (Syslog).

mon.value_messages

New log entries (messages).

mon.av_messages

New log entries (messages).

mon.dev_messages

New log entries (messages).

mon.value_temp0

CPU Temperature 0.

mon.av_temp0

CPU Temperature 0.

mon.dev_temp0

CPU Temperature 0.

mon.value_temp1

CPU Temperature 1.

mon.av_temp1

CPU Temperature 1.

mon.dev_temp1

CPU Temperature 1.

mon.value_temp2

CPU Temperature 2.

mon.av_temp2

CPU Temperature 2.

mon.dev_temp2

CPU Temperature 2.

mon.value_temp3

CPU Temperature 3.

mon.av_temp3

CPU Temperature 3.

mon.dev_temp3

CPU Temperature 3.

mon.value_cpu

%CPU utilization (all).

mon.av_cpu

%CPU utilization (all).

mon.dev_cpu

%CPU utilization (all).

mon.value_cpu0

%CPU utilization 0.

mon.av_cpu0

%CPU utilization 0.

mon.dev_cpu0

%CPU utilization 0.

mon.value_cpu1

%CPU utilization 1.

mon.av_cpu1

%CPU utilization 1.

mon.dev_cpu1

%CPU utilization 1.

mon.value_cpu2

%CPU utilization 2.

mon.av_cpu2

%CPU utilization 2.

mon.dev_cpu2

%CPU utilization 2.

mon.value_cpu3

%CPU utilization 3.

mon.av_cpu3

%CPU utilization 3.

mon.dev_cpu3

%CPU utilization 3.

mon.value_microsoft_ds_in

Samba/MS_ds name sessions (in).

mon.av_microsoft_ds_in

Samba/MS_ds name sessions (in).

mon.dev_microsoft_ds_in

Samba/MS_ds name sessions (in).

mon.value_microsoft_ds_out

Samba/MS_ds name sessions (out).

mon.av_microsoft_ds_out

Samba/MS_ds name sessions (out).

mon.dev_microsoft_ds_out

Samba/MS_ds name sessions (out).

mon.value_www_alt_in

Alternative web service connections (in).

mon.av_www_alt_in

Alternative web service connections (in).

mon.dev_www_alt_in

Alternative web service connections (in).

mon.value_www_alt_out

Alternative web client connections (out).

mon.av_www_alt_out

Alternative web client connections (out).

mon.dev_www_alt_out

Alternative web client connections (out).

mon.value_imaps_in

encrypted imap mail service sessions (in).

mon.av_imaps_in

encrypted imap mail service sessions (in).

mon.dev_imaps_in

encrypted imap mail service sessions (in).

mon.value_imaps_out

encrypted imap mail client sessions (out).

mon.av_imaps_out

encrypted imap mail client sessions (out).

mon.dev_imaps_out

encrypted imap mail client sessions (out).

mon.value_ldap_in

LDAP directory service service sessions (in).

mon.av_ldap_in

LDAP directory service service sessions (in).

mon.dev_ldap_in

LDAP directory service service sessions (in).

mon.value_ldap_out

LDAP directory service client sessions (out).

mon.av_ldap_out

LDAP directory service client sessions (out).

mon.dev_ldap_out

LDAP directory service client sessions (out).

mon.value_ldaps_in

LDAP directory service service sessions (in).

mon.av_ldaps_in

LDAP directory service service sessions (in).

mon.dev_ldaps_in

LDAP directory service service sessions (in).

mon.value_ldaps_out

LDAP directory service client sessions (out).

mon.av_ldaps_out

LDAP directory service client sessions (out).

mon.dev_ldaps_out

LDAP directory service client sessions (out).

mon.value_mongo_in

Mongo database service sessions (in).

mon.av_mongo_in

Mongo database service sessions (in).

mon.dev_mongo_in

Mongo database service sessions (in).

mon.value_mongo_out

Mongo database client sessions (out).

mon.av_mongo_out

Mongo database client sessions (out).

mon.dev_mongo_out

Mongo database client sessions (out).

mon.value_mysql_in

MySQL database service sessions (in).

mon.av_mysql_in

MySQL database service sessions (in).

mon.dev_mysql_in

MySQL database service sessions (in).

mon.value_mysql_out

MySQL database client sessions (out).

mon.av_mysql_out

MySQL database client sessions (out).

mon.dev_mysql_out

MySQL database client sessions (out).

mon.value_postgres_in

PostgreSQL database service sessions (in).

mon.av_postgres_in

PostgreSQL database service sessions (in).

mon.dev_postgres_in

PostgreSQL database service sessions (in).

mon.value_postgres_out

PostgreSQL database client sessions (out).

mon.av_postgres_out

PostgreSQL database client sessions (out).

mon.dev_postgres_out

PostgreSQL database client sessions (out).

mon.value_ipp_in

Internet Printer Protocol (in).

mon.av_ipp_in

Internet Printer Protocol (in).

mon.dev_ipp_in

Internet Printer Protocol (in).

mon.value_ipp_out

Internet Printer Protocol (out).

mon.av_ipp_out

Internet Printer Protocol (out).

mon.dev_ipp_out

Internet Printer Protocol (out).


connection

The context connection is used by the shortcut attribute in access promises to access information about the remote agent requesting access.

access:
    "/var/cfengine/cmdb/$(connection.key).json"
      shortcut   => "me.json",
      admit_keys => { "$(connection.key)" };

Note: The usage of the connection variables is strictly limited to literal strings within the promiser and admit/deny lists of access promise types; they cannot be passed into functions or stored in other variables. These variables can only be used with incoming connections that use protocol_version >=2 ( or "latest" ).

connection.key

This variable contains the public key sha of the connecting client in the form 'SHA=...'.

access:
    "/var/cfengine/cmdb/$(connection.key).json"
      shortcut   => "me.json",
      admit_keys => { "$(connection.key)" };
connection.ip

This variable contains the IP address of the connecting remote agent.

access:
    "/var/cfengine/cmdb/$(connection.ip).json"
      shortcut   => "myip.json",
      admit_keys => { "$(connection.key)" };
connection.hostname

This variable contains the hostname of the connecting client as determined by a reverse DNS lookup from cf-serverd.

access:
    "/var/cfengine/cmdb/$(connection.hostname).json"
      shortcut   => "myhostname.json",
      admit_keys => { "$(connection.key)" };

Note: Reverse lookups are only performed when necessary. To avoid the performance impact of reverse dns lookups for each connection avoid using admit_hostnames, using hostnames in your admit rules, and these connection variables.


const

CFEngine defines a number of variables for embedding unprintable values or values with special meanings in strings.

const.dollar
    reports:

       # This will report: The value of $(const.dollar) is $
       "The value of $(const.dollar)(const.dollar) is $(const.dollar)";

       # This will report: But the value of $(dollar) is $(dollar)
       "But the value of $(dollar) is $(dollar)";
const.dirsep
    reports:

       # On Unix hosts this will report: The value of $(const.dirsep) is /
       # On Windows hosts this will report: The value of $(const.dirsep) is \\
       "The value of $(const.dollar)(const.dirsep) is $(const.dirsep)";
const.endl
    reports:

      "A newline with either $(const.n) or with $(const.endl) is ok";
      "But a string with \n in it does not have a newline!";
const.n
    reports:

      "A newline with either $(const.n) or with $(const.endl) is ok";
      "But a string with \n in it does not have a newline!";
const.r
    reports:

      "A carriage return character is $(const.r)";
const.t
    reports:

      "A report with a$(const.t)tab in it";

this

The context this is used to access information about promises during their execution. It is context dependent and not universally meaningful or available, but provides a context for variables where one is needed (such as when passing the value of a list variable into a parameterized edit_line promise from a files promise).

    bundle agent resolver(s,n)
    {
    files:
      "$(sys.resolv)"

          create        => "true",
          edit_line     => doresolv("@(this.s)","@(this.n)"),
          edit_defaults => reconstruct;
    }

Note that every unqualified variable is automatically considered to be in context this, so that a reference to the variable $(foo) is identical to referencing $(this.foo). You are strongly encouraged to not take advantage of this behavior, but simply to be aware that if you attempt to declare a variable name with one of the following special reserved names, CFEngine will issue a warning (and you can reference your variable by qualifying it with the bundle name in which it is declared).

this.bundle

This variable contains the current bundle name.

this.handle

This variable points to the promise handle of the currently handled promise; it is useful for referring to the intention in log messages.

this.namespace

This variable contains the current namespace name.

this.promise_filename

This variable reveals the name of the file in which the current promise is defined.

this.promise_dirname

This variable contains the directory name of the file in which the current promise is defined.

this.promise_linenumber

This variable reveals the line number in the file at which it is used. It is useful to differentiate otherwise identical reports promises.

this.promiser

The special variable $(this.promiser) is used to refer to the current value of the promiser itself.

In files promises, where it is practical to use patterns or depth_search to match multiple objects, the variable refers to the file that is currently making the promise. However, the variable can only be used in selected attributes:

For example:

    bundle agent find666
    {
    files:
      "/home"
        file_select => world_writeable,
        transformer => "/bin/echo DETECTED $(this.promiser)",
        depth_search => recurse("inf");

      "/etc/.*"
        file_select => world_writeable,
        transformer => "/bin/echo DETECTED $(this.promiser)";
    }

    body file_select world_writeable
    {
      search_mode => { "o+w" };
      file_result => "mode";
    }
this.promiser_uid

This variable refers to the uid (user ID) of the user running the cf-agent program.

Note: This variable is reported by the platform dependent getuid function, and is always an integer.

this.promiser_gid

This variable refers to the gid (group ID) of the user running the cf-agent program.

Note: This variable is reported by the platform dependent getgid function, and is always an integer.

this.promiser_pid

This variable refers to the pid (process ID) of the cf-agent program.

Note: This variable is reported by the platform dependent getpid function, and is always an integer.

this.promiser_ppid

This variable refers to the ppid (parent process ID) of the cf-agent program.

Note: This variable is reported by the platform dependent getpid function, and is always an integer. On the Windows platform it's always 0.

this.service_policy

In a services promise, this variable is set to the value of the promise attribute service_policy. For example:

    services:

      "www"  service_policy => "start";
      service_bundle => non_standard_services;

This is typically used in the adaptations for custom services bundles in the service methods.

this.this

From version 3.3.0 on, this variable is reserved. It is used by functions like maplist() to represent the current object in a transformation map.


match

Each time CFEngine matches a string, these values are assigned to a special variable context $(match.*n*). The fragments can be referred to in the remainder of the promise. There are two places where this makes sense. One is in pattern replacement during file editing, and the other is in searching for files.

    bundle agent testbundle
    {
    files:

      "/home/mark/tmp/(cf[23])_(.*)"
           create    => "true",
           edit_line => myedit("second $(match.2)");

      # but more specifically...

      "/home/mark/tmp/cf3_(test)"
           create    => "true",
           edit_line => myedit("second $(match.1)");
    }
match.0

A string matching the complete regular expression whether or not back-references were used in the pattern.


edit

This context is used to access information about editing promises during their execution. It is context dependent and not universally meaningful or available.

    bundle agent testbundle
    {
    files:

      "/tmp/testfile"
         edit_line => test;
    }

    #

    bundle edit_line test
    {
    classes:
        "ok" expression => regline(".*mark.*","$(edit.filename)");

    reports:

      ok::
       "File matched $(edit.filename)";
    }
edit.filename

This variable points to the filename of the file currently making an edit promise. If the file has been arrived at through a search, this could be different from the files promiser.


sys

System variables are derived from CFEngine's automated discovery of system values. They are provided as variables in order to make automatically adaptive rules for configuration.

    files:

      "$(sys.resolv)"

          create        => "true",
          edit_line     => doresolv("@(this.list1)","@(this.list2)"),
          edit_defaults => reconstruct;
sys.arch

The variable gives the kernel's short architecture description.

    # arch = x86_64
sys.bindir

The name of the directory where CFEngine looks for its binaries..

    # bindir = /var/cfengine/bin

History: Introduced in CFEngine 3.6

sys.cdate

The date of the system in canonical form, i.e. in the form of a class, from when the agent initialized.

    # cdate = Sun_Dec__7_10_39_53_2008_
sys.cf_promises

A variable containing the path to the CFEngine syntax analyzer cf-promises on the platform you are using.

    classes:

      "syntax_ok" expression => returnszero("$(sys.cf_promises)");
sys.cf_version

The variable gives the version of the running CFEngine Core.

    # cf_version = 3.0.5
sys.cf_version_major

The variable gives the major version of the running CFEngine Core.

    # cf_version = 3.0.5
    # cf_version_major = 3

History: Was introduced in 3.5.1, Enterprise 3.5.1.

sys.cf_version_minor

The variable gives the minor version of the running CFEngine Core.

    # cf_version = 3.0.5
    # cf_version_minor = 0

History: Was introduced in 3.5.1, Enterprise 3.5.1.

sys.cf_version_patch

The variable gives the patch version of the running CFEngine Core.

    # cf_version = 3.0.5
    # cf_version_patch = 5

History: Was introduced in 3.5.1, Enterprise 3.5.1.

sys.class

This variable contains the name of the hard-class category for this host (i.e. its top level operating system type classification).

    # class = linux

See also: sys.os

sys.cpus

A variable containing the number of CPU cores detected. On systems which provide virtual cores, it is set to the total number of virtual, not physical, cores. In addition, on a single-core system the class 1_cpu is set, and on multi-core systems the class n_cpus is set, where n is the number of cores identified.

    reports:

     "Number of CPUS = $(sys.cpus)";
     8_cpus::
       "This system has 8 processors.";

History: Was introduced in 3.3.0, Enterprise 2.2.0 (2012)

sys.crontab

The variable gives the location of the current users's master crontab directory.

    # crontab = /var/spool/crontab/root
sys.date

The date of the system as a text string, from when the agent initialized.

    # date = Sun Dec  7 10:39:53 2008
sys.doc_root

A scalar variable containing the default path for the document root of the standard web server package.

History: Was introduced in 3.1.0, Enterprise 2.0.

sys.domain

The domain name as discovered by CFEngine. If the DNS is in use, it could be possible to derive the domain name from its DNS registration, but in general there is no way to discover this value automatically. The common control body permits the ultimate specification of this value.

    # domain = example.org
sys.enterprise_version

The variable gives the version of the running CFEngine Enterprise Edition.

    # enterprise_version = 3.0.0

History: Was introduced in 3.5.0, Enterprise 3.0.0

sys.expires
    reports:

     enterprise::

      "License expires $(sys.expires)";
sys.exports

The location of the system NFS exports file.

    # exports = /etc/exports
    # exports = /etc/dfs/dfstab
sys.failsafe_policy_path

The name of the failsafe policy file.

    # failsafe_policy_path = /var/cfengine/inputs/failsafe.cf

History: Introduced in CFEngine 3.6

sys.flavor, sys.flavour

A variable containing an operating system identification string that is used to determine the current release of the operating system in a form that can be used as a label in naming. This is used, for instance, to detect which package name to choose when updating software binaries for CFEngine.

These two variables are synonyms for each other.

History: Was introduced in 3.2.0, Enterprise 2.0

See also: sys.ostype

sys.fqhost

The fully qualified name of the host. In order to compute this value properly, the domain name must be defined.

    # fqhost = host.example.org

See also: sys.uqhost

sys.fstab

The location of the system filesystem (mount) table.

    # fstab = /etc/fstab
sys.hardware_addresses

This is a list variable containing a list of all known MAC addresses for system interfaces.

History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)

sys.hardware_mac[interface_name]

This contains the MAC address of the named interface. For example:

    reports:
        "Tell me $(sys.hardware_mac[eth0])";

History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)

sys.host

The name of the current host, according to the kernel. It is undefined whether this is qualified or unqualified with a domain name.

    # host = myhost
sys.inputdir

The name of the inputs directory where CFEngine looks for its policy files.

    # inputdir = /var/cfengine/inputs

History: Introduced in CFEngine 3.6

sys.interface

The assumed (default) name of the main system interface on this host.

    # interface = eth0
sys.interfaces

Displays a system list of configured interfaces currently active in use by the system. This list is detected at runtime and it passed in the variables report to the CFEngine Enterprise Database.

To use this list in a policy, you will need a local copy since only local variables can be iterated.

    bundle agent test
    {
    vars:

     # To iterate, we need a local copy

     "i1" slist => { @(sys.ip_addresses)} ;
     "i2" slist => { @(sys.interfaces)} ;

    reports:

        "Addresses: $(i1)";
        "Interfaces: $(i2)";
        "Addresses of the interfaces: $(sys.ipv4[$(i2)])";
    }

History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)

sys.interface_flags

Contains a space separated list of the flags of the named interface. e.g.

    reports:
        "eth0 flags: $(sys.interface_flags[eth0])";

Outputs:

R: eth0 flags: up broadcast running multicast

The following device flags are supported:

  • up
  • broadcast
  • debug
  • loopback
  • pointopoint
  • notrailers
  • running
  • noarp
  • promisc
  • allmulti
  • multicast

History: Was introduced in 3.5.0 (2013)

sys.ip_addresses

Displays a system list of IP addresses currently in use by the system. This list is detected at runtime and passed in the variables report to the CFEngine Enterprise Database.

To use this list in a policy, you will need a local copy since only local variables can be iterated.

    bundle agent test
    {
    vars:

     # To iterate, we need a local copy

     "i1" slist => { @(sys.ip_addresses)} ;
     "i2" slist => { @(sys.interfaces)} ;

    reports:

        "Addresses: $(i1)";
        "Interfaces: $(i2)";
        "Addresses of the interfaces: $(sys.ipv4[$(i2)])";
    }

History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)

sys.ipv4

All four octets of the IPv4 address of the first system interface.

Note:

If your system has a single ethernet interface, $(sys.ipv4) will contain your IPv4 address. However, if your system has multiple interfaces, then $(sys.ipv4) will simply be the IPv4 address of the first interface in the list that has an assigned address, Use $(sys.ipv4[interface_name]) for details on obtaining the IPv4 addresses of all interfaces on a system.

sys.ipv4[interface_name]

The full IPv4 address of the system interface named as the associative array index, e.g. $(ipv4[le0]) or $(ipv4[xr1]).

    # If the IPv4 address on the interfaces are
    #    le0 = 192.168.1.101
    #    xr1 = 10.12.7.254
    #
    # Then the octets of all interfaces are accessible as an associative array
    # ipv4_1[le0] = 192
    # ipv4_2[le0] = 192.168
    # ipv4_3[le0] = 192.168.1
    #   ipv4[le0] = 192.168.1.101
    # ipv4_1[xr1] = 10
    # ipv4_2[xr1] = 10.12
    # ipv4_3[xr1] = 10.12.7
    #   ipv4[xr1] = 10.12.7.254

Note:

The list of interfaces may be acquired with getindices("sys.ipv4") (or from any of the other associative arrays). Only those interfaces which are marked as "up" and have an IP address will be listed.

sys.ipv4_1[interface_name]

The first octet of the IPv4 address of the system interface named as the associative array index, e.g. $(ipv4_1[le0]) or $(ipv4_1[xr1]).

sys.ipv4_2[interface_name]

The first two octets of the IPv4 address of the system interface named as the associative array index, e.g. $(ipv4_2[le0]) or $(ipv4_2[xr1]).

sys.ipv4_3[interface_name]

The first three octets of the IPv4 address of the system interface named as the associative array index, e.g. $(ipv4_3[le0]) or $(ipv4_3[xr1]).

sys.key_digest

The digest of the host's cryptographic public key.

    # sys.key_digest = MD5=bc230448c9bec14b9123443e1608ac07
sys.last_policy_update

Timestamp when last policy change was seen by host

sys.libdir

The name of the directory where CFEngine looks for its libraries.

    # libdir = /var/cfengine/inputs/lib/3.6

History: Introduced in CFEngine 3.6

sys.local_libdir

The name of the directory where CFEngine looks for its libraries, without any prefixes.

    # local_libdir = lib/3.6

History: Introduced in CFEngine 3.6

sys.logdir

The name of the directory where CFEngine log files are saved

    # logdir = /var/cfengine/

History: Introduced in CFEngine 3.6

sys.license_owner
    reports:

     enterprise::

      "This version of CFEngine is licensed to $(sys.license_owner)";

History: Was introduced in version 3.1.4,Enterprise 2.0.2 (2011)

sys.licenses_granted
    reports:

     enterprise::

      "There are $(sys.licenses_granted) licenses granted for use";

History: Was introduced in version 3.1.4,Enterprise 2.0.2 (2011)

sys.long_arch

The long architecture name for this system kernel. This name is sometimes quite unwieldy but can be useful for logging purposes.

    # long_arch = linux_x86_64_2_6_22_19_0_1_default__1_SMP_2008_10_14_22_17_43__0200

See also: sys.ostype

sys.maildir

The name of the system email spool directory.

    # maildir = /var/spool/mail
sys.masterdir

The name of the directory on the hub where CFEngine looks for inputs to be validated and copied into sys.inputdir.

    # masterdir = /var/cfengine/masterfiles

History: Introduced in CFEngine 3.6

sys.os

The name of the operating system according to the kernel.

    # os = linux

See also: sys.ostype

sys.ostype

Another name for the operating system.

    # ostype = linux_x86_64

See also: sys.class

sys.piddir

The name of the directory where CFEngine saves the daemon pid files.

    # piddir = /var/cfengine/

History: Introduced in CFEngine 3.6

sys.policy_hub

Hostname of the machine acting as the policy server. This value is set during bootstrap. In case bootstrap was not performed, it is set to undefined.

    reports:

     "Policy hub is $(sys.policy_hub)";

History: Was introduced in version 3.1.0b1,Enterprise 2.0.0b1 (2010). Available in Community since 3.2.0

sys.release

The kernel release of the operating system.

    # release = 2.6.22.19-0.1-default
sys.resolv

The location of the system resolver file.

    # resolv = /etc/resolv.conf
sys.sysday

A variable containing the time since the UNIX Epoch (00:00:00 UTC, January 1, 1970), measured in days. It is equivalent to $(sys.systime) divided by the number of seconds in a day, expressed as an integer. No time zone conversion is performed, the direct result of the time() system call is used. This value is most commonly used in the /etc/shadow file.

   # sysday = 15656

   Corresponds to Monday, November 12, 2012.

History: Introduced in CFEngine 3.6

sys.systime

A variable containing the result of the time() system call, which is the time since the UNIX Epoch (00:00:00 UTC, January 1, 1970), measured in seconds. See also $(sys.sysday).

   # systime = 1352754900

   Corresponds to Mon Nov 12 21:15:00 2012 UTC.

History: Introduced in CFEngine 3.6

sys.update_policy_path

The name of the update policy file.

    # update_policy_path = /var/cfengine/inputs/update.cf

History: Introduced in CFEngine 3.6

sys.uptime

A variable containing the number of minutes which the system has been online. (Not implemented on the Windows platform.)

   # uptime = 69735

   Equivalent uptime command output:
    16:24:52 up 48 days, 10:15,  1 user,  load average: 0.00, 0.00, 0.00

History: Introduced in CFEngine 3.6

sys.uqhost

The unqualified name of the current host.

    # uqhost = myhost

See also: sys.fqhost

sys.version

The version of the running kernel. On Linux, this corresponds to the output of uname -v.

    # version = #55-Ubuntu SMP Mon Jan 10 23:42:43 UTC 2011

History: Was introduced in version 3.1.4,Enterprise 2.0.2 (2011)

sys.windir

On the Windows version of CFEngine Enterprise, this is the path to the Windows directory of this system.

    # windir = C:\WINDOWS
sys.winprogdir

On the Windows version of CFEngine Enterprise, this is the path to the program files directory of the system.

    # winprogdir = C:\Program Files
sys.winprogdir86

On 64 bit Windows versions of CFEngine Enterprise, this is the path to the 32 bit (x86) program files directory of the system.

    # winprogdir86 = C:\Program Files (x86)
sys.winsysdir

On the Windows version of CFEngine Enterprise, this is the path to the Windows system directory.

    # winsysdir = C:\WINDOWS\system32
sys.workdir

The location of the CFEngine work directory and cache. For the system privileged user this is normally:

    # workdir = /var/cfengine

For non-privileged users it is in the user's home directory:

    # workdir = /home/user/.cfagent

On the Windows version of CFEngine Enterprise, it is normally under program files (the directory name may change with the language of Windows):

    # workdir = C:\Program Files\CFEngine

Enterprise API Reference

The Enterprise API is a conventional REST API in the sense that it has a number of URI resources that support one or more GET, PUT, POST, or DELETE operations. While reporting is done using SQL, this query is always wrapped in a JSON request.

See Also: Enterprise API Examples

Requests

GET requests are one of listing or getting. Listing resources means that a number of results will be returned, but each entry may contain limited information. An example of a listing query is /api/user to list users. Notice that URI components are always non-plural. An exception to this is /api/settings, which returns the singleton resource for settings. Getting a resource specifies an individual resource to return, e.g. /api/user/homer.

PUT request typically create a new resource, e.g. a user.

POST requests typically updates an existing resource. DELETE requests are also supported in some cases.

Pagination

Pagination is handled by page and count query parameters to a GET request, e.g. /api/user?page=5&count=30 to get the 5th page of pages with 30 entries each. The default page is 1 and the default count is 50 if these are not specified explicitly.

Responses

Enterprise API responses are always of the following format, consisting of a 'meta' object and a 'data' array.

    {
      "meta": {
        "page": 1,
        "count": 1,
        "total": 1,
        "timestamp": 1350922925
      },
      "data": [
         ...
      ]
    }

page refers to the current page number of the request. count is the number of results in the current page, equaling the length of the data array. total is the number of results in all available pages combined. timestamp is the time the request was processed by the API. The data array is resource dependent, but will always contain objects. Response objects typically do not contain error codes.

If the response is not 200 OK, the appropriate HTTP error code returned along with a (possibly non-JSON) payload.

All timestamps are reported in Unix Time, i.e. seconds since 1970.

Authentication

The API supports both internal and external authentication. The internal users table will always be consulted first, followed by an external source specified in the settings. External sources are OpenLDAP or Active Directory servers configurable through POST /api/settings.

Authorization

Some resources require that the request user is a member of the admin role. Roles are managed with /api/role. Role Based Access Control (RBAC) is configurable through the settings. Users typically have permission to access their own resources, e.g. their own scheduled reports.


URI Resources

/api

Supported Operations: GET

Fields:

  • apiName (string) Human-friendly API name.
  • apiVersion (string) API version string.
  • enterpriseVersion (string) Version of the CFEngine Enterprise build.
  • uiVersion (string) The internal build number of the Enterprise UI (since 3.6)
  • coreVersion (string) The version of CFEngine Core (Community) the Enterprise version was built against.
  • databaseHostname (string) Hostname (or IP) of the database the API is connected to.
  • databasePort (integer) Port number of the database the API is connected to.
  • authenticated ("internal", "external"), Whether the request was authenticated using the internal users table or an external source.
  • license.expires (integer) Time when the license expires.
  • license.installTime (integer) Time when the license was installed.
  • license.owner (string) The name of the license owner.
  • license.granted (integer) Host number capacity granted by the license.
  • license.licenseUsage.lastMeasured (integer) Time when license usage was last updated.
  • license.licenseUsage.samples (integer) Number of samples collected for license usage.
  • license.licenseUsage.minObservedLevel (integer) Minimum number of observed host licenses in use.
  • license.licenseUsage.minObservedLevel (integer) Maximum number of observed host licenses in use.
  • license.licenseUsage.meanUsage (integer) Average number of observed host licenses in use.
  • license.licenseUsage.meanCumulativeUtilization (integer) (not sure)
  • license.licenseUsage.usedToday (integer) Total number of host licenses observed used today.
/api/settings

Supported Operations: GET, POST

Fields:

  • rbacEnabled (boolean) Whether RBAC is applied to requests.
  • hostIdentifier (string) The identfying string for hosts, such as name or IP.
  • ldapEnabled (boolean) Whether external authentication is activated.
  • ldapBaseDN (string) LDAP BaseDN to use for external LDAP requests.
  • ldapFilter (string) Filter for LDAP objects.
  • ldapEncryption ("plain", "ssl", "tls") Type of LDAP binding to establish to external LDAP server. (Default: "plain").
  • ldapHost (string) Hostname of external LDAP server.
  • ldapLoginAttribute (string) LDAP attribute to use for usernames. (default: "uid").
  • ldapUsername (string) LDAP username.
  • ldapPassword (string) LDAP password.
  • ldapUsersDirectory (string) Attribute and value to qualify the directory in which to look up users, e.g. "ou=people".
  • ldapPort (integer) Port for external LDAP connections not using SSL. (default 389).
  • ldapPortSSL (integer) Port for external LDAP connections using SSL. (default 636).
  • blueHostHorizon (integer) Time interval (seconds) for when to consider a host unreachable. (default 900).
  • logLevel ("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug") Syslog filter specifying the severity level at which messages produced by the API should be emitted to syslog and apache.log. (default: error).
  • sketchActivationAlertTimeout (integer) Global timeout in minutes for sketch activation alerts.
/api/user

Supported Operations: GET

Query Parameters:

  • id (regex string) Regular expression for filtering usernames.
  • external ("true", "false") Returns only internal users (false) or only external (true), or all if not specified.
/api/user/:id

Supported Operations: GET, PUT, POST, DELETE

Fields:

  • id (string) ID of a user.
  • password (string) Password of a user. (Never returned from API).
  • email (string) Email address associated with user.
  • roles (array of strings) Set of IDs of roles a user is in. (Default: empty)
  • external (boolean) Whether or not the user was found externally (LDAP).
/api/role

Supported Operations: GET

/api/role/:id

Supported Operations: GET, PUT, POST, DELETE

Fields:

  • id (string) ID of a role.
  • description (string) Arbitrary text describing the role
  • includeContext (comma delimited string of regular expression strings) Includes hosts visible to the users in the role.
  • excludeContext (comma delimited string of regular expression strings) Excludes bundles visible to the users in the role.
  • includeBundles (comma delimited string of regular expression strings) Includes bundles visible to the users in the role.
  • excludeBundles (comma delimited string of regular expression strings) Excludes bundles visible to the users in the role.
/api/host

Supported Operations: GET ,DELETE

Query Parameters:

  • include-context (comma delimited string of regular expression strings) Includes hosts having context matching the expression.
  • exclude-context (comma delimited string of regular expression strings) Excludes hosts having context matching the expression.
/api/host/:host-id
  • id (string) ID of a host.
  • hostname (string) Hostname of a host.
  • ip (string) IP address of a host.
/api/host/:host-id/vital/:vital-id

Supported Operations: GET

Query Parameters:

  • from (integer) Timestamp marking the start of the interval for which to fetch data. Data is only available going back one week.
  • to (integer) End of data interval to be fetched.

Fields:

  • id (string) ID of vital sign.
  • description (string) Description of vital sign.
  • units (string) Measurement unit of vital sign.
  • timestamp (integer) Timestamp of the last received data point.
  • values (array of [ t, y ], where t is the sample timestamp) Vital sign data.
/api/query

Supported Operations: POST

Fields:

  • query (string) SQL query string.
  • sortColumn (string) Column on which to sort results. This is applied to the result of the SQL query and can be considered post processing. The Mission Portal uses this to sort cached reports.
  • sortDescending (boolean) Apply post-sorting in descending order.
  • skip (integer) Number of results to skip for the processed query. The Mission Portal uses this for pagination on cached results.
  • limit (integer) Limit the number of results in the processed query.
  • disableCache (boolean) Don't use cached data
/api/query/async

Supported Operations: POST

Fields:

  • query (string) SQL query string.
  • id (string) ID of the query job.
  • error (string) Error if anything went wrong.
/api/query/async/:async-query-id

Supported Operations: GET, DELETE

Fields:

  • id (string) ID of the query job.
  • percentageComplete (integer) Processing status for the query.
  • href (string) Download link for the finished report.
  • error (string) Error if anything went wrong.
Enterprise Design Center API

Please see The Design Center API for the Design Center API commands that are wrapped by the following Enterprise API commands.

/api/dc/sketch
  • GET: List of sketches
{
    "meta": {
        "page": 1,
        "count": 69,
        "total": 69,
        "timestamp": 1383829723
        },
    "data": [
        {
            "Utilities::Staging": {
                "metadata": {
                    "authors": [
                        "Ted Zlatanov <tzz@lifelogs.com>"
                    ],
                    "version": 1,
                    "name": "Utilities::Staging",
                    "license": "MIT",
                    "description": "Stage a directory of content to a target directory.",
                    "tags": [
                        "cfdc",
                        "stage",
                        "directory",
                        "rsync"
                    ],
                    "depends": {
                        "cfengine": {
                            "version": "3.5.0"
                        },
                        "CFEngine::dclib::3.5.0": [],
                        "CFEngine::dclib": [],
                        "CFEngine::stdlib": {
                            "version": 109
                        }
                    }
                }
            }
        }]
    }
/api/dc/sketch/:name
  • GET: info about specific sketch
{
    "meta": {
        "page": 1,
        "count": 1,
        "total": 1,
        "timestamp": 1383831531
    },
    "data": [
        {
            "namespace": "cfdc_staging",
            "manifest": {
                "test.cf": {
                    "comment": "Test Policy"
                },
                "params/demo.json": {
                    "comment": "Demo parameters."
                },
                "README.md": {
                    "documentation": true
                },
                "test.pl": {
                    "test": true
                },
                "main.cf": {
                    "desc": "main file"
                }
            },
            "interface": [
                "main.cf"
            ],
            "metadata": {
                "authors": [
                    "Ted Zlatanov <tzz@lifelogs.com>"
                ],
                "version": 1,
                "name": "Utilities::Staging",
                "license": "MIT",
                "description": "Stage a directory of content to a target directory.",
                "tags": [
                    "cfdc",
                    "stage",
                    "directory",
                    "rsync"
                ],
                "depends": {
                    "cfengine": {
                        "version": "3.5.0"
                    },
                    "CFEngine::dclib::3.5.0": [],
                    "CFEngine::dclib": [],
                    "CFEngine::stdlib": {
                        "version": 109
                    }
                }
            },
            "entry_point": null,
            "api": {
                "stage": [
                    {
                        "name": "runenv",
                        "type": "environment"
                    },
                    {
                        "name": "metadata",
                        "type": "metadata"
                    },
                    {
                        "name": "source_dir",
                        "validation": "PATH_ABSOLUTE_UNIX_OR_WINDOWS",
                        "type": "string",
                        "description": "Directory where the content can be found."
                    },
                    {
                        "name": "dest_dir",
                        "validation": "PATH_ABSOLUTE_UNIX_OR_WINDOWS",
                        "type": "string",
                        "description": "Directory where the content will be installed."
                    },
                    {
                        "name": "owner",
                        "validation": "USERNAME_UNIX",
                        "type": "string",
                        "description": "Owner of the dest_dir after staging."
                    },
                    {
                        "name": "group",
                        "validation": "USERNAME_UNIX",
                        "type": "string",
                        "description": "Owner of the dest_dir after staging."
                    },
                    {
                        "name": "dirmode",
                        "validation": "DIGITS",
                        "type": "string",
                        "description": "Directory mode to install."
                    },
                    {
                        "name": "filemode",
                        "validation": "DIGITS",
                        "type": "string",
                        "description": "File mode to install."
                    },
                    {
                        "name": "options",
                        "type": "array",
                        "default": {
                            "precommand": "/bin/echo precommand",
                            "postcommand": "/bin/echo postcommand",
                            "excluded": [
                                ".cvs",
                                ".svn",
                                ".subversion",
                                ".git",
                                ".bzr"
                            ]
                        },
                        "description": "Staging options."
                    },
                    {
                        "name": "staged",
                        "type": "return"
                    },
                    {
                        "name": "directory",
                        "type": "return"
                    }
                ]
            }
        }
    ]
}
/api/dc/sketch/:sketchName
  • PUT: install sketch in the system
/api/dc/definition
  • GET: List of available definitions
{
    "meta": {
        "page": 1,
        "count": 1,
        "total": 28,
        "timestamp": 1383831645
    },
    "data": [
        {
            ...
            "e180fc753487e749056f422f89420d06": {
                "Data::Classes": {
                    "url_retriever": "/usr/bin/curl -s",
                    "CF_MP_ENTRY_POINT": "bynet",
                    "regex": "daas",
                    "url": "http://asw.as",
                    "classname": "as"
                }
            },
            "efce8022c7a53d3755ded38aa6b64730": {
                "Utilities::abortclasses": {
                    "alert_only": "1",
                    "trigger_file": "/COWBOY",
                    "timeout": {
                        "hours": 24,
                        "years": 0,
                        "minutes": 0,
                        "action": "abortclasses_timeout_action_noop",
                        "months": 0,
                        "enabled": false,
                        "days": "144"
                    },
                    "trigger_context": "any",
                    "abortclass": "class"
                }
            }
        }
    ]
}
/api/dc/definition/:definitionName
  • definitionName: name of the definition
  • PUT: Create new definition
  • Request Body:
    {
    "sketchName":"test",
    "params": {
        "hey":"ho"
        }
    }
/api/dc/environment
  • GET: List of available environments
{
    "meta": {
        "page": 1,
        "count": 1,
        "total": 6,
        "timestamp": 1383831817
    },
    "data": [
        {
            "092b04a40fdd4cb8bfdb685f2c4a0328": {
                "verbose": "",
                "test": "",
                "activated": {
                    "include": [
                        "cfengine_3"
                    ],
                    "class_function": [
                        {
                            "function": "classmatch",
                            "args": [
                                "cfengine_3"
                            ]
                        }
                    ],
                    "exclude": []
                }
            }
    ]
}
/api/dc/environment/:environmentName
  • environmentName: name of the environment
  • PUT: Create new environment
  • Request Body:
    {
    "environment": ["cfengine3"]
    }
/api/dc/activation?sketch=&details=1

GET: List of available activations

{
    "meta": {
        "page": 1,
        "count": 1,
        "total": 9,
        "timestamp": 1383831923
    },
    "data": [
        {
            "Data::Classes": [
                {
                    "params": [
                        "3603e753b8cb8ecc4d440dc91cd74742"
                    ],
                    "environment": "092b04a40fdd4cb8bfdb685f2c4a0328",
                    "target": "sketches",
                    "identifier": "cc",
                    "bundle": "byfile",
                    "metadata": {
                        "identifier": "cc",
                        "timestamp": 1379939700
                    }
                },
                {
                    "params": [
                        "e180fc753487e749056f422f89420d06"
                    ],
                    "environment": "092b04a40fdd4cb8bfdb685f2c4a0328",
                    "target": "sketches",
                    "identifier": "aaa",
                    "bundle": "bynet",
                    "metadata": {
                        "identifier": "aaa",
                        "timestamp": 1380011681
                    }
                }
            ],
            "Packages::removed": [
                {
                    "params": [
                        "8f068e0b3d7c2edc2d113a48b2485f94"
                    ],
                    "environment": "092b04a40fdd4cb8bfdb685f2c4a0328",
                    "target": "sketches",
                    "identifier": "12",
                    "bundle": "removed",
                    "metadata": {
                        "identifier": "12",
                        "timestamp": 1382366628
                    }
                },
                {
                    "params": [
                        "e3134847d954d98d7419137b437cfd3c"
                    ],
                    "environment": "092b04a40fdd4cb8bfdb685f2c4a0328",
                    "target": "sketches",
                    "identifier": "xz",
                    "bundle": "removed",
                    "metadata": {
                        "identifier": "xz",
                        "timestamp": 1382367291
                    }
                }
            ]

        }
    ]
}
/api/dc/activation/:id/:sketchName?details+
  • id: (identifier of activation)
  • sketchName: name of the sketch
  • Params details: 1 or 0 for host and other details

  • GET: Info about specific activations

{
    "meta": {
        "page": 1,
        "count": 1,
        "total": 1,
        "timestamp": 1383832020
    },
    "data": [
        [

            {
                "params": [
                    "087b875ad637c6392acc3b78b66910cb"
                ],
                "environment": "092b04a40fdd4cb8bfdb685f2c4a0328",
                "target": "sketches",
                "identifier": "pokemon",
                "bundle": "installed",
                "metadata": {
                    "identifier": "pokemon",
                    "timestamp": 1383306456
                },
                "details": {
                    "params": {
                        "CF_MP_ENTRY_POINT": "installed",
                        "pkgs_add": [
                            "po"
                        ]
                    },
                    "environments": {
                        "verbose": "",
                        "test": "",
                        "activated": {
                            "include": [
                                "cfengine_3"
                            ],
                            "class_function": [
                                {
                                    "function": "classmatch",
                                    "args": [
                                        "cfengine_3"
                                    ]
                                }
                            ],
                            "exclude": []
                        }
                    },
                    "hosts": []
                }
            }
        ]
    ]
}
  • PUT: Create new activation
    Request body:
    {

         "environmentName":"092b04a40fdd4cb8bfdb685f2c4a0328",
         "paramName":"c53db12b79d5b2b74f319b91caf7e88f",
         "bundleName": "installed"
    }
  • DELETE: Delete the activation
/api/dc/validation
  • GET: Get list of validations
/api/dc/validation/:id
  • GET: Get specific validations
/api/dc/validate/:validationType
  • validationType: specific validation type
  • POST: validate the data
  • Request Body:
    {
    "validationData":["asdasd"]
    }
/api/dc/workspace
  • GET: checks for the workspace and returns the path
/api/dc/workspace/commit
  • POST: Post the commits
    Request Body:
    {
    'message': "some message",
        'userEmail': 'email.com'
    }
/api/dc/workspace/reset
  • POST: Resets the user workspace
/api/dc/workspace/settings
  • GET: Returns the settings of the workspace (VCS settings), 404 if not found
  • POST: Create the settings. Content-Type header should be multipart/form-data
  • DELETE: Delete settings
        Request Body:
        {
         'gitServer':"serverurl",
         'gitEmail': "email.com" ,
         'gitBranch':"gitbranch name",
         'gitAuthor': "author name",
         'gitPrivateKey': "@filepath"
        }
        eg: curl -F "gitServer=servername" -F "gitEmail=mail" -F "gitPrivateKey=@/home/user1/Desktop/id_rsa" http://server

SQL Schema

CFEngine allows standardized SQL SELECT queries to be used with Query API. Queries can be used with following database schema.

curl -k --user admin:admin https://hub.cfengine.com/api/query -X POST -d "{ \"query\": \"SELECT Hosts.HostName, Hosts.IPAddress FROM Hosts WHERE hostname = 'hub'\"}"
Table: Hosts

Hosts table contains basic information about hosts managed by CFEngine.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • HostName (text) Host name locally detected on the host, configurable as hostIdentifier option in Settings API and Mission Portal settings UI.

  • IPAddress (text) IP address of the host.

  • LastReportTimeStamp (timestamp) Timestamp of the most recent successful report collection.

  • FirstReportTimeStamp (timestamp) Timestamp when the host reported to the hub for the first time, which indicate when the host was bootstrapped to the hub.

Example query:

SELECT hostkey,
       hostname,
       ipaddress,
       lastreporttimestamp,
       firstreporttimestamp
FROM   hosts;

Output:

-[ RECORD 1 ]--------+-----------------------
hostkey              | SHA=a4dd...
hostname             | host001
ipaddress            | 192.168.33.151
lastreporttimestamp  | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:40:20+00
-[ RECORD 2 ]--------+-----------------------
hostkey              | SHA=3b94...
hostname             | hub
ipaddress            | 192.168.33.65
lastreporttimestamp  | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:34:20+00
-[ RECORD 3 ]--------+-----------------------
hostkey              | SHA=2aab...
hostname             | host002
ipaddress            | 192.168.33.152
lastreporttimestamp  | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:40:20+00
Table: AgentStatus

Agent status contains information about last cf-agent execution.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • AgentExecutionInterval (integer) Estimated interval in which cf-agent is being executed, as cf-agent execution interval is expressed in cfengine context expressions (Min00_05 etc.) it can be not regular, this interval is discovered by analyzing last few cf-agent execution timestamps. Expressed in seconds.

  • LastAgentLocalExecutionTimeStamp (timestamp) Timestamp of last cf-agent execution on the host.

  • LastAgentExecutionStatus (OK/FAIL) cf-agent execution status. In case cf-agent will not execute within 3x AgentExecutionInterval from last execution, status will be set to FAIL. Failure may indicate cf-execd issues, or cf-agent crashes.

Example query:

SELECT hostkey,
       agentexecutioninterval,
       lastagentlocalexecutiontimestamp,
       lastagentexecutionstatus
FROM   agentstatus;

Output:

-[ RECORD 1 ]--------------------+-----------------------
hostkey                          | SHA=3b94d...
agentexecutioninterval           | 277
lastagentlocalexecutiontimestamp | 2015-03-11 12:37:39+00
lastagentexecutionstatus         | OK
-[ RECORD 2 ]--------------------+-----------------------
hostkey                          | SHA=a4dd5...
agentexecutioninterval           | 275
lastagentlocalexecutiontimestamp | 2015-03-11 12:36:36+00
lastagentexecutionstatus         | OK
-[ RECORD 3 ]--------------------+-----------------------
hostkey                          | SHA=2aab8...
agentexecutioninterval           | 284
lastagentlocalexecutiontimestamp | 2015-03-11 12:36:51+00
lastagentexecutionstatus         | OK
Table: Contexts

CFEngine contexts present on hosts at their last reported cf-agent execution.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • ContextName (text) CFEngine context set by cf-agent.

  • MetaTags (text[]) List of meta tags set for the context.

  • ChangeTimeStamp (timestamp) Timestamp since when context is set in its current form. Note: If any of context attributes change, the timestamp will be updated.

Example query:

SELECT hostkey,
       contextname,
       metatags,
       changetimestamp
FROM   contexts;

Output:

-[ RECORD 1 ]---+-------------------------------------------------------
hostkey         | SHA=a4dd5...
contextname     | enterprise_3_6_5
metatags        | {inventory,attribute_name=none,source=agent,hardclass}
changetimestamp | 2015-03-11 09:50:11+00
-[ RECORD 2 ]---+-------------------------------------------------------
hostkey         | SHA=a4dd5...
contextname     | production
metatags        | {report,"Production enviroment"}
changetimestamp | 2015-03-11 09:50:11+00
-[ RECORD 3 ]---+-------------------------------------------------------
hostkey         | SHA=a4dd5...
contextname     | enterprise_edition
metatags        | {inventory,attribute_name=none,source=agent,hardclass}
changetimestamp | 2015-03-11 09:50:11+00
Table: Variables

Variables and their values set on hosts at their last reported cf-agent execution.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • NameSpace (text) Namespace within which the variable is set. If no namespace is set then it is set as: default.

  • Bundle (text) Bundle name where the variable is set.

  • VariableName (text) Name of the variable.

  • VariableValue (text) Variable value serialized to string.

    • List types such as: slist, ilist, rlist are serialized with CFEngine list format: {'value','value'}.
    • Data type is serialized as JSON string.
  • VariableType (text) Type of the variable. List of supported variable types.

  • MetaTags (text[]) List of meta tags set for the variable.

  • ChangeTimeStamp (timestamp) Timestamp since when variable is set in its current form. Note: If any of variable attributes change such as its VariableValue or Bundle, the timestamp will be updated.

Example query:

SELECT hostkey,
       namespace,
       bundle,
       variablename,
       variablevalue,
       variabletype,
       metatags,
       changetimestamp
FROM   variables;

Output:

-[ RECORD 1 ]---+-------------------------------------------------------------
hostkey         | SHA=a4dd5...
namespace       | default
bundle          | cfe_autorun_inventory_memory
variablename    | total
variablevalue   | 490.00
variabletype    | string
metatags        | {source=promise,inventory,"attribute_name=Memory size (MB)"}
changetimestamp | 2015-03-11 09:51:41+00
-[ RECORD 2 ]---+-------------------------------------------------------------
hostkey         | SHA=a4dd5...
namespace       | default
bundle          | cfe_autorun_inventory_listening_ports
variablename    | ports
variablevalue   | {'22','111','5308','38854','50241'}
variabletype    | slist
metatags        | {source=promise,inventory,"attribute_name=Ports listening"}
changetimestamp | 2015-03-11 09:51:41+00
-[ RECORD 3 ]---+-------------------------------------------------------------
hostkey         | SHA=a4dd5...
namespace       | default
bundle          | cfe_autorun_inventory_memory
variablename    | free
variablevalue   | 69.66
variabletype    | string
metatags        | {source=promise,report}
changetimestamp | 2015-03-11 14:27:12+00
Table: Software

Software packages installed (according to local package manager) on the hosts. More information about CFEngine and package management can be found here.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • SoftwareName (text) Name of installed software package.

  • SoftwareVersion (text) Software package version.

  • SoftwareArchitecture (text) Architecture.

  • ChangeTimeStamp (timestamp) Timestamp when the package was discovered / installed on the host.

Example query:

SELECT hostkey,
       softwarename,
       softwareversion,
       softwarearchitecture,
       changetimestamp
FROM   software;

Output:

-[ RECORD 1 ]--------+-----------------------
hostkey              | SHA=a4dd5...
softwarename         | libgssapi-krb5-2
softwareversion      | 1.12+dfsg-2ubuntu4.2
softwarearchitecture | default
changetimestamp      | 2015-03-12 10:20:18+00
-[ RECORD 2 ]--------+-----------------------
hostkey              | SHA=a4dd5...
softwarename         | whiptail
softwareversion      | 0.52.15-2ubuntu5
softwarearchitecture | default
changetimestamp      | 2015-03-12 10:20:18+00
-[ RECORD 3 ]--------+-----------------------
hostkey              | SHA=a4dd5...
softwarename         | libruby1.9.1
softwareversion      | 1.9.3.484-2ubuntu1.2
softwarearchitecture | default
changetimestamp      | 2015-03-12 10:20:18+00
Table: SoftwareUpdates

Patches available for installed packages on the hosts (as reported by local package manager). The most up to date patch will be listed.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • PatchName (text) Name of the software.

  • PatchVersion (text) Patch version.

  • PatchArchitecture (text) Architecture of the patch.

  • PatchReportType (INSTALLED/AVAILABLE) Patch status (INSTALLED status is specific only to SUSE Linux).

  • ChangeTimeStamp (timestamp) Timestamp when the new patch / version was discovered as available on the host.

Example query:

SELECT hostkey,
       patchname,
       patchversion,
       patcharchitecture,
       patchreporttype,
       changetimestamp
FROM   softwareupdates;

Output:

-[ RECORD 1 ]-----+------------------------
hostkey           | SHA=a4dd5...
patchname         | libelf1
patchversion      | 0.158-0ubuntu5.2
patcharchitecture | default
patchreporttype   | AVAILABLE
changetimestamp   | 2015-03-12 10:20:18+00
-[ RECORD 2 ]-----+------------------------
hostkey           | SHA=a4dd5...
patchname         | libisccfg90
patchversion      | 1:9.9.5.dfsg-3ubuntu0.2
patcharchitecture | default
patchreporttype   | AVAILABLE
changetimestamp   | 2015-03-12 10:20:18+00
-[ RECORD 3 ]-----+------------------------
hostkey           | SHA=a4dd5...
patchname         | libc6-dev
patchversion      | 2.19-0ubuntu6.6
patcharchitecture | default
patchreporttype   | AVAILABLE
changetimestamp   | 2015-03-12 10:20:18+00
Table: PromiseExecutions

Promises executed on hosts during their last reported cf-agent run.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • PolicyFile (text) Path to the file where the promise is located in.

  • ReleaseId (text) Unique identifier of masterfiles version that is executed on the host.

  • PromiseHash (text) Unique identifier of a promise. It is a hash of all promise attributes and their values.

  • NameSpace (text) Namespace within which the promise is executed. If no namespace is set then it is set as: default.

  • BundleName (text) Bundle name where the promise is executed.

  • PromiseType (text) Type of the promise.

  • Promiser (text) Object affected by a promise.

  • StackPath (text) Call stack of the promise.

  • PromiseHandle (text) A unique id-tag string for referring promise.

  • PromiseOutcome (KEPT/NOTKEPT/REPAIRED) Promise execution result.

    • KEPT - System has been found in the state as desired by the promise. CFEngine did not have to do any action to correct the state.
    • REPAIRED - State of the system differed from the desired state. CFEngine took successful action to correct it according to promise specification.
    • NOTKEPT - CFEngine has failed to converge the system according to the promise specification.
  • LogMessages (text[]) List of 5 last messages generated during promise execution. If the promise is KEPT the messages are not reported. Log messages can be used for tracking specific changes made by CFEngine while repairing or failing promise execution.

  • Promisees (text[]) List of promisees defined for the promise.

  • ChangeTimeStamp (timestamp) Timestamp since when the promise is continuously executed by cf-agent in its current configuration and provides the same output. Note: If any of the promise dynamic attributes change, like promise outcome, log messages or the new policy version will be rolled out. This timestamp will be changed.

Example query:

SELECT hostkey,
       policyfile,
       releaseid,
       promisehash,
       namespace,
       bundlename,
       promisetype,
       promiser,
       stackpath,
       promisehandle,
       promiseoutcome,
       logmessages,
       promisees,
       changetimestamp
FROM   softwareupdates;

Output:

-[ RECORD 1 ]---+---------------------------------------------------------
hostkey         | SHA=a4dd5...
policyfile      | /var/cfengine/inputs/inventory/any.cf
releaseid       | 05c0cc909d6709d816521d6cedbc4508894cc497
promisehash     | fd6d5e40b734e35d9e8b2ed071dfe390f23148053adaae3dbb936...
namespace       | default
bundlename      | inventory_autorun
promisetype     | methods
promiser        | mtab
stackpath       | /default/inventory_autorun/methods/'mtab'[0]
promisehandle   | cfe_internal_autorun_inventory_mtab
promiseoutcome  | KEPT
logmessages     | {}
promisees       | {}
changetimestamp | 2015-03-12 10:20:18+00
-[ RECORD 2 ]---+---------------------------------------------------------
hostkey         | SHA=a4dd5...
policyfile      | /var/cfengine/inputs/promises.cf
releaseid       | 05c0cc909d6709d816521d6cedbc4508894cc497
promisehash     | 925b04453ef86ff2e43228a5ca5d56dc4d69ddf12378d6fdba28b...
namespace       | default
bundlename      | service_catalogue
promisetype     | methods
promiser        | security
stackpath       | /default/service_catalogue/methods/'security'[0]
promisehandle   | service_catalogue_change_management
promiseoutcome  | KEPT
logmessages     | {}
promisees       | {goal_infosec,goal_compliance}
changetimestamp | 2015-03-12 10:20:18+00
-[ RECORD 3 ]---+---------------------------------------------------------
hostkey         | SHA=3b94d...
policyfile      | /var/cfengine/inputs/lib/3.6/bundles.cf
releaseid       | 05c0cc909d6709d816521d6cedbc4508894cc497
promisehash     | 47f64d43f21bc6162b4f21bf385e715535617eebc649b259ebaca...
namespace       | default
bundlename      | logrotate
promisetype     | files
promiser        | /var/cfengine/cf3.hub.runlog
stackpath       | /default/cfe_internal_management/files/'any'/default/...
promisehandle   |
promiseoutcome  | REPAIRED
logmessages     | {"Rotating files '/var/cfengine/cf3.hub.runlog'"}
promisees       | {}
changetimestamp | 2015-03-12 14:52:36+00
Table: LastSeenHosts

Information about communication between CFEngine clients.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • LastSeenDirection (INCOMING/OUTGOING) Direction within which the connection was established.

    • INCOMING - host recieved incoming connection.
    • OUTGOING - host opened connection to remote host.
  • RemoteHostKey (text) HostKey of the remote host.

  • RemoteHostIP (text) IP address of the remote host.

  • LastSeenTimeStamp (timestamp) Time when the connection was established.

  • LastSeenInterval (real) Frequency within which both hosts open connection.

Example query:

SELECT hostkey,
       lastseendirection,
       remotehostkey,
       remotehostip,
       lastseentimestamp,
       lastseeninterval
FROM   lastseenhosts;

Output:

-[ RECORD 1 ]-----+-----------------------
hostkey           | SHA=3b94d...
lastseendirection | OUTGOING
remotehostkey     | SHA=2aab8...
remotehostip      | 192.168.33.152
lastseentimestamp | 2015-03-13 12:20:45+00
lastseeninterval  | 0
-[ RECORD 2 ]-----+------------------------
hostkey           | SHA=3b94d...
lastseendirection | INCOMING
remotehostkey     | SHA=a4dd5...
remotehostip      | 192.168.33.151
lastseentimestamp | 2015-03-13 12:22:06+00
lastseeninterval  | 0
-[ RECORD 3 ]-----+------------------------
hostkey           | SHA=2aab8...
lastseendirection | INCOMING
remotehostkey     | SHA=3b94d...
remotehostip      | 192.168.33.65
lastseentimestamp | 2015-03-13 12:20:45+00
lastseeninterval  | 0
Table: FileChangesLog

Log of changes detected to files that are set to be monitored by cf-agent.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • PromiseHandle (text) A Uniqueue id-tag string for referring promise.

  • FileName (text) Name of the file that have changed.

  • ChangeTimeStamp (timestamp) Timestamp when CFEngine have detected the change to the file.

  • ChangeType (text) Type of change detected on the monitored file.

    • DIFF - change in content (with file diff)
    • S - change in file stats
    • C - change in content (based on file hash)
  • ChangeDetails (text[]) Information about changes detected to the file. Such as file stats information, file diff etc.

Example query:

SELECT hostkey,
       promisehandle,
       filename,
       changetimestamp,
       changetype,
       changedetails
FROM   filechangeslog;

Output:

-[ RECORD 1 ]---+------------------------------------------------------------
hostkey         | SHA=3b94d...
promisehandle   | my_test_promise
filename        | /tmp/app.conf
changetimestamp | 2015-03-13 13:16:10+00
changetype      | C
changedetails   | {"Content changed"}
-[ RECORD 2 ]---+------------------------------------------------------------
hostkey         | SHA=3b94d...
promisehandle   | my_test_promise
filename        | /tmp/app.conf
changetimestamp | 2015-03-13 13:16:10+00
changetype      | DIFF
changedetails   | {"-,1,loglevel = info","+,1,loglevel = debug"}
-[ RECORD 3 ]---+------------------------------------------------------------
hostkey         | SHA=3b94d...
promisehandle   | my_test_promise
filename        | /tmp/app.conf
changetimestamp | 2015-03-09 11:46:36+00
changetype      | S
changedetails   | {"Modified time: Mon Mar 9 11:37:50 -> Mon Mar 9 11:42:27"}
Table: ContextsLog

CFEngine contexts set on hosts by CFEngine over period of time.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • ChangeTimeStamp (timestamp) Timestamp since when context is set in its current form. Note: The statement if true till present time or newer entry claims otherwise.

  • ChangeOperation (ADD,CHANGE,REMOVE,UNTRACKED) CFEngine uses incremental diffs to report it's state. ChangeOperation is a diff state describing current entry.

    • ADD - stands for introducing a new entry which did not exist before. In this case, new CFEngine context have been introduced.
    • CHANGE - stands for changing value or attribute such as MetaTags have changed.
    • REMOVE - Context have not been set.
    • UNTRACKED - CFEngine provides a mechanism for filtering unwanted data from being reported. UNTRACKED marker states that information about this context is being filtered and will not report any future information about it.
  • ContextName (text) CFEngine context set by cf-agent.

  • MetaTags (text[]) List of meta tags set for the context.

Example query:

SELECT hostkey,
       changetimestamp,
       changeoperation,
       contextname,
       metatags
FROM   contextslog;

Output:

-[ RECORD 1 ]---+-------------------------------------------------------
hostkey         | SHA=a4dd5...
changetimestamp | 2015-03-10 13:40:20+00
changeoperation | ADD
contextname     | debian
metatags        | {inventory,attribute_name=none,source=agent,hardclass}
-[ RECORD 2 ]---+-------------------------------------------------------
hostkey         | SHA=a4dd5...
changetimestamp | 2015-03-10 14:40:20+00
changeoperation | ADD
contextname     | ipv4_192_168
metatags        | {inventory,attribute_name=none,source=agent,hardclass}
-[ RECORD 3 ]---+-------------------------------------------------------
hostkey         | SHA=a4dd5...
changetimestamp | 2015-03-10 15:40:20+00
changeoperation | ADD
contextname     | nova_3_6_5
metatags        | {inventory,attribute_name=none,source=agent,hardclass}
Table: VariablesLog

CFEngine variables set on hosts by CFEngine over period of time.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • ChangeTimeStamp (timestamp) Timestamp since when variable is set in its current form. Note: The statement if true till present time or newer entry claims otherwise.

  • ChangeOperation (ADD,CHANGE,REMOVE,UNTRACKED) CFEngine uses incremental diffs to report it's state. ChangeOperation is a diff state describing current entry.

    • ADD - stands for introducing a new entry which did not exist before. In this case, new CFEngine variable have been introduced.
    • CHANGE - stands for changing value or attribute such as VariableValue or MetaTags have changed.
    • REMOVE - Variable have not been set.
    • UNTRACKED - CFEngine provides a mechanism for filtering unwanted data from being reported. UNTRACKED marker states that information is being filtered and will not report any future information about it.
  • NameSpace (text) Namespace within which the variable is set. If no namespace is set then it is set as: default.

  • Bundle (text) Bundle name where the variable is set.

  • VariableName (text) Name of the variable.

  • VariableValue (text) Variable value serialized to string.

    • List types such as: slist, ilist, rlist are serialized with CFEngine list format: {'value','value'}.
    • Data type is serialized as JSON string.
  • VariableType (text) Type of the variable. List of supported variable types.

  • MetaTags (text[]) List of meta tags set for the variable.

Example query:

SELECT hostkey,
       changetimestamp,
       changeoperation,
       namespace,
       bundle,
       variablename,
       variablevalue,
       variabletype,
       metatags
FROM   variableslog;

Output:

-[ RECORD 1 ]---+-----------------------------------------------------
hostkey         | SHA=2aab8...
changetimestamp | 2015-03-10 13:43:00+00
changeoperation | CHANGE
namespace       | default
bundle          | mon
variablename    | av_cpu
variablevalue   | 0.06
variabletype    | string
metatags        | {monitoring,source=environment}
-[ RECORD 2 ]---+-----------------------------------------------------
hostkey         | SHA=2aab8...
changetimestamp | 2015-03-10 13:40:20+00
changeoperation | ADD
namespace       | default
bundle          | sys
variablename    | arch
variablevalue   | x86_64
variabletype    | string
metatags        | {inventory,source=agent,attribute_name=Architecture}
-[ RECORD 3 ]---+-----------------------------------------------------
hostkey         | SHA=2aab8...
changetimestamp | 2015-03-10 13:43:00+00
changeoperation | CHANGE
namespace       | default
bundle          | mon
variablename    | av_diskfree
variablevalue   | 67.01
variabletype    | string
metatags        | {monitoring,source=environment}
Table: SoftwareLog

Software packages installed / deleted over period of time. More information about CFEngine and package management can be found here.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • ChangeTimeStamp (timestamp) Timestamp when the package state was discovered on the host. Note: The statement if true till present time or newer entry claims otherwise.

  • ChangeOperation (ADD,REMOVE) CFEngine uses incremental diffs to report it's state. ChangeOperation is a diff state describing current entry.

    • ADD - New package have been detected / installed. Package upgrate is considered as installing a new package with a different version.
    • REMOVE - Package have been detected to be removed / uninstalled. During upgrate older version of the package is removed and reported as so.
  • SoftwareName (text) Name of installed software package.

  • SoftwareVersion (text) Software package version.

  • SoftwareArchitecture (text) Architecture.

Example query:

SELECT hostkey,
       changetimestamp,
       changeoperation,
       softwarename,
       softwareversion,
       softwarearchitecture
FROM   softwarelog;

Output:

-[ RECORD 1 ]--------+-----------------------
hostkey              | SHA=3b94d...
changetimestamp      | 2015-03-10 13:38:14+00
changeoperation      | ADD
softwarename         | libgssapi-krb5-2
softwareversion      | 1.12+dfsg-2ubuntu4.2
softwarearchitecture | default
-[ RECORD 2 ]--------+-----------------------
hostkey              | SHA=3b94d...
changetimestamp      | 2015-03-10 13:38:14+00
changeoperation      | ADD
softwarename         | whiptail
softwareversion      | 0.52.15-2ubuntu5
softwarearchitecture | default
-[ RECORD 3 ]--------+-----------------------
hostkey              | SHA=3b94d...
changetimestamp      | 2015-03-10 13:38:14+00
changeoperation      | ADD
softwarename         | libruby1.9.1
softwareversion      | 1.9.3.484-2ubuntu1.2
softwarearchitecture | default
Table: SoftwareUpdatesLog

Patches available for installed packages on the hosts (as reported by local package manager) over period of time.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • ChangeTimeStamp (timestamp) Timestamp when the patch state was discovered on the host. Note: The statement if true till present time or newer entry claims otherwise.

  • ChangeOperation (ADD,REMOVE) CFEngine uses incremental diffs to report it's state. ChangeOperation is a diff state describing current entry.

    • ADD - New patch have been detected. This is a common in case of release of new patch version or new package was installed that have an upgrate available.
    • REMOVE - Patch is not longer available. Patch may be replaced with newer version, or installed package have been upgrated. Note: CFEngine reports only the most up to date version available.
  • PatchName (text) Name of the software.

  • PatchVersion (text) Patch version.

  • PatchArchitecture (text) Architecture of the patch.

  • PatchReportType (INSTALLED/AVAILABLE) Patch status (INSTALLED status is specific only to SUSE Linux).

Example query:

SELECT hostkey,
       changetimestamp,
       changeoperation,
       patchname,
       patchversion,
       patcharchitecture,
       patchreporttype
FROM   softwareupdateslog;

Output:

-[ RECORD 1 ]-----+------------------------
hostkey           | SHA=3b94d...
changetimestamp   | 2015-03-10 13:38:14+00
changeoperation   | ADD
patchname         | libelf1
patchversion      | 0.158-0ubuntu5.2
patcharchitecture | default
patchreporttype   | AVAILABLE
-[ RECORD 2 ]-----+------------------------
hostkey           | SHA=3b94d...
changetimestamp   | 2015-03-10 13:38:14+00
changeoperation   | ADD
patchname         | libisccfg90
patchversion      | 1:9.9.5.dfsg-3ubuntu0.2
patcharchitecture | default
patchreporttype   | AVAILABLE
-[ RECORD 3 ]-----+------------------------
hostkey           | SHA=3b94d...
changetimestamp   | 2015-03-10 13:38:14+00
changeoperation   | ADD
patchname         | libc6-dev
patchversion      | 2.19-0ubuntu6.6
patcharchitecture | default
patchreporttype   | AVAILABLE
Table: PromiseExecutionsLog

Promise status / outcome changes over period of time.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • ChangeTimeStamp (timestamp) Timestamp when the promise state or outcome changed. Note: The statement if true till present time or newer entry claims otherwise.

  • ChangeOperation (ADD,CHANGE,REMOVE,UNTRACKED) CFEngine uses incremental diffs to report it's state. ChangeOperation is a diff state describing current entry.

    • ADD - stands for introducing a new entry which did not exist at last execution. In this case, new promise executed, or the promise was not executed at previous cf-agent run.
    • CHANGE - stands for changing value or attribute such as PromiseOutcome, LogMessages or ReleaseId in case of new policy rollout.
    • REMOVE - Promise was not executed last time, but it was executed previously. This is a common report for promises that have been removed from policy at some point, or they are executed only periodically (like once a hour, day etc.).
    • UNTRACKED - CFEngine provides a mechanism for filtering unwanted data from being reported. UNTRACKED marker states that information is being filtered and will not report any future information about it.
  • PolicyFile (text) Path to the file where the promise is located in.

  • ReleaseId (text) Unique identifier of masterfiles version that is executed in the host.

  • PromiseHash (text) Unique identifier of a promise. It is a hash of all promise attributes and their values.

  • NameSpace (text) Namespace within which the promise is executed. If no namespace is set then it is set as: default.

  • BundleName (text) Bundle name where the promise is executed.

  • PromiseType (text) Type of the promise.

  • Promiser (text) Object affected by a promise.

  • StackPath (text) Call stack of the promise.

  • PromiseHandle (text) A unique id-tag string for referring promise.

  • PromiseOutcome (KEPT/NOTKEPT/REPAIRED) Promise execution result.

    • KEPT - System has been found in the state as desired by the promise. CFEngine did not have to do any action to correct the state.
    • REPAIRED - State of the system differed from the desired state. CFEngine took successful action to correct it according to promise specification.
    • NOTKEPT - CFEngine has failed to converge the system according to the promise specification.
  • LogMessages (text[]) List of 5 last messages generated during promise execution. If the promise is KEPT the messages are not reported. Log messages can be used for tracking specific changes made by CFEngine while repairing or failing promise execution.

  • Promisees (text[]) List of promisees defined for the promise.

Example query:

SELECT hostkey,
       changetimestamp,
       changeoperation,
       policyfile,
       releaseid,
       promisehash,
       namespace,
       bundlename,
       promisetype,
       promiser,
       stackpath,
       promisehandle,
       promiseoutcome,
       logmessages,
       promisees
FROM   promiseexecutionslog;

Output:

-[ RECORD 1 ]---+--------------------------------------------------
hostkey         | SHA=a4dd5...
changetimestamp | 2015-03-11 09:50:11+00
changeoperation | ADD
policyfile      | /var/cfengine/inputs/sketches/meta/api-runfile.cf
releaseid       | 05c0cc909d6709d816521d6cedbc4508894cc497
promisehash     | 48bc...
namespace       | default
bundlename      | cfsketch_run
promisetype     | methods
promiser        | cfsketch_g
stackpath       | /default/cfsketch_run/methods/'cfsketch_g'[0]
promisehandle   |
promiseoutcome  | KEPT
logmessages     | {}
promisees       | {}
-[ RECORD 2 ]---+--------------------------------------------------
hostkey         | SHA=3b94d...
changetimestamp | 2015-03-17 08:55:38+00
changeoperation | ADD
policyfile      | /var/cfengine/inputs/inventory/any.cf
releaseid       | 05c0cc909d6709d816521d6cedbc4508894cc497
promisehash     | 6eef8...
namespace       | default
bundlename      | inventory_autorun
promisetype     | methods
promiser        | disk
stackpath       | /default/inventory_autorun/methods/'disk'[0]
promisehandle   | cfe_internal_autorun_disk
promiseoutcome  | KEPT
logmessages     | {}
promisees       | {}
-[ RECORD 3 ]---+--------------------------------------------------
hostkey         | SHA=3b94d...
changetimestamp | 2015-03-10 13:43:28+00
changeoperation | CHANGE
policyfile      | /var/cfengine/inputs/inventory/any.cf
releaseid       | 05c0cc909d6709d816521d6cedbc4508894cc497
promisehash     | fd6d5...
namespace       | default
bundlename      | inventory_autorun
promisetype     | methods
promiser        | mtab
stackpath       | /default/inventory_autorun/methods/'mtab'[0]
promisehandle   | cfe_internal_autorun_inventory_mtab
promiseoutcome  | KEPT
logmessages     | {}
promisees       | {}
Table: BenchmarksLog

Data from internal cf-agent monitoring as also measurements promises.

Columns:

  • HostKey (text) Unique host identifier. All tables can be joined by HostKey to connect data concerning same hosts.

  • EventName (text) Name of measured event.

  • StandardDeviation (numeric) Dispersion of a set of data from its mean.

  • AverageValue (numeric) Average value.

  • LastValue (numeric) Last measured value.

  • CheckTimeStamp (timestamp) Measurement time.

Example query:

SELECT hostkey,
       eventname,
       standarddeviation,
       averagevalue,
       lastvalue,
       checktimestamp
FROM   benchmarkslog;

Output:

-[ RECORD 1 ]-----+--------------------------------------------------------
hostkey           | SHA=3b94d...
eventname         | CFEngine Execution ('/var/cfengine/inputs/promises.cf')
standarddeviation | 7.659365
averagevalue      | 3.569665
lastvalue         | 1.170841
checktimestamp    | 2015-03-10 14:08:12+00
-[ RECORD 2 ]---=-+--------------------------------------------------------
hostkey           | SHA=3b94d...
eventname         | CFEngine Execution ('/var/cfengine/inputs/update.cf')
standarddeviation | 0.131094
averagevalue      | 0.422757
lastvalue         | 0.370686
checktimestamp    | 2015-03-10 14:08:11+00
-[ RECORD 3 ]-----+--------------------------------------------------------
hostkey           | SHA=3b94d...
eventname         | DBReportCollectAll
standarddeviation | 0.041025
averagevalue      | 1.001964
lastvalue         | 1.002346
checktimestamp    | 2015-03-10 14:05:20+00
Table: HubConnectionErrors

Networking errors encountered by cf-hub during its operation.

Columns:

  • HostKey (text) Unique identifier of the host that cf-hub was connecting to.

  • CheckTimeStamp (timestamp) Timestamp when the error occurred.

  • Message (text) Error type / message.

  • QueryType (text) Type of query that was intended to be sent by hub during failed connection attempt.

Example query:

SELECT hostkey,
       checktimestamp,
       message,
       querytype,
FROM   hubconnectionErrors;

Output:

-[ RECORD 1 ]--+--------------------------
hostkey        | SHA=3b94d...
checktimestamp | 2015-03-13 13:16:10+00
message        | ServerNoReply
querytype      | delta
-[ RECORD 2 ]--+--------------------------
hostkey        | SHA=3b94d...
checktimestamp | 2015-03-13 14:16:10+00
message        | InvalidData
querytype      | rebase
-[ RECORD 3 ]--+--------------------------
hostkey        | SHA=3b94d...
checktimestamp | 2015-03-13 15:16:10+00
message        | ServerAuthenticationError
querytype      | delta
Table: Diagnostics

Diagnostic data generated by hub during its operation. It contains stats such as sizes of reports, processing time per component etc.

Columns:

  • Name (text) Measurement name.

  • Details (text) Additional details for the sample.

  • TimeStamp (timestamp) Measurement time.

  • Value (numeric) Sample value.

  • Units (text) Units of measurement.

Example query:

SELECT name,
       details,
       timestamp,
       value,
       units,
FROM   diagnostics;

Output:

-[ RECORD 1 ]-----------------------------
name      | redis_processing_time_per_host
details   | rebase
timestamp | 2015-03-10 13:35:20.866627+00
value     | 76.000000
units     | ms
-[ RECORD 2 ]-----------------------------
name      | hub_processing_time_per_host
details   | rebase
timestamp | 2015-03-10 13:35:20.871608+00
value     | 189.000000
units     | ms
-[ RECORD 3 ]-----------------------------
name      | recivied_data_size_per_host
details   | rebase
timestamp | 2015-03-10 13:35:20.878601+00
value     | 40.617188
units     | kb

Standard Library

The standard library contains commonly-used promise bundles and bodies. It provides definitions that you can use to build up solutions within CFEngine. The standard library is an interface layer that brings industry-wide standardization of CFEngine configuration scripting and hides the technical details.

To import elements of the CFEngine Standard Library into your CFEngine policy, enter the following:

body file control
{
    inputs => { "$(sys.libdir)/files.cf", "$(sys.libdir)/packages.cf" };
}

You may wish to use $(sys.libdir) (absolute) or $(sys.local_libdir) (relative) to locate these libraries, depending on your specific policy layout.

The available pieces are:

  • bodies and bundles for a specific promise type
  • bodies that apply to all promise types
  • utility bundles
    • bundles.cf: cron jobs, log rotating, filestat() interface, rm_rf, Git-related bundles
    • paths.cf: standard place to find the path for many common tools and system files
    • feature.cf: set and unset persistent classes easily

To import the entire CFEngine Standard Library, enter the following:

body file control
{
      # relative path
      "inputs" slist => { "$(sys.local_libdir)/stdlib.cf" };
      # absolute path
      "inputs" slist => { "$(sys.libdir)/stdlib.cf" };
}

Note this will not work with CFEngine 3.5 or older. For backward compatibility, you need to follow the approach shown in the standard promises.cf main entry point for policy:

bundle common cfengine_stdlib
{
  vars:
    cfengine_3_4::
      # This is the standard library for CFEngine 3.4 and earlier
      # (only 3.4 is explicitly supported)
      "inputs" slist => { "libraries/cfengine_stdlib.cf" };
    cfengine_3_5::
      # 3.5 doesn't have "body file control" so all the includes are listed here
      "inputs" slist => {
                          "lib/3.5/paths.cf",
                          "lib/3.5/common.cf",
                          "lib/3.5/commands.cf",
                          "lib/3.5/packages.cf",
                          "lib/3.5/files.cf",
                          "lib/3.5/services.cf",
                          "lib/3.5/processes.cf",
                          "lib/3.5/storage.cf",
                          "lib/3.5/databases.cf",
                          "lib/3.5/monitor.cf",
                          "lib/3.5/guest_environments.cf",
                          "lib/3.5/bundles.cf",
      };

    !(cfengine_3_4||cfengine_3_5)::
      # CFEngine 3.6 and higher can include through a secondary file
      "inputs" slist => { "$(sys.local_libdir)/stdlib.cf" };

  reports:
    verbose_mode::
      "$(this.bundle): defining inputs='$(inputs)'";
}

And then include @(cfengine_stdlib.inputs) in your main policy inputs. This is recommended only if you need to support CFEngine clients running 3.5 or older!

Feature

To use these bundles, add the following to your policy:

body file control
{
    inputs => { "features.cf" }
}
agent bundles
feature

Prototype: feature

Description: Finds feature_set_X and feature_unset_X classes and sets/unsets X persistently

Finds all classes named feature_unset_X and clear class X.

Finds all classes named feature_set_DURATION_X and sets class X persistently for DURATION. DURATION can be any digits followed by k, m, or g.

In inform mode (-I) it will report what it does.

Example: Set class xyz for 10 minutes, class qpr for 100 minutes, and ijk for 90m minutes. Unset class abc. cf-agent -I -f ./feature.cf -b feature -Dfeature_set_10_xyz,feature_set_100_qpr,feature_set_90m_ijk,feature_unset_abc

Implementation:

bundle agent feature
{
  classes:
      "parsed_$(on)" expression => regextract("feature_set_([0-9]+[kmgKMG]?)_(.*)",
                                              $(on),
                                              "extract_$(on)");

      "parsed_$(off)" expression => regextract("feature_unset_(.*)",
                                               $(off),
                                               "extract_$(off)");

      "$(extract_$(on)[2])" expression => "parsed_$(on)",
      persistence => "$(extract_$(on)[1])";

  vars:
      "on" slist => classesmatching("feature_set_.*");
      "off" slist => classesmatching("feature_unset_.*");

      "_$(off)" string => "off", classes => feature_cancel("$(extract_$(off)[1])");

  reports:
    inform_mode::
      "$(this.bundle): $(on) => SET class '$(extract_$(on)[2]) for '$(extract_$(on)[1])'"
      ifvarclass => "parsed_$(on)";

      "$(this.bundle): $(off) => UNSET class '$(extract_$(off)[1])'"
      ifvarclass => "parsed_$(off)";

      "$(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
      "$(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";

      "$(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
      "$(this.bundle): have no $(extract_$(off)[1])" ifvarclass => "!$(extract_$(off)[1])";
}
feature_test

Prototype: feature_test

Description: Finds feature_set_X and feature_unset_X classes and reports X

Note that this bundle is intended to be used exactly like feature and just show what's defined or undefined.

Example: Check classes xyz, qpr, ijk, and abc. cf-agent -I -f ./feature.cf -b feature_test -Dfeature_set_10_xyz,feature_set_100_qpr,feature_set_90m_ijk,feature_unset_abc

Implementation:

bundle agent feature_test
{
  classes:
      "parsed_$(on)" expression => regextract("feature_set_([0-9]+[kmgKMG]?)_(.*)",
                                              $(on),
                                              "extract_$(on)");

      "parsed_$(off)" expression => regextract("feature_unset_(.*)",
                                               $(off),
                                               "extract_$(off)");

  vars:
      "on" slist => classesmatching("feature_set_.*");
      "off" slist => classesmatching("feature_unset_.*");

  reports:
      "$(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
      "$(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";

      "$(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
      "$(this.bundle): have no $(extract_$(off)[1])" ifvarclass => "!$(extract_$(off)[1])";
}
Paths

To use these bundles, add the following to your policy:

body file control
{
    inputs => { "paths.cf" }
}
common bodies
paths

Prototype: paths

Description: Defines an array path with common paths to standard binaries, and classes for defined and existing paths.

If the current platform knows that binary XYZ should be present, _stdlib_has_path_XYZ is defined. Furthermore, if XYZ is actually present (i.e. the binary exists) in the expected location, _stdlib_path_exists_XYZ is defined.

Example:

bundle agent repair_newlines(filename)
{
commands:
  _stdlib_path_exists_sed::
     “$(path[sed])
       args => “-i 's/^M//' $(filename)
}

Implementation:

bundle common paths
{
  vars:

      #
      # Common full pathname of commands for OS
      #

    enterprise.(am_policy_hub|policy_server)::
      "path[git]"
        string => "$(sys.workdir)/bin/git",
        comment => "CFEngine Enterprise Hub ships with its own git which is used internally";

    !(enterprise.(am_policy_hub|policy_server))::
      "path[git]"      string => "/usr/bin/git";

    !(freebsd|darwin|smartos)::
      "path[npm]"      string => "/usr/bin/npm";
      "path[pip]"      string => "/usr/bin/pip";

    !(freebsd|darwin)::
      "path[getfacl]"  string => "/usr/bin/getfacl";

    freebsd|darwin::
      "path[npm]"      string => "/usr/local/bin/npm";
      "path[pip]"      string => "/usr/local/bin/pip";

    _have_bin_env::
      "path[env]"      string => "/bin/env";
    !_have_bin_env::
      "path[env]"      string => "/usr/bin/env";

    _have_bin_systemctl::
      "path[systemctl]"      string => "/bin/systemctl";
    !_have_bin_systemctl::
      "path[systemctl]"      string => "/bin/systemctl";

    linux::
      "path[lsattr]"        string => "/usr/bin/lsattr";
      "path[tar]"           string => "/bin/tar";

    aix::

      "path[awk]"      string => "/usr/bin/awk";
      "path[bc]"       string => "/usr/bin/bc";
      "path[cat]"      string => "/bin/cat";
      "path[cksum]"    string => "/usr/bin/cksum";
      "path[crontabs]" string => "/var/spool/cron/crontabs";
      "path[cut]"      string => "/usr/bin/cut";
      "path[dc]"       string => "/usr/bin/dc";
      "path[df]"       string => "/usr/bin/df";
      "path[diff]"     string => "/usr/bin/diff";
      "path[dig]"      string => "/usr/bin/dig";
      "path[echo]"     string => "/usr/bin/echo";
      "path[egrep]"    string => "/usr/bin/egrep";
      "path[find]"     string => "/usr/bin/find";
      "path[grep]"     string => "/usr/bin/grep";
      "path[ls]"       string => "/usr/bin/ls";
      "path[netstat]"  string => "/usr/bin/netstat";
      "path[ping]"     string => "/usr/bin/ping";
      "path[perl]"     string => "/usr/bin/perl";
      "path[printf]"   string => "/usr/bin/printf";
      "path[sed]"      string => "/usr/bin/sed";
      "path[sort]"     string => "/usr/bin/sort";
      "path[tr]"       string => "/usr/bin/tr";

    archlinux::

      "path[awk]"               string => "/usr/bin/awk";
      "path[bc]"                string => "/usr/bin/bc";
      "path[cat]"               string => "/usr/bin/cat";
      "path[cksum]"             string => "/usr/bin/cksum";
      "path[crontab]"           string => "/usr/bin/crontab";
      "path[cut]"               string => "/usr/bin/cut";
      "path[dc]"                string => "/usr/bin/dc";
      "path[df]"                string => "/usr/bin/df";
      "path[diff]"              string => "/usr/bin/diff";
      "path[dig]"               string => "/usr/bin/dig";
      "path[dmidecode]"         string => "/usr/bin/dmidecode";
      "path[echo]"              string => "/usr/bin/echo";
      "path[egrep]"             string => "/usr/bin/egrep";
      "path[ethtool]"           string => "/usr/bin/ethtool";
      "path[find]"              string => "/usr/bin/find";
      "path[free]"              string => "/usr/bin/free";
      "path[grep]"              string => "/usr/bin/grep";
      "path[hostname]"          string => "/usr/bin/hostname";
      "path[init]"              string => "/usr/bin/init";
      "path[iptables]"          string => "/usr/bin/iptables";
      "path[iptables_save]"     string => "/usr/bin/iptables-save";
      "path[iptables_restore]"  string => "/usr/bin/iptables-restore";
      "path[ls]"                string => "/usr/bin/ls";
      "path[lsof]"              string => "/usr/bin/lsof";
      "path[netstat]"           string => "/usr/bin/netstat";
      "path[ping]"              string => "/usr/bin/ping";
      "path[perl]"              string => "/usr/bin/perl";
      "path[printf]"            string => "/usr/bin/printf";
      "path[sed]"               string => "/usr/bin/sed";
      "path[sort]"              string => "/usr/bin/sort";
      "path[test]"              string => "/usr/bin/test";
      "path[top]"               string => "/usr/bin/top";
      "path[tr]"                string => "/usr/bin/tr";
      #
      "path[pacman]"            string => "/usr/bin/pacman";
      "path[yaourt]"            string => "/usr/bin/yaourt";
      "path[useradd]"           string => "/usr/bin/useradd";
      "path[groupadd]"          string => "/usr/bin/groupadd";
      "path[ip]"                string => "/usr/bin/ip";
      "path[ifconfig]"          string => "/usr/bin/ifconfig";
      "path[journalctl]"        string => "/usr/bin/journalctl";
      "path[netctl]"            string => "/usr/bin/netctl";

    freebsd|netbsd|openbsd::

      "path[awk]"      string => "/usr/bin/awk";
      "path[bc]"       string => "/usr/bin/bc";
      "path[cat]"      string => "/bin/cat";
      "path[crontabs]" string => "/var/cron/tabs";
      "path[cut]"      string => "/usr/bin/cut";
      "path[dc]"       string => "/usr/bin/dc";
      "path[df]"       string => "/bin/df";
      "path[diff]"     string => "/usr/bin/diff";
      "path[dig]"      string => "/usr/bin/dig";
      "path[echo]"     string => "/bin/echo";
      "path[egrep]"    string => "/usr/bin/egrep";
      "path[find]"     string => "/usr/bin/find";
      "path[grep]"     string => "/usr/bin/grep";
      "path[ls]"       string => "/bin/ls";
      "path[netstat]"  string => "/usr/bin/netstat";
      "path[perl]"     string => "/usr/bin/perl";
      "path[printf]"   string => "/usr/bin/printf";
      "path[sed]"      string => "/usr/bin/sed";
      "path[sort]"     string => "/usr/bin/sort";
      "path[tr]"       string => "/usr/bin/tr";

    freebsd.!(freebsd_9_3|freebsd_10|freebsd_11)|netbsd|openbsd::

      "path[ping]"     string => "/usr/bin/ping";

    freebsd_9_3|freebsd_10|freebsd_11::

      "path[ping]"     string => "/sbin/ping";

    freebsd|netbsd::

      "path[cksum]"    string => "/usr/bin/cksum";
      "path[realpath]" string => "/bin/realpath";

    freebsd::

      "path[getfacl]"  string => "/bin/getfacl";
      "path[dtrace]"   string => "/usr/sbin/dtrace";
      "path[zpool]"    string => "/sbin/zpool";
      "path[zfs]"      string => "/sbin/zfs";

    openbsd::

      "path[cksum]"    string => "/bin/cksum";

    smartos::
      "path[npm]"      string => "/opt/local/bin/npm";
      "path[pip]"      string => "/opt/local/bin/pip";

    solaris::

      "path[awk]"      string => "/usr/bin/awk";
      "path[bc]"       string => "/usr/bin/bc";
      "path[cat]"      string => "/usr/bin/cat";
      "path[cksum]"    string => "/usr/bin/cksum";
      "path[crontab]"  string => "/usr/bin/crontab";
      "path[crontabs]" string => "/var/spool/cron/crontabs";
      "path[curl]"     string => "/usr/bin/curl";
      "path[cut]"      string => "/usr/bin/cut";
      "path[dc]"       string => "/usr/bin/dc";
      "path[df]"       string => "/usr/bin/df";
      "path[diff]"     string => "/usr/bin/diff";
      "path[dig]"      string => "/usr/sbin/dig";
      "path[echo]"     string => "/usr/bin/echo";
      "path[egrep]"    string => "/usr/bin/egrep";
      "path[find]"     string => "/usr/bin/find";
      "path[grep]"     string => "/usr/bin/grep";
      "path[ls]"       string => "/usr/bin/ls";
      "path[netstat]"  string => "/usr/bin/netstat";
      "path[ping]"     string => "/usr/bin/ping";
      "path[perl]"     string => "/usr/bin/perl";
      "path[printf]"   string => "/usr/bin/printf";
      "path[sed]"      string => "/usr/bin/sed";
      "path[sort]"     string => "/usr/bin/sort";
      "path[tr]"       string => "/usr/bin/tr";
      "path[wget]"     string => "/usr/bin/wget";
      #
      "path[svcs]"     string => "/usr/bin/svcs";
      "path[svcadm]"   string => "/usr/sbin/svcadm";
      "path[svccfg]"   string => "/usr/sbin/svccfg";
      "path[svcprop]"  string => "/usr/bin/svcprop";
      "path[netadm]"   string => "/usr/sbin/netadm";
      "path[dladm]"    string => "/usr/sbin/dladm";
      "path[ipadm]"    string => "/usr/sbin/ipadm";
      "path[pkg]"      string => "/usr/bin/pkg";
      "path[pkginfo]"  string => "/usr/bin/pkginfo";
      "path[pkgadd]"   string => "/usr/sbin/pkgadd";
      "path[pkgrm]"    string => "/usr/sbin/pkgrm";
      "path[zoneadm]"  string => "/usr/sbin/zoneadm";
      "path[zonecfg]"  string => "/usr/sbin/zonecfg";

    redhat::

      "path[awk]"           string => "/bin/awk";
      "path[bc]"            string => "/usr/bin/bc";
      "path[cat]"           string => "/bin/cat";
      "path[cksum]"         string => "/usr/bin/cksum";
      "path[createrepo]"    string => "/usr/bin/createrepo";
      "path[crontab]"       string => "/usr/bin/crontab";
      "path[crontabs]"      string => "/var/spool/cron";
      "path[curl]"          string => "/usr/bin/curl";
      "path[cut]"           string => "/bin/cut";
      "path[dc]"            string => "/usr/bin/dc";
      "path[df]"            string => "/bin/df";
      "path[diff]"          string => "/usr/bin/diff";
      "path[dig]"           string => "/usr/bin/dig";
      "path[domainname]"    string => "/bin/domainname";
      "path[echo]"          string => "/bin/echo";
      "path[egrep]"         string => "/bin/egrep";
      "path[ethtool]"       string => "/usr/sbin/ethtool";
      "path[find]"          string => "/usr/bin/find";
      "path[free]"          string => "/usr/bin/free";
      "path[grep]"          string => "/bin/grep";
      "path[hostname]"      string => "/bin/hostname";
      "path[init]"          string => "/sbin/init";
      "path[iptables]"      string => "/sbin/iptables";
      "path[iptables_save]" string => "/sbin/iptables-save";
      "path[ls]"            string => "/bin/ls";
      "path[lsof]"          string => "/usr/sbin/lsof";
      "path[netstat]"       string => "/bin/netstat";
      "path[nologin]"       string => "/sbin/nologin";
      "path[ping]"          string => "/usr/bin/ping";
      "path[perl]"          string => "/usr/bin/perl";
      "path[printf]"        string => "/usr/bin/printf";
      "path[sed]"           string => "/bin/sed";
      "path[sort]"          string => "/bin/sort";
      "path[test]"          string => "/usr/bin/test";
      "path[tr]"            string => "/usr/bin/tr";
      "path[wc]"            string => "/usr/bin/wc";
      "path[wget]"          string => "/usr/bin/wget";
      "path[realpath]"      string => "/usr/bin/realpath";

      #
      "path[chkconfig]" string => "/sbin/chkconfig";
      "path[groupadd]"  string => "/usr/sbin/groupadd";
      "path[groupdel]"  string => "/usr/sbin/groupdel";
      "path[ifconfig]"  string => "/sbin/ifconfig";
      "path[ip]"        string => "/sbin/ip";
      "path[rpm]"       string => "/bin/rpm";
      "path[service]"   string => "/sbin/service";
      "path[svc]"       string => "/sbin/service";
      "path[useradd]"   string => "/usr/sbin/useradd";
      "path[userdel]"   string => "/usr/sbin/userdel";
      "path[yum]"       string => "/usr/bin/yum";

    darwin::
      "path[awk]"           string => "/usr/bin/awk";
      "path[bc]"            string => "/usr/bin/bc";
      "path[cat]"           string => "/bin/cat";
      "path[cksum]"         string => "/usr/bin/cksum";
      "path[createrepo]"    string => "/usr/bin/createrepo";
      "path[crontab]"       string => "/usr/bin/crontab";
      "path[crontabs]"      string => "/usr/lib/cron/tabs";
      "path[cut]"           string => "/usr/bin/cut";
      "path[dc]"            string => "/usr/bin/dc";
      "path[df]"            string => "/bin/df";
      "path[diff]"          string => "/usr/bin/diff";
      "path[dig]"           string => "/usr/bin/dig";
      "path[domainname]"    string => "/bin/domainname";
      "path[dscl]"          string => "/usr/bin/dscl";
      "path[echo]"          string => "/bin/echo";
      "path[egrep]"         string => "/usr/bin/egrep";
      "path[find]"          string => "/usr/bin/find";
      "path[grep]"          string => "/usr/bin/grep";
      "path[hostname]"      string => "/bin/hostname";
      "path[ls]"            string => "/bin/ls";
      "path[lsof]"          string => "/usr/sbin/lsof";
      "path[netstat]"       string => "/usr/sbin/netstat";
      "path[ping]"          string => "/sbin/ping";
      "path[perl]"          string => "/usr/bin/perl";
      "path[printf]"        string => "/usr/bin/printf";
      "path[sed]"           string => "/usr/bin/sed";
      "path[sort]"          string => "/usr/bin/sort";
      "path[test]"          string => "/bin/test";
      "path[tr]"            string => "/usr/bin/tr";

      #
      "path[brew]"           string => "/usr/local/bin/brew";
      "path[sudo]"           string => "/usr/bin/sudo";

    debian::

      "path[awk]"           string => "/usr/bin/awk";
      "path[bc]"            string => "/usr/bin/bc";
      "path[cat]"           string => "/bin/cat";
      "path[chkconfig]"     string => "/sbin/chkconfig";
      "path[cksum]"         string => "/usr/bin/cksum";
      "path[createrepo]"    string => "/usr/bin/createrepo";
      "path[crontab]"       string => "/usr/bin/crontab";
      "path[crontabs]"      string => "/var/spool/cron/crontabs";
      "path[curl]"          string => "/usr/bin/curl";
      "path[cut]"           string => "/usr/bin/cut";
      "path[dc]"            string => "/usr/bin/dc";
      "path[df]"            string => "/bin/df";
      "path[diff]"          string => "/usr/bin/diff";
      "path[dig]"           string => "/usr/bin/dig";
      "path[dmidecode]"     string => "/usr/sbin/dmidecode";
      "path[domainname]"    string => "/bin/domainname";
      "path[echo]"          string => "/bin/echo";
      "path[egrep]"         string => "/bin/egrep";
      "path[ethtool]"       string => "/sbin/ethtool";
      "path[find]"          string => "/usr/bin/find";
      "path[free]"          string => "/usr/bin/free";
      "path[grep]"          string => "/bin/grep";
      "path[hostname]"      string => "/bin/hostname";
      "path[init]"          string => "/sbin/init";
      "path[iptables]"      string => "/sbin/iptables";
      "path[iptables_save]" string => "/sbin/iptables-save";
      "path[ls]"            string => "/bin/ls";
      "path[lsof]"          string => "/usr/bin/lsof";
      "path[netstat]"       string => "/bin/netstat";
      "path[nologin]"       string => "/usr/sbin/nologin";
      "path[ping]"          string => "/bin/ping";
      "path[perl]"          string => "/usr/bin/perl";
      "path[printf]"        string => "/usr/bin/printf";
      "path[sed]"           string => "/bin/sed";
      "path[sort]"          string => "/usr/bin/sort";
      "path[test]"          string => "/usr/bin/test";
      "path[tr]"            string => "/usr/bin/tr";
      "path[wc]"            string => "/usr/bin/wc";
      "path[wget]"          string => "/usr/bin/wget";
      "path[realpath]"      string => "/usr/bin/realpath";

      #
      "path[apt_cache]"           string => "/usr/bin/apt-cache";
      "path[apt_config]"          string => "/usr/bin/apt-config";
      "path[apt_get]"             string => "/usr/bin/apt-get";
      "path[apt_key]"             string => "/usr/bin/apt-key";
      "path[aptitude]"            string => "/usr/bin/aptitude";
      "path[dpkg]"                string => "/usr/bin/dpkg";
      "path[groupadd]"            string => "/usr/sbin/groupadd";
      "path[ifconfig]"            string => "/sbin/ifconfig";
      "path[ip]"                  string => "/sbin/ip";
      "path[service]"             string => "/usr/sbin/service";
      "path[svc]"                 string => "/usr/sbin/service";
      "path[update_alternatives]" string => "/usr/bin/update-alternatives";
      "path[update_rc_d]"         string => "/usr/sbin/update-rc.d";
      "path[useradd]"             string => "/usr/sbin/useradd";

    archlinux||darwin::

      "path[sysctl]"        string => "/usr/bin/sysctl";

    !(archlinux||darwin)::

      "path[sysctl]"        string => "/sbin/sysctl";

    !suse::
      "path[logger]"        string => "/usr/bin/logger";

    suse::

      "path[awk]"           string => "/usr/bin/awk";
      "path[bc]"            string => "/usr/bin/bc";
      "path[cat]"           string => "/bin/cat";
      "path[cksum]"         string => "/usr/bin/cksum";
      "path[createrepo]"    string => "/usr/bin/createrepo";
      "path[crontab]"       string => "/usr/bin/crontab";
      "path[crontabs]"      string => "/var/spool/cron/tabs";
      "path[curl]"          string => "/usr/bin/curl";
      "path[cut]"           string => "/usr/bin/cut";
      "path[dc]"            string => "/usr/bin/dc";
      "path[df]"            string => "/bin/df";
      "path[diff]"          string => "/usr/bin/diff";
      "path[dig]"           string => "/usr/bin/dig";
      "path[dmidecode]"     string => "/usr/sbin/dmidecode";
      "path[domainname]"    string => "/bin/domainname";
      "path[echo]"          string => "/bin/echo";
      "path[egrep]"         string => "/usr/bin/egrep";
      "path[ethtool]"       string => "/usr/sbin/ethtool";
      "path[find]"          string => "/usr/bin/find";
      "path[free]"          string => "/usr/bin/free";
      "path[grep]"          string => "/usr/bin/grep";
      "path[hostname]"      string => "/bin/hostname";
      "path[init]"          string => "/sbin/init";
      "path[iptables]"      string => "/usr/sbin/iptables";
      "path[iptables_save]" string => "/usr/sbin/iptables-save";
      "path[ls]"            string => "/bin/ls";
      "path[lsof]"          string => "/usr/bin/lsof";
      "path[netstat]"       string => "/bin/netstat";
      "path[nologin]"       string => "/sbin/nologin";
      "path[ping]"          string => "/bin/ping";
      "path[perl]"          string => "/usr/bin/perl";
      "path[printf]"        string => "/usr/bin/printf";
      "path[sed]"           string => "/bin/sed";
      "path[sort]"          string => "/usr/bin/sort";
      "path[test]"          string => "/usr/bin/test";
      "path[tr]"            string => "/usr/bin/tr";
      "path[logger]"        string => "/bin/logger";
      "path[wget]"          string => "/usr/bin/wget";

      #
      "path[chkconfig]"     string => "/sbin/chkconfig";
      "path[groupadd]"      string => "/usr/sbin/groupadd";
      "path[groupdel]"      string => "/usr/sbin/groupdel";
      "path[groupmod]"      string => "/usr/sbin/groupmod";
      "path[ifconfig]"      string => "/sbin/ifconfig";
      "path[ip]"            string => "/sbin/ip";
      "path[rpm]"           string => "/bin/rpm";
      "path[service]"       string => "/sbin/service";
      "path[useradd]"       string => "/usr/sbin/useradd";
      "path[userdel]"       string => "/usr/sbin/userdel";
      "path[usermod]"       string => "/usr/sbin/usermod";
      "path[zypper]"        string => "/usr/bin/zypper";

    linux|solaris::

      "path[shadow]"       string => "/etc/shadow";

    freebsd|openbsd|netbsd|darwin::

      "path[shadow]"       string => "/etc/master.passwd";

    aix::

      "path[shadow]"       string => "/etc/security/passwd";

    any::

      "all_paths"     slist => getindices("path");
      "$(all_paths)" string => "$(path[$(all_paths)])";

  classes:
      "_have_bin_env" expression => fileexists("/bin/env");
      "_have_bin_systemctl" expression => fileexists("/bin/systemctl");

      "_stdlib_has_path_$(all_paths)"
      expression => isvariable("$(all_paths)"),
      comment    => "It's useful to know if a given path is defined";

      "_stdlib_path_exists_$(all_paths)"
      expression => fileexists("$(path[$(all_paths)])"),
      comment    => "It's useful to know if $(all_paths) exists on the filesystem as defined";
}

Common Bodies and Bundles

See the common promise attributes documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "common.cf", "bundles.cf" }
}
action bodies
if_elapsed

Prototype: if_elapsed(x)

Description: Evaluate the promise every x minutes

Arguments:

  • x: The time in minutes between promise evaluations

Implementation:

body action if_elapsed(x)
{
      ifelapsed => "$(x)";
      expireafter => "$(x)";
}
if_elapsed_day

Prototype: if_elapsed_day

Description: Evalute the promise once every 24 hours

Implementation:

body action if_elapsed_day
{
      ifelapsed => "1440";    # 60 x 24
      expireafter => "1400";
}
measure_performance

Prototype: measure_performance(x)

Description: Measure repairs of the promiser every x minutes

Repair-attempts are cancelled after x minutes.

Arguments:

  • x: The time in minutes between promise evaluations.

Implementation:

body action measure_performance(x)
{
      measurement_class => "Detect changes in $(this.promiser)";
      ifelapsed => "$(x)";
      expireafter => "$(x)";
}
measure_promise_time

Prototype: measure_promise_time(identifier)

Description: Performance will be measured and recorded under identifier

Arguments:

  • identifier: Measurement name.

Implementation:

body action measure_promise_time(identifier)
{
      measurement_class => "$(identifier)";
}
warn_only

Prototype: warn_only

Description: Warn once an hour if the promise needs to be repaired

The promise does not get repaired.

Implementation:

body action warn_only
{
      action_policy => "warn";
      ifelapsed => "60";
}
bg

Prototype: bg(elapsed, expire)

Description: Evaluate the promise in the background every elapsed minutes, for at most expire minutes

Arguments:

  • elapsed: The time in minutes between promise evaluations
  • expire: The time in minutes after which a repair-attempt gets cancelled

Implementation:

body action bg(elapsed,expire)
{
      ifelapsed   => "$(elapsed)";
      expireafter => "$(expire)";
      background  => "true";
}
ifwin_bg

Prototype: ifwin_bg

Description: Evaluate the promise in the background when running on Windows

Implementation:

body action ifwin_bg
{
    windows::
      background => "true";
}
immediate

Prototype: immediate

Description: Evaluate the promise at every cf-agent execution.

Implementation:

body action immediate
{
      ifelapsed => "0";
}
policy

Prototype: policy(p)

Description: Set the action_policy to p

Arguments:

  • p: The action policy

Implementation:

body action policy(p)
{
      action_policy => "$(p)";
}
log_repaired

Prototype: log_repaired(log, message)

Description: Log message to a file log=[/file|stdout]

Arguments:

  • log: The log file for repaired messages
  • message: The log message

Implementation:

body action log_repaired(log,message)
{
      log_string => "$(sys.date), $(message)";
      log_repaired => "$(log)";
}
log_verbose

Prototype: log_verbose

Description: Sets the log_level attribute to "verbose"

Implementation:

body action log_verbose
{
      log_level => "verbose";
}
sample_rate

Prototype: sample_rate(x)

Description: Evaluate the promise every x minutes, A repair-attempt is cancelled after 10 minutes

Arguments:

  • x: The time in minutes between promise evaluation

Implementation:

body action sample_rate(x)
{
      ifelapsed => "$(x)";
      expireafter => "10";
}
classes bodies
if_repaired

Prototype: if_repaired(x)

Description: Define class x if the promise has been repaired

Arguments:

  • x: The name of the class

Implementation:

body classes if_repaired(x)
{
      promise_repaired => { "$(x)" };
}
if_else

Prototype: if_else(yes, no)

Description: Define the classes yes or no depending on promise outcome

Arguments:

  • yes: The name of the class that should be defined if the promise is kept or repaired
  • no: The name of the class that should be defined if the promise could not be repaired

Implementation:

body classes if_else(yes,no)
{
      promise_kept     => { "$(yes)" };
      promise_repaired => { "$(yes)" };
      repair_failed    => { "$(no)" };
      repair_denied    => { "$(no)" };
      repair_timeout   => { "$(no)" };
}
cf2_if_else

Prototype: cf2_if_else(yes, no)

Description: Define the classes yes or no, depending on promise outcome

A version of if_else that matches CFEngine2 semantics. Neither class is set if the promise does not require any repair.

Arguments:

  • yes: The name of the class that should be defined if the promise is repaired
  • no: The name of the class that should be defind if teh promise could not be repaired

Implementation:

body classes cf2_if_else(yes,no)
{
      promise_repaired => { "$(yes)" };
      repair_failed    => { "$(no)" };
      repair_denied    => { "$(no)" };
      repair_timeout   => { "$(no)" };
}
if_notkept

Prototype: if_notkept(x)

Description: Define the class x if the promise is not kept and cannot be repaired.

Arguments:

  • x: The name of the class that should be defined

Implementation:

body classes if_notkept(x)
{
      repair_failed   => { "$(x)" };
      repair_denied   => { "$(x)" };
      repair_timeout  => { "$(x)" };
}
if_ok

Prototype: if_ok(x)

Description: Define the class x if the promise is kept or could be repaired

Arguments:

  • x: The name of the class that should be defined

Implementation:

body classes if_ok(x)
{
      promise_repaired => { "$(x)" };
      promise_kept => { "$(x)" };
}
if_ok_cancel

Prototype: if_ok_cancel(x)

Description: Cancel the class x if the promise ks kept or repaired

Arguments:

  • x: The name of the class that should be cancelled

Implementation:

body classes if_ok_cancel(x)
{
      cancel_repaired => { "$(x)" };
      cancel_kept => { "$(x)" };
}
cmd_repair

Prototype: cmd_repair(code, cl)

Description: Define the class cl if an external command in a commands, file or packages promise is executed with return code code

Arguments:

  • code: The return codes that indicate a successful repair
  • cl: The name of the class that should be defined

See also: repaired_returncodes

Implementation:

body classes cmd_repair(code,cl)
{
      repaired_returncodes => { "$(code)" };
      promise_repaired => { "$(cl)" };
}
classes_generic

Prototype: classes_generic(x)

Description: Define x prefixed/suffixed with promise outcome

Arguments:

  • x: The unique part of the classes to be defined

Implementation:

body classes classes_generic(x)
{
      promise_repaired => { "promise_repaired_$(x)", "$(x)_repaired", "$(x)_ok", "$(x)_reached" };
      repair_failed => { "repair_failed_$(x)", "$(x)_failed", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
      repair_denied => { "repair_denied_$(x)", "$(x)_denied", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
      repair_timeout => { "repair_timeout_$(x)", "$(x)_timeout", "$(x)_not_ok", "$(x)_error","$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
      promise_kept => { "promise_kept_$(x)", "$(x)_kept", "$(x)_ok", "$(x)_not_repaired", "$(x)_reached" };
}
scoped_classes_generic

Prototype: scoped_classes_generic(scope, x)

Description: Define x prefixed/suffixed with promise outcome

See also: scope

Arguments:

  • scope: The scope in which the class should be defined
  • x: The unique part of the classes to be defined

Implementation:

body classes scoped_classes_generic(scope, x)
{
      scope => "$(scope)";
      promise_repaired => { "promise_repaired_$(x)", "$(x)_repaired", "$(x)_ok", "$(x)_reached" };
      repair_failed => { "repair_failed_$(x)", "$(x)_failed", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
      repair_denied => { "repair_denied_$(x)", "$(x)_denied", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
      repair_timeout => { "repair_timeout_$(x)", "$(x)_timeout", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
      promise_kept => { "promise_kept_$(x)", "$(x)_kept", "$(x)_ok", "$(x)_not_repaired", "$(x)_reached" };
}
state_repaired

Prototype: state_repaired(x)

Description: Define x for 10 minutes if the promise was repaired

Arguments:

  • x: The name of the class that should be defined

Implementation:

body classes state_repaired(x)
{
      promise_repaired => { "$(x)" };
      persist_time => "10";
}
enumerate

Prototype: enumerate(x)

Description: Define x for 15 minutes if the promise is either kept or repaired This is used by commercial editions to count instances of jobs in a cluster

Arguments:

  • x: The unqiue part of the class that should be defind The class defined is prefixed with mXC_

Implementation:

body classes enumerate(x)
{
      promise_repaired => { "mXC_$(x)" };
      promise_kept => { "mXC_$(x)" };
      persist_time => "15";
}
always

Prototype: always(x)

Description: Define class x no matter what the outcome of the promise is

Arguments:

  • x: The name of the class to be defined

Implementation:

body classes always(x)
{
      promise_repaired => { "$(x)" };
      promise_kept => { "$(x)" };
      repair_failed => { "$(x)" };
      repair_denied => { "$(x)" };
      repair_timeout => { "$(x)" };
}
kept_successful_command

Prototype: kept_successful_command

Description: Set command to "kept" instead of "repaired" if it returns 0

Implementation:

body classes kept_successful_command
{
      kept_returncodes => { "0" };
}
Re-usable agent bundles

These agent bundles can be used via usebundle in methods promises.

methods:
    usebundle => library_bundle(parameters)

To use these bundles, add the following to your policy:

body file control
{
    inputs => { "bundles.cf" }
}
agent bundles
cronjob

Prototype: cronjob(commands, user, hours, mins)

Description: Defines a cron job for user

Adds a line to crontab, if necessary.

Arguments:

  • commands: The commands that should be run
  • user: The owner of crontab
  • hours: The hours at which the job should run
  • mins: The minutes at which the job should run

Example:

methods:
 "cron" usebundle => cronjob("/bin/ls","mark","*","5,10");

Implementation:

bundle agent cronjob(commands,user,hours,mins)
{
  vars:
    suse::
      "crontab" string => "/var/spool/cron/tabs";
    redhat|fedora::
      "crontab" string => "/var/spool/cron";
    freebsd::
      "crontab" string => "/var/cron/tabs";
    !(suse|redhat|fedora|freebsd)::
      "crontab" string => "/var/spool/cron/crontabs";

  files:

    !windows::
      "$(crontab)/$(user)"

      comment => "A user's regular batch jobs are added to this file",
      create => "true",
      edit_line => append_if_no_line("$(mins) $(hours) * * * $(commands)"),
      perms => mo("600","$(user)"),
      classes => if_repaired("changed_crontab");

  processes:

    changed_crontab::
      "cron"
      comment => "Most crons need to be huped after file changes",
      signals => { "hup" };

}
rm_rf

Prototype: rm_rf(name)

Description: recursively remove name to any depth, including base

Arguments:

  • name: the file or directory name

This bundle will remove name to any depth, including name itself.

Example:

methods:
    "bye" usebundle => rm_rf("/var/tmp/oldstuff");

Implementation:

bundle agent rm_rf(name)
{
  methods:
      "rm" usebundle => rm_rf_depth($(name),"inf");

}
rm_rf_depth

Prototype: rm_rf_depth(name, depth)

Description: recursively remove name to depth depth, including base

Arguments:

  • name: the file or directory name
  • depth: how far to descend

This bundle will remove name to depth depth, including name itself.

Example:

methods:
    "bye" usebundle => rm_rf_depth("/var/tmp/oldstuff", "100");

Implementation:

bundle agent rm_rf_depth(name,depth)
{
  classes:
      "isdir" expression => isdir($(name));
  files:
    isdir::
      "$(name)"
      file_select => all,
      depth_search => recurse_with_base($(depth)),
      delete => tidy;

      "$(name)/."
        delete => tidy;

    !isdir::
      "$(name)" delete => tidy;
}
fileinfo

Prototype: fileinfo(f)

Description: provide access to file stat fields from the bundle caller and report file stat info for file "f" if "verbose_mode" class is defined

Arguments:

  • f: file or files to stat

Example:

bundle agent example
{
  vars:
    "files" slist => { "/tmp/example1", "/tmp/example2" };

  files:
    "$(files)"
      create => "true",
      classes => if_ok("verbose_mode"),
      comment => "verbose_mode is defined because the fileinfo bundle restricts the report of the file info to verbose mode";

    "/tmp/example3"
      create => "true",
      classes => if_ok("verbose_mode"),
      comment => "verbose_mode is defined because the fileinfo bundle restricts the report of the file info to verbose mode";


  methods:
    "fileinfo" usebundle => fileinfo( @(files) );
    "fileinfo" usebundle => fileinfo( "/tmp/example3" );

  reports:
    "$(this.bundle): $(files): $(fileinfo.fields) = '$(fileinfo.stat[$(files)][$(fileinfo.fields)])'";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][size])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][gid])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][uid])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][ino])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][nlink])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][ctime])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][atime])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][mtime])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][mode])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][modeoct])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][permstr])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][permoct])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][type])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][devno])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][dev_minor])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][dev_major])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][basename])";
    "$(this.bundle): $(fileinfo.stat[/tmp/example3][dirname])";
}

Implementation:

bundle agent fileinfo(f)
{
  vars:
      "fields" slist => splitstring("size,gid,uid,ino,nlink,ctime,atime,mtime,mode,modeoct,permstr,permoct,type,devno,dev_minor,dev_major,basename,dirname,linktarget,linktarget_shallow", ",", 999);

      "stat[$(f)][$(fields)]" string => filestat($(f), $(fields));

  reports:
    verbose_mode::
      "$(this.bundle): file $(f) has $(fields) = $(stat[$(f)][$(fields)])";
}
logrotate

Prototype: logrotate(log_files, max_size, rotate_levels)

Description: rotate specified "log_files" larger than "max_size". Keep "rotate_levels" versions of the files before overwriting the oldest one

Arguments:

  • log_files: single file or list of files to evaluate for rotation
  • max_size: minimum size in bytes that the file will grow to before being rotated
  • rotate_levels: number of rotations to keep before overwriting the oldest one

Example:

bundle agent example
{
  vars:
    "logdirs" slist => { "/var/log/syslog", "/var/log/maillog"};

  methods:
    "logrotate" usebundle => logrotate( @(logdirs), "1M", "2" );
    "logrotate" usebundle => logrotate( "/var/log/mylog, "1", "5" );
    "logrotate" usebundle => logrotate( "/var/log/alog, "500k", "7" );
}

Implementation:

bundle agent logrotate(log_files, max_size, rotate_levels)
{
  files:
      "$(log_files)"
      comment     => "Rotate file if above specified size",
      rename      => rotate("$(rotate_levels)"),
      file_select => bigger_than("$(max_size)");
}
prunedir

Prototype: prunedir(dir, max_days)

Description: delete plain files inside "dir" older than "max_days" (not recursively).

Arguments:

  • dir: directory to examine for files
  • max_days: maximum number of days old a files mtime is allowed to before deletion

Example:

bundle agent example
{
  vars:
    "dirs" slist => { "/tmp/logs", "/tmp/logs2" };

  methods:
    "prunedir" usebundle => prunedir( @(dirs), "1" );
}

Implementation:

bundle agent prunedir(dir, max_days)
{
  files:
      "$(dir)"
      comment       => "Delete plain files inside directory older than max_days",
      delete        => tidy,
      file_select   => filetype_older_than("plain", "$(max_days)"),
      depth_search  => recurse("1");
}
url_ping

Prototype: url_ping(host, method, port, uri)

Description: ping HOST:PORT/URI using METHOD

Arguments:

  • host: the host name
  • method: the HTTP method (HEAD or GET)
  • port: the port number, e.g. 80
  • uri: the URI, e.g. /path/to/resource

This bundle will send a simple HTTP request and read 20 bytes back, then compare them to 200 OK.* (ignoring leading spaces).

If the data matches, the global class "url_ok_HOST" will be set, where HOST is the canonified host name, i.e. canonify($(host))

Example:

methods:
    "check" usebundle => url_ping("cfengine.com", "HEAD", "80", "/bill/was/here");
reports:
  url_ok_cfengine_com::
    "CFEngine's web site is up";
  url_not_ok_cfengine_com::
    "CFEngine's web site *may* be down.  Or you're offline.";

Implementation:

bundle agent url_ping(host, method, port, uri)
{
  vars:
      "url_check" string => readtcp($(host),
                                    $(port),
                                    "$(method) $(uri) HTTP/1.1$(const.r)$(const.n)Host:$(host)$(const.r)$(const.n)$(const.r)$(const.n)",
                                    20);

      "chost" string => canonify($(host));

  classes:
      "url_ok_$(chost)"
      scope => "namespace",
      expression => regcmp("[^\n]*200 OK.*\n.*",
                           $(url_check));

      "url_not_ok_$(chost)"
      scope => "namespace",
      not => regcmp("[^\n]*200 OK.*\n.*",
                           $(url_check));

  reports:
    verbose_mode::
      "$(this.bundle): $(method) $(host):$(port)/$(uri) got 200 OK"
      ifvarclass => "url_ok_$(chost)";
      "$(this.bundle): $(method) $(host):$(port)/$(uri) did *not* get 200 OK"
      ifvarclass => "url_not_ok_$(chost)";
}
cmerge

Prototype: cmerge(name, varlist)

Description: bundle to merge many data containers into one

Arguments:

  • name: the variable name to create
  • varlist: a list of variable names (**MUST** be a list)

The result will be in cmerge.$(name). You can also use cmerge.$(name)_str for a string version of the merged containers.

The name is variable so you can call this bundle for more than one merge.

If you merge a key-value map into an array or vice versa, the map always wins. So this example will result in a key-value map even though cmerge.$(name) starts as an array.

Example:

bundle agent run
{
  vars:
      # the "mymerge" tag is user-defined
      "a"       data  => parsejson('{ "mark": "b" }'), meta => { "mymerge" };
      "b"       data  => parsejson('{ "volker": "h" }'), meta => { "mymerge" };

      # you can list them explicitly: "default:run.a" through "default:run.d"
      "todo"    slist => variablesmatching(".*", "mymerge");

      # you can use cmerge.all_str instead of accessing the merged data directly
      "merged_str" string => format("%S", "cmerge.all");

  methods:
      "go" usebundle => cmerge("all", @(todo)); # merge a, b into container cmerge.all

  reports:
      "merged = $(cmerge.all_str)"; # will print a map with keys "mark" and "volker"
}

Implementation:

bundle agent cmerge(name, varlist)
{
  vars:
      "$(name)" data => parsejson('[]'),            policy => "free";
      "$(name)" data => mergedata($(name), $(varlist)), policy => "free"; # iterates!
      "$(name)_str" string => format("%S", $(name)),    policy => "free";
}
run_ifdefined

Prototype: run_ifdefined(namespace, mybundle)

Description: bundle to maybe run another bundle dynamically

Arguments:

  • namespace: the namespace, usually $(this.namespace)
  • mybundle: the bundle to maybe run

This bundle simply is a way to run another bundle only if it's defined.

Example:

bundle agent run
{
  methods:
      # does nothing if bundle "runthis" is not defined
      "go" usebundle => run_ifdefined($(this.namespace), runthis);
}

Implementation:

bundle agent run_ifdefined(namespace, mybundle)
{
  vars:
      "bundlesfound" slist => bundlesmatching("^$(namespace):$(mybundle)$");
      "count" int => length(bundlesfound);

  classes:
      "runidentified" expression => strcmp(1, $(count));

  methods:
      "any"
      usebundle  => $(bundlesfound),
      ifvarclass => "runidentified";

  reports:
    verbose_mode::
      "$(this.bundle): found matching bundles $(bundlesfound) for namespace '$(namespace)' and bundle '$(mybundle)'";
}

Commands Bundles and Bodies

See the commands promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "commands.cf" }
}
agent bundles
daemonize

Prototype: daemonize(command)

Description: Run a command as a daemon. I.e., fully detaches from Cfengine.

Arguments:

  • command: The command to run detached Note: There will be no output from the command reported by cf-agent. This bundle has no effect on windows

Example: cf3 methods: "Launch Daemon" usebundle => daemonize("/bin/sleep 30");

Implementation:

bundle agent daemonize(command)
{
  commands:
    !windows::
      "exec 1>&-; exec 2>&-; $(command) &"
        contain => in_shell;

  reports:
    windows.(inform_mode|verbose_mode)::
      "$(this.bundle): This bundle does not support Windows";
}
contain bodies
silent

Prototype: silent

Description: suppress command output

Implementation:

body contain silent
{
      no_output => "true";
}
in_dir

Prototype: in_dir(dir)

Description: run command after switching to directory "dir"

Arguments:

  • dir: directory to change into

Example:

 commands:
   "/bin/pwd"
     contain => in_dir("/tmp");

Implementation:

body contain in_dir(dir)
{
      chdir => "$(dir)";
}
in_dir_shell

Prototype: in_dir_shell(dir)

Description: run command after switching to directory "dir" with full shell

Arguments:

  • dir: directory to change into

Example:

 commands:
   "/bin/pwd | /bin/cat"
     contain => in_dir_shell("/tmp");

Implementation:

body contain in_dir_shell(dir)
{
      chdir => "$(dir)";
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
}
silent_in_dir

Prototype: silent_in_dir(dir)

Description: run command after switching to directory and suppress output

Arguments:

  • dir: directory to change into

Example:

   "/bin/pwd"
     contain => silent_in_dir("/tmp");

Implementation:

body contain silent_in_dir(dir)
{
      chdir => "$(dir)";
      no_output => "true";
}
in_shell

Prototype: in_shell

Description: run command in shell

Example:

 commands:
   "/bin/pwd | /bin/cat"
     contain => in_shell;

Implementation:

body contain in_shell
{
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
}
in_shell_bg

Prototype: in_shell_bg

Description: deprecated This bundle previously had an invalid background attribute that was caught by parser strictness enhancements. Backgrounding is handeled by the body action background attribute.

Implementation:

body contain in_shell_bg
{
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
}
in_shell_and_silent

Prototype: in_shell_and_silent

Description: run command in shell and suppress output

Example:

 commands:
   "/bin/pwd | /bin/cat"
     contain => in_shell_and_silent,
     comment => "Silently run command in shell";

Implementation:

body contain in_shell_and_silent
{
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
      no_output => "true";
}
in_dir_shell_and_silent

Prototype: in_dir_shell_and_silent(dir)

Description: run command in shell after switching to 'dir' and suppress output

Arguments:

  • dir: directory to change into

Example:

 commands:
   "/bin/pwd | /bin/cat"
     contain => in_dir_shell_and_silent("/tmp"),
     comment => "Silently run command in shell";

Implementation:

body contain in_dir_shell_and_silent(dir)
{
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
      no_output => "true";
      chdir => "$(dir)";
}
setuid

Prototype: setuid(owner)

Description: run command as specified user

Arguments:

  • owner: username or uid to run command as

Example:

 commands:
   "/usr/bin/id"
     contain => setuid("apache");
   "/usr/bin/id"
     contain => setuid("503");

Implementation:

body contain setuid(owner)
{
      exec_owner => "$(owner)";
}
setuid_sh

Prototype: setuid_sh(owner)

Description: run command as specified user in shell

Arguments:

  • owner: username or uid to run command as

Example:

 commands:
   "/usr/bin/id | /bin/cat"
     contain => setuid("apache");
   "/usr/bin/id | /bin/cat"
     contain => setuid("503");

Implementation:

body contain setuid_sh(owner)
{
      exec_owner => "$(owner)";
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
}
setuidgid_dir

Prototype: setuidgid_dir(owner, group, dir)

Description: run command as specified owner and group in shell

Arguments:

  • owner: username or uid to run command as
  • group: groupname or gid to run command as
  • dir: directory to run command from

Implementation:

body contain setuidgid_dir(owner,group,dir)
{
      exec_owner => "$(owner)";
      exec_group => "$(group)";
      chdir      => "$(dir)";
}
setuidgid_sh

Prototype: setuidgid_sh(owner, group)

Description: run command as specified owner and group in shell

Arguments:

  • owner: username or uid to run command as
  • group: groupname or gid to run command as

Implementation:

body contain setuidgid_sh(owner,group)
{
      exec_owner => "$(owner)";
      exec_group => "$(group)";
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
}
jail

Prototype: jail(owner, jail_root, dir)

Description: run command as specified user in specified directory of jail

Arguments:

  • owner: username or uid to run command as
  • jail_root: path that will be the root directory for the process
  • dir: directory to change to before running command (must be within 'jail_root')

Implementation:

body contain jail(owner,jail_root,dir)
{
      exec_owner => "$(owner)";
      useshell => "true"; # canonical "useshell" but this is backwards-compatible
      chdir => "$(dir)";
      chroot => "$(jail_root)";
}
setuid_umask

Prototype: setuid_umask(owner, umask)

Description: run command as specified user with umask

Valid Values Umask Octal (files) Symbolic (files) Octal (dirs) Symbolic (dirs)
0 000 666 (rw-rw-rw-) 777 (rwxrwxrwx)
002 002 664 (rw-rw-r--) 775 (rwxrwxr-x)
22, 022 022 644 (rw-r--r--) 755 (rwxr-xr-x)
27, 027 027 640 (rw-r-----) 750 (rwxr-x---)
77, 077 077 600 (rw-------) 700 (rwx------)
72, 072 072 604 (rw----r--) 705 (rwx---r-x)

Arguments:

  • owner: username or uid to run command as
  • umask: controls permissions of created files and directories

Example:

 commands:
   "/usr/bin/git pull"
     contain => setuid_umask("git", "022");

Implementation:

body contain setuid_umask(owner, umask)
{
      exec_owner => "$(owner)";
      umask => "$(umask)";
}
setuid_gid_umask

Prototype: setuid_gid_umask(uid, gid, umask)

Description: run command as specified user with umask

Valid Values Umask Octal (files) Symbolic (files) Octal (dirs) Symbolic (dirs)
0 000 666 (rw-rw-rw-) 777 (rwxrwxrwx)
002 002 664 (rw-rw-r--) 775 (rwxrwxr-x)
22, 022 022 644 (rw-r--r--) 755 (rwxr-xr-x)
27, 027 027 640 (rw-r-----) 750 (rwxr-x---)
77, 077 077 600 (rw-------) 700 (rwx------)
72, 072 072 604 (rw----r--) 705 (rwx---r-x)

Arguments:

  • uid: username or uid to run command as
  • gid: group name or gid to run command as
  • umask: controls permissions of created files and directories

Example:

 commands:
   "/usr/bin/git pull"
     contain => setuid_gid_umask("git", "minions", "022");

Implementation:

body contain setuid_gid_umask(uid, gid, umask)
{
      exec_owner => "$(uid)";
      exec_group => "$(uid)";
      umask => "$(umask)";
}

Databases Bundles and Bodies

See the databases promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "databases.cf" }
}
database_server bodies
local_mysql

Prototype: local_mysql(username, password)

Description: Defines a MySQL server running on localhost

Arguments:

  • username: The username for the server connection
  • password: The password for the server connection

See also: db_server_owner, db_server_password

Implementation:

body database_server local_mysql(username, password)
{
      db_server_owner => "$(username)";
      db_server_password => "$(password)";
      db_server_host => "localhost";
      db_server_type => "mysql";
      db_server_connection_db => "mysql";
}
local_postgresql

Prototype: local_postgresql(username, password)

Description: Defines a PostgreSQL server running on localhost

Arguments:

  • username: The username for the server connection
  • password: The password for the server connection

See also: db_server_owner, db_server_password

Implementation:

body database_server local_postgresql(username, password)
{
      db_server_owner => "$(username)";
      db_server_password => "$(password)";
      db_server_host => "localhost";
      db_server_type => "postgres";
      db_server_connection_db => "postgres";
}

Files Bundles and Bodies

See the files promises and edit_line bundles documentation for a comprehensive reference on the bundles, body types, and attributes used here.

To use these bodies and bundles, add the following to your policy:

body file control
{
    inputs => { "files.cf" }
}
edit_line bundles
insert_before_if_no_line

Prototype: insert_before_if_no_line(before, string)

Description: Insert string before before if string is not found in the file

Arguments:

  • before: The regular expression matching the line which string will be inserted before
  • string: The string to be prepended

Implementation:

bundle edit_line insert_before_if_no_line(before, string)
{
  insert_lines:
      "$(string)"
        location => before($(before)),
        comment => "Prepend a line to the file if it doesn't already exist";
}
insert_lines

Prototype: insert_lines(lines)

Description: Append lines if they don't exist in the file

Arguments:

  • lines: The lines to be appended

See also: insert_lines in edit_line

Implementation:

bundle edit_line insert_lines(lines)
{
  insert_lines:

      "$(lines)"
      comment => "Append lines if they don't exist";
}
insert_file

Prototype: insert_file(templatefile)

Description: Reads the lines from templatefile and inserts those into the file being edited.

Arguments:

  • templatefile: The name of the file from which to import lines.

Implementation:

bundle edit_line insert_file(templatefile)
{
  insert_lines:

      "$(templatefile)"
      comment => "Insert the template file into the file being edited",
      insert_type => "file";
}
comment_lines_matching

Prototype: comment_lines_matching(regex, comment)

Description: Comment lines in the file that matching an anchored regex

Arguments:

  • regex: Anchored regex that the entire line needs to match
  • comment: A string that is prepended to matching lines

Implementation:

bundle edit_line comment_lines_matching(regex,comment)
{
  replace_patterns:

      "^($(regex))$"

      replace_with => comment("$(comment)"),
      comment => "Search and replace string";
}
uncomment_lines_matching

Prototype: uncomment_lines_matching(regex, comment)

Description: Uncomment lines of the file where the regex matches the entire text after the comment string

Arguments:

  • regex: The regex that lines need to match after comment
  • comment: The prefix of the line that is removed

Implementation:

bundle edit_line uncomment_lines_matching(regex,comment)
{
  replace_patterns:

      "^$(comment)\s?($(regex))$"

      replace_with => uncomment,
      comment => "Uncomment lines matching a regular expression";
}
comment_lines_containing

Prototype: comment_lines_containing(regex, comment)

Description: Comment lines of the file matching a regex

Arguments:

  • regex: A regex that a part of the line needs to match
  • comment: A string that is prepended to matching lines

Implementation:

bundle edit_line comment_lines_containing(regex,comment)
{
  replace_patterns:

      "^((?!$(comment)).*$(regex).*)$"

      replace_with => comment("$(comment)"),
      comment => "Comment out lines in a file";
}
uncomment_lines_containing

Prototype: uncomment_lines_containing(regex, comment)

Description: Uncomment lines of the file where the regex matches parts of the text after the comment string

Arguments:

  • regex: The regex that lines need to match after comment
  • comment: The prefix of the line that is removed

Implementation:

bundle edit_line uncomment_lines_containing(regex,comment)
{
  replace_patterns:

      "^$(comment)\s?(.*$(regex).*)$"

      replace_with => uncomment,
      comment => "Uncomment a line containing a fragment";
}
delete_lines_matching

Prototype: delete_lines_matching(regex)

Description: Delete lines matching a regular expression

Arguments:

  • regex: The regular expression that the lines need to match

Implementation:

bundle edit_line delete_lines_matching(regex)
{
  delete_lines:

      "$(regex)"

      comment => "Delete lines matching regular expressions";
}
warn_lines_matching

Prototype: warn_lines_matching(regex)

Description: Warn about lines matching a regular expression

Arguments:

  • regex: The regular expression that the lines need to match

Implementation:

bundle edit_line warn_lines_matching(regex)
{
  delete_lines:

      "$(regex)"

      comment => "Warn about lines in a file",
      action => warn_only;
}
prepend_if_no_line

Prototype: prepend_if_no_line(string)

Description: Prepend string if it doesn't exist in the file

Arguments:

  • string: The string to be prepended

See also: insert_lines in edit_line

Implementation:

bundle edit_line prepend_if_no_line(string)
{
  insert_lines:
      "$(string)"
      location => start,
      comment => "Prepend a line to the file if it doesn't already exist";
}
replace_line_end

Prototype: replace_line_end(start, end)

Description: Give lines starting with start the ending given in end

Whitespaces will be left unmodified. For example, replace_line_end("ftp", "2121/tcp") would replace

"ftp 21/tcp"

with

"ftp 2121/tcp"

Arguments:

  • start: The string lines have to start with
  • end: The string lines should end with

Implementation:

bundle edit_line replace_line_end(start,end)
{
  field_edits:

      "\s*$(start)\s.*"
      comment => "Replace lines with $(this.start) and $(this.end)",
      edit_field => line("(^|\s)$(start)\s*", "2", "$(end)","set");
}
append_to_line_end

Prototype: append_to_line_end(start, end)

Description: Append end to any lines beginning with start

end will be appended to all lines starting with start and not already ending with end. Whitespaces will be left unmodified.

For example, append_to_line_end("kernel", "vga=791") would replace kernel /boot/vmlinuz root=/dev/sda7

with

kernel /boot/vmlinuz root=/dev/sda7 vga=791

WARNING: Be careful not to have multiple promises matching the same line, which would result in the line growing indefinitely.

Arguments:

  • start: pattern to match lines of interest
  • end: string to append to matched lines

Example:

 files:
     "/tmp/boot-options" edit_line => append_to_line_end("kernel", "vga=791");

Implementation:

bundle edit_line append_to_line_end(start,end)
{
  field_edits:

      "\s*$(start)\s.*"
      comment => "Append lines with $(this.start) and $(this.end)",
      edit_field => line("(^|\s)$(start)\s*", "2", "$(end)","append");
}
regex_replace

Prototype: regex_replace(find, replace)

Description: Find exactly a regular expression and replace exactly the match with a string. You can think of this like a PCRE powered sed.

Arguments:

  • find: The regular expression
  • replace: The replacement string

Implementation:

bundle edit_line regex_replace(find,replace)
{
  replace_patterns:

      "$(find)"
      replace_with => value("$(replace)"),
      comment => "Search and replace string";
}
resolvconf

Prototype: resolvconf(search, list)

Description: Adds search domains and name servers to the system resolver configuration.

Use this bundle to modify resolv.conf. Existing entries for search and nameserver are replaced.

Arguments:

  • search: The search domains with space
  • list: An slist of nameserver addresses

Implementation:

bundle edit_line resolvconf(search,list)
{
  delete_lines:

      "search.*"     comment => "Reset search lines from resolver";
      "nameserver.*" comment => "Reset nameservers in resolver";

  insert_lines:

      "search $(search)"    comment => "Add search domains to resolver";
      "nameserver $(list)"  comment => "Add name servers to resolver";
}
resolvconf_o

Prototype: resolvconf_o(search, list, options)

Description: Adds search domains, name servers and options to the system resolver configuration.

Use this bundle to modify resolv.conf. Existing entries for search, nameserver and options are replaced.

Arguments:

  • search: The search domains with space
  • list: An slist of nameserver addresses
  • options: is an slist of variables to modify the resolver

Implementation:

bundle edit_line resolvconf_o(search,list,options)
{
  delete_lines:

      "search.*"     comment => "Reset search lines from resolver";
      "nameserver.*" comment => "Reset nameservers in resolver";
      "options.*"    comment => "Reset options in resolver";

  insert_lines:

      "search $(search)"    comment => "Add search domains to resolver";
      "nameserver $(list)"  comment => "Add name servers to resolver";
      "options $(options)"  comment => "Add options to resolver";
}
manage_variable_values_ini

Prototype: manage_variable_values_ini(tab, sectionName)

Description: Sets the RHS of configuration items in the file of the form LHS=RHS

If the line is commented out with #, it gets uncommented first. Adds a new line if none exists. Removes any variable value pairs not defined for the ini section.

Arguments:

  • tab: An associative array containing tab[sectionName][LHS]="RHS". The value is not changed when the RHS is "dontchange"
  • sectionName: The section in the file within which values should be modified

See also: set_variable_values_ini()

Implementation:

bundle edit_line manage_variable_values_ini(tab, sectionName)
{
  vars:
      "index" slist => getindices("$(tab)[$(sectionName)]");

      # Be careful if the index string contains funny chars
      "cindex[$(index)]" string => canonify("$(index)");

  classes:
      "edit_$(cindex[$(index)])"     not => strcmp("$($(tab)[$(sectionName)][$(index)])","dontchange"),
      comment => "Create conditions to make changes";

  field_edits:

      # If the line is there, but commented out, first uncomment it
      "#+\s*$(index)\s*=.*"
      select_region => INI_section("$(sectionName)"),
      edit_field => col("=","1","$(index)","set"),
      ifvarclass => "edit_$(cindex[$(index)])";

      # match a line starting like the key something
      "$(index)\s*=.*"
      edit_field => col("=","2","$($(tab)[$(sectionName)][$(index)])","set"),
      select_region => INI_section("$(sectionName)"),
      classes => if_ok("manage_variable_values_ini_not_$(cindex[$(index)])"),
      ifvarclass => "edit_$(cindex[$(index)])";

  delete_lines:
      ".*"
      select_region => INI_section("$(sectionName)"),
      comment       => "Remove all entries in the region so there are no extra entries";

  insert_lines:
      "[$(sectionName)]"
      location => start,
      comment => "Insert lines";

      "$(index)=$($(tab)[$(sectionName)][$(index)])"
      select_region => INI_section("$(sectionName)"),
      ifvarclass => "!manage_variable_values_ini_not_$(cindex[$(index)]).edit_$(cindex[$(index)])";

}
set_variable_values_ini

Prototype: set_variable_values_ini(tab, sectionName)

Description: Sets the RHS of configuration items in the file of the form LHS=RHS

If the line is commented out with #, it gets uncommented first. Adds a new line if none exists.

Arguments:

  • tab: An associative array containing tab[sectionName][LHS]="RHS". The value is not changed when the RHS is "dontchange"
  • sectionName: The section in the file within which values should be modified

See also: manage_variable_values_ini()

Implementation:

bundle edit_line set_variable_values_ini(tab, sectionName)
{
  vars:
      "index" slist => getindices("$(tab)[$(sectionName)]");

      # Be careful if the index string contains funny chars
      "cindex[$(index)]" string => canonify("$(index)");

  classes:
      "edit_$(cindex[$(index)])"     not => strcmp("$($(tab)[$(sectionName)][$(index)])","dontchange"),
      comment => "Create conditions to make changes";

  field_edits:

      # If the line is there, but commented out, first uncomment it
      "#+\s*$(index)\s*=.*"
      select_region => INI_section("$(sectionName)"),
      edit_field => col("=","1","$(index)","set"),
      ifvarclass => "edit_$(cindex[$(index)])";

      # match a line starting like the key something
      "$(index)\s*=.*"
      edit_field => col("=","2","$($(tab)[$(sectionName)][$(index)])","set"),
      select_region => INI_section("$(sectionName)"),
      classes => if_ok("set_variable_values_ini_not_$(cindex[$(index)])"),
      ifvarclass => "edit_$(cindex[$(index)])";

  insert_lines:
      "[$(sectionName)]"
      location => start,
      comment => "Insert lines";

      "$(index)=$($(tab)[$(sectionName)][$(index)])"
      select_region => INI_section("$(sectionName)"),
      ifvarclass => "!set_variable_values_ini_not_$(cindex[$(index)]).edit_$(cindex[$(index)])";

}
insert_ini_section

Prototype: insert_ini_section(name, config)

Description: Inserts a INI section with content

# given an array "barray"
files:
    "myfile.ini" edit_line => insert_innit_section("foo", "barray");

Inserts a section in an INI file with the given configuration key-values from the array config.

Arguments:

  • name: the name of the INI section
  • config: The fully-qualified name of an associative array containing v[LHS]="rhs"

Implementation:

bundle edit_line insert_ini_section(name, config)
{
  vars:
      "k" slist => getindices($(config));

  insert_lines:
      "[$(name)]"
      location => start,
      comment => "Prepend a line to the file if it doesn't already exist";

      "$(k)=$($(config)[$(k)])";
}
set_quoted_values

Prototype: set_quoted_values(v)

Description: Sets the RHS of variables in shell-like files of the form:

     LHS="RHS"

Adds a new line if no LHS exists, and replaces RHS values if one does exist. If the line is commented out with #, it gets uncommented first.

Arguments:

  • v: The fully-qualified name of an associative array containing v[LHS]="rhs"

Example:

    vars:
       "stuff[lhs-1]" string => "rhs1";
       "stuff[lhs-2]" string => "rhs2";

    files:
       "myfile"
         edit_line => set_quoted_values(stuff)

See also: set_variable_values()

Implementation:

bundle edit_line set_quoted_values(v)
{
  meta:
      "tags"
      slist =>
      {
        "deprecated=3.6.0",
        "deprecation-reason=Generic reimplementation",
        "replaced-by=set_line_based"
      };

  vars:
      "index" slist => getindices("$(v)");
      # Be careful if the index string contains funny chars

      "cindex[$(index)]" string => canonify("$(index)");

  field_edits:
      # If the line is there, but commented out, first uncomment it
      "#+\s*$(index)\s*=.*"
      edit_field => col("=","1","$(index)","set");

      # match a line starting like the key = something
      "\s*$(index)\s*=.*"
      edit_field => col("=","2",'"$($(v)[$(index)])"',"set"),
      classes    => if_ok("$(cindex[$(index)])_in_file"),
      comment    => "Match a line starting like key = something";

  insert_lines:
      '$(index)="$($(v)[$(index)])"'
      comment    => "Insert a variable definition",
      ifvarclass => "!$(cindex[$(index)])_in_file";
}
set_variable_values

Prototype: set_variable_values(v)

Description: Sets the RHS of variables in files of the form:

     LHS=RHS

Adds a new line if no LHS exists, and replaces RHS values if one does exist. If the line is commented out with #, it gets uncommented first.

Arguments:

  • v: The fully-qualified name of an associative array containing v[LHS]="rhs"

Example:

    vars:
       "stuff[lhs-1]" string => "rhs1";
       "stuff[lhs-2]" string => "rhs2";

    files:
       "myfile"
         edit_line => set_quoted_values(stuff)

See also: set_quoted_values()

Implementation:

bundle edit_line set_variable_values(v)
{
  meta:
      "tags"
      slist =>
      {
        "deprecated=3.6.0",
        "deprecation-reason=Generic reimplementation",
        "replaced-by=set_line_based"
      };

  vars:

      "index" slist => getindices("$(v)");

      # Be careful if the index string contains funny chars

      "cindex[$(index)]" string => canonify("$(index)");
      "cv"               string => canonify("$(v)");

  field_edits:

      # match a line starting like the key = something

      "\s*$(index)\s*=.*"

      edit_field => col("\s*$(index)\s*=","2","$($(v)[$(index)])","set"),
      classes => if_ok("$(cv)_$(cindex[$(index)])_in_file"),
      comment => "Match a line starting like key = something";

  insert_lines:

      "$(index)=$($(v)[$(index)])"

      comment => "Insert a variable definition",
      ifvarclass => "!$(cv)_$(cindex[$(index)])_in_file";
}
set_config_values

Prototype: set_config_values(v)

Description: Sets the RHS of configuration items in the file of the form:

  LHS RHS

If the line is commented out with #, it gets uncommented first.

Adds a new line if none exists.

Arguments:

  • v: The fully-qualified name of an associative array containing v[LHS]="rhs"

Implementation:

bundle edit_line set_config_values(v)
{
  meta:
      "tags"
      slist =>
      {
        "deprecated=3.6.0",
        "deprecation-reason=Generic reimplementation",
        "replaced-by=set_line_based"
      };

  vars:
      "index" slist => getindices("$(v)");

      # Be careful if the index string contains funny chars
      "cindex[$(index)]" string => canonify("$(index)");

      # Escape the value (had a problem with special characters and regex's)
      "ev[$(index)]" string => escape("$($(v)[$(index)])");

      # Do we have more than one line commented out?
      "index_comment_matches_$(cindex[$(index)])" int => countlinesmatching("^\s*#\s*($(index)\s+.*|$(index))$","$(edit.filename)");


  classes:
      # Check to see if this line exists
      "line_exists_$(cindex[$(index)])" expression => regline("^\s*($(index)\s.*|$(index))$","$(edit.filename)");

      # if there's more than one comment, just add new (don't know who to use)
      "multiple_comments_$(cindex[$(index)])" expression => isgreaterthan("$(index_comment_matches_$(cindex[$(index)]))","1");


  replace_patterns:
      # If the line is commented out, uncomment and replace with
      # the correct value
      "^\s*#\s*($(index)\s+.*|$(index))$"
             comment => "Uncommented the value $(index)",
        replace_with => value("$(index) $($(v)[$(index)])"),
          ifvarclass => "!line_exists_$(cindex[$(index)]).!replace_attempted_$(cindex[$(index)]).!multiple_comments_$(cindex[$(index)])",
             classes => always("uncommented_$(cindex[$(index)])");

      # If the line is there with the wrong value, replace with
      # the correct value
      "^\s*($(index)\s+(?!$(ev[$(index)])$).*|$(index))$"
           comment => "Correct the value $(index)",
      replace_with => value("$(index) $($(v)[$(index)])"),
           classes => always("replace_attempted_$(cindex[$(index)])");

  insert_lines:
      # If the line doesn't exist, or there is more than one occurance
      # of the LHS commented out, insert a new line and try to place it
      # after the commented LHS (keep new line with old comments)
      "$(index) $($(v)[$(index)])"
         comment => "Insert the value, marker exists $(index)",
        location => after("^\s*#\s*($(index)\s+.*|$(index))$"),
      ifvarclass => "replace_attempted_$(cindex[$(index)]).multiple_comments_$(cindex[$(index)])";

      # If the line doesn't exist and there are no occurances
      # of the LHS commented out, insert a new line at the eof
      "$(index) $($(v)[$(index)])"
         comment => "Insert the value, marker doesn't exist $(index)",
      ifvarclass => "replace_attempted_$(cindex[$(index)]).!multiple_comments_$(cindex[$(index)])";

}
set_line_based

Prototype: set_line_based(v, sep, bp, kp, cp)

Description: Sets the RHS of configuration items in the file of the form:

  LHS$(sep)RHS

Example usage for x=y lines (e.g. rsyncd.conf):

"myfile"
edit_line => set_line_based("test.config", "=", "\s*=\s*", ".*", "\s*#\s*");

Example usage for x y lines (e.g. sshd_config):

"myfile"
edit_line => set_line_based("test.config", " ", "\s+", ".*", "\s*#\s*");

If the line is commented out with $(cp), it gets uncommented first.

Adds a new line if none exists or if more than one commented-out possible matches exist.

Originally set_config_values by Ed King.

Arguments:

  • v: The fully-qualified name of an associative array containing v[LHS]="rhs"
  • sep: The separator to insert, e.g. for space-separated
  • bp: The key-value separation regex, e.g. \s+ for space-separated
  • kp: The keys to select from v, use .* for all
  • cp: The comment pattern from line-start, e.g. \s*#\s*

Implementation:

bundle edit_line set_line_based(v, sep, bp, kp, cp)
{
  meta:
      "tags"
      slist =>
      {
        "replaces=set_config_values",
        "replaces=set_config_values_matching",
        "replaces=set_variable_values",
        "replaces=set_quoted_values",
        "replaces=maintain_key_values",
      };

  vars:
      "vkeys" slist => getindices("$(v)");
      "i" slist => grep($(kp), vkeys);

      # Be careful if the index string contains funny chars
      "ci[$(i)]" string => canonify("$(i)");

      # Escape the value (had a problem with special characters and regex's)
      "ev[$(i)]" string => escape("$($(v)[$(i)])");

      # Do we have more than one line commented out?
      "comment_matches_$(ci[$(i)])"
      int => countlinesmatching("^$(cp)($(i)$(bp).*|$(i))$",
                                $(edit.filename));


  classes:
      # Check to see if this line exists
      "exists_$(ci[$(i)])"
      expression => regline("^\s*($(i)$(bp).*|$(i))$",
                            $(edit.filename));

      # if there's more than one comment, just add new (don't know who to use)
      "multiple_comments_$(ci[$(i)])"
      expression => isgreaterthan("$(comment_matches_$(ci[$(i)]))",
                                  "1");


  replace_patterns:
      # If the line is commented out, uncomment and replace with
      # the correct value
      "^$(cp)($(i)$(bp).*|$(i))$"
             comment => "Uncommented the value '$(i)'",
        replace_with => value("$(i)$(sep)$($(v)[$(i)])"),
          ifvarclass => "!exists_$(ci[$(i)]).!replace_attempted_$(ci[$(i)]).!multiple_comments_$(ci[$(i)])",
             classes => always("uncommented_$(ci[$(i)])");

      # If the line is there with the wrong value, replace with
      # the correct value
      "^\s*($(i)$(bp)(?!$(ev[$(i)])$).*|$(i))$"
           comment => "Correct the value '$(i)'",
      replace_with => value("$(i)$(sep)$($(v)[$(i)])"),
           classes => always("replace_attempted_$(ci[$(i)])");

  insert_lines:
      # If the line doesn't exist, or there is more than one occurrence
      # of the LHS commented out, insert a new line and try to place it
      # after the commented LHS (keep new line with old comments)
      "$(i)$(sep)$($(v)[$(i)])"
         comment => "Insert the value, marker '$(i)' exists",
        location => after("^$(cp)($(i)$(bp).*|$(i))$"),
      ifvarclass => "replace_attempted_$(ci[$(i)]).multiple_comments_$(ci[$(i)])";

      # If the line doesn't exist and there are no occurrences
      # of the LHS commented out, insert a new line at the eof
      "$(i)$(sep)$($(v)[$(i)])"
         comment => "Insert the value, marker '$(i)' doesn't exist",
      ifvarclass => "replace_attempted_$(ci[$(i)]).!multiple_comments_$(ci[$(i)]).!exists_$(ci[$(i)])";

  reports:
    verbose_mode|EXTRA::
      "$(this.bundle): Line for '$(i)' exists" ifvarclass => "exists_$(ci[$(i)])";
      "$(this.bundle): Line for '$(i)' does not exist" ifvarclass => "!exists_$(ci[$(i)])";

}
set_config_values_matching

Prototype: set_config_values_matching(v, pat)

Description: Sets the RHS of configuration items in the file of the form

  LHS RHS

If the line is commented out with #, it gets uncommented first. Adds a new line if none exists.

Arguments:

  • v: the fully-qualified name of an associative array containing v[LHS]="rhs"
  • pat: Only elements of v that match the regex pat are use

Implementation:

bundle edit_line set_config_values_matching(v,pat)
{
  meta:
      "tags"
      slist =>
      {
        "deprecated=3.6.0",
        "deprecation-reason=Generic reimplementation",
        "replaced-by=set_line_based"
      };

  vars:
      "allparams" slist => getindices("$(v)");
      "index"     slist => grep("$(pat)", "allparams");

      # Be careful if the index string contains funny chars
      "cindex[$(index)]" string => canonify("$(index)");

  replace_patterns:
      # If the line is there, maybe commented out, uncomment and replace with
      # the correct value
      "^\s*($(index)\s+(?!$($(v)[$(index)])).*|# ?$(index)\s+.*)$"
      comment => "Correct the value",
      replace_with => value("$(index) $($(v)[$(index)])"),
      classes => always("replace_attempted_$(cindex[$(index)])");

  insert_lines:
      "$(index) $($(v)[$(index)])"
      ifvarclass => "replace_attempted_$(cindex[$(index)])";

}
append_users_starting

Prototype: append_users_starting(v)

Description: For adding to /etc/passwd or etc/shadow

Arguments:

  • v: An array v[username] string => "line..."

Note: To manage local users with CFEngine 3.6 and later, consider making users promises instead of modifying system files.

Implementation:

bundle edit_line append_users_starting(v)
{
  vars:

      "index"        slist => getindices("$(v)");

  classes:

      "add_$(index)"     not => userexists("$(index)"),
      comment => "Class created if user does not exist";

  insert_lines:

      "$($(v)[$(index)])"

      comment => "Append users into a password file format",
      ifvarclass => "add_$(index)";
}
append_groups_starting

Prototype: append_groups_starting(v)

Description: For adding groups to /etc/group

Arguments:

  • v: An array v[groupname] string => "line..."

Note: To manage local users with CFEngine 3.6 and later, consider making users promises instead of modifying system files.

Implementation:

bundle edit_line append_groups_starting(v)
{
  vars:

      "index"        slist => getindices("$(v)");

  classes:

      "add_$(index)"     not => groupexists("$(index)"),
      comment => "Class created if group does not exist";

  insert_lines:

      "$($(v)[$(index)])"

      comment => "Append users into a group file format",
      ifvarclass => "add_$(index)";

}
set_colon_field

Prototype: set_colon_field(key, field, val)

Description: Set the value of field number field of the line whose first field is key to the value val, in a colon-separated file.

Arguments:

  • key: The value the first field has to match
  • field: The field to be modified
  • val: The new value of field

Implementation:

bundle edit_line set_colon_field(key,field,val)
{
  field_edits:

      "$(key):.*"

      comment => "Edit a colon-separated file, using the first field as a key",
      edit_field => col(":","$(field)","$(val)","set");
}
set_user_field

Prototype: set_user_field(user, field, val)

Description: Set the value of field number "field" in a :-field formatted file like /etc/passwd

Arguments:

  • user: The user to be modified
  • field: The field that should be modified
  • val: The value for field

Note: To manage local users with CFEngine 3.6 and later, consider making users promises instead of modifying system files.

Implementation:

bundle edit_line set_user_field(user,field,val)
{
  field_edits:

      "$(user):.*"

      comment => "Edit a user attribute in the password file",
      edit_field => col(":","$(field)","$(val)","set");
}
append_user_field

Prototype: append_user_field(group, field, allusers)

Description: For adding users to to a file like /etc/group at field position field, comma separated subfields

Arguments:

  • group: The group to be modified
  • field: The field where users should be added
  • allusers: The list of users to add to field

Note: To manage local users with CFEngine 3.6 and later, consider making users promises instead of modifying system files.

Implementation:

bundle edit_line append_user_field(group,field,allusers)
{
  vars:

      "val" slist => { @(allusers) };

  field_edits:

      "$(group):.*"

      comment => "Append users into a password file format",
      edit_field => col(":","$(field)","$(val)","alphanum");
}
expand_template

Prototype: expand_template(templatefile)

Description: Read in the named text file and expand $(var) inside the file

Arguments:

  • templatefile: The name of the file

Implementation:

bundle edit_line expand_template(templatefile)
{
  insert_lines:

      "$(templatefile)"

      insert_type => "file",
      comment => "Expand variables in the template file",
      expand_scalars => "true";
}
replace_or_add

Prototype: replace_or_add(pattern, line)

Description: Replace a pattern in a file with a single line.

If the pattern is not found, add the line to the file.

Arguments:

  • pattern: The pattern that should be replaced The pattern must match the whole line (it is automatically anchored to the start and end of the line) to avoid ambiguity.
  • line: The line with which to replace matches of pattern

Implementation:

bundle edit_line replace_or_add(pattern,line)
{
  vars:
      "cline" string => canonify("$(line)");
      "eline" string => escape("$(line)");

  replace_patterns:
      "^(?!$(eline)$)$(pattern)$"
      comment => "Replace a pattern here",
      replace_with => value("$(line)"),
      classes => scoped_classes_generic("bundle", "replace_$(cline)");

  insert_lines:
      "$(line)"
      ifvarclass => "replace_$(cline)_reached";
}
converge

Prototype: converge(marker, lines)

Description: Converge lines marked with marker

Any content marked with marker is removed, then lines are inserted. Every line should contain marker.

Arguments:

  • marker: The marker (not a regular expression; will be escaped)
  • lines: The lines to insert; all must contain marker

Implementation:

bundle edit_line converge(marker, lines)
{
  vars:
      "regex" string => escape($(marker));

  delete_lines:
      "$(regex)" comment => "Delete lines matching the marker";
  insert_lines:
      "$(lines)" comment => "Insert the given lines";
}
fstab_option_editor

Prototype: fstab_option_editor(method, mount, option)

Description: Add or remove /etc/fstab options for a mount

This bundle edits the options field of a mount. The method is a field_operation which can be append, prepend, set, delete, or alphanum. The option is OS-specific.

Arguments:

  • method: field_operation to apply
  • mount: the mount point
  • option: the option to add or remove

Example:

 files:
     "/etc/fstab" edit_line => fstab_option_editor("delete", "/", "acl");
     "/etc/fstab" edit_line => fstab_option_editor("append", "/", "acl");

Implementation:

bundle edit_line fstab_option_editor(method, mount, option)
{
   field_edits:
      "(?!#)\S+\s+$(mount)\s.+"
      edit_field => fstab_options($(option), $(method));
}
agent bundles
file_mustache

Prototype: file_mustache(mustache_file, json_file, target_file)

Description: Make a file from a Mustache template and a JSON file

Arguments:

  • mustache_file: the file with the Mustache template
  • json_file: a file with JSON data
  • target_file: the target file to write

Example:

methods:
     "m" usebundle => file_mustache("x.mustache", "y.json", "z.txt");

Implementation:

bundle agent file_mustache(mustache_file, json_file, target_file)
{
  files:
      "$(target_file)"
      create => "true",
      edit_template => $(mustache_file),
      template_data => readjson($(json_file), "100k"),
      template_method => "mustache";
}
file_mustache_jsonstring

Prototype: file_mustache_jsonstring(mustache_file, json_string, target_file)

Description: Make a file from a Mustache template and a JSON string

Arguments:

  • mustache_file: the file with the Mustache template
  • json_string: a string with JSON data
  • target_file: the target file to write

Example:

methods:
     "m" usebundle => file_mustache_jsonstring("x.mustache", '{ "x": "y" }', "z.txt");

Implementation:

bundle agent file_mustache_jsonstring(mustache_file, json_string, target_file)
{
  files:
      "$(target_file)"
      create => "true",
      edit_template => $(mustache_file),
      template_data => parsejson($(json_string)),
      template_method => "mustache";
}
file_tidy

Prototype: file_tidy(file)

Description: Remove a file

Arguments:

  • file: to remove

Example:

methods:
     "" usebundle => file_tidy("/tmp/z.txt");

Implementation:

bundle agent file_tidy(file)
{
  files:
      "$(file)" delete => tidy;

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): deleting $(file) with delete => tidy";
}
dir_sync

Prototype: dir_sync(from, to)

Description: Synchronize a directory entire, deleting unknown files

Arguments:

  • from: source directory
  • to: destination directory

Example:

methods:
     "" usebundle => dir_sync("/tmp", "/var/tmp");

Implementation:

bundle agent dir_sync(from, to)
{
  files:
      "$(to)/."
      create => "true",
      depth_search => recurse("inf"),
      copy_from => copyfrom_sync($(from));

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): copying directory $(from) to $(to)";
}
file_copy

Prototype: file_copy(from, to)

Description: Copy a file

Arguments:

  • from: source file
  • to: destination file

Example:

methods:
     "" usebundle => file_copy("/tmp/z.txt", "/var/tmp/y.txt");

Implementation:

bundle agent file_copy(from, to)
{
  files:
      "$(to)"
      copy_from => copyfrom_sync($(from));

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): copying file $(from) to $(to)";
}
file_make

Prototype: file_make(file, str)

Description: Make a file from a string

Arguments:

  • file: target
  • str: the string data

Example:

methods:
     "" usebundle => file_make("/tmp/z.txt", "Some text
and some more text here");

Implementation:

bundle agent file_make(file, str)
{
  files:
      "$(file)"
      create => "true",
      edit_line => insert_lines($(str)),
      edit_defaults => empty;

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): creating $(file) with contents '$(str)'";
}
file_empty

Prototype: file_empty(file)

Description: Make an empty file

Arguments:

  • file: target

Example:

methods:
     "" usebundle => file_empty("/tmp/z.txt");

Implementation:

bundle agent file_empty(file)
{
  files:
      "$(file)"
      create => "true",
      edit_defaults => empty;

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): creating empty $(file) with 0 size";
}

Prototype: file_hardlink(target, link)

Description: Make a hard link to a file

Arguments:

  • target: of link
  • link: the hard link's location

Example:

methods:
     "" usebundle => file_hardlink("/tmp/z.txt", "/tmp/z.link");

Implementation:

bundle agent file_hardlink(target, link)
{
  files:
      "$(link)"
      move_obstructions => "true",
      link_from => linkfrom($(target), "hardlink");

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): $(link) will be a hard link to $(target)";
}

Prototype: file_link(target, link)

Description: Make a symlink to a file

Arguments:

  • target: of symlink
  • link: the symlink's location

Example:

methods:
     "" usebundle => file_link("/tmp/z.txt", "/tmp/z.link");

Implementation:

bundle agent file_link(target, link)
{
  files:
      "$(link)"
      move_obstructions => "true",
      link_from => linkfrom($(target), "symlink");

  reports:
    inform_mode|EXTRA::
      "$(this.bundle): $(link) will be a symlink to $(target)";
}
edit_field bodies
fstab_options

Prototype: fstab_options(newval, method)

Description: Edit the options field in a fstab format

Arguments:

This body edits the options field in the fstab file format. The method is a field_operation which can be append, prepend, set, delete, or alphanum. The newval option is OS-specific.

Example:

  # from the `fstab_options_editor`
  field_edits:
     "(?!#)\S+\s+$(mount)\s.+"
     edit_field => fstab_options($(option), $(method));

Implementation:

body edit_field fstab_options(newval, method)
{
      field_separator => "\s+";
      select_field    => "4";
      value_separator  => ",";
      field_value     => "$(newval)";
      field_operation => "$(method)";
}
quoted_var

Prototype: quoted_var(newval, method)

Description: Edit the quoted value of the matching line

Arguments:

  • newval: The new value
  • method: The method by which to edit the field

Implementation:

body edit_field quoted_var(newval,method)
{
      field_separator => "\"";
      select_field    => "2";
      value_separator  => " ";
      field_value     => "$(newval)";
      field_operation => "$(method)";
      extend_fields => "false";
      allow_blank_fields => "true";
}
col

Prototype: col(split, col, newval, method)

Description: Edit tabluar data with comma-separated sub-values

Arguments:

  • split: The separator that defines columns
  • col: The (1-based) index of the value to change
  • newval: The new value
  • method: The method by which to edit the field

Implementation:

body edit_field col(split,col,newval,method)
{
      field_separator    => "$(split)";
      select_field       => "$(col)";
      value_separator    => ",";
      field_value        => "$(newval)";
      field_operation    => "$(method)";
      extend_fields      => "true";
      allow_blank_fields => "true";
}
line

Prototype: line(split, col, newval, method)

Description: Edit tabular data with space-separated sub-values

Arguments:

  • split: The separator that defines columns
  • col: The (1-based) index of the value to change
  • newval: The new value
  • method: The method by which to edit the field

Implementation:

body edit_field line(split,col,newval,method)
{
      field_separator    => "$(split)";
      select_field       => "$(col)";
      value_separator    => " ";
      field_value        => "$(newval)";
      field_operation    => "$(method)";
      extend_fields      => "true";
      allow_blank_fields => "true";
}
replace_with bodies
value

Prototype: value(x)

Description: Replace matching lines

Arguments:

  • x: The replacement string

Implementation:

body replace_with value(x)
{
      replace_value => "$(x)";
      occurrences => "all";
}
select_region bodies
INI_section

Prototype: INI_section(x)

Description: Restrict the edit_line promise to the lines in section [x]

Arguments:

  • x: The name of the section in an INI-like configuration file

Implementation:

body select_region INI_section(x)
{
      select_start => "\[$(x)\]\s*";
      select_end => "\[.*\]\s*";
}
edit_defaults bodies
std_defs

Prototype: std_defs

Description: Standard definitions for edit_defaults Don't empty the file before editing starts and don't make a backup.

Implementation:

body edit_defaults std_defs
{
      empty_file_before_editing => "false";
      edit_backup => "false";
      #max_file_size => "300000";
}
empty

Prototype: empty

Description: Empty the file before editing

No backup is made

Implementation:

body edit_defaults empty
{
      empty_file_before_editing => "true";
      edit_backup => "false";
      #max_file_size => "300000";
}
no_backup

Prototype: no_backup

Description: Don't make a backup of the file before editing

Implementation:

body edit_defaults no_backup
{
      edit_backup => "false";
}
backup_timestamp

Prototype: backup_timestamp

Description: Make a timestamped backup of the file before editing

Implementation:

body edit_defaults backup_timestamp
{
      empty_file_before_editing => "false";
      edit_backup => "timestamp";
      #max_file_size => "300000";
}
location bodies
start

Prototype: start

Description: Editing occurs before the matched line

Implementation:

body location start
{
      before_after => "before";
}
after

Prototype: after(str)

Description: Editing occurs after the line matching str

Arguments:

  • str: Regular expression matching the file line location

Implementation:

body location after(str)
{
      before_after => "after";
      select_line_matching => "$(str)";
}
before

Prototype: before(str)

Description: Editing occurs before the line matching str

Arguments:

  • str: Regular expression matching the file line location

Implementation:

body location before(str)
{
      before_after => "before";
      select_line_matching => "$(str)";
}
replace_with bodies
comment

Prototype: comment(c)

Description: Comment all lines matching the pattern by preprending c

Arguments:

  • c: The prefix that comments out lines

Implementation:

body replace_with comment(c)
{
      replace_value => "$(c) $(match.1)";
      occurrences => "all";
}
uncomment

Prototype: uncomment

Description: Uncomment all lines matching the pattern by removing anything outside the matching string

Implementation:

body replace_with uncomment
{
      replace_value => "$(match.1)";
      occurrences => "all";
}
copy_from bodies
secure_cp

Prototype: secure_cp(from, server)

Description: Download a file from a remote server over an encrypted channel

Only copy the file if it is different from the local copy, and verify that the copy is correct.

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

Implementation:

body copy_from secure_cp(from,server)
{
      source      => "$(from)";
      servers     => { "$(server)" };
      compare     => "digest";
      encrypt     => "true";
      verify      => "true";
}
remote_cp

Prototype: remote_cp(from, server)

Description: Download a file from a remote server.

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

Implementation:

body copy_from remote_cp(from,server)
{
      servers     => { "$(server)" };
      source      => "$(from)";
      compare     => "mtime";
}
remote_dcp

Prototype: remote_dcp(from, server)

Description: Download a file from a remote server if it is different from the local copy.

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

Implementation:

body copy_from remote_dcp(from,server)
{
      servers     => { "$(server)" };
      source      => "$(from)";
      compare     => "digest";
}
local_cp

Prototype: local_cp(from)

Description: Copy a local file.

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from local_cp(from)
{
      source      => "$(from)";
}
local_dcp

Prototype: local_dcp(from)

Description: Copy a local file if it is different from the existing copy.

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from local_dcp(from)
{
      source      => "$(from)";
      compare     => "digest";
}
perms_cp

Prototype: perms_cp(from)

Description: Copy a local file and preserve file permissions on the local copy.

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from perms_cp(from)
{
      source      => "$(from)";
      preserve    => "true";
}
perms_dcp

Prototype: perms_dcp(from)

Description: Copy a local file if it is different from the existing copy and preserve file permissions on the local copy.

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from perms_dcp(from)
{
      source      => "$(from)";
      preserve    => "true";
      compare     => "digest";
}
backup_local_cp

Prototype: backup_local_cp(from)

Description: Copy a local file and keep a backup of old versions.

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from backup_local_cp(from)
{
      source      => "$(from)";
      copy_backup => "timestamp";
}
seed_cp

Prototype: seed_cp(from)

Description: Copy a local file if the file does not already exist, i.e. seed the placement

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from seed_cp(from)
{
      source      => "$(from)";
      compare     => "exists";
}
sync_cp

Prototype: sync_cp(from, server)

Description: Download a file if the local copy does not already exist, i.e. seed the placement

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

Implementation:

body copy_from sync_cp(from,server)
{
      servers     => { "$(server)" };
      source      => "$(from)";
      purge       => "true";
      preserve    => "true";
      type_check  => "false";
}
no_backup_cp

Prototype: no_backup_cp(from)

Description: Copy a local file and don't make any backup of the previous version

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from no_backup_cp(from)
{
      source      => "$(from)";
      copy_backup => "false";
}
no_backup_dcp

Prototype: no_backup_dcp(from)

Description: Copy a local file if contents have changed, and don't make any backup of the previous version

Arguments:

  • from: The path to the source file.

Implementation:

body copy_from no_backup_dcp(from)
{
      source      => "$(from)";
      copy_backup => "false";
      compare     => "digest";
}
no_backup_rcp

Prototype: no_backup_rcp(from, server)

Description: Download a file if it's newer than the local copy, and don't make any backup of the previous version

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

Implementation:

body copy_from no_backup_rcp(from,server)
{
      servers     => { "$(server)" };
      source      => "$(from)";
      compare     => "mtime";
      copy_backup => "false";
}
ln_s

Prototype: ln_s(x)

Description: Create a symbolink link to x The link is created even if the source of the link does not exist.

Arguments:

  • x: The source of the link

Implementation:

body link_from ln_s(x)
{
      link_type => "symlink";
      source => "$(x)";
      when_no_source => "force";
}
linkchildren

Prototype: linkchildren(tofile)

Description: Create a symbolink link to tofile If the promiser is a directory, children are linked to the source, unless entries with identical names already exist. The link is created even if the source of the link does not exist.

Arguments:

  • tofile: The source of the link

Implementation:

body link_from linkchildren(tofile)
{
      source        => "$(tofile)";
      link_type     => "symlink";
      when_no_source  => "force";
      link_children => "true";
      when_linking_children => "if_no_such_file"; # "override_file";
}
linkfrom

Prototype: linkfrom(source, type)

Description: Make any kind of link to a file

Arguments:

  • source: link to this
  • type: the link's type (symlink or hardlink)

Implementation:

body link_from linkfrom(source, type)
{
      source => $(source);
      link_type => $(type);
}
perms bodies
m

Prototype: m(mode)

Description: Set the file mode

Arguments:

  • mode: The new mode

Implementation:

body perms m(mode)
{
      mode   => "$(mode)";
}
mo

Prototype: mo(mode, user)

Description: Set the file's mode and owners

Arguments:

  • mode: The new mode
  • user: The username of the new owner

Implementation:

body perms mo(mode,user)
{
      owners => { "$(user)" };
      mode   => "$(mode)";
}
mog

Prototype: mog(mode, user, group)

Description: Set the file's mode, owner and group

Arguments:

  • mode: The new mode
  • user: The username of the new owner
  • group: The group name

Implementation:

body perms mog(mode,user,group)
{
      owners => { "$(user)" };
      groups => { "$(group)" };
      mode   => "$(mode)";
}
og

Prototype: og(u, g)

Description: Set the file's owner and group

Arguments:

  • u: The username of the new owner
  • g: The group name

Implementation:

body perms og(u,g)
{
      owners => { "$(u)" };
      groups => { "$(g)" };
}
owner

Prototype: owner(user)

Description: Set the file's owner

Arguments:

  • user: The username of the new owner

Implementation:

body perms owner(user)
{
      owners => { "$(user)" };
}
system_owned

Prototype: system_owned(mode)

Description: Set the file owner and group to the system default

Arguments:

  • mode: the access permission in octal format

Example:

files:
    "/etc/passwd" perms => system_owned("0644");

Implementation:

body perms system_owned(mode)
{
      mode   => "$(mode)";
      owners => { "root" };

    freebsd|openbsd|netbsd|darwin::
      groups => { "wheel" };

    linux::
      groups => { "root" };

    solaris::
      groups => { "sys" };
}
acl bodies
access_generic

Prototype: access_generic(acl)

Description: Set the aces of the access control as specified

Default/inherited ACLs are left unchanged. This body is applicable for both files and directories on all platforms.

Arguments:

  • acl: The aces to be set

Implementation:

body acl access_generic(acl)
{
      acl_method => "overwrite";
      aces => { "@(acl)" };

    windows::
      acl_type => "ntfs";

    !windows::
      acl_type => "posix";
}
ntfs

Prototype: ntfs(acl)

Description: Set the aces on NTFS file systems, and overwrite existing ACLs.

This body requires CFEngine Enterprise.

Arguments:

  • acl: The aces to be set

Implementation:

body acl ntfs(acl)
{
      acl_type => "ntfs";
      acl_method => "overwrite";
      aces => { "@(acl)" };
}
strict

Prototype: strict

Description: Limit file access via ACLs to users with administrator privileges, overwriting existing ACLs.

Note: May need to take ownership of file/dir to be sure no-one else is allowed access.

Implementation:

body acl strict
{
      acl_method => "overwrite";

    windows::
      aces => { "user:Administrator:rwx" };
    !windows::
      aces => { "user:root:rwx" };
}
depth_search bodies
recurse

Prototype: recurse(d)

Description: Search files and direcories recursively, up to the specified depth Directories on different devices are included.

Arguments:

  • d: The maximum search depth

Implementation:

body depth_search recurse(d)
{
      depth => "$(d)";
      xdev  => "true";
}
recurse_ignore

Prototype: recurse_ignore(d, list)

Description: Search files and directories recursively, but don't recurse into the specified directories

Arguments:

  • d: The maximum search depth
  • list: The list of directories to be excluded

Implementation:

body depth_search recurse_ignore(d,list)
{
      depth => "$(d)";
      exclude_dirs => { @(list) };
}
include_base

Prototype: include_base

Description: Search files and directories recursively, starting from the base directory.

Implementation:

body depth_search include_base
{
      include_basedir => "true";
}
recurse_with_base

Prototype: recurse_with_base(d)

Description: Search files and directories recursively up to the specified depth, starting from the base directory and including directories on other devices.

Arguments:

  • d: The maximum search depth

Implementation:

body depth_search recurse_with_base(d)
{
      depth => "$(d)";
      xdev  => "true";
      include_basedir => "true";
}
delete bodies
tidy

Prototype: tidy

Description: Delete the file and remove empty directories and links to directories

Implementation:

body delete tidy
{
      dirlinks => "delete";
      rmdirs   => "true";
}
rename bodies
disable

Prototype: disable

Description: Disable the file

Implementation:

body rename disable
{
      disable => "true";
}
rotate

Prototype: rotate(level)

Description: Rotate and store up to level backups of the file

Arguments:

  • level: The number of backups to store

Implementation:

body rename rotate(level)
{
      rotate => "$(level)";
}
to

Prototype: to(file)

Description: Rename the file to file

Arguments:

  • file: The new name of the file

Implementation:

body rename to(file)
{
      newname => "$(file)";
}
file_select bodies
name_age

Prototype: name_age(name, days)

Description: Select files that have a matching name and have not been modified for at least days

Arguments:

  • name: A regex that matches the file name
  • days: Number of days

Implementation:

body file_select name_age(name,days)
{
      leaf_name   => { "$(name)" };
      mtime       => irange(0,ago(0,0,"$(days)",0,0,0));
      file_result => "mtime.leaf_name";
}
days_old

Prototype: days_old(days)

Description: Select files that have not been modified for at least days

Arguments:

  • days: Number of days

Implementation:

body file_select days_old(days)
{
      mtime       => irange(0,ago(0,0,"$(days)",0,0,0));
      file_result => "mtime";
}
size_range

Prototype: size_range(from, to)

Description: Select files that have a size within the specified range

Arguments:

  • from: The lower bound of the allowed file size
  • to: The upper bound of the allowed file size

Implementation:

body file_select size_range(from,to)
{
      search_size => irange("$(from)","$(to)");
      file_result => "size";
}
bigger_than

Prototype: bigger_than(size)

Description: Select files that are above a given size

Arguments:

  • size: The number of bytes files have

Implementation:

body file_select bigger_than(size)
{
      search_size => irange("0","$(size)");
      file_result => "!size";
}
exclude

Prototype: exclude(name)

Description: Select all files except those that match name

Arguments:

  • name: A regular expression

Implementation:

body file_select exclude(name)
{
      leaf_name  => { "$(name)"};
      file_result => "!leaf_name";
}
plain

Prototype: plain

Description: Select plain, regular files

Implementation:

body file_select plain
{
      file_types  => { "plain" };
      file_result => "file_types";
}
dirs

Prototype: dirs

Description: Select directories

Implementation:

body file_select dirs
{
      file_types  => { "dir" };
      file_result => "file_types";
}
by_name

Prototype: by_name(names)

Description: Select files that match names

Arguments:

  • names: A regular expression

Implementation:

body file_select by_name(names)
{
      leaf_name  => { @(names)};
      file_result => "leaf_name";
}
ex_list

Prototype: ex_list(names)

Description: Select all files except those that match names

Arguments:

  • names: A list of regular expressions

Implementation:

body file_select ex_list(names)
{
      leaf_name  => { @(names)};
      file_result => "!leaf_name";
}
all

Prototype: all

Description: Select all file system entries

Implementation:

body file_select all
{
      leaf_name => { ".*" };
      file_result => "leaf_name";
}
older_than

Prototype: older_than(years, months, days, hours, minutes, seconds)

Description: Select files older than the date-time specified

Arguments:

  • years: Number of years
  • months: Number of months
  • days: Number of days
  • hours: Number of hours
  • minutes: Number of minutes
  • seconds: Number of seconds

Generic older_than selection body, aimed to have a common definition handy for every case possible.

Implementation:

body file_select older_than(years, months, days, hours, minutes, seconds)
{
      mtime       => irange(0,ago("$(years)","$(months)","$(days)","$(hours)","$(minutes)","$(seconds)"));
      file_result => "mtime";
}
filetype_older_than

Prototype: filetype_older_than(filetype, days)

Description: Select files of specified type older than specified number of days

Arguments:

  • filetype: File type to select
  • days: Number of days

This body only takes a single filetype, see filetypes_older_than() if you want to select more than one type of file.

Implementation:

body file_select filetype_older_than(filetype, days)
{
      file_types => { "$(filetype)" };
      mtime      => irange(0,ago(0,0,"$(days)",0,0,0));
      file_result => "file_types.mtime";
}
filetypes_older_than

Prototype: filetypes_older_than(filetypes, days)

Description: Select files of specified types older than specified number of days

This body only takes a list of filetypes

Arguments:

  • filetypes: A list of file types
  • days: Number of days

See also: filetype_older_than()

Implementation:

body file_select filetypes_older_than(filetypes, days)
{
      file_types => { @(filetypes) };
      mtime      => irange(0,ago(0,0,"$(days)",0,0,0));
      file_result => "file_types.mtime";
}
changes bodies
detect_all_change

Prototype: detect_all_change

Description: Detect all file changes using the best hash method

This is fierce, and will cost disk cycles

Implementation:

body changes detect_all_change
{
      hash           => "best";
      report_changes => "all";
      update_hashes  => "yes";
}
detect_all_change_using

Prototype: detect_all_change_using(hash)

Description: Detect all file changes using a given hash method

Detect all changes using a configurable hashing algorithm for times when you care about both content and file stats e.g. mtime

Arguments:

  • hash: supported hashing algorithm (md5, sha1, sha224, sha256, sha384, sha512, best)

Implementation:

body changes detect_all_change_using(hash)
{
      hash           => "$(hash)";
      report_changes => "all";
      update_hashes  => "yes";
}
detect_content

Prototype: detect_content

Description: Detect file content changes using md5

This is a cheaper alternative

Implementation:

body changes detect_content
{
      hash           => "md5";
      report_changes => "content";
      update_hashes  => "yes";
}
detect_content_using

Prototype: detect_content_using(hash)

Description: Detect file content changes using a given hash algorithm.

For times when you only care about content, not file stats e.g. mtime

Arguments:

  • hash: - supported hashing algorithm (md5, sha1, sha224, sha256, sha384, sha512, best)

Implementation:

body changes detect_content_using(hash)
{
      hash           => "$(hash)";
      report_changes => "content";
      update_hashes  => "yes";
}
noupdate

Prototype: noupdate

Description: Detect content changes in (small) files that should never change

Implementation:

body changes noupdate
{
      hash           => "sha256";
      report_changes => "content";
      update_hashes  => "no";
}
diff

Prototype: diff

Description: Detect file content changes using sha256 and report the diff to CFEngine Enterprise

Implementation:

body changes diff
{
      hash           => "sha256";
      report_changes => "content";
      report_diffs   => "true";
      update_hashes  => "yes";
}
all_changes

Prototype: all_changes

Description: Detect all file changes using sha256 and report the diff to CFEngine Enterprise

Implementation:

body changes all_changes
{
      hash           => "sha256";
      report_changes => "all";
      report_diffs   => "true";
      update_hashes  => "yes";
}
diff_noupdate

Prototype: diff_noupdate

Description: Detect content changes in (small) files and report the diff to CFEngine Enterprise

Implementation:

body changes diff_noupdate
{
      hash           => "sha256";
      report_changes => "content";
      report_diffs   => "true";
      update_hashes  => "no";
}
copy_from bodies
copyfrom_sync

Prototype: copyfrom_sync(f)

Description: Copy a directory or file with digest checksums, preserving attributes and purging leftovers

Arguments:

  • f: the file or directory

Implementation:

body copy_from copyfrom_sync(f)
{
      source => "$(f)";
      purge => "true";
      preserve => "true";
      type_check => "false";
      compare => "digest";
}

Guest Environments Bundles and Bodies

See the guest_environments promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "guest_environments.cf" }
}
environment_resources bodies
kvm

Prototype: kvm(name, arch, cpu_count, mem_kb, disk_file)

Description: An environment_resources body for a KVM virtual machine.

The env_spec attribute is set to a KVM XML specification.

Arguments:

  • name: The name of the virtual machine
  • arch: The architecture
  • cpu_count: The number of CPUs the virtual machine should have
  • mem_kb: The amount of RAM in kilobyte
  • disk_file: The file on the host system for the virtual machine's harddrive

Example:

bundle agent manage_vm
{
guest_environments:
  am_vm_host::
    "db_server"
      environment_host      => atlas,
      environment_type      => "kvm",
      environment_state     => "create",
      environment_resources => kvm("PSQL1, "x86_64", "4", "4096", "/var/lib/libvirt/images/psql1.iso")
}

Implementation:

body environment_resources kvm(name, arch, cpu_count, mem_kb, disk_file)
{
      env_spec =>
      "<domain type='kvm'>
  <name>$(name)</name>
  <memory>$(mem_kb)</memory>
  <currentMemory>$(mem_kb)</currentMemory>
  <vcpu>$(cpu_count)</vcpu>
  <os>
    <type arch='$(arch)'>hvm</type>
  </os>
  <features>
    <acpi/>
    <apic/>
    <pae/>
  </features>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    <disk type='file' device='disk'>
      <source file='$(disk_file)'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <interface type='network'>
      <source network='default'/>
    </interface>
    <input type='mouse' bus='ps2'/>
    <graphics type='vnc' port='-1' autoport='yes'/>
  </devices>
</domain>";
}

Monitor Bundles and Bodies

This is an Enterprise-only feature.

See the measurements promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "monitor.cf" }
}
match_value bodies
scan_log

Prototype: scan_log(line)

Description: Selects lines matching line in a growing file

Arguments:

  • line: Regular expression for matching lines.

See also: select_line_matching, track_growing_file

Implementation:

body match_value scan_log(line)
{
      select_line_matching => "$(line)";
      track_growing_file => "true";
}
scan_changing_file

Prototype: scan_changing_file(line)

Description: Selects lines matching line in a changing file

Arguments:

  • line: Regular expression for matching lines.

See also: select_line_matching, track_growing_file

Implementation:

body match_value scan_changing_file(line)
{
      select_line_matching => "$(line)";
      track_growing_file => "false";
}
single_value

Prototype: single_value(regex)

Description: Extract lines matching regex as values

Arguments:

  • regex: Regular expression matching lines and values

See also: select_line_matching, extraction_regex

Implementation:

body match_value single_value(regex)
{
      select_line_matching => "$(regex)";
      extraction_regex => "($(regex))";
}
line_match_value

Prototype: line_match_value(line_match, extract_regex)

Description: Find lines matching line_match and extract a value matching extract_regex

Arguments:

  • line_match: Regular expression matching line where value is found
  • extract_regex: Regular expression matching value to extract

See also: select_line_matching, extraction_regex

Example:

bundle monitor example
{
  vars:
     "regex_vsz" string => "root\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9.]+\s+[0-9.]+\s+([0-9]+).*";
   measurements:
     "/var/cfengine/state/cf_procs"
             handle => "cf_serverd_vsz",
             comment => "Tracking the memory consumption of a process can help us identify possible memory leaks",
             stream_type => "file",
             data_type => "int",
             history_type => "weekly",
             units => "kB",
             match_value => line_match_value(".*cf-serverd.*", "$(regex_vsz)");
}

Implementation:

body match_value line_match_value(line_match, extract_regex)
{
      select_line_matching => "$(line_match)";
      extraction_regex => "$(extract_regex)";
}

Packages Bundles and Bodies

See the packages promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies and bundles, add the following to your policy:

body file control
{
    inputs => { "packages.cf" }
}
common_knowledge

Prototype: common_knowledge

Description: common packages knowledge bundle

This common bundle defines general things about platforms.

Implementation:

bundle common common_knowledge
{
  vars:
      "list_update_ifelapsed" string => "240";
}
debian_knowledge

Prototype: debian_knowledge

Description: common Debian knowledge bundle

This common bundle has useful information about Debian.

Implementation:

bundle common debian_knowledge
{
  vars:
      # Debian default package architecture, see https://wiki.debian.org/Multiarch/Tuples
      "default_arch" string => ifelse("x86_64", "amd64",
                                      "i386", "i386",
                                      $(sys.arch));

      "apt_prefix" string => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C PATH=/bin:/sbin/:/usr/bin:/usr/sbin";
      "call_dpkg" string => "$(apt_prefix) $(paths.path[dpkg])";
      "call_apt_get" string => "$(apt_prefix) $(paths.path[apt_get])";
      "call_aptitude" string => "$(apt_prefix) $(paths.path[aptitude])";
      "dpkg_options" string => "-o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef";

      "dpkg_compare_equal" string => "$(call_dpkg) --compare-versions '$(v1)' eq '$(v2)'";
      "dpkg_compare_less" string => "$(call_dpkg) --compare-versions '$(v1)' lt '$(v2)'";

      "list_name_regex" string => "^.i\s+([^\s:]+).*";
      "list_version_regex" string => "^.i\s+[^\s]+\s+([^\s]+).*";

      "patch_name_regex" string => "^Inst\s+(\S+)\s+.*";
      "patch_version_regex" string => "^Inst\s+\S+\s+\[\S+\]\s+\((\S+)\s+.*";
}
rpm_knowledge

Prototype: rpm_knowledge

Description: common RPM knowledge bundle

This common bundle has useful information about platforms using RPM

Implementation:

bundle common rpm_knowledge
{
  vars:
      "call_rpm" string => "$(paths.rpm)";

      "rpm_output_format" string => "i | repos | %{name} | %{version}-%{release} | %{arch}\n";
      "rpm_name_regex" string => "[^|]+\|[^|]+\|\s+([^\s|]+).*";
      "rpm_version_regex" string => "[^|]+\|[^|]+\|[^|]+\|\s+([^\s|]+).*";
      "rpm_arch_regex" string => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*";

      "rpm2_output_format" string => "%{name} %{version}-%{release} %{arch}\n";
      "rpm2_name_regex" string => "^(\S+?)\s\S+?\s\S+$";
      "rpm2_version_regex" string => "^\S+?\s(\S+?)\s\S+$";
      "rpm2_arch_regex" string => "^\S+?\s\S+?\s(\S+)$";

      "rpm3_output_format" string => "%{name} %{arch} %{version}-%{release}\n";
      "rpm3_name_regex" string => "(\S+).*";
      "rpm3_version_regex" string => "\S+\s+\S+\s+(\S+).*";
      "rpm3_arch_regex" string => "\S+\s+(\S+).*";
}
redhat_knowledge

Prototype: redhat_knowledge

Description: common Red Hat knowledge bundle

This common bundle has useful information about Red Hat and its derivatives

Implementation:

bundle common redhat_knowledge
{
  vars:
      # Red Hat default package architecture
      "default_arch" string => $(sys.arch);

      "call_yum" string => "$(paths.path[yum])";
      "call_rpmvercmp" string => "$(sys.bindir)/rpmvercmp";

      # on RHEL 4, Yum doesn't know how to be --quiet
      "yum_options" string => ifelse("centos_4|redhat_4", "",
                                     "--quiet");
      "yum_offline_options" string => "$(yum_options) -C";

      "rpm_compare_equal" string => "$(call_rpmvercmp) '$(v1)' eq '$(v2)'";
      "rpm_compare_less" string => "$(call_rpmvercmp)  '$(v1)' lt '$(v2)'";
      # yum check-update prints a lot of extra useless lines, but the format of
      # the actual package lines is:
      #
      #   <name>.<arch>    <version>    <repo>
      #
      # We try to match that format as closely as possible, so we reject
      # possibly interspersed error messages.
      "patch_name_regex" string    => "^(\S+)\.[^\s.]+\s+\S+\s+\S+\s*$";
      "patch_version_regex" string => "^\S+\.[^\s.]+\s+(\S+)\s+\S+\s*$";
      "patch_arch_regex" string    => "^\S+\.([^\s.]+)\s+\S+\s+\S+\s*$";

      # Combine multiline entries into one line. A line without at least three
      # fields gets combined with the next line, if that line starts with a
      # space.
      "check_update_postproc" string => "| $(paths.sed) -r -n -e '
        :begin;
        /\S+\s+\S+\s+\S+/!{    # Check for valid line.
            N;                 # If not, read in the next line and append it.
            /\n /!{            # Check whether that line started with a space.
                h;             # If not, copy buffer to clipboard.
                s/\n[^\n]*$//; # Erase last line.
                p;             # Print current buffer.
                x;             # Restore from clipboard.
                s/^.*\n//;     # Erase everything but last line.
            };
            s/\n / /;          # Combine lines by removing newline.
            bbegin;            # Jump back to begin.
        };
        p;                     # Print current buffer.'";
}
suse_knowledge

Prototype: suse_knowledge

Description: common SUSE knowledge bundle

Implementation:

bundle common suse_knowledge
{
  vars:
      # SUSE default package architecture
      "default_arch" string => $(sys.arch);

      "call_zypper" string => "$(paths.zypper)";
}
darwin_knowledge

Prototype: darwin_knowledge

Description: common Darwin / Mac OS X knowledge bundle

This common bundle has useful information about Darwin / Mac OS X.

Implementation:

bundle common darwin_knowledge
{
  vars:
      "call_brew" string => "$(paths.path[brew])";
      "call_sudo" string => "$(paths.path[sudo])";

      # used with brew list --versions format '%{name} %{version}\n'

      "brew_name_regex" string => "([\S]+)\s[\S]+";
      "brew_version_regex" string => "[\S]+\s([\S]+)";
}
npm_knowledge

Prototype: npm_knowledge

Description: Node.js `npm' knowledge bundle

This common bundle has useful information about the Node.js `npm' package manager.

Implementation:

bundle common npm_knowledge
{
  vars:
      "call_npm" string => "$(paths.path[npm])";

      "npm_list_name_regex"    string => "^[^ /]+ ([\w\d-._~]+)@[\d.]+";
      "npm_list_version_regex" string => "^[^ /]+ [\w\d-._~]+@([\d.]+)";
      "npm_installed_regex"    string => "^[^ /]+ ([\w\d-._~]+@[\d.]+)";
}
pip_knowledge

Prototype: pip_knowledge

Description: Python `pip' knowledge bundle

This common bundle has useful information about the Python `pip' package manager.

Implementation:

bundle common pip_knowledge
{
  vars:
      "call_pip" string => "$(paths.path[pip])";

      "pip_list_name_regex"    string => "^([[:alnum:]-_]+)\s\([\d.]+\)";
      "pip_list_version_regex" string => "^[[:alnum:]-_]+\s\(([\d.]+)\)";
      "pip_installed_regex"    string => "^([[:alnum:]-_]+\s\([\d.]+\))";
}
solaris_knowledge

Prototype: solaris_knowledge

Description: Solaris knowledge bundle

This common bundle has useful information about the Solaris packages.

Implementation:

bundle common solaris_knowledge
{
  vars:
      "call_pkgadd" string => "$(paths.path[pkgadd])";
      "call_pkgrm" string => "$(paths.path[pkgrm])";
      "call_pkginfo" string => "$(paths.path[pkginfo])";

      "admin_nocheck" string => "mail=
instance=unique
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=nocheck
setuid=nocheck
conflict=nocheck
action=nocheck
networktimeout=60
networkretries=3
authentication=quit
keystore=/var/sadm/security
proxy=
basedir=default";

}
edit_line bundles
create_solaris_admin_file

Prototype: create_solaris_admin_file

Description: The following bundle is part of a package setup for solaris

See unit examples.

Implementation:

bundle edit_line create_solaris_admin_file
{
  insert_lines:

      "$(solaris_knowledge.admin_nocheck)"
      comment => "Insert contents of Solaris admin file (automatically install packages)";
}
agent bundles
package_absent

Prototype: package_absent(package)

Description: Ensure package is absent

Arguments:

  • package: the packages to remove

This package method will remove package, using package_ensure.

Example:

methods:
    "nozip" usebundle => package_absent("zip");

Implementation:

bundle agent package_absent(package)
{
  packages:
    debian::
      "$(package)"
      package_policy => "delete",
      package_method => apt_get_permissive;

    redhat::
      "$(package)"
      package_policy => "delete",
      package_method => yum_rpm_permissive;

    suse::
      "$(package)"
      package_policy => "delete",
      package_method => zypper;

    !debian.!redhat.!suse::
      "$(package)"
      package_policy => "delete",
      package_method => generic;
}
package_present

Prototype: package_present(package)

Description: Ensure package is present

Arguments:

  • package: the packages to install

This package method will install package. On Debian, it will use apt_get_permissive. On Red Hat, yum_rpm_permissive. Otherwise, generic.

Example:

methods:
    "pleasezip" usebundle => package_present("zip");

Implementation:

bundle agent package_present(package)
{
  packages:
    debian::
      "$(package)"
      package_policy => "add",
      package_method => apt_get_permissive;

    redhat::
      "$(package)"
      package_policy => "add",
      package_method => yum_rpm_permissive;

    suse::
      "$(package)"
      package_policy => "add",
      package_method => zypper;

    !debian.!redhat.!suse::
      "$(package)"
      package_policy => "add",
      package_method => generic;
}
package_latest

Prototype: package_latest(package)

Description: Ensure package is present and updated

Arguments:

  • package: the package to add/update

This package method will install package or update it to the latest version. On Debian, it will use apt_get_permissive. On Red Hat, yum_rpm_permissive. Otherwise, generic.

Example:

methods:
    "latestzip" usebundle => package_latest("zip");

Implementation:

bundle agent package_latest(package)
{
  packages:
    debian::
      "$(package)"
      package_policy => "addupdate",
      package_version => "999999999",
      package_method => apt_get_permissive;

    redhat::
      "$(package)"
      package_policy => "addupdate",
      package_version => "999999999",
      package_method => yum_rpm_permissive;

    suse::
      "$(package)"
      package_policy => "addupdate",
      package_version => "999999999",
      package_method => zypper;

    !debian.!redhat.!suse::
      "$(package)"
      package_policy => "addupdate",
      package_method => generic;
}
package_specific_present

Prototype: package_specific_present(packageorfile, package_version, package_arch)

Description: Ensure package is present

Arguments:

This package method will add packageorfile as a package or file, using package_specific.

Example:

methods:
     "addfilezip"
     usebundle => package_specific_present("/mydir/zip",
                                           "3.0-7",
                                           $(debian_knowledge.default_arch));

Implementation:

bundle agent package_specific_present(packageorfile, package_version, package_arch)
{
  methods:
      "ensure" usebundle => package_specific($(packageorfile),
                                             "add",
                                             $(package_version),
                                             $(package_arch));
}
package_specific_absent

Prototype: package_specific_absent(packageorfile, package_version, package_arch)

Description: Ensure package is absent

Arguments:

This package method will remove packageorfile as a package or file, using package_specific.

Example:

methods:
     "addfilezip"
     usebundle => package_specific_absent("/mydir/zip",
                                          "3.0-7",
                                          $(debian_knowledge.default_arch));

Implementation:

bundle agent package_specific_absent(packageorfile, package_version, package_arch)
{
  methods:
      "ensure" usebundle => package_specific($(packageorfile),
                                             "delete",
                                             $(package_version),
                                             $(package_arch));
}
package_specific_latest

Prototype: package_specific_latest(packageorfile, package_version, package_arch)

Description: Ensure package is added or updated

Arguments:

This package method will add or update packageorfile as a package or file, using package_specific.

Example:

methods:
     "latestfilezip"
     usebundle => package_specific_latest("/mydir/zip",
                                          "3.0-7",
                                          $(debian_knowledge.default_arch));
     "latestzip"
     usebundle => package_specific_latest("/mydir/zip",
                                          "3.0-7",
                                          $(debian_knowledge.default_arch));

Implementation:

bundle agent package_specific_latest(packageorfile, package_version, package_arch)
{
  methods:
      "ensure" usebundle => package_specific($(packageorfile),
                                             "addupdate",
                                             $(package_version),
                                             $(package_arch));
}
package_specific

Prototype: package_specific(package_name, desired, package_version, package_arch)

Description: Ensure package_name has the desired state

Arguments:

  • package_name: the packages to ensure (can be files)
  • desired: the desired package_policy, add or delete or addupdate
  • package_version: the desired package_version
  • package_arch: the desired package architecture

This package method will manage packages with package_policy set to desired, using package_version, and package_arch.

If package_name is not a file name: on Debian, it will use apt_get. On Red Hat, yum_rpm. Otherwise, generic.

If package_name is a file name, it will use dpkg_version or rpm_version from the file's directory.

For convenience on systems where sys.arch is not correct, you can use debian_knowledge.default_arch and redhat_knowledge.default_arch.

Solaris is only supported with pkgadd. Patches welcome.

Example:

methods:
     "ensure" usebundle => package_specific("zsh", "add", "1.2.3", "amd64");
     "ensure" usebundle => package_specific("/mydir/package.deb", "add", "9.8.7", "amd64");
     "ensure" usebundle => package_specific("tcsh", "delete", "2.3.4", "x86_64");

Implementation:

bundle agent package_specific(package_name, desired, package_version, package_arch)
{
  classes:
      "filebased" expression => fileexists($(package_name));
      "solaris_pkgadd" and => { "solaris", "_stdlib_path_exists_pkgadd" };

  vars:
      "solaris_adminfile" string => "/tmp/cfe-adminfile";

    filebased::
      "package_basename" string => lastnode($(package_name), "/");
      "dir" string => dirname($(package_name));

  methods:
    solaris_pkgadd.filebased::
      "" usebundle => file_make($(solaris_adminfile),
                                $(solaris_knowledge.admin_nocheck)),
      classes => scoped_classes_generic("bundle", "solaris_adminfile");

  packages:

    debian.!filebased::

      "$(package_name)"
      package_policy => $(desired),
      package_select => '>=', # see verify_packages.c
      package_version => $(package_version),
      package_architectures => { $(package_arch) },
      package_method => apt_get;

    debian.filebased::

      "$(package_basename)"
      package_policy => $(desired),
      package_select => '>=',
      package_version => $(package_version),
      package_architectures => { $(package_arch) },
      package_method => dpkg_version($(dir));

    redhat.!filebased::

      "$(package_name)"
      package_policy => $(desired),
      package_select => '>=', # see verify_packages.c
      package_version => $(package_version),
      package_architectures => { $(package_arch) },
      package_method => yum_rpm;

    suse::

      "$(package_name)"
      package_policy => $(desired),
      package_select => '>=', # see verify_packages.c
      package_version => $(package_version),
      package_architectures => { $(package_arch) },
      package_method => zypper;

    (redhat|aix).filebased::

      "$(package_basename)"
      package_policy => $(desired),
      package_select => '>=',
      package_version => $(package_version),
      package_architectures => { $(package_arch) },
      package_method => rpm_version($(dir));

    solaris_adminfile_ok::

      "$(package_name)"
      package_policy => $(desired),
      package_select => '>=',
      package_version => $(package_version),
      package_method => solaris_install($(solaris_admin_file));

    !filebased.!debian.!redhat.!suse::

      "$(package_name)"
      package_policy => $(desired),
      package_method => generic;

  reports:
    (inform_mode||verbose_mode).filebased.!suse.!debian.!redhat.!aix.!solaris_pkgadd::
      "$(this.bundle): sorry, can't do file-based installs on $(sys.os)";
}
package_method bodies
pip

Prototype: pip(flags)

Description: Python `pip' package management

`pip' is a package manager for Python http://www.pip-installer.org/en/latest/

Available commands : add, delete, (add)update, verify

Arguments:

  • flags: The command line parameter passed to pip

Note: "update" command preforms recursive upgrade (of dependencies) by default. Set $flags to "--no-deps" to preform non-recursive upgrade. http://www.pip-installer.org/en/latest/cookbook.html#non-recursive-upgrades

Example:

packages:
    "Django"              package_method => pip(""), package_policy => "add";
    "django-registration" package_method => pip(""), package_policy => "delete";
    "requests"            package_method => pip(""), package_policy => "verify";

Note: "Django" with a capital 'D' in the example above. Explicitly match the name of the package, capitalization does count!

    $ pip search django | egrep "^Django\s+-"
    Django - A high-level Python Web framework [..output trimmed..]

Implementation:

body package_method pip(flags)
{
      package_changes => "individual";

      package_noverify_regex => "";

      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_list_name_regex    => "$(pip_knowledge.pip_list_name_regex)";
      package_list_version_regex => "$(pip_knowledge.pip_list_version_regex)";
      package_installed_regex    => "$(pip_knowledge.pip_installed_regex)";

      package_name_convention   => "$(name)";
      package_delete_convention => "$(name)";

      package_list_command   => "$(paths.path[pip]) list $(flags)";
      package_verify_command => "$(paths.path[pip]) show $(flags)";
      package_add_command    => "$(paths.path[pip]) install $(flags)";
      package_delete_command => "$(paths.path[pip]) uninstall --yes $(flags)";
      package_update_command => "$(paths.path[pip]) install --upgrade $(flags)";
}
npm

Prototype: npm(dir)

Description: Node.js `npm' local-mode package management

`npm' is a package manager for Node.js https://npmjs.org/package/npm

Available commands : add, delete, (add)update, verify

For the difference between local and global install see here: https://npmjs.org/doc/cli/npm-install.html

Arguments:

  • dir: The prefix path to ./node_modules/

Example:

vars:
    "dirs"    slist => { "/root/myproject", "/home/somedev/someproject" };

packages:
    "express"              package_method => npm("$(dirs)"), package_policy => "add";
    "redis"                package_method => npm("$(dirs)"), package_policy => "delete";

Implementation:

body package_method npm(dir)
{
      package_changes => "individual";

      package_noverify_regex => "";

      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_list_name_regex    => "$(npm_knowledge.npm_list_name_regex)";
      package_list_version_regex => "$(npm_knowledge.npm_list_version_regex)";
      package_installed_regex    => "$(npm_knowledge.npm_installed_regex)";

      package_name_convention   => "$(name)";
      package_delete_convention => "$(name)";

      package_list_command   => "$(npm_knowledge.call_npm) list --prefix $(dir)";
      package_verify_command => "$(npm_knowledge.call_npm) list --prefix $(dir)";
      package_add_command    => "$(npm_knowledge.call_npm) install --prefix $(dir)";
      package_delete_command => "$(npm_knowledge.call_npm) remove --prefix $(dir)";
      package_update_command => "$(npm_knowledge.call_npm) update --prefix $(dir)";
}
npm_g

Prototype: npm_g

Description: Node.js `npm' global-mode package management

`npm' is a package manager for Node.js https://npmjs.org/package/npm

Available commands : add, delete, (add)update, verify

For the difference between global and local install see here: https://npmjs.org/doc/cli/npm-install.html

Example:

packages:
    "express"              package_method => npm_g, package_policy => "add";
    "redis"                package_method => npm_g, package_policy => "delete";

Implementation:

body package_method npm_g
{
      package_changes => "individual";

      package_noverify_regex => "";

      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_list_name_regex    => "$(npm_knowledge.npm_list_name_regex)";
      package_list_version_regex => "$(npm_knowledge.npm_list_version_regex)";
      package_installed_regex    => "$(npm_knowledge.npm_installed_regex)";

      package_name_convention   => "$(name)";
      package_delete_convention => "$(name)";

      package_list_command   => "$(npm_knowledge.call_npm) list --global";
      package_verify_command => "$(npm_knowledge.call_npm) list --global";
      package_add_command    => "$(npm_knowledge.call_npm) install --global";
      package_delete_command => "$(npm_knowledge.call_npm) remove --global";
      package_update_command => "$(npm_knowledge.call_npm) update --global";
}
brew

Prototype: brew(user)

Description: Darwin/Mac OS X + Homebrew installation method

Homebrew is a package manager for OS X -- http://brew.sh

Available commands : add, delete, (add)update (with package_version).

Arguments:

  • user: The user under which to run the commands

Homebrew expects a regular (non-root) user to install packages. https://github.com/mxcl/homebrew/wiki/FAQ#why-does-homebrew-say-sudo-is-bad As CFEngine doesn't give the possibility to run package_add_command with a different user, this body uses sudo -u.

Example:

packages:
    "mypackage" package_method => brew("adminuser"), package_policy => "add";
    "uppackage" package_method => brew("adminuser"), package_policy => "update", package_version => "3.5.2";

Implementation:

body package_method brew(user)
{

      package_changes               => "bulk";
      package_add_command           => "$(darwin_knowledge.call_sudo) -u $(user) $(darwin_knowledge.call_brew) install";
      package_delete_command        => "$(darwin_knowledge.call_sudo) -u $(user) $(darwin_knowledge.call_brew) uninstall";
      package_delete_convention     => "$(name)";
      package_name_convention       => "$(name)";

      # Homebrew can list only installed packages along versions.
      # for a complete list of packages, we could use `brew search`, but there's no easy
      # way to determine the version or wether it's installed.
      package_installed_regex       => ".*";
      package_list_command          => "$(darwin_knowledge.call_sudo) -u $(user) $(darwin_knowledge.call_brew) list --versions";
      package_list_name_regex       => "$(darwin_knowledge.brew_name_regex)";
      package_list_version_regex    => "$(darwin_knowledge.brew_version_regex)";
      package_list_update_command   => "$(darwin_knowledge.call_sudo) -u $(user) $(darwin_knowledge.call_brew) update";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      # brew list [package] will print the installed files and return 1 if not found.
      package_verify_command        => "$(darwin_knowledge.call_sudo) -u $(user) $(darwin_knowledge.call_brew) list";
      package_noverify_returncode   => "1";

      # remember to specify the package version
      package_update_command        => "$(darwin_knowledge.call_sudo) -u $(user) $(darwin_knowledge.call_brew) upgrade";

}
apt

Prototype: apt

Description: APT installation package method

This package method interacts with the APT package manager through aptitude.

Example:

packages:
    "mypackage" package_method => apt, package_policy => "add";

Implementation:

body package_method apt
{
      package_changes => "bulk";
      package_list_command => "$(debian_knowledge.call_dpkg) -l";
      package_list_name_regex => "$(debian_knowledge.list_name_regex)";
      package_list_version_regex => "$(debian_knowledge.list_version_regex)";
      package_installed_regex => ".i.*"; # packages that have been uninstalled may be listed
      package_name_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      # make correct version comparisons
      package_version_less_command => "$(debian_knowledge.dpkg_compare_less)";
      package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";

    have_aptitude::
      package_add_command => "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes install";
      package_list_update_command => "$(debian_knowledge.call_aptitude) update";
      package_delete_command => "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes -q remove";
      package_update_command =>  "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes install";
      package_patch_command =>  "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes install";
      package_verify_command =>  "$(debian_knowledge.call_aptitude) show";
      package_noverify_regex => "(State: not installed|E: Unable to locate package .*)";

      package_patch_list_command => "$(debian_knowledge.call_aptitude) --assume-yes --simulate --verbose full-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

    !have_aptitude::
      package_add_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_list_update_command => "$(debian_knowledge.call_apt_get) update";
      package_delete_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes -q remove";
      package_update_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_patch_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_verify_command => "$(debian_knowledge.call_dpkg) -s";
      package_noverify_returncode => "1";

      package_patch_list_command => "$(debian_knowledge.call_apt_get) --just-print dist-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

}
apt_get

Prototype: apt_get

Description: APT installation package method

This package method interacts with the APT package manager through apt-get.

Example:

packages:
    "mypackage" package_method => apt_get, package_policy => "add";

Implementation:

body package_method apt_get
{
      package_changes => "bulk";
      package_list_command => "$(debian_knowledge.call_dpkg) -l";
      package_list_name_regex => "$(debian_knowledge.list_name_regex)";
      package_list_version_regex => "$(debian_knowledge.list_version_regex)";
      package_installed_regex => ".i.*"; # packages that have been uninstalled may be listed
      package_name_convention => "$(name)=$(version)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      # Target a specific release, such as backports
      package_add_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_list_update_command => "$(debian_knowledge.call_apt_get) update";
      package_delete_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes -q remove";
      package_update_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_patch_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_verify_command => "$(debian_knowledge.call_dpkg) -s";
      package_noverify_returncode => "1";

      package_patch_list_command => "$(debian_knowledge.call_apt_get) --just-print dist-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

      # make correct version comparisons
      package_version_less_command => "$(debian_knowledge.dpkg_compare_less)";
      package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";
}
apt_get_permissive

Prototype: apt_get_permissive

Description: APT permissive (just by name) package method

This package method interacts with the APT package manager through apt-get.

Normally you have to specify the package version, and it defaults to *, which then triggers the bug of installing xyz-abc when you ask for xyz.

This "permissive" body sets

package_name_convention => "$(name)";

which is permissive in the sense of not requiring the version.

Example:

packages:
    "mypackage" package_method => apt_get_permissive, package_policy => "add";

Implementation:

body package_method apt_get_permissive
{
      package_changes => "bulk";
      package_list_command => "$(debian_knowledge.call_dpkg) -l";
      package_list_name_regex => "$(debian_knowledge.list_name_regex)";
      package_list_version_regex => "$(debian_knowledge.list_version_regex)";
      package_installed_regex => ".i.*"; # packages that have been uninstalled may be listed
      package_name_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      # Target a specific release, such as backports
      package_add_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_list_update_command => "$(debian_knowledge.call_apt_get) update";
      package_delete_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes -q remove";
      package_update_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_patch_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
      package_verify_command => "$(debian_knowledge.call_dpkg) -s";
      package_noverify_returncode => "1";

      package_patch_list_command => "$(debian_knowledge.call_apt_get) --just-print dist-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

      # make correct version comparisons
      package_version_less_command => "$(debian_knowledge.dpkg_compare_less)";
      package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";
}
apt_get_release

Prototype: apt_get_release(release)

Description: APT installation package method

Arguments:

  • release: specific release to use

This package method interacts with the APT package manager through apt-get but sets a specific target release.

Example:

packages:
    "mypackage" package_method => apt_get_release("xyz"), package_policy => "add";

Implementation:

body package_method apt_get_release(release)
{
      package_changes => "bulk";
      package_list_command => "$(debian_knowledge.call_dpkg) -l";
      package_list_name_regex => "$(debian_knowledge.list_name_regex)";
      package_list_version_regex => "$(debian_knowledge.list_version_regex)";
      package_installed_regex => ".i.*"; # packages that have been uninstalled may be listed
      package_name_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      # Target a specific release, such as backports
      package_add_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes --target-release $(release) install";
      package_list_update_command => "$(debian_knowledge.call_apt_get) update";
      package_delete_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes -q remove";
      package_update_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes --target-release $(release) install";
      package_patch_command =>  "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes --target-release $(release) install";
      package_verify_command => "$(debian_knowledge.call_dpkg) -s";
      package_noverify_returncode => "1";

      package_patch_list_command => "$(debian_knowledge.call_apt_get) --just-print dist-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

      # make correct version comparisons
      package_version_less_command => "$(debian_knowledge.dpkg_compare_less)";
      package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";

}
dpkg_version

Prototype: dpkg_version(repo)

Description: dpkg installation package method

Arguments:

  • repo: specific repo to use

This package method interacts with dpkg.

Example:

packages:
    "mypackage" package_method => dpkg_version("xyz"), package_policy => "add";

Implementation:

body package_method dpkg_version(repo)
{
      package_changes => "individual";
      package_list_command => "$(debian_knowledge.call_dpkg) -l";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(debian_knowledge.call_apt_get) update";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_list_name_regex => "$(debian_knowledge.list_name_regex)";
      package_list_version_regex => "$(debian_knowledge.list_version_regex)";

      package_installed_regex => ".i.*"; # packages that have been uninstalled may be listed

      package_file_repositories => { "$(repo)" };

    debian.x86_64::
      package_name_convention => "$(name)_$(version)_amd64.deb";

    debian.i686::
      package_name_convention => "$(name)_$(version)_i386.deb";

    have_aptitude::
      package_patch_list_command => "$(debian_knowledge.call_aptitude) --assume-yes --simulate --verbose full-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

    !have_aptitude::
      package_patch_list_command => "$(debian_knowledge.call_apt_get) --just-print dist-upgrade";
      package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";

    debian::
      package_add_command => "$(debian_knowledge.call_dpkg) --install";
      package_delete_command => "$(debian_knowledge.call_dpkg) --purge";
      package_update_command =>  "$(debian_knowledge.call_dpkg) --install";
      package_patch_command =>  "$(debian_knowledge.call_dpkg) --install";

      # make correct version comparisons
      package_version_less_command => "$(debian_knowledge.dpkg_compare_less)";
      package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";
}
rpm_version

Prototype: rpm_version(repo)

Description: RPM direct installation method

Arguments:

This package method interacts with the RPM package manager for a specific repo.

Example:

packages:
    "mypackage" package_method => rpm_version("myrepo"), package_policy => "add";

Implementation:

body package_method rpm_version(repo)
{
      package_changes => "individual";

      package_list_command => "$(rpm_knowledge.call_rpm) -qa --queryformat \"$(rpm_knowledge.rpm_output_format)\"";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_list_name_regex    => "$(rpm_knowledge.rpm_name_regex)";
      package_list_version_regex => "$(rpm_knowledge.rpm_version_regex)";
      package_list_arch_regex    => "$(rpm_knowledge.rpm_arch_regex)";

      package_installed_regex => "i.*";

      package_file_repositories => { "$(repo)" };

      package_name_convention => "$(name)-$(version).$(arch).rpm";

      package_add_command => "$(rpm_knowledge.call_rpm) -ivh ";
      package_update_command => "$(rpm_knowledge.call_rpm) -Uvh ";
      package_patch_command => "$(rpm_knowledge.call_rpm) -Uvh ";
      package_delete_command => "$(rpm_knowledge.call_rpm) -e --nodeps";
      package_verify_command => "$(rpm_knowledge.call_rpm) -V";
      package_noverify_regex => ".*[^\s].*";
      package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
      package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
}
windows_feature

Prototype: windows_feature

Description: Method for managing Windows features

Implementation:

body package_method windows_feature
{
      package_changes => "individual";

      package_name_convention   => "$(name)";
      package_delete_convention => "$(name)";

      package_installed_regex => ".*";
      package_list_name_regex => "(.*)";
      package_list_version_regex => "(.*)";  # FIXME: the listing does not give version, so takes name for version too now

      package_add_command    => "$(sys.winsysdir)\\WindowsPowerShell\\v1.0\\powershell.exe -Command \"Import-Module ServerManager; Add-WindowsFeature -Name\"";
      package_delete_command => "$(sys.winsysdir)\\WindowsPowerShell\\v1.0\\powershell.exe -Command \"Import-Module ServerManager; Remove-WindowsFeature -confirm:$false -Name\"";
      package_list_command   => "$(sys.winsysdir)\\WindowsPowerShell\\v1.0\\powershell.exe -Command \"Import-Module ServerManager; Get-WindowsFeature | where {$_.installed -eq $True} |foreach {$_.Name}\"";
}
msi_implicit

Prototype: msi_implicit(repo)

Description: Windows MSI method

Arguments:

  • repo: The package file repository

Uses the whole file name as promiser, e.g. "7-Zip-4.50-x86_64.msi". The name, version and arch is then deduced from the promiser.

See also: msi_explicit()

Implementation:

body package_method msi_implicit(repo)
{
      package_changes => "individual";
      package_file_repositories => { "$(repo)" };

      package_installed_regex => ".*";

      package_name_convention => "$(name)-$(version)-$(arch).msi";
      package_delete_convention => "$(firstrepo)$(name)-$(version)-$(arch).msi";

      package_name_regex => "^(\S+)-(\d+\.?)+";
      package_version_regex => "^\S+-((\d+\.?)+)";
      package_arch_regex => "^\S+-[\d\.]+-(.*).msi";

      package_add_command => "\"$(sys.winsysdir)\msiexec.exe\" /qn /i";
      package_update_command => "\"$(sys.winsysdir)\msiexec.exe\" /qn /i";
      package_delete_command => "\"$(sys.winsysdir)\msiexec.exe\" /qn /x";
}
msi_explicit

Prototype: msi_explicit(repo)

Description: Windows MSI method

Arguments:

  • repo: The package file repository

Uses software name as promiser, e.g. "7-Zip", and explicitly specify any package_version and package_arch.

See also: msi_implicit()

Implementation:

body package_method msi_explicit(repo)
{
      package_changes => "individual";
      package_file_repositories => { "$(repo)" };

      package_installed_regex => ".*";

      package_name_convention => "$(name)-$(version)-$(arch).msi";
      package_delete_convention => "$(firstrepo)$(name)-$(version)-$(arch).msi";

      package_add_command => "\"$(sys.winsysdir)\msiexec.exe\" /qn /i";
      package_update_command => "\"$(sys.winsysdir)\msiexec.exe\" /qn /i";
      package_delete_command => "\"$(sys.winsysdir)\msiexec.exe\" /qn /x";
}
yum

Prototype: yum

Description: Yum+RPM installation method

This package method interacts with the Yum and RPM package managers. It is a copy of yum_rpm(), which was contributed by Trond Hasle Amundsen. The old yum package method has been removed.

This is an efficient package method for RPM-based systems - uses rpm instead of yum to list installed packages.

It will use rpm -e to remove packages. Please note that if several packages with the same name but varying versions or architectures are installed, rpm -e will return an error and not delete any of them.

Example:

packages:
    "mypackage" package_method => yum, package_policy => "add";

Implementation:

body package_method yum
{
      package_changes => "bulk";
      package_list_command => "$(rpm_knowledge.call_rpm) -qa --qf '$(rpm_knowledge.rpm3_output_format)'";
      package_patch_list_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_offline_options) check-update $(redhat_knowledge.check_update_postproc)";

      package_list_name_regex    => "$(rpm_knowledge.rpm3_name_regex)";
      package_list_version_regex => "$(rpm_knowledge.rpm3_version_regex)";
      package_list_arch_regex    => "$(rpm_knowledge.rpm3_arch_regex)";

      package_installed_regex => ".*";
      package_name_convention => "$(name)-$(version).$(arch)";

      # just give the package name to rpm to delete, otherwise it gets "name.*" (from package_name_convention above)
      package_delete_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_patch_name_regex    => "$(redhat_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(redhat_knowledge.patch_version_regex)";
      package_patch_arch_regex    => "$(redhat_knowledge.patch_arch_regex)";

      package_add_command    => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y install";
      package_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_patch_command  => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_delete_command => "$(rpm_knowledge.call_rpm) -e --nodeps";
      package_verify_command => "$(rpm_knowledge.call_rpm) -V";
      package_noverify_returncode => "1";
      package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
      package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
}
yum_rpm

Prototype: yum_rpm

Description: Yum+RPM installation method

This package method interacts with the Yum and RPM package managers.

Contributed by Trond Hasle Amundsen

This is an efficient package method for RPM-based systems - uses rpm instead of yum to list installed packages.

It will use rpm -e to remove packages. Please note that if several packages with the same name but varying versions or architectures are installed, rpm -e will return an error and not delete any of them.

Example:

packages:
    "mypackage" package_method => yum_rpm, package_policy => "add";

Implementation:

body package_method yum_rpm
{
      package_changes => "bulk";
      package_list_command => "$(rpm_knowledge.call_rpm) -qa --qf '$(rpm_knowledge.rpm3_output_format)'";
      package_patch_list_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_offline_options) check-update $(redhat_knowledge.check_update_postproc)";

      package_list_name_regex    => "$(rpm_knowledge.rpm3_name_regex)";
      package_list_version_regex => "$(rpm_knowledge.rpm3_version_regex)";
      package_list_arch_regex    => "$(rpm_knowledge.rpm3_arch_regex)";

      package_installed_regex => ".*";
      package_name_convention => "$(name)-$(version).$(arch)";

      # just give the package name to rpm to delete, otherwise it gets "name.*" (from package_name_convention above)
      package_delete_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_patch_name_regex    => "$(redhat_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(redhat_knowledge.patch_version_regex)";
      package_patch_arch_regex    => "$(redhat_knowledge.patch_arch_regex)";

      package_add_command    => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y install";
      package_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_patch_command  => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_delete_command => "$(rpm_knowledge.call_rpm) -e --nodeps";
      package_verify_command => "$(rpm_knowledge.call_rpm) -V";
      package_noverify_returncode => "1";
      package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
      package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
}
yum_rpm_permissive

Prototype: yum_rpm_permissive

Description: Yum+RPM permissive (just by name) package method

This package method interacts with the Yum and RPM package managers.

Copy of yum_rpm which was contributed by Trond Hasle Amundsen

This is an efficient package method for RPM-based systems - uses rpm instead of yum to list installed packages. It can't delete packages and can't take a target version or architecture, so only the "add" and "addupdate" methods should be used.

Normally you have to specify the package version, and it defaults to *, which then triggers the bug of installing xyz-abc when you ask for xyz.

This "permissive" body sets

package_name_convention => "$(name)";

which is permissive in the sense of not requiring the version.

Example:

packages:
    "mypackage" package_method => yum_rpm_permissive, package_policy => "add";

Implementation:

body package_method yum_rpm_permissive
{
      package_changes => "bulk";
      package_list_command => "$(rpm_knowledge.call_rpm) -qa --qf '$(rpm_knowledge.rpm3_output_format)'";
      package_patch_list_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_offline_options) check-update $(redhat_knowledge.check_update_postproc)";

      package_list_name_regex    => "$(rpm_knowledge.rpm3_name_regex)";
      package_list_version_regex => "$(rpm_knowledge.rpm3_version_regex)";
      package_list_arch_regex    => "$(rpm_knowledge.rpm3_arch_regex)";

      package_installed_regex => ".*";
      package_name_convention => "$(name)";

      # not needed, same as package_name_convention above
      package_delete_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_patch_name_regex    => "$(redhat_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(redhat_knowledge.patch_version_regex)";
      package_patch_arch_regex    => "$(redhat_knowledge.patch_arch_regex)";

      package_add_command    => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y install";
      package_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_patch_command  => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_delete_command => "$(rpm_knowledge.call_rpm) -e --nodeps";
      package_verify_command => "$(rpm_knowledge.call_rpm) -V";
      package_noverify_returncode => "1";
      package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
      package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
}
yum_rpm_enable_repo

Prototype: yum_rpm_enable_repo(repoid)

Description: Yum+RPM repo-specific installation method

Arguments:

  • repoid: the repository name as in yum --enablerepo=???

This package method interacts with the RPM package manager for a specific repo.

Based on yum_rpm() with addition to enable a repository for the install.

Sometimes repositories are configured but disabled by default. For example this pacakge_method could be used when installing a package that exists in the EPEL, which normally you do not want to install packages from.

Example:

packages:
    "mypackage" package_method => yum_rpm_enable_repo("myrepo"), package_policy => "add";

Implementation:

body package_method yum_rpm_enable_repo(repoid)
{
      package_changes => "bulk";
      package_list_command => "$(rpm_knowledge.call_rpm) -qa --qf '$(rpm_knowledge.rpm2_output_format)'";
      package_patch_list_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_offline_options) check-update $(redhat_knowledge.check_update_postproc)";

      package_list_name_regex    => "$(rpm_knowledge.rpm2_name_regex)";
      package_list_version_regex => "$(rpm_knowledge.rpm2_version_regex)";
      package_list_arch_regex    => "$(rpm_knowledge.rpm2_arch_regex)";

      package_installed_regex => ".*";
      package_name_convention => "$(name)";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_patch_name_regex    => "$(redhat_knowledge.patch_name_regex)";
      package_patch_version_regex => "$(redhat_knowledge.patch_version_regex)";
      package_patch_arch_regex    => "$(redhat_knowledge.patch_arch_regex)";

      package_add_command    => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) --enablerepo=$(repoid) -y install";
      package_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) --enablerepo=$(repoid) -y update";
      package_patch_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
      package_delete_command => "$(rpm_knowledge.call_rpm) -e --nodeps --allmatches";
      package_verify_command => "$(rpm_knowledge.call_rpm) -V";
      package_noverify_returncode => "1";
      package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
      package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
}
yum_group

Prototype: yum_group

Description: RPM direct installation method

Makes use of the "groups of packages" feature of Yum possible. (yum groupinstall, yum groupremove)

Groups must be specified by their groupids, available through yum grouplist -v (between parentheses). For example, below network-tools is the groupid.

$ yum grouplist -v|grep Networking|head -n 1
  Networking Tools (network-tools)

Example:

Policies examples:

-Install "web-server" group:

vars:
  "groups"  slist  => { "debugging", "php" };

packages:
  "$(groups)"
     package_policy   =>   "delete",
     package_method   =>   yum_group;

Implementation:

body package_method yum_group
{
      package_add_command             =>  "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) groupinstall -y";
      package_changes                 =>  "bulk";
      package_delete_command          =>  "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) groupremove -y";
      package_delete_convention       =>  "$(name)";
      package_installed_regex         =>  "^i.*";

      # Generate a dpkg -l like listing, "i" means installed, "a" available, and a dummy version 1
      package_list_command            =>
      "$(redhat_knowledge.call_yum) grouplist -v|awk '$0 ~ /^Done$/ {next} {sub(/.*\(/, \"\");sub(/\).*/, \"\")} /Available/ {h=\"a\";next} /Installed/ {h=\"i\";next} h==\"i\" || h==\"a\" {print h\" \"$0\" 1\"}'";

      package_list_name_regex         =>  "a|i ([^\s]+) 1";
      package_list_update_command     =>  "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed   =>  "$(common_knowledge.list_update_ifelapsed)";
      package_list_version_regex      =>  "(1)";
      package_name_convention         =>  "$(name)";
      package_name_regex              =>  "(.*)";
      package_noverify_returncode     =>  "0";
      package_update_command          =>  "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) groupupdate";

      # grep -x to only get full line matching
      package_verify_command          => "$(redhat_knowledge.call_yum) grouplist -v|awk '$0 ~ /^Done$/ {next} {sub(/.*\(/, \"\");sub(/\).*/, \"\")} /Available/ {h=\"a\";next} /Installed/ {h=\"i\";next} h==\"i\"|grep -qx";
}
rpm_filebased

Prototype: rpm_filebased(path)

Description: install packages from local filesystem-based RPM repository.

Arguments:

  • path: the path to the local package repository

Contributed by Aleksey Tsalolikhin. Written on 29-Feb-2012. Based on yum_rpm() body by Trond Hasle Amundsen.

Example:

packages:
    "epel-release"
    package_policy => "add",
    package_version => "5-4",
    package_architectures => { "noarch" },
    package_method => rpm_filebased("/repo/RPMs");

Implementation:

body package_method rpm_filebased(path)
{
      package_file_repositories => { "$(path)" };
      # the above is an addition to Trond's yum_rpm body

      package_add_command => "$(rpm_knowledge.call_rpm) -ihv ";
      # The above is a change from Trond's yum_rpm body, this makes the commands rpm only.
      # The reason I changed the install command from yum to rpm is yum will be default
      # refuse to install the epel-release RPM as it does not have the EPEL GPG key,
      # but rpm goes ahead and installs the epel-release RPM and the EPEL GPG key.

      package_name_convention => "$(name)-$(version).$(arch).rpm";
      # The above is a change from Tron's yum_rpm body. When package_file_repositories is in play,
      # package_name_convention has to match the file name, not the package name, per the
      # CFEngine 3 Reference Manual

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      # The rest is unchanged from Trond's yum_rpm body
      package_changes => "bulk";
      package_list_command => "$(rpm_knowledge.call_rpm) -qa --qf '$(rpm_knowledge.rpm2_output_format)'";

      package_list_name_regex    => "$(rpm_knowledge.rpm2_name_regex)";
      package_list_version_regex => "$(rpm_knowledge.rpm2_version_regex)";
      package_list_arch_regex    => "$(rpm_knowledge.rpm2_arch_regex)";

      package_installed_regex => ".*";

      package_delete_command => "$(rpm_knowledge.call_rpm) -e --allmatches";
      package_verify_command => "$(rpm_knowledge.call_rpm) -V";
      package_noverify_returncode => "1";
      package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
      package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
}
ips

Prototype: ips

Description: Image Package System method, used by OpenSolaris based systems (Solaris 11, Illumos, etc)

A note about Solaris 11.1 versioning format:

$ pkg list -v --no-refresh zsh
FMRI                                                                         IFO
pkg://solaris/shell/zsh@4.3.17,5.11-0.175.1.0.0.24.0:20120904T174236Z        i--
name--------- |<----->| |/________________________\|
version---------------- |\                        /|

Notice that the publisher and timestamp aren't used. And that the package version then must have the commas replaced by underscores.

Thus, 4.3.17,5.11-0.175.1.0.0.24.0 Becomes: 4.3.17_5.11-0.175.1.0.0.24.0

Therefore, a properly formatted package promise looks like this:

   "shell/zsh"
     package_policy  => "addupdate",
     package_method  => ips,
     package_select  => ">=",
     package_version => "4.3.17_5.11-0.175.1.0.0.24.0";

Implementation:

body package_method ips
{
      package_changes => "bulk";
      package_list_command => "$(paths.path[pkg]) list -v --no-refresh";
      package_list_name_regex    => "pkg://.+?(?<=/)([^\s]+)@.*$";
      package_list_version_regex => "[^\s]+@([^\s]+):.*";
      package_installed_regex => ".*(i..)"; # all reported are installed

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(paths.path[pkg]) refresh --full";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_add_command => "$(paths.path[pkg]) install --accept ";
      package_delete_command => "$(paths.path[pkg]) uninstall";
      package_update_command =>  "$(paths.path[pkg]) install --accept";
      package_patch_command =>  "$(paths.path[pkg]) install --accept";
      package_verify_command =>  "$(paths.path[pkg]) list -a -v --no-refresh";
      package_noverify_regex => "(.*---|pkg list: no packages matching .* installed)";
}
smartos

Prototype: smartos

Description: pkgin method for SmartOS (solaris 10 fork by Joyent)

Implementation:

body package_method smartos
{
      package_changes => "bulk";
      package_list_command => "/opt/local/bin/pkgin list";
      package_list_name_regex    => "([^\s]+)\-[0-9][^\s;]+.*[\s;]";
      package_list_version_regex => "[^\s]+\-([0-9][^\s;]+).*[\s;]";

      package_installed_regex => ".*"; # all reported are installed

      package_list_update_command => "/opt/local/bin/pkgin -y update";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_add_command => "/opt/local/bin/pkgin -y install";

      package_delete_command => "/opt/local/bin/pkgin -y remove";

      # pkgin update doesn't do what you think it does. pkgin install against and
      # already installed package will upgrade it however.

      package_update_command =>  "/opt/local/bin/pkgin -y install";
}
opencsw

Prototype: opencsw

Description: OpenCSW (Solaris software packages) method

Implementation:

body package_method opencsw
{
      package_changes => "bulk";
      package_list_command => "/opt/csw/bin/pkgutil -c";
      package_list_name_regex    => "CSW(.*?)\s.*";
      package_list_version_regex => ".*?\s+(.*),.*";

      package_installed_regex => ".*"; # all reported are installed

      package_list_update_command => "/opt/csw/bin/pkgutil -U";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_add_command => "/opt/csw/bin/pkgutil -yi";

      package_delete_command => "/opt/csw/bin/pkgutil -yr";
      package_update_command =>  "/opt/csw/bin/pkgutil -yu";
}
solaris

Prototype: solaris(pkgname, spoolfile, adminfile)

Description: Package method for old Solaris package system

Arguments:

  • pkgname: Not used
  • spoolfile: The spool file, located in /tmp
  • adminfile: The admin file, located in /tmp

The older solaris package system is poorly designed, with too many different names to track. See the example in tests/units/unit_package_solaris.cf to see how to use this.

Implementation:

body package_method solaris(pkgname, spoolfile, adminfile)
{
      package_changes => "individual";
      package_list_command => "$(solaris_knowledge.call_pkginfo) -l";
      package_multiline_start    =>  "\s*PKGINST:\s+[^\s]+.*";
      package_list_name_regex    => "\s*PKGINST:\s+([^\s]+).*";
      package_list_version_regex => "\s*VERSION:\s+([^\s]+).*";
      package_list_arch_regex    => "\s*ARCH:\s+([^\s]+)";
      package_installed_regex => "\s*STATUS:\s*(completely|partially)\s+installed.*";
      package_name_convention => "$(name)";
      package_add_command => "$(solaris_knowledge.call_pkgadd) -n -a /tmp/$(adminfile) -d /tmp/$(spoolfile)";
      package_delete_command => "$(solaris_knowledge.call_pkgrm) -n -a /tmp/$(adminfile)";
}
solaris_install

Prototype: solaris_install(adminfile)

Description: Package method for old Solaris package system

Arguments:

  • adminfile: The admin file created by create_solaris_admin_file

Implementation:

body package_method solaris_install(adminfile)
{
      package_changes => "individual";
      package_list_command => "$(solaris_knowledge.call_pkginfo) -l";
      package_multiline_start    =>  "\s*PKGINST:\s+[^\s]+.*";
      package_list_name_regex    => "\s*PKGINST:\s+([^\s]+).*";
      package_list_version_regex => "\s*VERSION:\s+([^\s]+).*";
      package_list_arch_regex    => "\s*ARCH:\s+([^\s]+)";
      package_installed_regex => "\s*STATUS:\s*(completely|partially)\s+installed.*";
      package_name_convention => "$(name)";
      package_add_command => "$(solaris_knowledge.call_pkgadd) -n -a $(adminfile)";
      package_delete_command => "$(solaris_knowledge.call_pkgrm) -n -a $(adminfile)";
}
freebsd

Prototype: freebsd

Description: FreeBSD pkg_add installation package method

This package method interacts with FreeBSD pkg_add to install from remote repositories.

Example: NOTE: Do not use this method on pkgng systems! It will appear to operate normally but is highly likely to break your package system.

This example installs "perl5" from a non-default repository:















Processes Bundles and Bodies

See the processes promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "processes.cf" }
}
agent bundles
process_kill

Prototype: process_kill(name)

Description: Kill a process by name (can be a regular expression)

Arguments:

  • name: the regular expression or string

Example:

methods:
     "kill" usebundle => process_kill("badprocess");

Implementation:

bundle agent process_kill(name)
{
  processes:
    !windows::
      # Signals are presented as an ordered list to the process.
      "$(name)" signals => { "term", "kill" };
    windows::
      # On Windows, only the kill signal is supported, which terminates the process.
      "$(name)" signals => { "kill" };
}
process_select bodies
exclude_procs

Prototype: exclude_procs(x)

Description: Select all processes excluding those matching x

Arguments:

  • x: Regular expression matching the command/cmd field of the processes that should be excluded

Implementation:

body process_select exclude_procs(x)
{
      command => "$(x)";
      process_result => "!command";
}
days_older_than

Prototype: days_older_than(d)

Description: Select all processes that are older than d days

Arguments:

  • d: Days that processes need to be old to be selected

Implementation:

body process_select days_older_than(d)
{
      stime_range    => irange(ago(0,0,"$(d)",0,0,0),now);
      process_result => "!stime";
}
by_owner

Prototype: by_owner(u)

Description: Select processes owned by user u

Arguments:

  • u: The name of the user

Matches processes against the given username and the given username's uid in case only uid is visible in process list.

Implementation:

body process_select by_owner(u)
{
      process_owner => { "$(u)", canonify(getuid("$(u)")) };
      process_result => "process_owner";
}
by_pid

Prototype: by_pid(pid)

Description: Select a process matching the given PID

Arguments:

  • pid: PID of the process to be matched

Implementation:

body process_select by_pid(pid)
{
      pid => irange("$(pid)","$(pid)");
      process_result => "pid";
}
process_count bodies
any_count

Prototype: any_count(cl)

Description: Define class cl if the process is running

Arguments:

  • cl: Name of the class to be defined

Implementation:

body process_count any_count(cl)
{
      match_range => "0,0";
      out_of_range_define => { "$(cl)" };
}
check_range

Prototype: check_range(name, lower, upper)

Description: Define a class if the number of processes is not within the specified range.

Arguments:

  • name: The name part of the class $(name)_out_of_range
  • lower: The lower bound of the range
  • upper: The upper bound of the range

Implementation:

body process_count check_range(name,lower,upper)
{
      match_range => irange("$(lower)","$(upper)");
      out_of_range_define => { "$(name)_out_of_range" };
}

Users Bundles and Bodies

See the users promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "users.cf" }
}
password bodies
plaintext_password

Prototype: plaintext_password(text)

Description: Sets the plaintext password for the user to text

Arguments:

  • text: the plain text version of the password

Note: Don't use that unless you really have no choice

See also: hashed_password()

Implementation:

body password plaintext_password(text)
{
    format => "plaintext";
    data => $(text);
}
hashed_password

Prototype: hashed_password(hash)

Description: Sets the hashed password for the user to hash

Arguments:

  • hash: the hashed representation of the password

The hashing method is up to the platform.

See also: plaintext_password()

Implementation:

body password hashed_password(hash)
{
    format => "hash";
    data => $(hash);
}

Services Bundles and Bodies

See the services promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies and bundles, add the following to your policy:

body file control
{
    inputs => { "services.cf" }
}
agent bundles
standard_services

Prototype: standard_services(service, state)

Description: Standard services bundle, used by CFEngine by default

Arguments:

  • service: Name of service to control
  • state: The desired state for that service: "start", "restart", "reload", "stop", or "disable"

This bundle is used by CFEngine if you don't specify a services handler explicitly, and will work with systemd or chkconfig or other non-sysvinit service managers. It will try to automate service discovery, unlike classic_services which requires known service names. If it can't do the automatic management, it will pass control to classic_services.

This bundle receives the service name and the desired service state, then does the needful to reach the desired state.

If you're running systemd, systemctl will be used.

Else, if chkconfig is present, it will be used.

Else, if the service command is available, if will be used.

Else, if the svcadm command is available, if will be used. Note you have to supply the full SMF service identifier.

Else, control is passed to classic_services.

Note you do not have to call this bundle from services promises. You can simply make a methods call to it. That would enable you to use systemd states like try-restart for instance.

Example:

services:
    "sshd" service_policy => "start"; # uses `standard_services`

methods:
    "" usebundle => standard_services("sshd", "start"); # direct

Implementation:

bundle agent standard_services(service,state)
{
  vars:
      "call_systemctl" string => "$(paths.systemctl) --no-ask-password --global --system";
      "systemd_properties" string => "-pLoadState,CanStop,UnitFileState,ActiveState,LoadState,CanStart,CanReload";
      "init" string => "/etc/init.d/$(service)";
      "c_service" string => canonify("$(service)");

    start|restart|reload::
      "chkconfig_mode" string => "on";
      "svcadm_mode" string => "enable";

    stop|disable::
      "chkconfig_mode" string => "off";
      "svcadm_mode" string => "disable";

    systemd::
      "systemd_service_info" slist => string_split(execresult("$(call_systemctl) $(systemd_properties) show $(service)", "noshell"), "\n", "10");

  classes:
      # define a class named after the desired state
      "$(state)" expression => "any";
      "non_disabling" or => { "start", "stop", "restart", "reload" };

      "chkconfig" expression => "!systemd._stdlib_path_exists_chkconfig";
      "sysvservice" expression => "!systemd.!chkconfig._stdlib_path_exists_service";
      "smf" expression => "!systemd.!chkconfig.!sysvservice._stdlib_path_exists_svcadm";
      "fallback" expression => "!systemd.!chkconfig.!sysvservice.!smf";

      "have_init" expression => fileexists($(init));

    chkconfig.have_init::
      "running" expression => returnszero("$(init) status > /dev/null", "useshell");

    chkconfig::
      "onboot"
      expression => returnszero("$(paths.chkconfig) $(service)", "noshell"),
      comment => "We need to know if the service is configured to start at boot or not";

      # We redirect stderr and stdout to dev null so that we do not create noise in the logs
      "chkconfig_$(c_service)_unregistered"
      not => returnszero("$(paths.chkconfig) --list $(service) &> /dev/null", "useshell"),
      comment => "We need to know if the service is registered with chkconfig so that we can
      perform other chkconfig operations, if the service is not registered it must be added.
      Note we do not automatically try to add the service at this time.";

### BEGIN ###
# @brief probe the state of a systemd service
# @author Bryan Burke
#
# A collection of classes to determine the capabilities of a given systemd
# service, then start, stop, etc. the service. Also supports a custom action
# for anything not supported
#
    systemd::
      "service_enabled" expression => reglist(@(systemd_service_info), "UnitFileState=enabled");
      "service_active"  expression => reglist(@(systemd_service_info), "ActiveState=active");
      "service_loaded"  expression => reglist(@(systemd_service_info), "LoadState=loaded");
      "service_notfound" expression => reglist(@(systemd_service_info), "LoadState=not-found");

      "can_stop_service"   expression => reglist(@(systemd_service_info), "CanStop=yes");
      "can_start_service"  expression => reglist(@(systemd_service_info), "CanStart=yes");
      "can_reload_service" expression => reglist(@(systemd_service_info), "CanReload=yes");

      "request_start"   expression => strcmp("start", "$(state)");
      "request_stop"    expression => strcmp("stop", "$(state)");
      "request_reload"  expression => strcmp("reload", "$(state)");
      "request_restart" expression => strcmp("restart", "$(state)");

      "action_custom"  expression => "!(request_start|request_stop|request_reload|request_restart)";
      "action_start"   expression => "request_start.!service_active.can_start_service";
      "action_stop"    expression => "request_stop.service_active.can_stop_service";
      "action_reload"  expression => "request_reload.service_active.can_reload_service";
      "action_restart"         or => {
                                      "request_restart.service_active",

                                      # Possibly undesirable... if a reload is
                                      # requested, and the service "can't" be
                                      # reloaded, then we restart it instead.
                                      "request_reload.!can_reload_service.service_active",
                                     };

      # Starting a service implicitly enables it
      "action_enable"  expression => "request_start.!service_enabled";

      # Respectively, stopping it implicitly disables it
      "action_disable" expression => "request_stop.service_enabled";

  commands:
    systemd.service_loaded:: # note this class is defined in `inventory/linux.cf`
      # conveniently, systemd states map to `services` states, except
      # for `enable`

      "$(call_systemctl) -q start $(service)"
        ifvarclass => "action_start";

      "$(call_systemctl) -q stop $(service)"
        ifvarclass => "action_stop";

      "$(call_systemctl) -q reload $(service)"
        ifvarclass => "action_reload";

      "$(call_systemctl) -q restart $(service)"
        ifvarclass => "action_restart";

      "$(call_systemctl) -q enable $(service)"
        ifvarclass => "action_enable";

      "$(call_systemctl) -q disable $(service)"
        ifvarclass => "action_disable";

      # Custom action for any of the non-standard systemd actions such a
      # status, try-restart, isolate, et al.
      "$(call_systemctl) $(state) $(service)"
        ifvarclass => "action_custom";

### END systemd section ###

    chkconfig.stop.onboot::
      # Only chkconfig disable if it's currently set to start on boot
      "$(paths.chkconfig) $(service) $(chkconfig_mode)"
      classes => kept_successful_command,
      contain => silent;

    chkconfig.start.!onboot::
      # Only chkconfig enable service if it's not already set to start on boot, and if its a registered chkconfig service
      "$(paths.chkconfig) $(service) $(chkconfig_mode)"
      ifvarclass => "!chkconfig_$(c_service)_unregistered",
      classes => kept_successful_command,
      contain => silent;

    chkconfig.have_init.(((start|restart).!running)|((stop|restart|reload).running)).non_disabling::
      "$(init) $(state)"
      contain => silent;

    sysvservice.non_disabling::
      "$(paths.service) $(service) $(state)"
      classes => kept_successful_command;

    sysvservice.disable::
      "$(paths.service) $(service) stop"
      classes => kept_successful_command;

    smf::
      "$(paths.svcadm) $(svcadm_mode) $(service)"
      classes => kept_successful_command;

  methods:
    fallback::
      "classic" usebundle => classic_services($(service), $(state));

  reports:
    inform_mode.systemd::
      "$(this.bundle): using systemd layer to $(state) $(service)";
    inform_mode.systemd.!service_loaded::
      "$(this.bundle): Service $(service) unit file is not loaded; doing nothing";
    inform_mode.chkconfig::
      "$(this.bundle): using chkconfig layer to $(state) $(service) (chkconfig mode $(chkconfig_mode))"
        ifvarclass => "!chkconfig_$(c_service)_unregistered.((start.!onboot)|(stop.onboot))";
    inform_mode.chkconfig::
      "$(this.bundle): skipping chkconfig layer to $(state) $(service) because $(service) is not registered with chkconfig (chkconfig --list $(service))"
        ifvarclass => "chkconfig_$(c_service)_unregistered";
    inform_mode.sysvservice::
      "$(this.bundle): using System V service / Upstart layer to $(state) $(service)";
    inform_mode.smf::
      "$(this.bundle): using Solaris SMF to $(state) $(service) (svcadm mode $(svcadm_mode))";
    inform_mode.fallback::
      "$(this.bundle): falling back to classic_services to $(state) $(service)";

    systemd.service_notfound::
        "$(this.bundle): Could not find service: $(service)";
}
classic_services

Prototype: classic_services(service, state)

Description: Classic services bundle

Arguments:

  • service: specific service to control
  • state: desired state for that service

This bundle is used by standard_services if it doesn't have an automatic driver for the current service manager.

It receives the service name and the desired service state, then does the needful to reach the desired state.

Example:

services:
    "ntp" service_policy => "start";
    "ssh" service_policy => "stop";

There's multiple ways you can add new services to this list. Here's few examples:

a) The zeroconf mode; If the new service matches these rules, you don't need to add anything to the standard_services:

  1. Your init script basename = $(service)
  2. Your init script argument = $(state)
  3. Your init script lives in /etc/init.d/ (for non-*bsd), or /etc/rc.d/ (for *bsd)
  4. Your process regex pattern = \b$(service)\b
  5. You call the init as /etc/init.d/<script> <arg> (for non-*bsd), or /etc/rc.d/<script> <arg> (for *bsd)

b) If the 1st rule doesn't match, but rest does:

Use the baseinit[$(service)] array to point towards your init script's basename. For example:

   "baseinit[www]" string => "httpd";

This would fire up init script /etc/init.d/httpd, instead of the default /etc/init.d/www. From /etc/rc.d/ if you're on *bsd system.

c) If the 4th rule doesn't match, but rest does:

Use the pattern[$(service)] array to specify your own regex match. It's advisable to use conservative regex so there's less chance of getting a mismatch.

   "pattern[www]" string => ".*httpd.*";

Instead of matching the default '\bwww\b', this now matches your given string,

d) 5th rule doesn't match:

If you can specify the init system used. Currently supported: sysvinitd, sysvservice, systemd

    "init[www]" string => "sysvservice";
    "init[www]" string => "sysvinitd";
    "init[www]" string => "systemd";

^ The above is not a valid syntax as you can only use one init[] per service, but it shows all the currently supported ones.

    "sysvservice" == /(usr/)?sbin/service
    "sysvinitd"   == /etc/init.d/ (non-*bsd) | /etc/rc.d/ (*bsd)
    "systemd"     == /bin/systemctl

e) 2nd and 3rd rule matches, but rest doesn't:

Use a combination of the pattern[], baseinit[] and init[], to fill your need.

    "baseinit[www]" string => "httpd";
    "pattern[www]"  string => ".*httpd.*";
    "init[www]"     string => "sysvservice";

f) As a fallback, if none of the above rules match, you can also define exactly what you need for each $(state).

   "startcommand[rhnsd]"   string => "/sbin/service rhnsd start";
   "restartcommand[rhnsd]" string => "/sbin/service rhnsd restart";
   "reloadcommand[rhnsd]"  string => "/sbin/service rhnsd reload";
   "stopcommand[rhnsd]"    string => "/sbin/service rhnsd stop";
   "pattern[rhnsd]"        string => "rhnsd";

If any of the (re)?(start|load|stop)command variables are set for your service, they take priority in case there's conflict of intent with other data.

Say you'd have the following service definition:

   "startcommand[qwerty]"   string => "/sbin/service qwerty start";
   "stopcommand[qwerty]"    string => "/sbin/service qwerty stop";
   "pattern[qwerty]"        string => ".*qwerty.*";
   "baseinit[qwerty]"       string => "asdfgh"
   "init[qwerty]"           string => "systemd";

There's a conflict of intent now. As the ~command definitions takes priority, this kind of service config for qwerty would execute the following commands:

  start:   "/sbin/service qwerty start"
  stop:    "/sbin/service qwerty stop"
  restart: "/bin/systemctl asdfgh restart"
  reload:  "/bin/systemctl asdfgh reload"

Implementation:

bundle agent classic_services(service,state)
{
  vars:
      "all_states" slist => { "start", "restart", "reload", "stop", "disable" };

      "inits" slist => { "sysvinitd", "sysvservice", "systemd", "chkconfig" },
      comment => "Currently handled init systems";

      "default[prefix][sysvservice]" string => "$(paths.service) ",
      comment => "Command for sysv service interactions";

      "default[prefix][systemd]" string => "$(paths.systemctl) ",
      comment => "Command for systemd interactions";

      "default[prefix][sysvinitd]" string => ifelse("openbsd", "/etc/rc.d/",
                                                    "freebsd", "/etc/rc.d/",
                                                    "netbsd", "/etc/rc.d/",
                                                    "/etc/init.d/"),
      comment => "Command prefix for sysv init script interactions";

      "default[prefix][chkconfig]" string => "$(default[prefix][sysvinitd])",
      comment => "Command prefix for chkconfig init script interactions";

      "default[cmd][$(inits)]" string => "$(default[prefix][$(inits)])$(service) $(state)",
      comment => "Default command to control the service";

      "default[pattern]" string => "\b$(service)\b",
      comment => "Set default pattern for proc matching";

    _stdlib_path_exists_chkconfig::
      "default[init]" string => "chkconfig",
      comment => "Use chkconfig as the default init system if one isn't defined";

    !_stdlib_path_exists_chkconfig::
      "default[init]" string => "sysvinitd",
      comment => "Use sysvinitd as the default init system if one isn't defined";

    no_inits_set::
      "init_system" string => "$(default[init])";

    any::
      "init_system" string => "$(init[$(service)])",
      ifvarclass => "$(inits_set)";

    start|restart|reload::
      "chkconfig_mode" string => "on";

    stop|disable::
      "chkconfig_mode" string => "off";

    any::
      "stakeholders[cfengine3]" slist => { "cfengine_in" };
      "stakeholders[acpid]" slist => { "cpu", "cpu0", "cpu1", "cpu2", "cpu3" };
      "stakeholders[postfix]" slist => { "smtp_in" };
      "stakeholders[sendmail]" slist => { "smtp_in" };
      "stakeholders[www]" slist => { "www_in", "wwws_in", "www_alt_in" };
      "stakeholders[ssh]" slist => { "ssh_in" };
      "stakeholders[mysql]" slist => { "mysql_in" };
      "stakeholders[nfs]" slist => { "nfsd_in" };
      "stakeholders[syslog]" slist => { "syslog" };
      "stakeholders[rsyslog]" slist => { "syslog" };
      "stakeholders[tomcat5]" slist => { "www_alt_in" };
      "stakeholders[tomcat6]" slist => { "www_alt_in" };

    linux::

      "pattern[acpid]"            string => ".*acpid.*";
      "pattern[cfengine3]"        string => ".*cf-execd.*";
      "pattern[fancontrol]"       string => ".*fancontrol.*";
      "pattern[hddtemp]"          string => ".*hddtemp.*";
      "pattern[irqbalance]"       string => ".*irqbalance.*";
      "pattern[lm-sensor]"        string => ".*psensor.*";
      "pattern[openvpn]"          string => ".*openvpn.*";
      "pattern[postfix]"          string => ".*postfix.*";
      "pattern[rsync]"            string => ".*rsync.*";
      "pattern[rsyslog]"          string => ".*rsyslogd.*";
      "pattern[sendmail]"         string => ".*sendmail.*";
      "pattern[tomcat5]"          string => ".*tomcat5.*";
      "pattern[tomcat6]"          string => ".*tomcat6.*";
      "pattern[varnish]"          string => ".*varnish.*";
      "pattern[wpa_supplicant]"   string => ".*wpa_supplicant.*";

    suse::

      "baseinit[mysql]"           string => "mysqld";
      "pattern[mysql]"            string => ".*mysqld.*";

      "baseinit[www]"             string => "apache2";
      "pattern[www]"              string => ".*apache2.*";

      "baseinit[ssh]"             string => "sshd";
      # filter out "sshd: ..." children
      "pattern[ssh]"              string => ".*\Ssshd.*";

      "pattern[ntpd]"             string => ".*ntpd.*";

    redhat::

      "pattern[anacron]"          string => ".*anacron.*";
      "pattern[atd]"              string => ".*sbin/atd.*";
      "pattern[auditd]"           string => ".*auditd$";
      "pattern[autofs]"           string => ".*automount.*";
      "pattern[capi]"             string => ".*capiinit.*";
      "pattern[conman]"           string => ".*conmand.*";
      "pattern[cpuspeed]"         string => ".*cpuspeed.*";
      "pattern[crond]"            string => ".*crond.*";
      "pattern[dc_client]"        string => ".*dc_client.*";
      "pattern[dc_server]"        string => ".*dc_server.*";
      "pattern[dnsmasq]"          string => ".*dnsmasq.*";
      "pattern[dund]"             string => ".*dund.*";
      "pattern[gpm]"              string => ".*gpm.*";
      "pattern[haldaemon]"        string => ".*hald.*";
      "pattern[hidd]"             string => ".*hidd.*";
      "pattern[irda]"             string => ".*irattach.*";
      "pattern[iscsid]"           string => ".*iscsid.*";
      "pattern[isdn]"             string => ".*isdnlog.*";
      "pattern[lvm2-monitor]"     string => ".*vgchange.*";
      "pattern[mcstrans]"         string => ".*mcstransd.*";
      "pattern[mdmonitor]"        string => ".*mdadm.*";
      "pattern[mdmpd]"            string => ".*mdmpd.*";
      "pattern[messagebus]"       string => ".*dbus-daemon.*";
      "pattern[microcode_ctl]"    string => ".*microcode_ctl.*";
      "pattern[multipathd]"       string => ".*multipathd.*";
      "pattern[netplugd]"         string => ".*netplugd.*";
      "pattern[NetworkManager]"   string => ".*NetworkManager.*";
      "pattern[nfs]"              string => ".*nfsd.*";
      "pattern[nfslock]"          string => ".*rpc.statd.*";
      "pattern[nscd]"             string => ".*nscd.*";
      "pattern[ntpd]"             string => ".*ntpd.*";
      "pattern[oddjobd]"          string => ".*oddjobd.*";
      "pattern[pand]"             string => ".*pand.*";
      "pattern[pcscd]"            string => ".*pcscd.*";
      "pattern[portmap]"          string => ".*portmap.*";
      "pattern[postgresql]"       string => ".*postmaster.*";
      "pattern[rdisc]"            string => ".*rdisc.*";
      "pattern[readahead_early]"  string => ".*readahead.*early.*";
      "pattern[readahead_later]"  string => ".*readahead.*later.*";
      "pattern[restorecond]"      string => ".*restorecond.*";
      "pattern[rpcgssd]"          string => ".*rpc.gssd.*";
      "pattern[rpcidmapd]"        string => ".*rpc.idmapd.*";
      "pattern[rpcsvcgssd]"       string => ".*rpc.svcgssd.*";
      "pattern[saslauthd]"        string => ".*saslauthd.*";
      "pattern[smartd]"           string => ".*smartd.*";
      "pattern[svnserve]"         string => ".*svnserve.*";
      "pattern[syslog]"           string => ".*syslogd.*";
      "pattern[tcsd]"             string => ".*tcsd.*";
      "pattern[xfs]"              string => ".*xfs.*";
      "pattern[ypbind]"           string => ".*ypbind.*";
      "pattern[yum-updatesd]"     string => ".*yum-updatesd.*";
      "pattern[munin-node]"       string => ".*munin-node.*";

      "baseinit[bluetoothd]"      string => "bluetooth";
      "pattern[bluetoothd]"       string => ".*hcid.*";

      "baseinit[mysql]"           string => "mysqld";
      "pattern[mysql]"            string => ".*mysqld.*";

      "baseinit[www]"             string => "httpd";
      "pattern[www]"              string => ".*httpd.*";

      "baseinit[ssh]"             string => "sshd";
      # filter out "sshd: ..." children
      "pattern[ssh]"              string => ".*\Ssshd.*";

      "init[rhnsd]"               string => "sysvservice";
      "pattern[rhnsd]"            string => "rhnsd";

      "baseinit[snmpd]"           string => "snmpd";
      "pattern[snmpd]"            string => "/usr/sbin/snmpd";

    debian|ubuntu::

      "pattern[atd]"              string => "atd.*";
      "pattern[bluetoothd]"       string => ".*bluetoothd.*";
      "pattern[bootlogd]"         string => ".*bootlogd.*";
      "pattern[crond]"            string => ".*cron.*";
      "pattern[kerneloops]"       string => ".*kerneloops.*";
      "pattern[mysql]"            string => ".*mysqld.*";
      "pattern[NetworkManager]"   string => ".*NetworkManager.*";
      "pattern[ondemand]"         string => ".*ondemand.*";
      "pattern[plymouth]"         string => ".*plymouthd.*";
      "pattern[saned]"            string => ".*saned.*";
      "pattern[udev]"             string => ".*udev.*";
      "pattern[udevmonitor]"      string => ".*udevadm.*monitor.*";
      "pattern[snmpd]"            string => "/usr/sbin/snmpd";
      "pattern[pgbouncer]"        string => ".*pgbouncer.*";
      "pattern[supervisor]"       string => ".*supervisord.*";
      "pattern[munin-node]"       string => ".*munin-node.*";
      "pattern[carbon-cache]"     string => ".*carbon-cache.*";
      "pattern[cassandra]"        string => ".*jsvc\.exec.*apache-cassandra\.jar.*";
      # filter out "sshd: ..." children
      "pattern[ssh]"              string => ".*\Ssshd.*";

      "baseinit[ntpd]"            string => "ntp";
      "pattern[ntpd]"             string => ".*ntpd.*";

      "baseinit[postgresql84]"    string => "postgresql-8.4";
      "pattern[postgresql84]"     string => ".*postgresql.*";

      "baseinit[postgresql91]"    string => "postgresql-9.1";
      "pattern[postgresql91]"     string => ".*postgresql.*";

      "baseinit[www]"             string => "apache2";
      "pattern[www]"              string => ".*apache2.*";

      "baseinit[nrpe]"            string => "nagios-nrpe-server";
      "pattern[nrpe]"             string => ".*nrpe.*";

      "baseinit[omsa-dataeng]"    string => "dataeng";
      "pattern[omsa-dataeng]"     string => ".*dsm_sa_datamgr.*";

    freebsd::

      "pattern[ntpd]"       string => ".*ntpd.*";

      "baseinit[ssh]"       string => "sshd";
      "pattern[ssh]"        string => "/usr/sbin/sshd.*";

      "baseinit[syslog]"    string => "syslogd";
      "pattern[syslog]"     string => "/usr/sbin/syslogd.*";

      "baseinit[crond]"     string => "cron";
      "pattern[crond]"      string => "/usr/sbin/cron.*";

      "baseinit[snmpd]"     string => "bsnmpd";
      "pattern[snmpd]"      string => "/usr/sbin/bsnmpd.*";

      "pattern[newsyslog]"  string => "/usr/sbin/newsyslog.*";

  classes:
      # Set classes for each possible state after $(all_states)
      "$(all_states)" expression => strcmp($(all_states), $(state)),
      comment => "Set a class named after the desired state";

      "$(inits)_set" expression => strcmp("$(init[$(service)])","$(inits)"),
      comment => "Check if init system is specified";
      "no_inits_set" not => isvariable("init[$(service)]"),
      comment => "Check if no init system is specified";

      # define a class to tell us what init system we're using
      "using_$(init_system)" expression => "any";

  commands:
    using_chkconfig::
      "$(paths.chkconfig) $(service) $(chkconfig_mode)"
      classes => kept_successful_command;

  processes:

    start::

      "$(pattern[$(service)])" ->  { "@(stakeholders[$(service)])" }

      comment => "Verify that the service appears in the process table",
      restart_class => "start_$(service)",
      ifvarclass => and(isvariable("pattern[$(service)]"));

      "$(default[pattern])" ->  { "@(stakeholders[$(service)])" }

      comment => "Verify that the service appears in the process table",
      restart_class => "start_$(service)",
      ifvarclass => not(isvariable("pattern[$(service)]"));

    stop|disable::

      "$(pattern[$(service)])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(stopcommand[$(service)])",
      signals => { "term", "kill"},
      ifvarclass => and(isvariable("stopcommand[$(service)]"),
                        isvariable("pattern[$(service)]"));

      "$(default[pattern])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(stopcommand[$(service)])",
      signals => { "term", "kill"},
      ifvarclass => and(isvariable("stopcommand[$(service)]"),
                        not(isvariable("pattern[$(service)]")));

      "$(pattern[$(service)])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[prefix][$(default[init])])$(baseinit[$(service)]) $(state)",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        isvariable("pattern[$(service)]"),
                        "no_inits_set");

      "$(pattern[$(service)])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[prefix][$(inits)])$(baseinit[$(service)]) $(state)",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        isvariable("pattern[$(service)]"),
                        canonify("$(inits)_set"));

##
      "$(default[pattern])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[prefix][$(default[init])])$(baseinit[$(service)]) $(state)",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        not(isvariable("pattern[$(service)]")),
                        "no_inits_set");

      "$(default[pattern])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[prefix][$(inits)])$(baseinit[$(service)]) $(state)",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        not(isvariable("pattern[$(service)]")),
                        canonify("$(inits)_set"));

##
      "$(pattern[$(service)])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[cmd][$(default[init])])",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        isvariable("pattern[$(service)]"),
                        "no_inits_set");

      "$(pattern[$(service)])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[cmd][$(inits)])",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        isvariable("pattern[$(service)]"),
                        canonify("$(inits)_set"));

##
      "$(default[pattern])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[cmd][$(default[init])])",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        not(isvariable("pattern[$(service)]")),
                        "no_inits_set");

      "$(default[pattern])" -> { "@(stakeholders[$(service)])" }

      comment => "Verify that the service does not appear in the process",
      process_stop => "$(default[cmd][$(inits)])",
      signals => { "term", "kill"},
      ifvarclass => and(not(isvariable("stopcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        not(isvariable("pattern[$(service)]")),
                        canonify("$(inits)_set"));

  commands:

      "$(startcommand[$(service)])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute command to start the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(isvariable("startcommand[$(service)]"),
                        canonify("start_$(service)"));
##
      "$(default[prefix][$(default[init])])$(baseinit[$(service)]) $(state)" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (baseinit init) command to start the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("startcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        canonify("start_$(service)"),
                        "no_inits_set");

      "$(default[prefix][$(inits)])$(baseinit[$(service)]) $(state)" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (baseinit init) command to start the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("startcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        canonify("start_$(service)"),
                        canonify("$(inits)_set"));
##
      "$(default[cmd][$(default[init])])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (default) command to start the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("startcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        canonify("start_$(service)"),
                        "no_inits_set");

      "$(default[cmd][$(inits)])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (default) command to start the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("startcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        canonify("start_$(service)"),
                        canonify("$(inits)_set"));

    restart::
      "$(restartcommand[$(service)])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute command to restart the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(isvariable("restartcommand[$(service)]"));
##

      "$(default[prefix][$(default[init])])$(baseinit[$(service)]) $(state)" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (baseinit init) command to restart the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("restartcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        "no_inits_set");

      "$(default[prefix][$(inits)])$(baseinit[$(service)]) $(state)" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (baseinit init) command to restart the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("restartcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        canonify("$(inits)_set"));
##
      "$(default[cmd][$(default[init])])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (default) command to restart the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("restartcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        "no_inits_set");

      "$(default[cmd][$(inits)])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (default) command to restart the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("restartcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        canonify("$(inits)_set"));

    reload::
      "$(reloadcommand[$(service)])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute command to reload the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(isvariable("reloadcommand[$(service)]"));
##
      "$(default[prefix][$(default[init])])$(baseinit[$(service)]) $(state)" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (baseinit init) command to reload the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("reloadcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        "no_inits_set");

      "$(default[prefix][$(inits)])$(baseinit[$(service)]) $(state)" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (baseinit init) command to reload the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("reloadcommand[$(service)]")),
                        isvariable("baseinit[$(service)]"),
                        canonify("$(inits)_set"));
##
      "$(default[cmd][$(default[init])])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (default) command to reload the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("reloadcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        "no_inits_set");

      "$(default[cmd][$(inits)])" -> { "@(stakeholders[$(service)])" }
      comment => "Execute (default) command to reload the $(service) service",
      classes => kept_successful_command,
      ifvarclass => and(not(isvariable("reloadcommand[$(service)]")),
                        not(isvariable("baseinit[$(service)]")),
                        canonify("$(inits)_set"));

  reports:
    inform_mode::
      "$(this.bundle): Using init system $(inits)"
      ifvarclass => and(canonify("$(inits)_set"));

      "$(this.bundle): No init system is set, using $(default[init])"
      ifvarclass => "no_inits_set";

      "$(this.bundle): The service $(service) needs to be started"
      ifvarclass => and(canonify("start_$(service)"));

      "$(this.bundle): The service pattern is provided: $(pattern[$(service)])"
      ifvarclass => and(isvariable("pattern[$(service)]"));

      "$(this.bundle): The default service pattern was used: $(default[pattern])"
      ifvarclass => not(isvariable("pattern[$(service)]"));

      "$(this.bundle): The stopcommand is provided: $(stopcommand[$(service)])"
      ifvarclass => and(isvariable("stopcommand[$(service)]"));

      "$(this.bundle): The stopcommand is NOT provided, using default"
      ifvarclass => not(isvariable("stopcommand[$(service)]"));

      "$(this.bundle): The startcommand is provided: $(startcommand[$(service)])"
      ifvarclass => and(isvariable("startcommand[$(service)]"));

      "$(this.bundle): The startcommand is NOT provided, using default"
      ifvarclass => not(isvariable("startcommand[$(service)]"));

      "$(this.bundle): The restartcommand is provided: $(restartcommand[$(service)])"
      ifvarclass => and(isvariable("restartcommand[$(service)]"));

      "$(this.bundle): The restartcommand is NOT provided, using default"
      ifvarclass => not(isvariable("restartcommand[$(service)]"));

      "$(this.bundle): The reloadcommand is provided: $(reloadcommand[$(service)])"
      ifvarclass => and(isvariable("reloadcommand[$(service)]"));

      "$(this.bundle): The reloadcommand is NOT provided, using default"
      ifvarclass => not(isvariable("reloadcommand[$(service)]"));

      "$(this.bundle): The baseinit is provided: $(baseinit[$(service)])"
      ifvarclass => and(isvariable("baseinit[$(service)]"));

      "$(this.bundle): The baseinit is NOT provided, using default"
      ifvarclass => not(isvariable("baseinit[$(service)]"));
}
service_method bodies
bootstart

Prototype: bootstart

Description: Start the service and all its dependencies at boot time

See also: service_autostart_policy, service_dependence_chain

Implementation:

body service_method bootstart
{
      service_autostart_policy => "boot_time";
      service_dependence_chain => "start_parent_services";
    windows::
      service_type => "windows";
}
force_deps

Prototype: force_deps

Description: Start all dependendencies when this service starts, and stop all dependent services when this service stops.

The service does not get automatically started.

See also: service_autostart_policy, service_dependence_chain

Implementation:

body service_method force_deps
{
      service_dependence_chain => "all_related";
    windows::
      service_type => "windows";
}

Storage Bundles and Bodies

See the storage promises documentation for a comprehensive reference on the body types and attributes used here.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "storage.cf" }
}
volume bodies
min_free_space

Prototype: min_free_space(free)

Description: Warn if the storage doesn't have at least free free space.

A warnings is also generated if the storage is smaller than 10K or as less than 2 file entries.

Arguments:

  • free: Absolute or percentage minimum disk space that should be available before warning

Implementation:

body volume min_free_space(free)
{
      check_foreign  => "false";
      freespace      => "$(free)";
      sensible_size  => "10000";
      sensible_count => "2";
}
mount bodies
nfs

Prototype: nfs(server, source)

Description: Mounts the storage at source on server via nfs protocol.

Also modifies the file system table.

Arguments:

  • server: Hostname or IP of remote server
  • source: Path of remote file system to mount

See also: nfs_p(), unmount()

Implementation:

body mount nfs(server,source)
{
      mount_type => "nfs";
      mount_source => "$(source)";
      mount_server => "$(server)";
      edit_fstab => "true";
}
nfs_p

Prototype: nfs_p(server, source, perm)

Description: Mounts the storage via nfs, with perm passed as options to mount.

Also modifies the file system table.

Arguments:

  • server: Hostname or IP of remote server
  • source: Path of remote file system to mount
  • perm: A string of options that's passed to the mount command

See also: nfs, unmount()

Implementation:

body mount nfs_p(server,source,perm)
{
      mount_type => "nfs";
      mount_source => "$(source)";
      mount_server => "$(server)";
      mount_options => {"$(perm)"};
      edit_fstab => "true";
}
unmount

Prototype: unmount

Description: Unmounts the nfs storage.

Also modifies the file system table.

See also: nfs(), nfs_p()

Implementation:

body mount unmount
{
      mount_type => "nfs";
      edit_fstab => "true";
      unmount => "true";
}

Version Control Bodies and Bundles

The vcs.cf library provides bundles for working with version control tools.

To use these bodies, add the following to your policy:

body file control
{
    inputs => { "vcs.cf" }
}
agent bundles
git_init

Prototype: git_init(repo_path)

Description: initializes a new git repository if it does not already exist

Arguments:

  • repo_path: absolute path of where to initialize a git repository

Example:

bundle agent my_git_repositories
{
  vars:
    "basedir"  string => "/var/git";
    "repos"    slist  => { "myrepo", "myproject", "myPlugForMoreHaskell" };

  files:
    "$(basedir)/$(repos)/."
      create => "true";

  methods:
    "git_init" usebundle => git_init("$(basedir)/$(repos)");
}

Implementation:

bundle agent git_init(repo_path)
{
  classes:
    "ok_norepo" not => fileexists("$(repo_path)/.git");

  methods:
    ok_norepo::
      "git_init"  usebundle => git("$(repo_path)", "init", "");
}
git_add

Prototype: git_add(repo_path, file)

Description: adds files to the supplied repository's index

Arguments:

  • repo_path: absolute path to a git repository
  • file: a file to stage in the index

Example:

bundle agent add_files_to_git_index
{
  vars:
    "repo"  string => "/var/git/myrepo";
    "files" slist  => { "fileA", "fileB", "fileC" };

  methods:
    "git_add" usebundle => git_add("$(repo)", "$(files)");
}

Implementation:

bundle agent git_add(repo_path, file)
{
  classes:
    "ok_repo" expression => fileexists("$(repo_path)/.git");

  methods:
    ok_repo::
      "git_add" usebundle => git("$(repo_path)", "add", "$(file)");
}
git_checkout

Prototype: git_checkout(repo_path, branch)

Description: checks out an existing branch in the supplied git repository

Arguments:

  • repo_path: absolute path to a git repository
  • branch: the name of an existing git branch to checkout

Example:

bundle agent git_checkout_some_existing_branch
{
  vars:
    "repo"   string => "/var/git/myrepo";
    "branch" string => "dev/some-topic-branch";

  methods:
    "git_checkout" usebundle => git_checkout("$(repo)", "$(branch)");
}

Implementation:

bundle agent git_checkout(repo_path, branch)
{
  classes:
    "ok_repo" expression => fileexists("$(repo_path)/.git");

  methods:
    ok_repo::
      "git_checkout" usebundle => git("$(repo_path)", "checkout", "$(branch)");
}
git_checkout_new_branch

Prototype: git_checkout_new_branch(repo_path, new_branch)

Description: checks out and creates a new branch in the supplied git repository

Arguments:

  • repo_path: absolute path to a git repository
  • new_branch: the name of the git branch to create and checkout

Example:

bundle agent git_checkout_new_branches
{
  vars:
    "repo[myrepo]"    string => "/var/git/myrepo";
    "branch[myrepo]"  string => "dev/some-new-topic-branch";

    "repo[myproject]"   string => "/var/git/myproject";
    "branch[myproject]" string => "dev/another-new-topic-branch";

    "repo_names"        slist => getindices("repo");

  methods:
    "git_checkout_new_branch" usebundle => git_checkout_new_branch("$(repo[$(repo_names)])", "$(branch[$(repo_names)])");
}

Implementation:

bundle agent git_checkout_new_branch(repo_path, new_branch)
{
  classes:
    "ok_repo" expression => fileexists("$(repo_path)/.git");

  methods:
    ok_repo::
      "git_checkout" usebundle => git("$(repo_path)", "checkout -b", "$(branch)");
}
git_clean

Prototype: git_clean(repo_path)

Description: Ensure that a given git repo is clean

Arguments:

  • repo_path: Path to the clone

Example:

 methods:
   "test"
     usebundle => git_clean("/opt/cfengine/masterfiles_staging_tmp"),
     comment => "Ensure that the staging area is a clean clone";

Implementation:

bundle agent git_clean(repo_path)
{
  methods:
      "" usebundle => git("$(repo_path)", "clean", ' --force -d'),
      comment => "To have a clean clone we must remove any untracked files and
                  directories. These should have all been stashed, but in case
                  of error we go ahead and clean anyway.";
}
git_stash

Prototype: git_stash(repo_path, stash_name)

Description: Stash any changes (including untracked files) in repo_path

Arguments:

  • repo_path: Path to the clone
  • stash_name: Stash name

Example:

 methods:
   "test"
     usebundle => git_stash("/opt/cfengine/masterfiles_staging_tmp", "temp"),
     comment => "Stash any changes, including untracked files";

Implementation:

bundle agent git_stash(repo_path, stash_name)
{
  methods:
      "" usebundle => git($(repo_path), "stash", 'save --quiet --include-untracked "$(stash_name)"'),
      comment => "So that we don't lose any trail of what happend and so that
                    we don't accidentally delete something important we stash any
                    changes.
  Note:
                      1. This promise will fail if user.email is not set
                      2. We are respecting ignored files.";
}
git_stash_and_clean

Prototype: git_stash_and_clean(repo_path)

Description: Ensure that a given git repo is clean and attempt to save any modifications

Arguments:

  • repo_path: Path to the clone

Example:

 methods:
   "test"
     usebundle => git_stash_and_clean("/opt/cfengine/masterfiles_staging_tmp"),
     comment => "Ensure that the staging area is a clean clone after attempting to stash any changes";

Implementation:

bundle agent git_stash_and_clean(repo_path)
{
  vars:
      "stash" string => "CFEngine AUTOSTASH: $(sys.date)";

  methods:
      "" usebundle => git_stash($(repo_path), $(stash)),
      classes => scoped_classes_generic("bundle", "git_stash");

    git_stash_ok::
      "" usebundle => git_clean($(repo_path));

  reports:
    git_stash_not_ok::
      "$(this.bundle):: Warning: Not saving changes or cleaning. Git stash failed. Perhaps 'user.email' or 'user.name' is not set.";
}
git_commit

Prototype: git_commit(repo_path, message)

Description: executes a commit to the specificed git repository

Arguments:

  • repo_path: absolute path to a git repository
  • message: the message to associate to the commmit

Example:

bundle agent make_git_commit
{
  vars:
    "repo"  string => "/var/git/myrepo";
    "msg"   string => "dituri added some bundles for common git operations";

  methods:
    "git_commit" usebundle => git_commit("$(repo)", "$(msg)");
}

Implementation:

bundle agent git_commit(repo_path, message)
{
  classes:
    "ok_repo" expression => fileexists("$(repo_path)/.git");

  methods:
    ok_repo::
      "git_commit" usebundle => git("$(repo_path)", "commit", '-m "$(message)"');
}
git

Prototype: git(repo_path, subcmd, args)

Description: generic interface to git

Arguments:

  • repo_path: absolute path to a new or existing git repository
  • subcmd: any valid git sub-command
  • args: a single string of arguments to pass

This bundle will drop privileges if running as root (uid 0) and the repository is owned by a different user. Use inform_mode (from the command line, -I) to see every Git command it runs.

Example:

bundle agent git_rm_files_from_staging
{
  vars:
    "repo"        string => "/var/git/myrepo";
    "git_cmd"     string => "reset --soft";
    "files"       slist  => { "fileA", "fileB", "fileC" };

  methods:
    "git_reset" usebundle => git("$(repo)", "$(git_cmd)", "HEAD -- $(files)");
}

Implementation:

bundle agent git(repo_path, subcmd, args)
{
  vars:
      "oneliner" string => "$(paths.path[git])";

      "repo_uid"
      string  => filestat($(repo_path), "uid"),
      comment => "So that we don't mess up permissions, we will just execute
                    all commands as the current owner of .git";

      "repo_gid"
      string  => filestat($(repo_path), "gid"),
      comment => "So that we don't mess up permissions, we will just execute
                    all commands as the current group of .git";

  classes:
      "am_root" expression => strcmp($(this.promiser_uid), "0");
      "need_to_drop" not => strcmp($(this.promiser_uid), $(repo_uid));

  commands:
    am_root.need_to_drop::
      "$(oneliner)"
      args => "$(subcmd) $(args)",
      classes => kept_successful_command,
      contain => setuidgid_dir( $(repo_uid), $(repo_gid), $(repo_path) );

    !am_root|!need_to_drop::
      "$(oneliner)"
      args => "$(subcmd) $(args)",
      classes => kept_successful_command,
      contain => in_dir( $(repo_path) );

  reports:
    inform_mode.am_root.need_to_drop::
      "$(this.bundle): with dropped privileges to uid '$(repo_uid)' and gid '$(repo_gid)', in directory '$(repo_path)', running Git command '$(oneliner) $(subcmd) $(args)'";

    inform_mode.(!am_root|!need_to_drop)::
      "$(this.bundle): with current privileges, in directory '$(repo_path)', running Git command '$(oneliner) $(subcmd) $(args)'";
}

Design Center

The Design Center is a public repository for customizable CFEngine design patterns and code. Here you will find reference pages for its API and code structure. For a guide to its functionality, start with Design Center Overview.

See Also: Design Center Overview, Design Center in CFEngine Enterprise


Sketch Structure

All Design Center sketches consists of at least two files:

There might be additional supporting files for testing and additional CFEngine policy files (*.cf) for more advanced sketches.

sketch.json

This file contains metadata about the sketch and declares the interface to the sketch. A minimal sketch.json file looks like this:

{

    manifest:
    {
        "main.cf": { desc: "main file", "version": "1.05.2" },
    },

    metadata:
    {
        "name": "Category::sketch_name",
        "description": "What the sketch does",
        "version": "1.0",
        "license": "MIT",
        "tags": [ "cfdc", "enterprise_compatible" ],
        "authors": [ "user@example.com" ],
        "depends": { "Other::Dependency::Sketch": { }, "cfengine": { "version": "3.6.0" }, "os": [ { "ubuntu" : "Ubuntu", "gentoo" : "Gentoo" } ] }
    },

    api:
    {
        bundlename:
        [
            { type: "environment", name: "runenv", },
            { type: "metadata", name: "metadata", },
            { type: "string", name: "mystring", description: "Purpose of mystring", validation: "MYSTRING_VALIDATION", example: "example mystring contents" },
            { type: "list", name: "mylist", description: "Purpose of mylist", validation: "MYLIST_VALIDATION", example: "example mylist item contents" }
        ],
    },

    namespace: "cfdc_sketch_name_namespace",

    interface: [ "main.cf" ]
}
CFEngine Enterprise Compatibility

For a sketch to work well with the CFEngine Enterprise Design Center graphical user interface (GUI), all of the above attributes must be specified. Some additional requirements are noted below.

The depends.os attribute is checked when a user is activating a sketch, to warn on cases where a user attempts to activate a sketch on an operating system the sketch does not (yet) support. It is therefore useful to make sure that all the operating systems listed in depends.os is working well with the sketch. Each element has the format { "os_class" : "OS friendly name" }. "OS friendly name" is displayed in the GUI.

The enterprise_compatible tag must be set, otherwise it will not show up as an available sketch in the GUI.

All items in api.bundlename:

  • any element that takes input (excluding e.g. runenv and metadata) must have type either string or list (support for more types will be added in the future)
  • validation must be a validation that has been defined in the API (living either in constdata.conf or vardata.conf)
  • the referenced validation can use minimum_value, maximum_value, or valid_regex. Other choices are available in Enterprise 3.6.
Upgrading sketches

There are three ways to upgrade a Design Center sketch repository.

Upgrade a Design Center sketch repository from the Github master branch of Design Center

cf-sketch --install-all --inputs=/var/cfengine/design-center

The installsource is omitted but defaults to the Github master branch, so the above is equivalent to:

cf-sketch --install-all --inputs=/var/cfengine/design-center --installsource=https://raw.githubusercontent.com/cfengine/design-center/master/sketches/cfsketches.json

Upgrade a Design Center sketch repository from the Github 3.6.x branch of Design Center

cf-sketch --install-all --inputs=/var/cfengine/design-center --installsource=https://raw.githubusercontent.com/cfengine/design-center/3.6.x/sketches/cfsketches.json

Upgrade a Design Center sketch repository from your own sketch repository

You would do this if you maintain sketches for your own organization.

cf-sketch --install-all --inputs=/var/cfengine/design-center --installsource=/myrepo/sketches/cfsketches.json


The Design Center API

API General Information

The Design Center API (DC API or just API henceforth) is a simple JSON-based protocol for communicating with the Design Center backend. The backend may be running locally or remotely; the API makes no assumptions about the transport channel and is entirely a line-based text protocol consisting of one JSON line each way.

The API client makes a request and gets a response over the same channel. Again, the request and the response can only be a single line of text, ended by the transport channel's standard line-ending sequence, e.g. CRLF for HTTP. JSON escapes such sequences so they should not happen anywhere in the payloads.

API requests have the following general structure:

{ dc_api_version: "3.6.0", request: { ...commands... } }

The version is strictly semantically versioned as major.minor.patch. It must match exactly, so you can't have a 3.6.0 client talking to a 3.6.1 server for instance (the client has to say "3.6.1" to be usable). We expect backward compatibility, this is just a way to avoid misunderstandings.

It's possible that CFEngine 3.7.x will keep using the 3.6.0 API, for instance. Think of the API version as the minimum CFEngine version required to use it.

NOTE: Generally, only one command may be specified per request.

API responses look like this:

{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "log": [],
        "tags": {},
        "data": {...response data...
        }
    }
}

The top key can be one of the following:

  • api_ok: the command was processed correctly and the response is enclosed as valid JSON (note that this doesn't mean the response indicates success!!!)

  • api_error: the command was not processed correctly and the response may not be valid JSON at all. It may be good JSON and even contain keys like api_ok promises, e.g. warnings or success, but you can't rely on that.

The API client may wish to replace unparseable data with {api_error: "BAD JSON (escaped data here)"} or something similar to make the response handler simpler.

Inside the API response, under the api_ok key, you can expect to find the following:

  • success: indicates, generally speaking, that the command succeeded or failed. Any complex commands can fail in subtle ways, but the API will do its best to make this a good indicator.

  • errors and warnings: lists of strings that log errors and warnings for the command.

  • error_tags: key-value array of tag strings assigned to the error messages. This lets the client tell what stages or areas of the command triggered the errors.

  • log: list of general message strings. This is optional and purely informational.

  • tags: key-value array of tag strings assigned to the response, not associated with errors. This lets the client tell what stages or areas of the command triggered messages or warnings, or more generally what stages or areas of the command were executed. This is optional and purely informational.

  • data: the meat of the response plate, if you will. This key contains all the response data that the API command generated. Each command has different return data so the specifics are listed per command.

API Commands

The API commands and their data responses are listed below. Generally they are exclusive of each other, and the order below is the order in which they are answered. Thus, for instance, a request that issues both list and search will get just the list results.

Many commands take terms. Terms are one of the following:

  • a string (matches any field)
  • a list of strings (any of them may match any field)
  • a list of lists, with each one in the following format: either [FIELD, "matches", REGEX] or [FIELD, "equals", STRING] or [[FIELD1, FIELD2,...], "matches", STRING].
list

The list command lists installed sketches.

Here are examples of three list commands. The first one lists everything installed.

{ dc_api_version: "3.6.0", request: {list: true } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "list": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "CFEngine::dclib::3.5.0": "CFEngine::dclib::3.5.0",
                    "CFEngine::dclib": "CFEngine::dclib",
                    "CFEngine::sketch_template": "CFEngine::sketch_template",
                    "VCS::vcs_mirror": "VCS::vcs_mirror",
                    "Security::SSH": "Security::SSH",
                    "Utilities::ping_report": "Utilities::ping_report",
                    "Monitoring::SNMP::Walk": "Monitoring::SNMP::Walk",
                    "Data::Classes": "Data::Classes",
                    "CFEngine::stdlib": "CFEngine::stdlib",
                    "Utilities::ipverify": "Utilities::ipverify"
                },
            "count": 10
            }
        },
        "log": [],
        "tags": {}
    }
}

Note the top-level key under data/list is the name of the repository, which is always a local directory.

The next one takes terms and lists all the sketches whose name satisfies the terms.

{ dc_api_version: "3.6.0", request: {list: [["name", "matches", "(Cloud|CFEngine|Security)"]] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "list": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "Security::SSH": "Security::SSH",
                    "CFEngine::dclib::3.5.0": "CFEngine::dclib::3.5.0",
                    "CFEngine::dclib": "CFEngine::dclib",
                    "CFEngine::sketch_template": "CFEngine::sketch_template",
                    "CFEngine::stdlib": "CFEngine::stdlib"
                }
            },
            "count": 5
        },
        "log": [],
        "tags": {}
    }
}
option: count_only

When count_only is given as a top-level option with a value of true, only the count is returned..

option: describe

When describe is given as a top-level option with a value of true, as in the example below, the returned data is the contents of sketch.json.

{ dc_api_version: "3.6.0", request: {describe: true, list: [["name", "matches", "ping"]] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "list": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "Utilities::ping_report": {
                        "namespace": "cfdc_ping",
                        "manifest": {
                            "changelog": {
                                "comment": "changelog"
                            },
                            "test.cf": {
                                "comment": "Test Policy"
                            },
                            "README.md": {
                                "documentation": true
                            },
                            "params/example.json": {
                                "comment": "Example parameters to report on a few hosts connectivity."
                            },
                            "main.cf": {
                                "desc": "main file"
                            }
                        },
                        "interface": ["main.cf"],
                        "metadata": {
                            "authors": ["Nick Anderson <nick@cmdln.org>", "Ted Zlatanov <tzz@lifelogs.com>"],
                            "version": 1.2,
                            "name": "Utilities::ping_report",
                            "license": "MIT",
                            "description": "Report on pingability of hosts",
                            "tags": ["cfdc"],
                            "depends": {
                                "cfengine": {
                                    "version": "3.4.0"
                                },
                                "CFEngine::dclib": {},
                                "os": ["linux"],
                                "CFEngine::stdlib": {
                                    "version": 105
                                }
                            }
                        },
                        "entry_point": null,
                        "api": {
                            "ping": [{
                                "name": "runenv",
                                "type": "environment"
                            },
                            {
                                "name": "metadata",
                                "type": "metadata"
                            },
                            {
                                "name": "hosts",
                                "type": "list"
                            },
                            {
                                "name": "count",
                                "type": "string"
                            },
                            {
                                "name": "reached",
                                "type": "return"
                            },
                            {
                                "name": "not_reached",
                                "type": "return"
                            }]
                        }
                    }
                }
            }
        },
        "log": [],
        "tags": {}
    }
}

When describe is given as a top-level option with a value of README, as in the example below, the returned data is actually the sketch's auto-generated README.md file (which comes from sketch.json). The tools/test/Makefile testing Makefile has a convenience regenerate_readme target to do this for all the DC sketches.

If the manifest includes a README.include file, it will be included verbatim in the README.md in the Description section. That makes it easier to write documentation for your sketches.

{ dc_api_version: "3.6.0", request: {describe: "README", list: [["name", "matches", "ping"]] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "list": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "Utilities::ping_report": ["/home/tzz/.cfagent/inputs/sketches/utilities/ping_report", "# Utilities::ping_report version 1.2\n\nLicense: MIT\nTags: cfdc\nAuthors: Nick Anderson <nick@cmdln.org>, Ted Zlatanov <tzz@lifelogs.com>\n\n## Description\nReport on pingability of hosts\n\n## Dependencies\nCFEngine::dclib, CFEngine::stdlib\n\n## API\n### bundle: ping\n* parameter _environment_ *runenv* (default: none, description: none)\n\n* parameter _metadata_ *metadata* (default: none, description: none)\n\n* parameter _list_ *hosts* (default: none, description: none)\n\n* parameter _string_ *count* (default: none, description: none)\n\n* returns _return_ *reached* (default: none, description: none)\n\n* returns _return_ *not_reached* (default: none, description: none)\n\n\n## SAMPLE USAGE\nSee `test.cf` or the example parameters provided\n\n"]
                }
            },
            "count": 1
        },
        "log": [],
        "tags": {}
    }
}

The search command works exactly like list above, except that the candidate list contains all available sketches (from recognized_sources), not just the installed sketches.

option: count_only

When count_only is given as a top-level option with a value of true, only the count is returned..

option: describe

The describe option to search works exactly like it does for list above.

describe

The describe command gives the contents of sketch.json for the matching installed sketches by name.

{ dc_api_version: "3.6.0", request: {describe:"Security::SSH"} }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "describe": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "Security::SSH": [{
                        "namespace": "cfdc_sshd",
                        "manifest": {
                            "ssh.cf": {
                                "desc": "main file"
                            },
                            "README.md": {
                                "documentation": true
                            },
                            "params/simple.json": {}
                        },
                        "interface": ["ssh.cf"],
                        "metadata": {
                            "authors": ["Diego Zamboni <diego.zamboni@cfengine.com>", "Ted Zlatanov <tzz@lifelogs.com>"],
                            "version": 1.1,
                            "name": "Security::SSH",
                            "license": "MIT",
                            "description": "Configure and enable sshd",
                            "tags": ["cfdc"],
                            "depends": {
                                "cfengine": {
                                    "version": "3.4.0"
                                },
                                "CFEngine::dclib": {
                                    "version": "1.0.0"
                                },
                                "CFEngine::stdlib": {
                                    "version": 105
                                }
                            }
                        },
                        "api": {
                            "sshd": [{
                                "name": "runenv",
                                "type": "environment"
                            },
                            {
                                "name": "metadata",
                                "type": "metadata"
                            },
                            {
                                "name": "params",
                                "type": "array"
                            }]
                        }
                    }]
                },
                "/home/tzz/source/design-center/sketches": {
                    "Security::SSH": [{
                        "namespace": "cfdc_sshd",
                        "manifest": {
                            "ssh.cf": {
                                "desc": "main file"
                            },
                            "README.md": {
                                "documentation": true
                            },
                            "params/simple.json": {}
                        },
                        "interface": ["ssh.cf"],
                        "metadata": {
                            "authors": ["Diego Zamboni <diego.zamboni@cfengine.com>", "Ted Zlatanov <tzz@lifelogs.com>"],
                            "version": 1.1,
                            "name": "Security::SSH",
                            "license": "MIT",
                            "description": "Configure and enable sshd",
                            "tags": ["cfdc"],
                            "depends": {
                                "cfengine": {
                                    "version": "3.4.0"
                                },
                                "CFEngine::dclib": {
                                    "version": "1.0.0"
                                },
                                "CFEngine::stdlib": {
                                    "version": 105
                                }
                            }
                        },
                        "api": {
                            "sshd": [{
                                "name": "runenv",
                                "type": "environment"
                            },
                            {
                                "name": "metadata",
                                "type": "metadata"
                            },
                            {
                                "name": "params",
                                "type": "array"
                            }]
                        }
                    }]
                }
            }
        },
        "log": [],
        "tags": {}
    }
}
install

The install command installs any number of sketches. The data provides is a list of key-value arrays with keys:

  • force: boolean, false by default. Whether any existing installations of the sketch should be respected or overwritten. Also asks the API to ignore OS and CFEngine version dependencies.

  • sketch: the sketch name.

  • target: the sketch install directory. Must be in the API's repolist. Optional; when not given, the first element of the repolist will be used.

  • source: the sketch source repository. Must be in the API's recognized_sources. Optional; when not given, every element of the recognized_sources will be tried. Can be a string or an array of strings.

{
    dc_api_version: "3.6.0",
    request: {
        install: [{
            sketch: "CFEngine::sketch_template",
            force: true,
        },
        {
            sketch: "VCS::vcs_mirror",
            force: true,
            target: "~/.cfagent/inputs/sketches",
            source: "/home/tzz/source/design-center/tools/test/../../sketches"
        }]
    }
}

The return data is a key-value array as follows, describing the installation details.

{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "VCS::vcs_mirror": {
                "params/thrift-lib-perl.json": "/home/tzz/.cfagent/inputs/sketches/utilities/vcs_mirror/params/thrift-lib-perl.json",
                "README.md": "/home/tzz/.cfagent/inputs/sketches/utilities/vcs_mirror/README.md",
                "params/cfengine-core.json": "/home/tzz/.cfagent/inputs/sketches/utilities/vcs_mirror/params/cfengine-core.json",
                "params/cfengine-copbl.json": "/home/tzz/.cfagent/inputs/sketches/utilities/vcs_mirror/params/cfengine-copbl.json",
                "main.cf": "/home/tzz/.cfagent/inputs/sketches/utilities/vcs_mirror/main.cf",
                "params/cfengine-core-runas-tzz.json": "/home/tzz/.cfagent/inputs/sketches/utilities/vcs_mirror/params/cfengine-core-runas-tzz.json"
            },
            "install": {
                "~/.cfagent/inputs/sketches": {
                    "VCS::vcs_mirror": 1,
                    "CFEngine::sketch_template": 1
                }
            },
            "inventory_save": 1,
            "CFEngine::sketch_template": {
                "test.cf": "/home/tzz/.cfagent/inputs/sketches/sketch_template/test.cf",
                "scripts/sample.sh": "/home/tzz/.cfagent/inputs/sketches/sketch_template/scripts/sample.sh",
                "params/demo.json": "/home/tzz/.cfagent/inputs/sketches/sketch_template/params/demo.json",
                "README.md": "/home/tzz/.cfagent/inputs/sketches/sketch_template/README.md",
                "modules/mymodule": "/home/tzz/.cfagent/inputs/sketches/sketch_template/modules/mymodule",
                "main.cf": "/home/tzz/.cfagent/inputs/sketches/sketch_template/main.cf"
            }
        },
        "log": [],
        "tags": {
            "VCS::vcs_mirror": 1,
            "installation": 7,
            "CFEngine::sketch_template": 1
        }
    }
}
uninstall

The uninstall command simply deletes the top-level sketch directory and everything under it. It takes a list of key-value arrays with keys:

  • sketch: the sketch name.

  • target: the sketch install directory we want to clean. Must be in the API's repolist.

{ dc_api_version: "3.6.0", request: {uninstall: [ { sketch: "CFEngine::stdlib", target: "~/.cfagent/inputs/sketches" } ] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "inventory_save": 1,
            "uninstall": {
                "~/.cfagent/inputs/sketches": {
                    "CFEngine::stdlib": 1
                }
            }
        },
        "log": [],
        "tags": {
            "uninstallation": 1,
            "CFEngine::stdlib": 1
        }
    }
}

The inventory_save key in the return indicates whether the inventory (cfsketches.json) was written successfully.

compositions

The compositions command lists the defined compositions.

{ dc_api_version: "3.6.0", request: {compositions: true} }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "compositions": {
                "mirror_to_template_2": {
                    "destination_sketch": "CFEngine::sketch_template",
                    "source_scalar": "deploy_path",
                    "source_sketch": "VCS::vcs_mirror",
                    "destination_scalar": "myip"
                },
                "mirror_to_template_1": {
                    "destination_sketch": "CFEngine::sketch_template",
                    "source_scalar": "deploy_path",
                    "source_sketch": "VCS::vcs_mirror",
                    "destination_list": "mylist"
                }
            }
        },
        "log": [],
        "tags": {}
    }
}
compose

The compose command defines a composition. It returns the same data as compositions.

{
    dc_api_version: "3.6.0",
    request: {
        compose: {
            mirror_to_template_1: {
                destination_sketch: "CFEngine::sketch_template",
                destination_list: "mylist",
                source_sketch: "VCS::vcs_mirror",
                source_scalar: "deploy_path"
            },
            mirror_to_template_2: {
                destination_sketch: "CFEngine::sketch_template",
                destination_scalar: "myip",
                source_sketch: "VCS::vcs_mirror",
                source_scalar: "deploy_path"
            }
        }
    }
}
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "compositions": {
                "mirror_to_template_2": {
                    "destination_sketch": "CFEngine::sketch_template",
                    "source_scalar": "deploy_path",
                    "source_sketch": "VCS::vcs_mirror",
                    "destination_scalar": "myip"
                },
                "mirror_to_template_1": {
                    "destination_sketch": "CFEngine::sketch_template",
                    "source_scalar": "deploy_path",
                    "source_sketch": "VCS::vcs_mirror",
                    "destination_list": "mylist"
                }
            }
        },
        "log": [],
        "tags": {
            "compose": 1
        }
    }
}
decompose

The decompose command undefines a composition by name. It returns the same data as compositions.

{ dc_api_version: "3.6.0", request: {decompose: "mirror_to_template_1" } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "compositions": {
                "destination_sketch": "CFEngine::sketch_template",
                "source_scalar": "deploy_path",
                "source_sketch": "VCS::vcs_mirror",
                "destination_list": "mylist"
            }
        },
        "log": [],
        "tags": {
            "compose": 1
        }
    }
}

(Note that Monty Python has beaten us to this joke by decades with "The Decomposing Composers.")

activations

The activations command lists the defined activations.

{ dc_api_version: "3.6.0", request: {activations:true} }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "activations": {
                "VCS::vcs_mirror": [{
                    "params": ["vcs_base", "git_mirror_core"],
                    "environment": "testing",
                    "target": "~/.cfagent/inputs/sketches"
                },
                {
                    "params": ["vcs_base", "svn_mirror_thrift"],
                    "environment": "testing",
                    "target": "~/.cfagent/inputs/sketches"
                }],
                "CFEngine::sketch_template": [{
                    "params": ["incomplete_sketch"],
                    "environment": "testing",
                    "target": "~/.cfagent/inputs/sketches",
                    "compositions": ["mirror_to_template_1", "mirror_to_template_2"]
                }]
            }
        },
        "log": [],
        "tags": {}
    }
}

Under each activation you may find an optional hash key identifying it uniquely (it's a hash of the resolved parameters, bundle and sketch name, and run environment name), but this should never be considered mandatory.

activate

The activate command defines a new activation of a sketch.

An activation is a matching of a sketch bundle with parameters, a run environment, and optionally compositions. The sketch name is matched with a target (so the API knows which installed sketch to inspect), a run environment name, and a list of parameter names.

{ dc_api_version: "3.6.0", request: {activate: { "VCS::vcs_mirror": { target: "~/.cfagent/inputs/sketches", environment: "testing", params: [ "vcs_base", "git_mirror_core" ] } } } }

The sketch bundle will be selected based on which one is satisfied by the given parameters and compositions. You can use the __bundle__ parameter key to specify the bundle explicitly.

{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "activate": {
                "VCS::vcs_mirror": {
                    "params": ["vcs_base", "git_mirror_core"],
                    "environment": "testing",
                    "target": "~/.cfagent/inputs/sketches"
                }
            }
        },
        "log": [],
        "tags": {
            "VCS::vcs_mirror": 1
        }
    }
}

You can pass a identifier parameter to an activate command, which can then be used to deactivate an activation specifically, and which will show up in the classes and prefixes of that activation.

You can pass a priority parameter to an activate command, which will be used for sorting the activations. By default all activations get priority 1. The priorities are sorted lexicographically (00a comes before 00b and 10 comes before 9).

You can pass a metadata parameter to an activate command, which will show up under the activation key in the metadata.

You can pass a target parameter to an activate command with an install location, which will only activate sketches that exist in that location.

You may find a hash key in the result, as described in the activations command above.

option: compose

When the activate command has a compose key with a list of composition names, those compositions are considered whenever the parameters alone are not enough to activate the sketch. Thus compositions and parameters work together, as late and immediate bindings of the passed data respectively.

deactivate

The deactivate command removes sketch activations. It can take either the name of a sketch or true to indicate all activations should be removed.

{ dc_api_version: "3.6.0", request: {deactivate: "VCS::vcs_mirror" } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "deactivate": {
                "VCS::vcs_mirror": 1
            }
        },
        "log": [],
        "tags": {
            "deactivate": 1
        }
    }
}
{ dc_api_version: "3.6.0", request: {deactivate: true } }

(No activations existed at this point, so the return data is empty.)

{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {},
        "log": [],
        "tags": {}
    }
}
definitions

The definitions command lists the parameter definitions. This is the DC API's central library of knowledge. Every parameter definition is a source of configuration data (like a CFEngine common bundle, but applied directly to a sketch bundle). Parameter definitions have names, which are used when you want to activate a sketch, and can contain more than one sketch's parameters or only part of a sketch's parameters.

{ dc_api_version: "3.6.0", request: {definitions:true} }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "definitions": {
                "simple_ssh": {
                    "Security::SSH": {
                        "params": {
                            "X11Forwarding": "yes",
                            "Protocol": "2",
                            "PermitRootLogin": "yes"
                        }
                    }
                },
            }
        },
        "log": [],
        "tags": {}
    }
}
define

The define command creates a parameter definition with a name. The example here creates some base parameters for the VCS::vcs_mirror sketch and then lays specific configuration to mirror the [https://github.com/cfengine/core.git] repository's master branch from Git. In this case, we do it in two steps, but could have done it in one step.

Note that the reply doesn't tell you more than "I got it, thanks."

You can use the function expression in data, as shown below, to make sure that the DC API will make a function call and not just pass a string. So, instead of getenv("LOGNAME", "128") you need to use { "function": "getenv", "args": ["LOGNAME", "128"] } to make sure the function call is preserved.

{ dc_api_version: "3.6.0", request: {define: { "vcs_base": { "VCS::vcs_mirror": { options: { parent_dir: { owner: { "function": "getenv", "args": ["LOGNAME", "128"] }, group: { "function": "getenv", "args": ["LOGNAME", "128"] }, perms: "755", ensure: true }, nowipe: true, vcs: { runas: { "function": "getenv", "args": ["LOGNAME", "128"] }, umask: "000" } } } } } } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "define": {
                "vcs_base": 1
            }
        },
        "log": [],
        "tags": {
            "vcs_base": 1
        }
    }
}
{ dc_api_version: "3.6.0", request: {define: { "git_mirror_core": { "VCS::vcs_mirror": { vcs: "/usr/bin/git", path: "/tmp/q/cfengine-core", branch: "master", origin: "https://github.com/cfengine/core.git" } } } } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "define": {
                "git_mirror_core": 1
            }
        },
        "log": [],
        "tags": {
            "git_mirror_core": 1
        }
    }
}
undefine

The undefine command removes a parameter definition by name. You can pass a list of string parameter definition names or simply true to remove all the parameter definitions.

{ dc_api_version: "3.6.0", request: {undefine: ["git_mirror_core"] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "undefine": {
                "git_mirror_core": "1"
            }
        },
        "log": [],
        "tags": {
            "git_mirror_core": 1
        }
    }
}
environments

The environments command lists the run environments.

A run environment is a common bundle of general settings. It affects the execution of bundles globally, so it's not intended to be specific for each bundle activation.

The sketch bundle chooses to have a run environment by specifying a parameter with type environment. Only a run environment can satisfy that API parameter.

Good examples of run environments are production, production_debug, or development_nodebug. In a run environment you'd expect to find at least the activated, verbose, and test variables. For each of those, the DC API will also provide a class named runenv_ENVIRONMENTNAME_ENVIRONMENTVARIABLE. Here's an example of a testing run environment, as it appears in the generated runfile:

bundle common testing
{
  vars:
      "activated" string => "1";
      "env_vars" slist => { "activated", "test", "verbose" };
      "test" string => "1";
      "verbose" string => "1";
  classes:
      "runenv_testing_activated" expression => "any";
      "runenv_testing_test" expression => "any";
      "runenv_testing_verbose" expression => "any";
}

And here is the definition of that run environment:

{ dc_api_version: "3.6.0", request: {environments:true} }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "environments": {
                "testing": {
                    "verbose": "1",
                    "test": "1",
                    "activated": "1"
                }
            }
        },
        "log": [],
        "tags": {}
    }
}

The last thing to note is that any run environment variable can have values other than true and false. If they are a string, then that string is a class expression. So, for instance, if activated is Monday then the run environment will only be activated on Mondays.

It's trivial to do AND and OR in such a string, as normal for CFEngine contexts.

define_environment

The define_environemnt command defines a run environment. The testing example above can be defined like so:

{ dc_api_version: "3.6.0", request: {define_environment: { "testing": { activated: true, test: true, verbose: true } } } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "define_environment": {
                "testing": 1
            }
        },
        "log": [],
        "tags": {
            "testing": 1
        }
    }
}

Again, remember that each of those variables can be a string, to be interpreted as a class expression, and that you can have more than those three variables.

undefine_environment

The undefine_environemnt command removes a run environment. It takes a list of environment names.

{ dc_api_version: "3.6.0", request: {undefine_environment: [ "testing" ] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "undefine_environment": {
                "testing": "1"
            }
        },
        "log": [],
        "tags": {
            "testing": 1
        }
    }
}
validations

The validations command lists the data validations.

The data validations are just strings that have a key-value array associated with them. Specific keys trigger specific validation behavior in order, as follows. Note that the examples below are not necessarily in your API installation already.

// only the inside of the request is shown for brevity
define_validation: { DIGITS: { valid_regex: "^[0-9]+$" } }
define_validation: { NUMBER: { derived: [ "DIGITS" ] } }
define_validation: { AB: { choice: [ "A", "B" ] } }
define_validation: { 8BIT_NUMBER: { minimum_value: 0, maximum_value: 255 } }
define_validation: { LIST_OF_NUMBERS: { list: [ "NUMBER" ] } }
define_validation: { MOG_SEQUENCE: { sequence: [ "OCTAL", "UID", "GID"   ] } }
define_validation: { ARRAY_OF_NUMBERS_TO_URLS: { array_k: [ "NUMBER" ], array_v: [ "URL" ] } }
  • derived defines a parent data validation. So a NUMBER validation requires that DIGITS and any other parent data validations be checked first.

  • choice defines a list of exact string matches. So AB must be given A or B to pass validation.

  • minimum_value and then maximum_value are numeric checks. So 8BIT_NUMBER has to be between 0 and 255. Any invalid numbers, e.g. hello, will be treated as 0.

  • invalid_regex and then valid_regex are regular expressions written as strings. They follow the Perl regex syntax right now. So DIGITS can only contain the decimal digits 0 through 9 and will reject the empty string `or hello`.

  • invalid_ipv4 and valid_ipv4 are TODO.

  • list ensures that the given data is a list of one of several data types. So in the example, LIST_OF_NUMBERS will check that every element passes the NUMBER validation.

  • sequence is like a record: it ensures that the data is a sequence (list) of the given data types. So for example, MOG_SEQUENCE has to have three elements, of which the first one passes OCTAL validation, the second passed UID validation, and the third passes GID validation.

  • array_k and array_v are almost exactly like list but they validate the keys and values of a key-value array, respectively, against a list of several data types. So ARRAY_OF_NUMBERS_TO_URLS requires that every key pass the NUMBER validation and every value pass the URL validation.

define_validation

The define_validation command defines a data validation. In the return data you will find all the currently defined data validations.

{ dc_api_version: "3.6.0", request: {define_validation: { NONEMPTY_STRING: { valid_regex: "." } } } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "validations": {
                "NONEMPTY_STRING": {
                    "valid_regex": "."
                },
            }
        },
        "log": [],
        "tags": {
            "define_validation": 1
        }
    }
}
undefine_validation

The undefine_validation command removes a data validation by name.

{ dc_api_version: "3.6.0", request: {undefine_validation: "NONEMPTY_STRING" } }'
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "validations": {
                "valid_regex": "."
            }
        },
        "log": [],
        "tags": {
            "undefine_validation": 1
        }
    }
}
validate

The validate command validates data using a named data validation.

{ dc_api_version: "3.6.0", request: {validate: { validation: "ARRAY_OF_NUMBERS_TO_URLS", data: { "20": "http://this.that", "30": "not a URL" } } } }

It's useful to look at the log output here. This example failed:

DCAPI::log4(Validation.pm:73): Validating ARRAY_OF_NUMBERS_TO_URLS against data '{"30":"not a URL","20":"http://this.that"}'
DCAPI::log4(Validation.pm:282): Validating ARRAY_OF_NUMBERS_TO_URLS: checking 'array_k' is ["NUMBER"]
DCAPI::log4(Validation.pm:73): Validating NUMBER against data '30'
DCAPI::log4(Validation.pm:73): Validating DIGITS against data '30'
DCAPI::log4(Validation.pm:166): Validating DIGITS: checking valid_regex ^[0-9]+$
DCAPI::log4(Validation.pm:85): Validating NUMBER: checking parent data type DIGITS
DCAPI::log4(Validation.pm:73): Validating NUMBER against data '20'
DCAPI::log4(Validation.pm:73): Validating DIGITS against data '20'
DCAPI::log4(Validation.pm:166): Validating DIGITS: checking valid_regex ^[0-9]+$
DCAPI::log4(Validation.pm:85): Validating NUMBER: checking parent data type DIGITS
DCAPI::log4(Validation.pm:282): Validating ARRAY_OF_NUMBERS_TO_URLS: checking 'array_v' is ["URL"]
DCAPI::log4(Validation.pm:73): Validating URL against data 'not a URL'
DCAPI::log4(Validation.pm:166): Validating URL: checking valid_regex ^[A-Za-z]{3,9}://.+
DCAPI::log4(Validation.pm:73): Validating URL against data 'http://this.that'
DCAPI::log4(Validation.pm:166): Validating URL: checking valid_regex ^[A-Za-z]{3,9}://.+
{
    "api_ok": {
        "warnings": [],
        "success": false,
        "errors": ["Could not validate any of the allowed array_v types [URL]"],
        "error_tags": {
            "array_v": 1,
            "validation": 1
        },
        "data": {},
        "log": [],
        "tags": {}
    }
}

This example succeeded:

{ dc_api_version: "3.6.0", request: {validate: { validation: "ARRAY_OF_NUMBERS_TO_URLS", data: { "20": "http://this.that", "30": "http://this.that2" } } } }
DCAPI::log4(Validation.pm:73): Validating ARRAY_OF_NUMBERS_TO_URLS against data '{"30":"http://this.that2","20":"http://this.that"}'
DCAPI::log4(Validation.pm:282): Validating ARRAY_OF_NUMBERS_TO_URLS: checking 'array_k' is ["NUMBER"]
DCAPI::log4(Validation.pm:73): Validating NUMBER against data '30'
DCAPI::log4(Validation.pm:73): Validating DIGITS against data '30'
DCAPI::log4(Validation.pm:166): Validating DIGITS: checking valid_regex ^[0-9]+$
DCAPI::log4(Validation.pm:85): Validating NUMBER: checking parent data type DIGITS
DCAPI::log4(Validation.pm:73): Validating NUMBER against data '20'
DCAPI::log4(Validation.pm:73): Validating DIGITS against data '20'
DCAPI::log4(Validation.pm:166): Validating DIGITS: checking valid_regex ^[0-9]+$
DCAPI::log4(Validation.pm:85): Validating NUMBER: checking parent data type DIGITS
DCAPI::log4(Validation.pm:282): Validating ARRAY_OF_NUMBERS_TO_URLS: checking 'array_v' is ["URL"]
DCAPI::log4(Validation.pm:73): Validating URL against data 'http://this.that2'
DCAPI::log4(Validation.pm:166): Validating URL: checking valid_regex ^[A-Za-z]{3,9}://.+
DCAPI::log4(Validation.pm:73): Validating URL against data 'http://this.that'
DCAPI::log4(Validation.pm:166): Validating URL: checking valid_regex ^[A-Za-z]{3,9}://.+
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {},
        "log": [],
        "tags": {}
    }
}
regenerate

The regenerate command writes the API runfile (as specified in the API configuration) from all the known activations, compositions, run environments, parameter definitions, and data validations.

The command does not allow the user to change the runfile location, as that is a possible security risk.

In the returned data you can find the runfile name and also each activation in a key-value map, indexed by the internal activation unique name, and with each value a list of name, sketch, bundle, parameter checksum. The parameter checksum is calculated from the final parameters after the parameter definitions have been resolved, so compositions will be recognized as well. Here's an example regeneration with just one activation, with the runfile location in a specific user's home directory (this is typically how cf-sketch.pl will configure itself if it runs as non-root).

{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "___001_System_motd_entry": ["", "System::motd", "entry", "b3172b7755c090fd49e0b250f6320880"],
            "runfile":"/home/tzz/.cfagent/inputs/sketches/meta/api-runfile.cf"
            },
        "log": [],
        "tags": {
            "activations": 1
        }
    }
}
regenerate_index

The regenerate_index command takes a directory parameter (string) and writes the cfsketches.json index from all the sketches found in a given directory. The directory must be local and listed in the API configuration's recognized_sources. The command returns an error if the index could not be written or if an error happened while loading any sketch.json files.

{ dc_api_version: "3.6.0", request: {regenerate_index: "~/source/cfengine/design-center/sketches" } }
DCAPI::log3(DCAPI.pm:1500): Regenerating index: searching for sketches in ~/source/cfengine/design-center/sketches
DCAPI::log3(DCAPI.pm:1523): Regenerating index: on sketch dir applications/memcached
...
DCAPI::log3(DCAPI.pm:1523): Regenerating index: on sketch dir web_servers/apache
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {},
        "log": [],
        "tags": {}
    }
test

The test command tests installed sketches. It always returns true if the test harness ran, even if the individual tests failed. It's up to you to check the result of each sketch's test.

Here are examples of two test commands. The first one tests everything installed (shown when no sketches were installed for brevity; see below for a full test example).

{ dc_api_version: "3.6.0", request: {test: true } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "coverage": 0,
            "test": {},
            "total": 0
            }
        },
        "log": [],
        "tags": {}
    }
}

Under data you will find a coverage and a total key, which respectively represent the number of covered sketches and the total number of sketches inspected. So if you asked to test 10 sketches but only one had any test scripts, your coverage would be 1/10.

The top-level key under data.test is the name of the repository, which is always a local directory.

The next one takes terms and tests all the sketches whose name satisfies the terms. The return format is the same: for each repository and each sketch tested, you'll get a key-value array with keys log (the text log of the output); failed (with tests that failed); and total (with all the tests).

The format inside each test is according to the Perl module Test::Harness. For instance the good key will be 1 if all the planned tests succeeded.

The bench key will give you some timings, but more precise timings may be added in the future. Do not depend on the format of the bench value.

{ dc_api_version: "3.6.0", request: {test: ["Applications::Memcached"] } }
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "test": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "Applications::Memcached": {
                        "log": "/home/tzz/.cfagent/inputs/sketches/applications/memcached/test.pl .. \n1..6\n# Running under perl version 5.014002 for linux\n# Current time local: Tue May  7 18:08:08 2013\n# Current time GMT:   Tue May  7 22:08:08 2013\n# Using Test.pm version 1.25_02\nok 1\nok 2\nok 3\nok 4\nok 5\nok 6\nok\n",
                        "failed": {},
                        "total": {
                            "files": 1,
                            "max": 6,
                            "bonus": 0,
                            "skipped": 0,
                            "sub_skipped": 0,
                            "ok": 6,
                            "bad": 0,
                            "good": 1,
                            "tests": 1,
                            "bench": " 1 wallclock secs ( 0.02 usr  0.00 sys +  0.45 cusr  0.01 csys =  0.48 CPU)",
                            "todo": 0
                        }
                    }
                }
            }
        },
        "log": [],
        "tags": {}
    }
}

You can skip the actual testing and just get the coverage if you give the test command the coverage parameter. Here's how you can inspect the coverage of every single installed sketch:

{"dc_api_version":"3.6.0","request":{"coverage":1,"test":["1"]}}
{
    "api_ok": {
        "warnings": [],
        "success": true,
        "errors": [],
        "error_tags": {},
        "data": {
            "coverage": 7,
            "test": {
                "/home/tzz/.cfagent/inputs/sketches": {
                    "System::Syslog": 0,
                    "Networking::NTP::Client": 0,
// ...
                    "Packages::installed": 1,
                    "CFEngine::dclib::3.5.0": 1,
                }
            },
            "total": 32
        },
        "log": [],
        "tags": {}
    }
}
selftests

The selftests command lists the internal API tests. It's for internal use only and so left undocumented.

selftest

The selftest command runs internal API tests. It's for internal use only and so left undocumented.

API CLI Interface and config.json

From the command line, you can run cd tools/cf-sketch; ./cf-dc-api.pl config.json where config.json contains the necessary configuration for the API:

log

Either STDOUT or STDERR or a file name.

log_level

1-5 currently. 4 or 5 for debugging; 1 or 2 for normal usage.

3 is for people who can't make up their mind.

repolist

A list of local directories where sketches may be installed.

recognized_sources

A list of DC repositories where sketches may be installed FROM. There can be local directories or URLs.

runfile

A key-value array.

The key location specifies the place where the runfile is written. If not specified, it defaults to the first element of repolist plus /meta/api-runfile.cf.

The standalone, standalone_inputs, and relocate_path keys were available in older versions but are ignored as of DC API version 3.6.0.

If you specify the key filter_inputs with an array value, any inputs matching any elements in that array will be omitted from the generated runfile. That way you can, for example, exclude the cfengine_stdlib.cf that Design Center provides.

If you specify the string header under runfile, it will be inserted before any other comments (so you can have, for instance, a comment).

vardata

The file location where the API will record all data. If not specified, it defaults to the first element of repolist plus /meta/vardata.conf.

You should avoid changing this explicitly without very good reason.

A vardata of - (just a dash) means that the API will not try to write to the vardata file, but will pretend everything is OK.

constdata

The file location for the pre-defined API validations and other constant DC data. If not specified, it defaults to the first element of repolist plus /meta/constdata.conf.

You should avoid changing this explicitly without very good reason.

A constdata of - (just a dash) means that the API will not try to load the constdata file, but will pretend everything is OK.

Full config.json example

The constdata, vardata, and runfile location are left as the default.

{
 log: "STDERR",
 log_level: 4,
 repolist: [ "~/.cfagent/inputs/sketches" ],
 recognized_sources: [ "~/source/design-center/sketches" ],
 runfile: {
            header: "# This file is maintained by CFEngine",
            filter_inputs: [ "some bad file" ],
          },
}

Maintaining your own sketch repository

Some users and customers would like to maintain their own Design Center repo. It's pretty simple if you copy and paste these commands to run them as root. Add -v to see debug output if something goes wrong.

Create Your Sketches

For example, say we start with Utilities::Roles. Copy the roles directory from the Design Center (/var/cfengine/share/*Base/sketches/utilities/roles) to /my/repo/sketches and edit sketch.json and main.cf as needed, let's say changing the list of roles.

cp -rp /var/cfengine/share/*Base/sketches/utilities/roles /my/repo/sketches/
Copy The Sketch Template
cp -rp /var/cfengine/share/*Base/sketches/sketch_template /my/repo/sketches/
Make The cfsketches.json File

This will fail if the sketch.json files found are invalid, or if /my/repo/sketches doesn't exist. It creates the sketch index file.

/var/cfengine/design-center/bin/cf-sketch --make_cfsketches --inputs /my/repo --is=/my/repo/sketches/cfsketches.json
(Optional) Regenerate The README.md Files

This will regenerate the README.md files for each sketch, which will please your users.

/var/cfengine/design-center/bin/cf-sketch --make_readme --is=/my/repo/sketches/cfsketches.json
Install Your Sketches!
/var/cfengine/design-center/bin/cf-sketch --install-all --is=/my/repo/sketches/cfsketches.json --inputs=/var/cfengine/design-center

See also: Package The Sketch


All Promise and Body Types

All Promise Types
Common Attributes
access
build_xpath
classes
  • and: clist in range [a-zA-Z0-9_!&@@$|.()\[\]{}:]+
  • dist: rlist in range -9.99999E100,9.99999E100
  • expression: class expression in range [a-zA-Z0-9_!&@@$|.()\[\]{}:]+
  • not: class expression in range [a-zA-Z0-9_!&@@$|.()\[\]{}:]+
  • or: clist in range [a-zA-Z0-9_!&@@$|.()\[\]{}:]+
  • persistence: int in range 0,99999999999
  • scope: one of namespace, bundle
  • select_class: clist in range [a-zA-Z0-9_!&@@$|.()\[\]{}:]+
  • xor: clist in range [a-zA-Z0-9_!&@@$|.()\[\]{}:]+
commands
databases
defaults
delete_attribute
delete_lines
delete_text
delete_tree
field_edits
files
guest_environments
insert_lines
insert_text
insert_tree
measurements
meta
methods
packages
processes
replace_patterns
reports
roles
services
set_attribute
set_text
storage
users
vars
All Body Types
acl
  • aces: slist in range ((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
  • acl_default: one of nochange, access, specify, clear
  • acl_directory_inheritdeprecated: one of nochange, parent, specify, clear
  • acl_inherit: one of true, false, yes, no, on, off, nochange
  • acl_method: one of append, overwrite
  • acl_type: one of generic, posix, ntfs
  • specify_default_aces: slist in range ((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
  • specify_inherit_acesdeprecated: slist in range ((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
action
agent
changes
classes
common
contain
copy_from
database_server
delete
delete_select
edit_defaults
edit_field
environment_interface
environment_resources
executor
file
file_select
hub
insert_select
location
match_value
monitor
mount
package_method
password
perms
  • bsdflags: slist in range [+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+
  • groups: slist in range [a-zA-Z0-9_$.-]+
  • mode: string in range [0-7augorwxst,+-]+
  • owners: slist in range [a-zA-Z0-9_$.-]+
  • rxdirs: boolean
printfile
process_count
process_select
rename
replace_with
report_data_select
runagent
select_region
server
service_method
volume