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


Macros

Minimum version

You can conditionally include policy test using the @if macro.

For example, you could say

bundle agent extractor
{
@if minimum_version(3.8)
# the function `new_function_3_8()` was introduced in 3.8
vars: "container" data => new_function_3_8(...);
@endif
}

The text will be inserted verbatim in the policy. This happens before syntax validation, so any version of CFEngine that supports the @if macro will be able to exclude syntax from later, possibly incompatible versions. In CFEngine 3.7, the above text will never be seen by the parser. In 3.8 and later, it will.

@if calls have to match up: you can't nest them and each one requires a matching @endif before the end of the file.

History: This macro was introduced in CFEngine 3.7.0

Features

You can conditionally include policy test using the @if macro.

bundle agent extractor
{
  @if feature(xml)
# the yaml library may not be compiled in
  vars: "container" data => parseyaml(...);
  @endif
}

The text will be inserted verbatim in the policy. This happens before syntax validation, so any CFEngine binary that is not compiled with the feature support macro will be able to exclude syntax from possibly incompatible versions.

Currently available features are : * xml * yaml * curl

History: This macro was introduced in CFEngine 3.8.0


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 by cf-agent and in what order they will be executed. The list refers to the names of bundles (which might be parameterized, function-like objects).

The default value for bundlesequence is { "main" }.

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"
                       };
    }

Note: Only common and agent bundles are allowed to be listed in the bundlesequence.

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" };
    }

History: The default to { "main" } was introduced in version 3.7.0, so if you expect your policies to be run by older version, you'll need an explicit bundlesequence.

bwlimit

Description: Coarse control of bandwidth any cf-serverd or cf-agent process will send out. In Bytes/sec.

Bandwidth limit is meant to set an upper bound of traffic coming out of CFEngine agents or servers, as a countermeasure against network abuse from them. The limit is applied to all interfaces (in total), a single process at a time. It can prevent network being flooded by CFEngine traffic when large files or many agents hit a single cf-serverd.

For more fine-grained control, please use operating system (eg. iptables) facilities.

Note: Bandwidth limiting is currently not supported on Windows.

Type: [float][float]

Default value: none (no limit)

Example:

    body common control

    {
      bwlimit => "10M";
    }

In this example, bwlimit is set to 10MBytes/sec = 80Mbit/s meaning that CFEngine would only consume up to ~80% of any 100Mbit ethernet interface.

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";
    }
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)

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.

package_inventory

Description: List of package module bodies to query for package lists.

Defines the list of package module bodies which will be queries for package lists, for use in packagematching(), packageupdatesmatching() and in Enterprise inventory reporting.

Type: slist

Allowed input range: (body names)

Example:

body common control
{
    package_inventory => { "apt_get" };
}
package_module

Description: The default package module body to use.

Defines the default package module body to use for package promises, if none is specified in the promise.

Type: string

Allowed input range: (body name)

Example:

body common control
{
    package_module => "apt_get";
}
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: 2

  • 0 -
  • undefined -
  • 1 - Classic protocol
  • classic - Alias to protocol 1
  • 2 - TLS
  • latest - Alias to protocol 2

Note: protocol_version can be specified at the individual promise level using the body copy_from protocol_version attribute.

See also: body copy_from protocol_version, allowlegacyconnects, allowtlsversion, allowciphers, tls_min_version, tls_ciphers, encrypt, logencryptedtransfers, ifencrypted

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";
    }
tls_ciphers

Description: List of ciphers allowed when making outgoing connections.

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

Type: string

Allowed input range: (arbitrary string)

Default value: undefined

Example:

body common control
{
    # Use one of these ciphers when making outbound connections
    tls_ciphers => "AES128-SHA";
}

See also: protocol_version, allowciphers, tls_min_version, allowtlsversion, encrypt, logencryptedtransfers, ifencrypted

History: Introduced in CFEngine 3.7.0

tls_min_version

Description: Minimum tls version to allow for outgoing connections.

Type: string

Allowed input range: (arbitrary string)

Default value: 1.0

body common control
{
    # Allow only TLSv1.1 or higher for outgoing connections
    tls_min_version => "1.1";
}

See also: protocol_version, allowciphers, tls_ciphers, allowtlsversion, encrypt, ifencrypted, logencryptedtransfers

History: Introduced in CFEngine 3.7.0

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";
    }
Deprecated attributes in body common control

The following attributes were functional in previous versions of CFEngine, but today they are deprecated, either because their functionality is being handled trasparently or because it doesn't apply to current CFEngine version.

  • fips_mode
  • host_licenses_paid

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
  --workdir     , -w value - Override the default /var/cfengine work directory for testing (same as setting CFENGINE_TEST_OVERRIDE_WORKDIR)
  --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
  --timing-output, -t       - Output timing information on console when in verbose mode
  --trust-server, -T value - Possible values: 'yes' (default, trust the server when bootstrapping), 'no' (server key must already be trusted)
  --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)
  --timestamp   , -l       - Log timestamps on each line of log output
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
{
  # Agent email report settings based on their domain.

    alpha_cfengine_com::
      domain => "alpha.cfengine.com";
      mailto => "admins@alpha.cfengine.com";

    beta_domain_com::
      domain => "beta.cfengine.com";
      mailto => "admins@beta.cfengine.com";

    any::
      mailfrom => "root";
}
abortclasses

Description: The abortclasses slist contains regular expressions that match 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.

Type: slist

Allowed input range: .*

Example:

body agent control
{
  abortclasses => { "danger.*", "should_not_continue" };
}

Note: CFEngine class expressions are not supported. To handle class expressions, simply create an alias for the expression with a single name.

For example:

body agent control
{
  abortclasses => { "danger.*", "should_not_continue" };
}

bundle agent bundle_a
{
  classes:
    "abort_condition_a"
      expression => "any",
      scope => "namespace";
}

bundle common bundle_b
{
  classes:
    "abort_condition_b" expression => "any";
}

bundle agent bundle_c
{
  classes:

    # Here we define a class that will match the abortclasses under more complex
    # conditions

    "should_not_continue"
      expression => "(abort_condition_a.abort_condition_b).!something_else",
      scope => "namespace";
}
abortbundleclasses

Description: The abortbundleclasses slist contains regular expressions that match 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";
}

See Also: body copy_from timeout, cf-runagent timeout

Notes:

  • cf-serverd will time out any transfer that takes longer than 10 minutes (this is not currently tunable).
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";
    }
select_end_match_eof

Description: When select_end_match_eof is set to true select_end will consider end of file as the end region if it is unable to match the end pattern. For more details see edit_line promise.

Note: bundle edit_line select_end_match_eof can override this setting at the individual promise level.

Type: boolean

Default value: false

Example:

     body agent control
     {
     select_end_match_eof => "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 skip and warn about if found during any file search.

If CFEngine sees these names during recursive (depth) file searches, it will skip them and output a warning message.

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";
    }
report_class_log

Description: The report_class_log option enables logging of classes set by cf-agent. Each class set by cf-agent will be logged at the end of agent execution (all classes defined during the same cf-agent execution will have the same timestamp).

Time classes are ignored. Destination: '/var/cfengine/state/classes.jsonl'

Format(jsonl):

{"name":"class_123","timestamp":1456933993}\r\n
{"name":"pk_sha_123","timestamp":1456933993}\r\n

Type: boolean

Default value: false

Example:

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

History:

  • Added in 3.9.0

Notes:

  • Available in CFEngine Enterprise.
  • Persistent classes are logged with the timestamp of each agent run.

The following classes are excluded from logging:

  • Time based classes (Hr01, Tuesday, Morning, etc ...)
  • license_expired
  • any
  • from_cfexecd
  • Life cycle (Lcycle_0, GMT_Lcycle_3)

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
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
  --timestamp   , -l       - Log timestamps on each line of log output
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" };
        allowallconnects      => { "127.0.0.1" , "::1" };

        # Uncomment me under controlled circumstances
        #trustkeysfrom         => { "127.0.0.1" , "::1" };
    }
allowconnects

Description: List of IP addresses that may connect to the server port. They are denoted in either IP or subnet form. For compatibility reasons, regular expressions are also accepted.

This is the first line of defence; clients who are not in this list may not connect or send any 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.0/24",
         "200\.1\.10\..*",
         };
allowallconnects

Description: List of IP addresses that may have more than one connection to the server port. They are denoted in either IP or subnet form. For compatibility reasons, regular expressions are also accepted.

The clients that are not listed here may have only one open connection at the time with the server.

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.0/24",
         "200\.1\.10\..*",
         };
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, for compatibility with pre-3.6 CFEngine versions.

Type: slist

Allowed input range: (arbitrary string)

See also: protocol_version

allowciphers

Description: List of TLS ciphers the server accepts for incoming connections. 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

Example:

body server control
{
      # Only this non-default cipher is to be accepted
      allowciphers    => "RC4-MD5";
}

Note: When used with protocol_version 1 (classic protocol), this does not do anything as the classic protocol does not support TLS ciphers.

See also: protocol_version, tls_ciphers, tls_min_version, allowtlsversion, encrypt, logencryptedtransfers, ifencrypted

History: Introduced in CFEngine 3.6.0

allowtlsversion

Description: Minimum TLS version allowed for incoming connections.

Type: string

Allowed input range: (arbitrary string)

Default value: 1.0

Example:

body server control
{
      # Allow only TLSv1.1 or higher
      allowtlsversion => "1.1";
}

Note: When used with protocol_version 1 (classic protocol), this attribute does not do anything.

See also: protocol_version, tls_ciphers, tls_min_version, allowciphers, encrypt, logencryptedtransfers, ifencrypted

History: Introduced in CFEngine 3.7.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" };
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.0/24" , "::1" };
        allowallconnects      => { "10.10.10.0/24" , "::1" };
        trustkeysfrom         => { "10.10.10.0/24" , "::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: 30.

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 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" };
    }
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. Only applies to classic protocol connections (because the new protocol uses TLS which enforces encryption for everything).

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";
    }

See also: ifencrypted, encrypt, tls_ciphers, tls_min_version, allowciphers, allowtlsversion, protocol_version

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 the server will accept and trust new (untrusted) public keys. They are denoted in either IP or subnet form. For compatibility reasons, regular expressions are also accepted.

The new accepted public keys are written to the ppkeys directory, and a message is logged:

192.168.122.254> Trusting new key: MD5=0d5603d68dd62d35bab2150e35d055ae

NOTE: trustkeysfrom should normally be an empty list except in controlled circumstances, for example when the network is being set up and keys are to be exchanged for the first time.

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.0.0/16"};
    }
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)

Deprecated attributes in body server control

The following attributes were functional in previous versions of CFEngine, but today they are deprecated, either because their functionality is being handled trasparently or because it doesn't apply to current CFEngine version.

  • auditing
  • dynamicaddresses
  • hostnamekeys
  • keycacheTTL

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
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
  --timestamp   , -l       - Log timestamps on each line of log output
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: 120

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";
mailfilter_exclude

Description: List of anchored regular expressions that, if matched by a log entry, will cause that log entry to be excluded from agent execution emails.

If no filter is set, cf-execd acts as if no log entry matches the exclude pattern. If a log entry also matches a pattern in mailfilter_include, the exclude pattern takes precedence.

Type: slist

Allowed input range: .*

Note: Merely adding or removing a pattern that causes the number of matching log entries to change, does not guarantee that the next agent execution will generate an email from cf-execd. The actual output from cf-agent still has to be different from the previous run for an email to be generated.

Example:

    body executor control
    {
        # Ignore agent execution emails about permission errors.
        mailfilter_exclude => { ".*Permission denied.*" };
    }

History: Introduced in CFEngine 3.9.

mailfilter_include

Description: List of anchored regular expressions that must match a log entry in order for it to be included in agent execution emails.

If no filter is set, cf-execd acts as if every log entry matches the include pattern. If a log entry also matches a pattern in mailfilter_exclude, the exclude pattern takes precedence.

Type: slist

Allowed input range: .*

Note: Merely adding or removing a pattern that causes the number of matching log entries to change, does not guarantee that the next agent execution will generate an email from cf-execd. The actual output from cf-agent still has to be different from the previous run for an email to be generated.

Example:

    body executor control
    {
        # Only include reports in agent execution emails.
        mailfilter_include => { "R:.*" };
    }

History: Introduced in CFEngine 3.9.

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
  --workdir     , -w value - Override the work directory for testing (same as setting CFENGINE_TEST_OVERRIDE_WORKDIR)
  --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' (this file only), 'cf-full', 'json-full' (all parsed promises). 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
  --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
  --timestamp   , -l       - Log timestamps on each line of log output

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
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
  --timestamp   , -l       - Log timestamps on each line of log output
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'
  --timestamp              - Log timestamps on each line of log output
  --numeric     , -n       - Do not lookup host names

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 - (deprecated)
  --diagnostic  , -x       - (deprecated)
  --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
  --color       , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
  --timestamp   , -l       - Log timestamps on each line of log output
  --remote-bundles, - value - Bundles to execute on the remote agent
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";
}

See Also: body copy_from timeout, agent default_timeout


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.9/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. A namespace applies until the next namespace declaration in a file, or until the end of a file. This is similar to how class expressions apply until the next class expression or end of bundle.

See Also: namespaces


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
defaults - a default value for bundle parameters x x x x
classes - a class, representing a state of the system x x x x
meta - information about promise bundles x x x x
reports - report a message x x x x
vars - a variable, representing a value x x x x
commands - execute a command x
databases - configure a database 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 - manage services or define new abstractions x
storage - verify attached storage x
users - add or remove users 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

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

Common Body Attributes

The following attributes are available to all body types.

inherit_from

Description: Inherits all attributes from another body of the same type as a function call. For a detailed description, see Bodies.

Type: fncall

Allowed input range: (arbitrary body invocation)

Example:

A simple example first, which has no parameters:

    body TYPE parent
    {
      atribute1 => 100;
      atribute2 => { "a" };
      atribute3 => 75;
    }

    body TYPE child
    {
      inherit_from => parent; # same as parent()
      atribute3 => 300; # overwrites parent's attribute3
      # has atribute1 => 100;
      # has atribute2 => { "a" };
    }

Now with parameters. The child calls the parent as a function call. Note that the child's parameters can be passed up to the parent.

    body TYPE parent(a1, a2)
    {
      atribute1 => $(a1);
      atribute2 => { $(a2) };
      atribute3 => 75;
    }

    body TYPE child(aaa)
    {
      inherit_from => parent(5, $(aaa));
      atribute3 => 300; # overwrites parent's attribute3
      # has atribute1 => 5;
      # has atribute2 => { $(aaa) };
    }

History: Was introduced in 3.8.0.

meta

Description: A list of meta attributes.

Type: slist

Allowed input range: (arbitrary string list)

Example:

    body ANYTYPE mybody
    {
      meta => { "deprecated" };
    }

History: Was introduced in 3.7.0.

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. When a persistent class is activated it gets scope namespace.

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. If the handle is likely to contain non-identifier characters, you can use canonify() to turn them into such characters.

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.

History: Has the if alias (and unless opposite) since 3.7.0.

if

Description: Describes extended classes ANDed with context. This is an exact alias for ifvarclass; see its description for details.

Type: string

Allowed input range: (arbitrary string)

Example:

The generic example has the form:

     promise-type:

       "promiser"

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

A specific example would be:

    bundle agent example
    {
    commands:

     any::

        "/bin/echo This is linux"

           if => "linux";
    }

History: Was introduced in 3.7.0.

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)

unless

Description: Describes negated extended classes ANDed with context. This is exactly like ifvarclass but logically inverted; see its description for details.

Type: string

Allowed input range: (arbitrary string)

Example:

The generic example has the form:

     promise-type:

       "promiser"

         unless = "forbidden";

A specific example would be:

    bundle agent example
    {
    commands:

     any::

        "/bin/echo This is NOT linux"

           unless => "linux";
    }

History: Was introduced in 3.7.0.


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

Common Body Attributes

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

inherit_from
meta

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"; # always defined
      "two"; # always defined

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

Note: You can use the following attributes to make a complete promise.

  • and
  • expression
  • dist
  • or
  • not
  • xor

If you omit any of them, the class is always defined (as if you said expression => "any").

For example, the following promise defines the class web when a file exists:

bundle agent example
{
  classes:
      "web"
        if => fileexists("/etc/httpd/httpd.conf");
}

History: The context attributes expression, and, or, not, xor, dist were made optional in CFEngine 3.9.0. Before that, one of them was required. So the following examples were the valid equivalents of the example above before 3.9.0:

bundle agent example
{
  classes:
      "web"
        expression => fileexists("/etc/httpd/httpd.conf");

      "webserver"
        expression => "any",
        if => fileexists("/etc/httpd/httpd.conf");
}

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. With classes, the notion of "true" is not a boolean state, because classes can never be false. They are not booleans. They can be defined or undefined, but it's important to understand that a class may be defined during the execution of the agent, so the result of an expression may change during execution.

Expressions can be:

  • class names, with or without a namespace

  • the literals true (always defined) and false (never defined) that allow JSON booleans to be used inside expressions

  • the logical and operation, expressed as a&b or a.b, which is true if both a and b are true

  • the logical or operation, expressed as a|b, which is true if either a or b are true

  • the logical not operation, expressed as !a, which is true if a is not true. Note again here that a could become true during the execution. So if you have "myclass" expression => "!x" and x starts undefined but is defined later, you could have both x and myclass defined!

  • parenthesis (whatever) which operate as expected to prioritize expression evaluation

  • the return value of a function that returns a class, such as fileexists() and() userexists() etc.

Type: class

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

Example:

    classes:

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

      # it's unlikely a machine will become Linux during execution
      # so this is fairly safe
      "not_linux"   expression => "!linux";

      "a_or_b"   expression => "a|b";
      # yes, it's OK to define a class twice, and this is the same outcome
      # with different syntax
      "a_and_b"   expression => "a&b";
      "a_and_b"   expression => "a.b";

      # yes, it's OK to define a class twice, and this is the same outcome
      # with different syntax
      "linux_and_has_toor" expression => and(userexists("toor"), "linux");
      "linux_and_has_toor" and => { userexists("toor"), "linux" };
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).

If a list is used as the input to select_class the promise will only actuate if the list is expandable. If the list has not yet been evaluated, the select_class will be skipped and wait for a subsequent evaluation pass.

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 anchored 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 main

{     
  defaults:

      # We can have default values even if variables are not defined at all.
      # This is equivalent to a variable definition, so not particularly useful.

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

  methods:

      # More useful, defaults if parameters are passed to a param bundle

      "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)";

}
R: The value of a is AAAAAAAAA
R: The value of b is bbb
R: The value of no_return is ok
R: The default value of X is I am a default value
R: The default value of Y is I am a default list item 1
R: The default value of Y is I am a default list item 2

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


packages (deprecated)

NOTE: This package promise is deprecated and has been superseded by the new package promise. It is recommended to use the new package promise whenever possible. Simply using attributes from the new package promise interface will select the new implementation.

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

Common Body Attributes

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

inherit_from
meta

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";

methods

Methods are compound promises that refer to whole bundles of promises. Methods may be parameterized.

    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. Note that if the bundle you specify requires no parameters you may omit the usebundle attribute and give the bundle name directly in the promiser string.

    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(), readyaml(), parsejson(), or parseyaml(), the various data_* functions, or from merging existing data containers with mergedata(). They can NOT be modified, once created.

Inline YAML and JSON data

Data containers can be specified inline, without calling functions. For example:

    vars:
      # JSON or YAML can be inlined since CFEngine 3.7
      "inline1" data => '{"key":"value"}'; # JSON
      "inline2" data => '---\n- key2: value2'; # YAML requires "---\n" header

Inline YAML data has to begin with the ---\n preamble. This preamble is normally optional but here (for inline YAML) it's required by CFEngine.

Inline JSON or YAML data may contain CFEngine variable references. They will be expanded at runtime as if they were simply calls to readjson() or readyaml(), which also means that syntax error in the JSON or YAML data will only be caught when your policy is actually being evaluated.

If the inline JSON or YAML data does not contain CFEngine variable references, it will be parsed at compile time, which means that cf-promises will be able to find syntax errors in your JSON or YAML data early. Thus it is highly recommended that you try to avoid variable references in your inline JSON or YAML data.

Passing data containers to bundles

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
  • to extract just container[x], use mergedata("container[x]")
  • to wrap a container in an array, use mergedata("[ container ]")
  • to wrap a container in a map, use mergedata('{ "mykey": container }')
  • 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"}');
     "loaded3" data => readyaml("/tmp/myfile.yaml", 40000);
     "loaded4" data => parseyaml('- key2: value2');
     "merged1" data => mergedata(loaded1, loaded2, loaded3, loaded4);

     # JSON or YAML can be inlined since CFEngine 3.7
     "inline1" data => '{"key":"value"}'; # JSON
     "inline2" data => '---\n- key2: value2'; # YAML requires "---\n" header

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

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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.0/24" };


}

Using the built-in report_data_select body default_data_select_host:

      report_data_select => default_data_select_host,
      admit => { @(def.policy_servers) };

    policy_server.enterprise::
      "$(query_types)"
      handle => "report_access_grant_$(query_types)_for_hub",
      comment => "Grant $(query_types) reporting query for the hub on the policy server",
      resource_type => "query",
      report_data_select => default_data_select_policy_hub,
      admit => { @(def.policy_servers) };
}

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_hostnames => { ".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_hostnames => { ".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 can contain a mix of entries in the syntax of admit_ips, admit_hostnames and admit_keys, and offers the same functionality. It's a legacy attribute that was split in the aforementioned attributes, and it's not recommended to use in new policy.

deny

Description: The deny slist can contain a mix of entries in the syntax of deny_ips, deny_hostnames and deny_keys, and offers the same functionality. It's a legacy attribute that was split in the aforementioned attributes, and it's not recommended to use in new policy. Example:

bundle server access_rules()
{
access:

  "/directory/"

    admit   => { "127.0.0.1", ".example.org" },
    deny    => { "badhost_1.example.org", "badhost_1.example.org" };
}

The best way to write the same policy would be the following:

bundle server access_rules()
{
access:

  "/directory/"

    admit_ips       => { "127.0.0.1" },
    admit_hostnames => { ".example.org" },
    deny_hostnames  => { "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";

Note: This attribute is a noop when used with protocol_version 2 or greater.

See also: protocol_version, allowtlsversion, allowciphers, tls_min_version, tls_ciphers, encrypt, logencryptedtransfers, ifencrypted

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():

      report_data_select => default_data_select_host,
      admit => { @(def.policy_servers) };

    policy_server.enterprise::
      "$(query_types)"
      handle => "report_access_grant_$(query_types)_for_hub",
      comment => "Grant $(query_types) reporting query for the hub on the policy server",
      resource_type => "query",
      report_data_select => default_data_select_policy_hub,
      admit => { @(def.policy_servers) };
}
      report_data_select => default_data_select_policy_hub,
      admit => { @(def.policy_servers) };
}

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 (resource_type => "path"). 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
  • bundle

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).

If the resource type is bundle then the specific bundles are allowed to be remotely executed with cf-runagent --remote-bundles from the specified hosts. The promiser is an anchored regular expression.

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 => { "$(sys.policy_hub)" };

 "delta"
    comment => "Grant access to cfengine hub to collect report deltas",
    resource_type => "query",
    admit_ips   => { "$(sys.policy_hub)"  };

 "full"
          comment => "Grant access to cfengine hub to collect full report dump",
    resource_type => "query",
        admit_ips => { "$(sys.policy_hub)"  };

 "magic_bundle"
          comment => "Grant access to the hub to activate magic_bundle with cf-runagent",
    resource_type => "bundle",
        admit_ips => { "$(sys.policy_hub)" };

 am_policy_hub::

  "collect_calls"
     comment       => "Enable call-collect report collection for the specific client",
     resource_type => "query",
     admit_ips     => { "1.2.3.4" };
}
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 parts of command lines in the system process table.

      processes:

        "regex contained in process line"

            process_select = process_filter_body,
            restart_class = "activation class for process",
            ..;

Note: CFEngine uses the output from the ps command to inspect running processes, and these formats differ between platforms. You can see how cfengine views the process table for your platform by inspecting cf_otherprocs, cf_procs, and cf_rootprocs which can be found in $(sys.workdir)/state/ (typically /var/cfengine/state).

This is an example showing how to restart a splunk process owned by root:

bundle agent example
{
  processes:
      "splunkd"
        process_owner => { "root" },
        handle => "example_splunk_stop_gracefully",
        process_stop => "/opt/splunkforwarder/bin/splunk stop",
        comment => "Find splunkd processes owned by root. Stop it gracefully
                    with the internal splunk binary.";

      "splunkd"
        restart_class => "splunk_not_running",
        comment => "Set splunk_not_running class if we cant find any root owned
            splunkd processes so that we can restart it using a
                    commands promise";

  commands:
    splunk_not_running::
      "/opt/splunkforwarder/bin/splunk"
        args => "--accept-license --answer-yes --no-prompt start";
}

This example shows using process_select and process_count to define a class when a process has been running for longer than a day.

bundle agent main

{
  processes:

      "init"
        process_count   => any_count("booted_over_1_day_ago"),
        process_select  => days_older_than(1),
    comment => "Define a class indicating we found an init process running
                    for more than 1 day.";

  reports:

    booted_over_1_day_ago::

      "This system was booted over 1 days ago since there is an init process
       that is older than 1 day.";

    !booted_over_1_day_ago::
      "This system has been rebooted recently as the init process has been
       running for less than a day";
}



body process_count any_count(cl)
{
      match_range => "0,0";
      out_of_range_define => { "$(cl)" };
}


body process_select days_older_than(d)
{
      stime_range    => irange(ago(0,0,"$(d)",0,0,0),now);
      process_result => "!stime";
}

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

Take care to not oversimplify your patterns as it may match unexpected processes. 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", (the PCRE pattern anchors "\b" and "\B" may prove very useful to you).

process_stop should only be used for commands that stop processes. To start or restart a process, you should set a class to activate and then use a commands promise together with that class.

    processes:
        "/path/executable"
          restart_class => "restart_me";

    commands:
      restart_me::
       "/path/executable" ... ;
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

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

command

Description: Regular expression matching the command/cmd field of a process

Note: For historical reasons, this attribute is identical to the match performed by using the promiser, except that the regular expression is anchored.

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

Common Body Attributes

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

inherit_from
meta

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_net",
           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

Common Body Attributes

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

inherit_from
meta

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 environment_resources

Common Body Attributes

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

inherit_from
meta

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";
  select_end_match_eof => "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.

Common Body Attributes

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

inherit_from
meta

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: Anchored 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: Anchored 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. The default behavior can be modified by using select_end_match_eof or select_end_match_eof.

select_end_match_eof

Description: When select_end_match_eof is set to true select_end will consider end of file as the end region if it is unable to match the end pattern.

See also select_start and select_end. These delimiters mark out the region of a file to be edited.

Type: boolean

Default value: false

Example:

     body select_region example(x)
     {
     select_start => "\[$(x)\]";
     select_end => "\[.*\]";
     select_end_match_eof => "true";
     }

If the select_end attribute is omitted, the selected region will run to the end of the file no matter what the value of select_end_match_eof is set to.

Note: The global body agent control `select_end_match_eof sets the default behaviour for the entire policy. The local edit_line promise constraint takes precedence over the body agent control configuration option.

History: This attribute was introduced in CFEngine version 3.9.0 (2016)


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";
     }
Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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

See Also: location bodies in the standard library start location body in the standard library before(srt) location body in the standard library after(srt) location body in the standard library

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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. Use enable to enable the service permanently, for instance in systemd environments.

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
    enable
    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

Common Body Attributes

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

inherit_from
meta

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"
    }
Common Body Attributes

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

inherit_from
meta

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.

Platform notes

Platforms that support named sockets (basically all Unix systems, but not Windows), may not work correctly when using a files promise to alter such a socket. This is a known issue, documented in this ticket.


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";
}
Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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

The copy_from body specifies the details for making remote copies.

Note: For improved performance, connections from cf-agent to cf-serverd are re-used. Currently connection caching is done per pass in each bundle activation.

Common Body Attributes

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

inherit_from
meta

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";
     }

Note: When used with protocol_version 2 or greater this attribute is a noop as the entire session is encrypted.

See also: protocol_version, ifencrypted, protocol_version, tls_ciphers, tls_min_version, allowciphers, allowtlsversion

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_timeout

Example:

     body copy_from example
     {
     timeout => "10";
     }

See Also: agent default_timeout, cf-runagent timeout

Notes:

  • cf-serverd will time out any transfer that takes longer than 10 minutes (this is not currently tunable).
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

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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(), the various data_* functions, readyaml(), parseyaml(), 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/

CFEngine-specific extensions:

Mustache templates in CFEngine can replace the $variable expression with the compact one-line JSON representation of that variable. For instance, if myvar contains the data {"x": "y"}, that's exactly what will show up in the output. This is the same as evaluating format("%S", myvar) into a string and using that string in the Mustache template, except there are no string size limitations and it's much more efficient.

Furthermore, you can use %variable to obtain the full multi-line representation of a variable, just like calling storejson(variable) except there are no string size limitations and it's much more efficient.

When iterating over an array, Mustache templates in CFEngine can replace the @ variable with the current iteration's key. The example below will show it.

To iterate over the top-level container, Mustache templates in CFEngine can use {{#-top-}} ... {{/-top-}}.

These extensions are not in the Mustache standard.

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 }
  ],
 "map":
  {
   "789": 0,
   "-1": -2,
   "logdir": "/var/log"
  }
}');
}

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}}
{{#map}}{{@}}={{.}}, {{/map}}

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,
789=0, -1=-1, logdir=/var/log,

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!

Example:

The policy

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

bundle agent main
{
   vars:
      solar_system::
         "home_star" string => "sol";
         "planets" slist => { "mercury", "venus", "earth" };
         "a[moon]" string => "luna";

      star::
         "a[star]" slist => { "rigel", "vega", "polaris" };

      earth::
         "earth" data => parsejson('
         [
            {
               "oceans" : [ "atlantic", "pacific", "indian", "arctic" ],
               "seas" : [ "caribbean", "dead", "black", "coral" ],
               "position" : "3",
               "orbit" : "1au",
            }
         ]
         ');

   files:
      "/tmp/mytemplate"
         create          => 'true',
         template_method => 'mustache',
         edit_template   => '${sys.workdir}/inputs/mustache.tmp';
}

The template:

This file is edited by CFEngine and is always in place.

{{#classes.solar_system}}
The star is {{vars.main.home_star}}.
{{#vars.main.planets}}{{.}} is a planet.
{{/vars.main.planets}}

But {{vars.main.a[moon]}} is a moon.
{{/classes.solar_system}}

{{#classes.star}}
Some stars are:
{{#vars.main.a[star]}}{{.}}, {{/vars.main.a[star]}}.
{{/classes.star}}

{{#classes.earth}}
{{#vars.main.earth}}
Earth is planet number {{position}}, at an orbit of {{orbit}}.
Oceans include {{#oceans}} {{.}},{{/oceans}}.
Seas include {{#seas}} {{.}},{{/seas}}.
{{/vars.main.earth}}
{{/classes.earth}}
  • {{#classes.solar_system}} starts the beginning of a class block. Unlike CFEngine’s normal code this block must be ended with {/classes.solar_system}}. Everything in-between is evaluated when the class solar_system is true.

  • Strings take the form of {{vars.bundle.name}} as seen in {{vars.main.home_star}} and {{vars.main.a[moon]}}. It’s best to avoid arrays and use JSON data containers instead.

  • {{#vars.main.planets}} starts the iteration of the list main.planets. Everything between that and {{/vars.main.planets}} will be duplicated for each element in the list. Each element will be printed where {{.}} is found.

  • {{#vars.main.earth}} tells the agent to begin iterating through the JSON data container called earth. From there you can use short forms of the JSON data like {{position}} for the string position and {{#oceans}} {{.}},{{/oceans}} for the list oceans and the element position. Note that unlike classic CFEngine templates, mustache templates will print all duplicate lines.

The resulting file:

This file is edited by CFEngine and is always in place.

The star is sol.
mercury is a planet.
venus is a planet.
earth is a planet.

But luna is a moon.

Some stars are:
rigel, vega, polaris, .

Earth is planet number 3, at an orbit of 1au.
Oceans include  atlantic, pacific, indian, arctic,.
Seas include  caribbean, dead, black, coral,.

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(), readyaml(), parseyaml(), mergedata(), data

edit_xml

Type: bundle edit_xml

file_select

Type: body file_select

Common Body Attributes

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

inherit_from
meta

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

file_type

Description: By default, regular files are created, when specifying create => "true". You can create fifos through this mechanism as well, by specifying fifo in file_type.

Type: string

Allowed input range:

    regular
    fifo

Type: (menu option)

Allowed input range:

  • regular
  • fifo

Default value: cfengine

Type: body link_from

Common Body Attributes

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

inherit_from
meta

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 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";
}

body link_from linkfrom(source, type)
{
      source => $(source);
      link_type => $(type);
}

Example usage:

body file control
{
  inputs => { "$(sys.libdir)/stdlib.cf" };
}

bundle agent main
{
  files:

      # This will make symlinks to each file in /var/cfengine/bin
      # for example:
      # '/usr/local/sbin/cf-agent' -> '/var/cfengine/bin/cf-agent'
      # '/usr/local/sbin/cf-serverd' -> '/var/cfengine/bin/cf-serverd'
      "/usr/local/sbin"
        link_from => linkchildren("/var/cfengine/bin"),
    comment => "We like for cfengine binaries to be available inside of the
                    common $PATH";
}

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

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 impelementation:

body link_from ln_s(x)
{
      link_type => "symlink";
      source => "$(x)";
      when_no_source => "force";
}
     body link_from example
     {
     link_type => "symlink";
     source => "/tmp/source";
     }

Example usage:

body file control
{
  inputs => { "$(sys.libdir)/stdlib.cf" };  
}

bundle agent main
{
  files:

      # We use move_obstructions because we want the symlink to replace a
      # regular file if necessary.
      "/etc/apache2/sites-enabled/www.cfengine.com" -> { "webmaster@cfengine.com" }
        link_from => ln_s( "/etc/apache2/sites-available/www.cfengine.com" ),
        move_obstructions => "true",
        comment => "We always want our website to be enabled.";
}

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

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

Common Body Attributes

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

inherit_from
meta

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

Common Body Attributes

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

inherit_from
meta

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 container to be passed to the template (Mustache only). It can come from a function call like mergedata() or from a data container reference like @(mycontainer).

Type: data

Allowed input range: (arbitrary string)

Example:

    files:

     "/path/file"
     ...
     edit_template => "mytemplate.mustache",
     template_data => parsejson('{"message":"hello"}'),
     template_method => "mustache";

Example:

    vars:
     "mycontainer" data => '[ 1, 2, 3 ]';

    files:

     "/path/file"
     ...
     edit_template => "mytemplate.mustache",
     template_data => @(mycontainer),
     template_method => "mustache";

If this attribute 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

CFEngine 3.7 and later supports package management through a simple promise interface. Using a small set of attributes you can make promises about the state of software on a host, whether it should be installed, not installed, or at a specific version.

CFEngine 3.6 and older had a different package promise implementation, which is still functional, but considered deprecated. However, it may still be in use by existing policy files, and it may cover platforms which the new implementation does not currently cover. To read about the old package promise, go to the old package promise section.

The actual communication with the package manager on the system is handled by so called package modules, which are specifically written for each type of package manager. CFEngine comes with out-of-the-box support for the following package managers:

  • yum: YUM package manager and accompanying rpm package manager.
  • apt_get: Apt package manager and accompanying dpkg package manager.
  • freebsd_ports: FreeBSD Ports
  • nimclient - AIX NIM client
  • pkg - FreeBSD pkg
  • pkgsrc - pkgsrc

Both yum and apt_get package managers require Python version 2 to be installed on the host.

  packages:
      "apache2"
        policy => "present",
        package_module => apt_get,
        version => "2.2.22";

In this example, we want the software package "apache2" to be present on the system, and we want it to be version 2.2.22. If this requirement cannot be fulfilled (for example because the package repository doesn't have it), the promise will fail.

It is also possible to specify a package file name, if the package resides on the local filesystem, like this:

  packages:
      "/mnt/nfs/packages/apache2-2.2.22.x86_64.rpm"
        policy => "present",
        package_module => yum;

The default package module can be globally specified with the package_module attribute in body common control.

Note that if your policy attribute specifies "absent", then the promiser string needs to be a bare package name, you cannot use a file name for this.

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

architecture

Description: The architecture we want the promise to consider.

The promise will only consider the architecture specified when performing package manipulations, but depending on the underlying package manager, this may indirectly affect other architectures.

Type: string

Allowed input range: (arbitrary string)

Example:

  packages:
    "apache"
        policy => "present",
        package_module => apt_get,
        architecture => "x86_64";
options

Description: Options to pass to the underlying package module.

options is a catchall attribute in order to pass arbitrary data into the package module which is carrying out package operations. It is meant as a rescue solution when a package module has added functionality which is not covered by the package promise API. As such there is no official documentation for this attribute, its usage depends on the package module in question.

Type: slist

Allowed input range: (arbitrary string)

Example:

  packages:
    "apache"
        policy => "present",
        package_module => my_package_module,
        options => { "repository=myrepo" };
policy

Description: Whether the package should be present or absent on the system.

policy is the only mandatory package promise attribute.

Type: string

Allowed input range: present|absent

Example:

  packages:
    "apache"
        policy => "absent",
        package_module => apt_get;
version

Description: The version we want the promise to consider.

Type: string

Allowed input range: (arbitrary string)

Note: When policy present is used version may be set to latest to ensure the latest available version from a repository is installed.

Example:

  packages:
    "apache"
        policy => "absent",
        package_module => apt_get,
        version => "2.2.22";

    "ssh"
        policy => "present",
        package_module => apt_get,
        version => "latest";
package_module

Type: body package_module

The package module body you wish to use for the package promise. The default is platform dependent, see package_module in Components and Common Control. The name of the body is expected to be the same as the name of the package module inside /var/cfengine/modules/packages.

Common Body Attributes

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

inherit_from
meta

default_options

Description: Options to pass to to the package module by default.

See the options attribute for details on what options do.

Type: slist

Allowed input range: (arbitrary string)

Example:

body package_module apt_get
{
    default_options => { "use_curl=1" };
}
query_installed_ifelapsed

Description: How often to query the system for currently installed packages.

For performance reasons, CFEngine maintains a cache of currently installed packages, to avoid calling the package manager too often. This attribute tells CFEngine how often to update this cache (in minutes).

The cache is always updated when CFEngine makes changes to the system.

Type: int

Allowed input range: (Positive integer)

Example:

body package_module apt_get
{
    # Query the package database only every four hours.
    query_installed_ifelapsed => "240";
}

Note for package_module authors: list-installed will be called when the agent repairs a package using the given package_module, when the lock has expired or when the agent is run without locks.

See Also: Package Modules

query_updates_ifelapsed

Description: How often to query the package manager for new updates.

In order not to query repository servers too often, CFEngine maintains a cache of the currently available package updates. This attribute tells CFEngine how often to update this cache (in minutes).

Even when making package changes to the system, CFEngine will not query this information more often than this attribute specifies, however it may make a local query in order to update the cache from local, already downloaded data.

Type: int

Allowed input range: (Positive integer)

Example:

body package_module apt_get
{
    # Query package updates only every 24 hours.
    query_updates_ifelapsed => "1440";
}

Note for package_module authors: list-updates will be called when the lock has expired or when the agent is run without locks. list-updates-local is called in all other conditions.

See Also: Package Modules


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, so it is recommended that reports are guarded appropriately.

bundle agent main
{
  reports:
      "It's reccomended that you always guard reports"
        comment => "Remember by default output from cf-agent when run
                    from cf-execd will be emailed";

    DEBUG|DEBUG_main::
      "Run with --define DEBUG or --define DEBUG_main to display this report";

  methods:
      "Actuate bundle that reports with a return value"
        usebundle => bundle_with_return_value,
        useresult => "return_array",
        comment => "Reports can be used to return data into a parent bundle.
                    This is useful in some re-usable bundle patterns.";

  reports:
      "I got '$(return_array[key])' returned from bundle_with_return_value";

      "Reports can be redirected and appended to files"
        report_to_file => "$(sys.workdir)/report_output.txt",
        comment => "It's important to note that this will suppress the report
                   from stdout.";

      "Report content of a file:$(const.n)$(const.n)------------------------"
        printfile => cat( $(this.promise_filename) );
}

bundle agent bundle_with_return_value
{
  reports:
      "value from bundle_with_return_value"
        bundle_return_value_index => "key";
}

body printfile cat(file)
{
      file_to_print   => "$(file)";
      number_of_lines => "inf";
}

@if minimum_version(3.8)
body printfile head(file)
{
      inherit_from => "cat";
      # GNU head defaults to 10
      number_of_lines => "10";
}
@endif

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

Messages output by report promises are prefixed with the letter R to distinguish them from other output.

bundle agent report
{
  reports:

    loadavg_high::

      "Processes:"
         printfile => cat("$(sys.statedir)/cf_procs");
}

Reports do not fundamentaly make changes to the system and report type promise outcomes are always considered kept.

bundle agent report
{
  vars:
    "classes" slist => classesmatching("report_.*");

  reports:
    "HI"
      classes => scoped_classes_generic("bundle", "report");

    "found class: $(classes)";
}

body classes scoped_classes_generic(scope, x)
# Define x prefixed/suffixed with promise outcome
{
  scope => "$(scope)";
  promise_repaired => { "promise_repaired_$(x)", "$(x)_repaired", "$(x)_ok", "$(x)_reached" };
  repair_failed => { "repair_failed_$(x)", "$(x)_failed", "$(x)_not_ok", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
  repair_denied => { "repair_denied_$(x)", "$(x)_denied", "$(x)_not_ok", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
  repair_timeout => { "repair_timeout_$(x)", "$(x)_timeout", "$(x)_not_ok", "$(x)_not_kept", "$(x)_not_repaired", "$(x)_reached" };
  promise_kept => { "promise_kept_$(x)", "$(x)_kept", "$(x)_ok", "$(x)_not_repaired", "$(x)_reached" };
}
$ cf-agent -KIf ./example_report_outcomes.cf -b report
2015-05-13T12:48:12-0500     info: Using command line specified bundlesequence
R: HI
R: found class: report_ok
R: found class: report_kept
R: found class: report_reached
R: found class: report_not_repaired

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

Deprecated: This attribute is kept for source compatibility, and has no effect. Deprecated in CFEngine 3.4.

intermittency

Deprecated: This attribute is kept for source compatibility, and has no effect. Deprecated in CFEngine 3.4.

printfile

Description: Outputs the content of a file to standard output

Type: body printfile

Common Body Attributes

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

inherit_from
meta

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

Default value: 5

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 main
{

  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:

bundle agent main
{
  methods:

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

  reports:

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

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";

     "bundle_return_value_index is not required to be numerical"
        bundle_return_value_index => "named";
}

History: Introduced in 3.4.0.

lastseen

Deprecated: This attribute is kept for source compatibility, and has no effect. Deprecated in CFEngine 3.4.

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: Commands executed with CFEngine get the environment variables set in environment in body agent control. If you want to set environment variables for an individual command you can prefix the command with env and set variables before executing the command.

bundle agent example
{
  commands:
    "/usr/bin/env MY_ENVIRONMENT_VARIABLE=something_special /tmp/cmd";

    # Or equivlent
    "/usr/bin/env"
      args => "ME=something_special /tmp/cmd";
}

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

See also: arglist, join(), concat(), format()

arglist

Description: Allows to separate the arguments to the command from the command itself, using an slist.

As with args, it is convenient to separate command and arguments. With arglist you can use a slist directly instead of having to provide a single string as with args. That's particularly useful when there are embedded spaces and quotes in your arguments, but also when you want to get them directly from a slist without going through join() or other functions.

The arglist is appended to args if that's defined, to preserve backwards compatibility.

Type: slist

Allowed input range: (arbitrary string)

commands:

  "/bin/echo one"

   args => "two three",
   arglist => { "four", "five" };

So in the example above the command would be:

 /bin/echo one two three four five

History: Was introduced in CFEngine 3.9.0.

See also: args, join(), concat(), format()

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";
    }
Common Body Attributes

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

inherit_from
meta

useshell

Description: Specifies whether or not to use a shell 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 if module is false, true if module is true.

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
    • ^persistence=10 sets any following classes to persist for 10 minutes (use 0 to reset)
    • ^persistence=0 sets any following classes to have no persistence (this is the default)
  • 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.

WARNING: Variables defined by the module protocol are currently limited to alphanumeric characters and _, ., -, [, and ]. Note that classic arrays defined within policy accept additional characters inside of the array index for example: "path[/etc/httpd.conf]" is allowed when defined directly in policy but will produce an error if defined via the module protocol. This limitation is tracked in CFE-2478.

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"
     /bin/echo "^persistence=10"
     /bin/echo "+persistent_10_minute_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.

See Also: usemodule()


Functions

Functions take zero or more values as arguments and return a value. Argument values need to be of the type and range as documented for each function. Some functions are documented with a ..., in which case they take an arbitrary amount of arguments.

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 argument 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.

Function Skipping

If a variable passed to a function is unable to be resolved the function will be skipped. The function will be evaluated during a later pass when all variables passed as arguments are able to be resolved. The function will never be evaluated if any argument contains a variable that never resolves.

Collecting Functions

Some function arguments are marked as collecting which means they can "collect" an argument from various sources. The data is normalized into the JSON format internally, so all of the following data types have consistent behavior.

  • If a key inside a data container is specified (mycontainer[key]), the value under that key is collected. The key can be a string for JSON objects or a number for JSON arrays.

  • If a single data container, CFEngine array, or slist is specified (mycontainer or myarray or myslist), the contents of it are collected.

  • If a single data container, CFEngine array, or slist is specified with @() around it (@(mycontainer) or @(myarray) or @(myslist)), the contents of it are collected.

  • If a function call that returns a data container or slist is specified, that function call is evaluated and the results are inserted, so you can say for instance sort(data_expand(...), "lex") to expand a data container then sort it.

  • If a list (slist, ilist, or rlist) is named, its entries are collected.

  • If any CFEngine "classic" array (array[key]) is named, it's first converted to a JSON key-value map, then collected.

  • If a literal JSON string like [ 1,2,3 ] or { "x": 500 } is provided, it will be parsed and used.

  • If any of the above-mentioned ways to reference variables are used inside a literal JSON string they will be expanded (or the function call will fail). This is similar to the behavior of Javascript, for instance. For example, mergedata('[ thing, { "mykey": otherthing[123] } ]') will wrap the thing in a JSON array; then the contents of otherthing[123] will be wrapped in a JSON map which will also go in the array.

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 internal io data
accessedbefore() host2ip() bundlestate() findprocesses() callstack_callers() countlinesmatching() accumulated()
changedbefore() hostrange() classesmatching() getenv() callstack_promisers() data_readstringarray() ago()
dirname() hostsseen() classmatch() getuid() data_readstringarrayidx() and()
diskfree() hostswithclass() countclassesmatching() getusers() parseintarray() bundlesmatching()
file_hash() hubknowledge() datastate() groupexists() parsejson() canonify()
fileexists() ip2host() execresult() hostinnetgroup() parserealarray() canonifyuniquely()
filesexist() iprange() getclassmetatags() now() parsestringarray() classify()
filesize() ldaparray() getvariablemetatags() packagesmatching() parsestringarrayidx() concat()
filestat() ldaplist() isvariable() packageupdatesmatching() parseyaml() data_expand()
findfiles() ldapvalue() returnszero() processexists() readcsv() data_regextract()
isdir() network_connections() splayclass() registryvalue() readdata() difference()
isexecutable() peerleader() usemodule() userexists() readfile() escape()
islink() peerleaders() variablesmatching() readintarray() eval()
isnewerthan() peers() readintlist() every()
isplain() readtcp() readjson() expandrange()
laterthan() regldap() readrealarray() filter()
lsdir() remoteclassesmatching() readreallist() format()
translatepath() remotescalar() readstringarray() getfields()
selectservers() readstringarrayidx() getgid()
url_get() readstringlist() getindices()
readyaml() getvalues()
regline() grep()
hash()
hashmatch()
ifelse()
intersection()
irange()
isgreaterthan()
islessthan()
join()
lastnode()
length()
makerule()
maparray()
mapdata()
maplist()
max()
mean()
mergedata()
min()
none()
not()
nth()
on()
or()
product()
randomint()
regarray()
regcmp()
regex_replace()
regextract()
reglist()
reverse()
rrange()
shuffle()
some()
sort()
splitstring()
storejson()
strcmp()
strftime()
string_downcase()
string_head()
string_length()
string_mustache()
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() bundlestate() accessedbefore()
product() rrange() canonify() ago() callstack_promisers() callstack_callers() changedbefore()
sum() canonifyuniquely() countclassesmatching() classesmatching() data_expand() classify()
variance() concat() countlinesmatching() difference() data_readstringarray() classmatch()
dirname() diskfree() expandrange() data_readstringarrayidx() every()
escape() filesize() filter() data_regextract() fileexists()
eval() getfields() findfiles() datastate() filesexist()
execresult() getgid() getclassmetatags() findprocesses() groupexists()
file_hash() getuid() getindices() mapdata() hashmatch()
filestat() length() getusers() mergedata() hostinnetgroup()
format() now() getvalues() network_connections() hostrange()
getenv() on() getvariablemetatags() packagesmatching() iprange()
hash() parseintarray() grep() packageupdatesmatching() isdir()
host2ip() parserealarray() hostsseen() parsejson() isexecutable()
hubknowledge() parsestringarray() hostswithclass() parseyaml() isgreaterthan()
ifelse() parsestringarrayidx() intersection() readcsv() islessthan()
ip2host() randomint() ldaplist() readdata() islink()
join() readintarray() lsdir() readjson() isnewerthan()
lastnode() readrealarray() maparray() readyaml() isplain()
ldapvalue() readstringarray() maplist() url_get() isvariable()
makerule() readstringarrayidx() peerleaders() laterthan()
max() selectservers() peers() ldaparray()
min() string_length() readintlist() none()
not() readreallist() processexists()
nth() readstringlist() regarray()
or() reverse() regcmp()
peerleader() shuffle() regextract()
readfile() sort() regldap()
readtcp() splitstring() regline()
regex_replace() string_split() reglist()
registryvalue() sublist() remoteclassesmatching()
remotescalar() unique() returnszero()
storejson() variablesmatching() some()
strftime() splayclass()
string_downcase() strcmp()
string_head() usemodule()
string_mustache() userexists()
string_reverse()
string_tail()
string_upcase()
translatepath()

regex_replace

Prototype: regex_replace(string, regex, replacement, options)

Return type: string

Description: In a given string, replaces a regular expression with something else.

Arguments:

  • string: string, in the range: .*
  • regex: regular expression, in the range: .*
  • replacement: string, in the range: .*
  • options: string, in the range: .*

The supported options are single letters you place in the options string in any order. Consult http://pcre.org/pcre.txt for the exact meaning of the uppercase options, and note that some can be turned on inside the regular expression, e.g. (?s).

  • i: case-insensitive
  • m: multiline (PCRE_MULTILINE)
  • s: dot matches newlines too (PCRE_DOTALL)
  • x: extended regular expressions (PCRE_EXTENDED, very nice for readability)
  • U: ungreedy (PCRE_UNGREEDY)
  • T: this is not a regular expression, just replace the exact string

In the replacement, $1 and \1 refer to the first capture group. $2 and \2 refer to the second, and so on, except there is no \10 or higher, you have to use $10 etc.

In addition, $+ is replaced with the capture count. $' (dollar sign + single quote) is the part of the string after the regex match. $` (dollar sign + backtick) is the part of the string before the regex match. $& holds the entire regex match.

Example:

bundle agent main
{
  vars:
      # global regex replace A with B
      "AB" string => regex_replace("This has AAA rating", "A", "B", "g");
      # global regex replace [Aa] with B (case insensitive)
      "AaB" string => regex_replace("This has AAA rating", "A", "B", "gi");
      # global replace every three characters with [cap=thecharacters] using $1
      "cap123" string => regex_replace("abcdefghijklmn", "(...)", "[cap=$1]", "g");
      # multiple captures using \1 \2 (just like $1 $2 but can only go up to \9)
      "path_breakdown" string => regex_replace("/a/b/c/example.txt", "(.+)/(.+)", "dirname = \1 file basename = \2", "");

  reports:
      # in order, the above...
      "AB replacement = '$(AB)'";
      "AaB replacement = '$(AaB)'";
      "cap123 replacement = '$(cap123)'";
      "path_breakdown replacement = '$(path_breakdown)'";
}

Output:

R: AB replacement = 'This has BBB rating'
R: AaB replacement = 'This hBs BBB rBting'
R: cap123 replacement = '[cap=abc][cap=def][cap=ghi][cap=jkl]mn'
R: path_breakdown replacement = 'dirname = /a/b/c file basename = example.txt'

History: Was introduced in version 3.8.0 (2015)

See also: data_regextract() regextract()


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)";
}

See Also: Module Protocol


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 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.

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()).


readdata

Prototype: readdata(filename, filetype)

Return type: data

Description: Parses CSV, JSON, or YAML data from file filename and returns the result as a data variable.

When filetype is auto, the file type is guessed from the extension (ignoring case): .csv means CSV; .json means JSON; .yaml means YAML. If the file doesn't match any of those names, JSON is used.

When filetype is CSV, this function behaves exactly like readcsv() and returns the same data structure.

When filetype is JSON, this function behaves exactly like readjson() and returns the same data structure, except there is no data size limit (maxbytes is inf).

When filetype is YAML, this function behaves exactly like readyaml() and returns the same data structure, except there is no data size limit (maxbytes is inf).

Arguments:

  • filename: string, in the range: "?(/.*)
  • filetype: one of
    • CSV
    • YAML
    • JSON
    • auto

Example:

Prepare:

echo -n 1,2,3 > /tmp/file.csv
echo -n '{ "x": 200 }' > /tmp/file.json
echo '- a' > /tmp/file.yaml
echo '- b' >> /tmp/file.yaml

Run:

bundle agent main
{
  vars:

      "csv" data => readdata("/tmp/file.csv", "auto"); # or file type "CSV"
      "json" data => readdata("/tmp/file.json", "auto"); # or file type "JSON"

      "csv_str" string => format("%S", csv);
      "json_str" string => format("%S", json);

    feature_yaml:: # we can only test YAML data if libyaml is compiled in
      "yaml" data => readdata("/tmp/file.yaml", "auto"); # or file type "YAML"
      "yaml_str" string => format("%S", yaml);
  reports:

      "From /tmp/file.csv, got data $(csv_str)";
      "From /tmp/file.json, got data $(json_str)";
    feature_yaml::
      "From /tmp/file.yaml, we would get data $(yaml_str)";
    !feature_yaml:: # show the output anyway
      'From /tmp/file.yaml, we would get data ["a","b"]';

}

Output:

R: From /tmp/file.csv, got data [["1","2","3"]]
R: From /tmp/file.json, got data {"x":200}
R: From /tmp/file.yaml, we would get data ["a","b"]

See also: readcsv(), readyaml(), readjson(), and data documentation.

History: Was introduced in 3.7.0.


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.

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:

  • 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 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.

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.

This function can accept many types of data parameters.

Lexicographical, integer, real, IP, and MAC address sorting is supported currently. The example below will show each sorting mode in action. mode is optional, and defaults to lex.

Note IPv6 addresses can not use uppercase hexadecimal characters (A-Z) but must use lowercase (a-z) instead.

Arguments:

  • list: string, in the range: .*
  • 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'

History: - Function added in 3.6.0. - Collecting function behavior added in 3.9.0. - Optional mode defaulting to lex behavior added in 3.9.0.

See also: shuffle(), about collecting functions, and data documentation.


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 1 minute ago
      stime_range => irange(ago(100,0,0,5,30,0),ago(0,0,0,0,1,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.

This function can accept many types of data parameters.

Arguments:

  • regex: regular expression, in the range: .*
  • list: string, in the range: .*

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 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

History: The collecting function behavior was added in 3.9.

See also: About collecting functions, 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.
    • xattr : a string with newline-separated extended attributes and SELinux contexts in key=value<NEWLINE>key2=value2<NEWLINE>tag1<NEWLINE>tag2 format.

On Mac OS X, you can list and set extended attributes with the xattr utility.

On SELinux, the contexts are the same as what you see with ls -Z.

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:

  • linktarget will prepend the directory name to relative symlink targets, in order to be able to resolve them. Use linktarget_shallow to get the exact link as-is in case it is a relative link.
  • 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.

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)


readcsv

Description: Parses CSV data from the first 1 MB of file filename and returns the result as a data variable.

While it may seem similar to data_readstringarrayidx() and data_readstringarray(), the readcsv() function is more capable because it follows RFC 4180, especially regarding quoting. This is not possible if you just split strings on a regular expression delimiter.

The returned data is in the same format as data_readstringarrayidx(), that is, a data container that holds a JSON array of JSON arrays.

Example:

Prepare:

echo -n 1,2,3 > /tmp/csv

Run:

bundle agent main
{
  vars:

      # note that the CSV file has to have ^M (DOS) EOL terminators
      # thus the prep step uses `echo -n` and just one line, so it will work on Unix
      "csv" data => readcsv("/tmp/csv");
      "csv_str" string => format("%S", csv);

  reports:

      "From /tmp/csv, got data $(csv_str)";

}

Output:

R: From /tmp/csv, got data [["1","2","3"]]

Note: CSV files formatted according to RFC 4180 must end with the CRLF sequence. Thus a text file created on Unix with standard Unix tools like vi will not, by default, have those line endings.

See also: readdata(), data_readstringarrayidx(),data_readstringarray(), parsejson(), storejson(), mergedata(), and data documentation.

History: Was introduced in 3.7.0.


storejson

Prototype: storejson(data_container)

Return type: string

Description: Converts a data container to a JSON string.

This function can accept many types of data parameters.

Arguments:

  • data_container: string, in the range: .*

Example:

   vars:

      "loadthis"
         data =>  readjson("/tmp/data.json", 4000);
      "andback"
         string =>  storejson(loadthis);
   reports:
      "Converted /tmp/data.json to '$(andback)'";

History: The collecting function behavior was added in 3.9.

See also: readjson(), readyaml(), parsejson(), parseyaml(), about collecting functions, and data documentation.


unique

Prototype: unique(list)

Return type: slist

Description: Returns list of unique elements from list.

This function can accept many types of data parameters.

Arguments:

  • list: 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 => 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

History: The collecting function behavior was added in 3.9.

See also: filter(), about collecting functions, and data documentation.


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.

This function can accept many types of data parameters.

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: .*

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");
      "values_sorted" slist => sort(values, lex);

      # works with data containers too
      "d" data => parsejson('{ "k": [ 1, 2, 3, "a", "b", "c" ] }');

      "cvalues"        slist => getvalues("d[k]");
      "cvalues_sorted" slist => sort(cvalues, lex);

  reports:
      "Found values: $(values_sorted)";
      "Found container values: $(cvalues_sorted)";

}

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

History: The collecting function behavior was added in 3.9.

See also: getindices(), about collecting functions, and data documentation.


intersection

Prototype: intersection(list1, list2)

Return type: slist

Description: Returns the unique elements in list1 that are also in list2.

This function can accept many types of data parameters.

Arguments:

  • list1: string, in the range: .*
  • list2: string, in the range: .*

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: About collecting functions, 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: .*
  • 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().


readyaml

Prototype: readyaml(filename, maxbytes)

Return type: data

Description: Parses YAML data from the file filename and returns the result as a data variable. maxbytes is optional, if specified, only the first maxbytes bytes are read from filename.

Arguments:

  • filename: string, in the range: "?(/.*)
  • maxbytes: int, in the range: 0,99999999999

Example:

    vars:

      "loadthis"

         data =>  readyaml("/tmp/data.yaml", 4000);

See also: readdata(), parsejson(), parseyaml(), storejson(), mergedata(), and data documentation.


callstack_promisers

Prototype: callstack_promisers()

Return type: slist

Description: Return the promisers along the call stack for the current promise.

This is a call stack inspection function and the specific content may be tied to a specific CFEngine version. Using it requires writing code that takes the specific CFEngine version into account.

The returned data container is a slist of promiser names. It's a much simpler version of callstack_callers() intended for quick debugging.

Example:

    vars:
      "my_promisers" slist => callstack_promisers();

Output:

    { "my_promisers" }

History: Introduced in CFEngine 3.9

See also: callstack_callers()


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-08-18T14:13:28+0100 error: Proposed executable file '/usr/local/bin/mycommand' doesn't exist
2014-08-18T14:13:28+0100 error: returnszero '/usr/local/bin/mycommand' is assumed to be executable but isn't
error: Proposed executable file '/usr/local/bin/mycommand' doesn't exist
error: 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().


file_hash

Prototype: file_hash(file, algorithm)

Return type: string

Description: Return the hash of file using the hash algorithm.

This function is much more efficient that calling hash() on a string with the contents of file.

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:

  • file: string, in the range: "?(/.*)
  • algorithm: one of
    • md5
    • sha1
    • sha256
    • sha384
    • sha512

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:

      "md5" string => file_hash("/tmp/1","md5");
      "sha256" string => file_hash("/tmp/2","sha256");
      "sha384" string => hash("/tmp/3","sha384");
      "sha512" string => hash("/tmp/3","sha512");

  reports:

      "'1\n' hashed to: md5 $(md5)";
      "'2\n' hashed to: sha256 $(sha256)";
      "'3\n' hashed to: sha384 $(sha384)";
      "'3\n' hashed to: sha512 $(sha512)";

}

Output:

R: '1\n' hashed to: md5 b026324c6904b2a9cb4b88d6d61c81d1
R: '2\n' hashed to: sha256 53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3
R: '3\n' hashed to: sha384 54f7379844b41bf513c0557a7195ca96a8ac90d0f8cc87d3607ef7ab593a7c61732759387afaabaf72ca2c0bd599373e
R: '3\n' hashed to: sha512 48b3c46b24db82059b5c87603066cf8d2165837d66e268286feb384644c808c06edf99aeaca0d879f4ee6ec70ebfaa0b98d5b77c12f7c0a68de3f7302dec6e21

History: Introduced in CFEngine 3.7.0

See also: hash()


sum

Prototype: sum(list)

Return type: real

Description: Return the sum of the reals in list.

This function can accept many types of data parameters.

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: .*

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). The collecting function behavior was added in 3.9.

See also: product(), about collecting functions, and data documentation.


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, tag1, tag2, ...)

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.

You can optionally restrict the search by tags, which you can list after the regular expression.

Example:

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

bundle agent example
{
  classes:

      "do_it" and => { classmatch("cfengine_3.*"), "any" };
      "have_hardclass_nonesuch" expression => classmatch("nonesuchclass_sodonttryit", hardclass);
  reports:

    do_it::

      "Host matches pattern";

    have_hardclass_nonesuch::

      "Host has that really weird hardclass";
}

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.

This function can accept many types of data parameters.

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: .*
  • 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

History: The collecting function behavior was added in 3.9.

See also: length(), about collecting functions, and data documentation.


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.

NOTE: The randomness produced by randomint is not safe for cryptographic usage.

Arguments:

  • lower: int, in the range: -99999999999,99999999999
  • upper: int, in the range: -99999999999,99999999999

Example:

bundle agent main
{
  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";

    show_random::
      "Randomly generated '$(random)'";
}

Output: (when show_random is defined)

R: The generated random number was above 3
R: Randomly generated '9'
R: Randomly generated '52'
R: Randomly generated '26'

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: .*

Examples:

This example shows a way to determine if IPV4 forwarding is enabled or not.

bundle agent main
{
  vars:

    linux::

      "file" string => "/proc/sys/net/ipv4/ip_forward";
      "reg_enabled" string => "^1$";
      "reg_disabled" string => "^0$";

  classes:

    linux::

      "ipv4_forwarding_enabled" -> { "SecOps" }
        expression => regline( $(reg_enabled) , $(file) ),
        comment => "We want to know if ip forwarding is enabled because it is a
                    potential security issue.";

      "ipv4_forwarding_disabled" -> { "SecOps" }
        expression => regline( $(reg_disabled) , $(file) );

  reports:

    ipv4_forwarding_enabled::
      "I found that IPv4 forwarding is enabled!";

    ipv4_forwarding_disabled::
      "I found that IPv4 forwarding is disabled.";
}
R: I found that IPv4 forwarding is disabled.

For edit_line applications it may be useful 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)"),
      scope => "bundle"; # Unless you need the class outside of the bundle you
                         # should always scope it to the bundle. This can
                         # prevent issues when the bundle is used multiple
                         # times, and the classes promise is expected to be
                         # re-evaluated. If the class is namespace scoped the
                         # class will be available to other bundles and persist
                         # until it is explicitly canceled or until the end of
                         # the agent run.

  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";
}

bundlestate

Prototype: bundlestate(bundlename)

Return type: data

Description: Returns the current evaluation data state for bundle bundlename.

The returned data container will have keys corresponding to the variables in bundle bundlename. The value is converted to a data container (JSON format) if necessary. So for example the variable x holding the CFEngine slist { "1", "a", "foo" } will be converted to the equivalent JSON array under the key x: "x": [ "1", "a", "foo" ].

Note: unlike datastate() classes are not collected.

The namespace of the bundle should not be included if it's in the default: namespace (all CFEngine bundles are, unless you override that). But if the bundle is in another namespace, you must prefix the name with the namespace in the normal mynamespace:mybundle fashion.

Arguments:

  • bundlename: string, in the range: [a-zA-Z0-9_$(){}\[\].:]+

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:
      "bundle_state" data => bundlestate("holder");

      # all the variables in bundle "holder" defined as of the execution of bundlestate() will be here
      "holderstate" string => format("%S", "bundle_state");

  reports:
      "holder vars = $(holderstate)";
}

Output:

R: holder vars = {"d":[4,5,6],"list":["element1","element2"],"s":"Hello!"}

See also: getindices(), classesmatching(), variablesmatching(), mergedata(), template_method, mustache, datastate()


none

Prototype: none(regex, list)

Return type: boolean

Description: Returns whether no element in list matches the regular expression regex.

This function can accept many types of data parameters.

Arguments:

  • regex: regular expression, in the range: .*
  • list: string, in the range: .*

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

History: The collecting function behavior was added in 3.9.

See also: About collecting functions, 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()).

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*
  • 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

History: Was introduced in version 3.6.0 (2014). The collecting function behavior was added in 3.9.

See also: sort(), variance(), sum(), max(), mean(), about collecting functions, and data documentation.


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 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 : 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 # Comment >> /tmp/cfe_list_ints
echo 2 >> /tmp/cfe_list_ints
echo # Another Comment >> /tmp/cfe_list_ints
echo 3 >> /tmp/cfe_list_ints
echo 1.1 > /tmp/cfe_list_reals
echo # Comment >> /tmp/cfe_list_reals
echo 2.2 >> /tmp/cfe_list_reals
echo # Another Comment >> /tmp/cfe_list_reals
echo 3 >> /tmp/cfe_list_reals
echo alpha > /tmp/cfe_list_strings
echo # Comment >> /tmp/cfe_list_strings
echo beta >> /tmp/cfe_list_strings
echo # Another Comment >> /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]*","[\n]",10,400);
      "strings" slist => readstringlist("/tmp/cfe_list_strings", "#[^\n]*", "\s", 10, 400);
      "reals" rlist => readreallist("/tmp/cfe_list_reals","#[^\n]*","[\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

url_get

Prototype: url_get(url, options_container)

Return type: data

Description: Retrieves the contents of a url using options from a data container. The data is returned in a data container.

NOTE that the options_container can be specified as inline JSON

This function can accept many types of data parameters.

Currently only file, http, and ftp URLs are supported. Internally, libcurl is used.

url_get() caches its results. To invalidate the cache, use a different set of options, e.g. by modifying an unused key with the system time.

If the libcurl integration is not available, the function will exit with an error and the variable will remain undefined. If the libcurl initialization fails, the function will also exit with an error. In every other normal case, the function will return a valid data container. In official CFEngine packages, libcurl integration is always provided.

The available options currently are:

  • url.max_content: if present, specifies the maximum number of content bytes to retrieve.
  • url.max_headers: if present, specifies the maximum number of response headers to retrieve.
  • url.verbose: if 1, libcurl will be more verbose while retrieving the content
  • url.timeout: if present, libcurl will time out the request after that many seconds
  • url.referer: if present, libcurl will set the Referer to this
  • url.user-agent: if present, libcurl will set the User-Agent to this
  • url.headers: an array of strings in the format Foo: bar specifying headers for the request

The returned data container will have the following keys:

  • returncode: the HTTP response code, e.g. 200.
  • rc: the libcurl integer result code, either 0 for success or something else for failure
  • error_message: when present, indicates the request was unsuccessful and explains why
  • success: a boolean. When success is false, the result code was not 0 and the request was unsuccessful.
  • content: the response content as a string
  • headers: the response headers as a string

Arguments:

  • url: string, in the range: .*
  • options_container: string, in the range: .*

Example:

This example retrieves two URLs using one set of options. The options are specified in JSON and parsed into a data container options. That data container is then passed to each invocation of url_get.

bundle agent main
{
  vars:
      "options_str" string => '
{
  "url.max_content": 512,
  "url.verbose": 0,
  "url.headers": [ "Foo: bar" ]
}';
      "options" data => parsejson($(options_str));
      "url" string => "http://cfengine.com";
      "res" data => url_get($(url), options);
      "out" string => format("%S", res);

      "url2" string => "http://nosuchcfenginehost.com";
      "res2" data => url_get($(url2), options);
      "out2" string => format("%S", res2);

  reports:
      "$(this.bundle): from $(url) with options $(options_str) we got $(out)";
      "$(this.bundle): from $(url2) with options $(options_str) we got $(out2)";
}

Output:

R: main: from http://cfengine.com with options
{
  "cfengine.max_content": 512,
  "curl.verbose": 0,
  "curl.headers": [ "Foo: bar" ]
} we got {"returncode":200,"rc":0,"success":true,"content":"\n<!DOCTYPE html>\n<!--[if lt IE 7]>\n<html class=\"no-js lt-ie9 lt-ie8 lt-ie7\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <![endif]-->\n<!--[if IE 7]>\n<html class=\"no-js lt-ie9 lt-ie8\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <![endif]-->\n<!--[if IE 8]>\n<html class=\"no-js lt-ie9\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <![endif]-->\n<!--[if gt IE 8]><!-->\n<html class=\"no-js\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <!--<![endif]-->\n<head>\n\n    \n    <meta charset=\"utf-8\">\n\n    <title>\n        CFEng","headers":"HTTP/1.1 200 OK\r\nDate: Fri, 27 Mar 2015 18:13:01 GMT\r\nServer: Apache\r\nX-Powered-By: PHP/5.3.3\r\nX-Pingback: http://cfengine.com/xmlrpc.php\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n"}

R: main: from http://nosuchcfenginehost.com with options
{
  "cfengine.max_content": 512,
  "curl.verbose": 0,
  "curl.headers": [ "Foo: bar" ]
} we got {"returncode":0,"rc":6,"success":false,"content":"","headers":""}

History: Introduced in CFEngine 3.8. The collecting function behavior was added in 3.9.

See also: readtcp(), mergedata(), parsejson(), about collecting functions, and data documentation.


expandrange

Prototype: expandrange(string_template, stepsize)

Return type: slist

Description: Generates a list based on an ordered list of numbers selected from a range of integers, in steps specified by the second argument.

The function is the inverse of functions like iprange() which match patterns of numerical ranges that cannot be represented as regular expressions. The list of strings is composed from the text as quoted in the first argument, and a numerical range in square brackets is replaced by successive numbers from the range.

Arguments:

  • string_template: string, in the range: .*
  • stepsize: string, in the range: .*
vars:

 "int_group1" slist => {
                       "swp10",
                       "swp11",
                       "swp12",
                       expandrange("swp[13-15]", 1)
                       };

interfaces:

 "$(int_group)"

     tagged_vlans => { "100", "145" },
    untagged_vlan => "1",
       link_state => up;

History: Introduced in CFEngine 3.7


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.

This function can accept many types of data parameters.

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 '.'

History: The collecting function behavior was added in 3.9.

See also: About collecting functions, 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.

This function can accept many types of data parameters.

Arguments:

  • regex: regular expression, in the range: .*
  • list: string, in the range: .*

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-- }

History: The collecting function behavior was added in 3.9.

See also: About collecting functions, filter(), every(), some(), and none().


shuffle

Prototype: shuffle(list, seed)

Return type: slist

Description: Return list shuffled with seed.

This function can accept many types of data parameters.

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: .*
  • 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'

History: The collecting function behavior was added in 3.9.

See also: sort(), about collecting functions, and data documentation.


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.

This function can accept many types of data parameters.

Arguments:

  • list1: string, in the range: .*
  • list2: string, in the range: .*

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 ''

History: The collecting function behavior was added in 3.9.

See also: About collecting functions, 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:

[
   {
      "arch":"default",
      "method":"dpkg",
      "name":"zsh-common",
      "version":"5.0.7-5ubuntu1"
   }
]

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: .*

Argument Descriptions:

  • package_regex - Regular expression matching packge name
  • version_regex - Regular expression matching package version
  • arch_regex - Regular expression matching package architecutre
  • method_regex - Regular expression matching package method (apt-get, rpm, etc ...)

The following code extracts just the package names, then looks for some desired packages, and finally reports if they are installed.

IMPORTANT: Please note that you need to provide package_inventory attribute in body common control in order to be able to use this function. Also depending on the value(s) of package_inventory only packages from selected package modules will be returned. For more information about package_inventory please read package_inventory section.

body common control

{
      bundlesequence => { "missing_packages" };
}


bundle agent missing_packages
{
  vars:
    # List of desired packages
    "desired" slist => { "mypackage1", "mypackage2" };

    # Get info on all installed packages
    "installed" data => packagesmatching(".*",".*",".*",".*");
    "installed_indices" slist => getindices(installed);

    # Build a simple array of the package names so that we can use
    # getvalues to pull a unified list of package names that are installed.
    "installed_name[$(installed_indices)]"
      string => "$(installed[$(installed_indices)][name])";

    # Get unified list of installed packages
    "installed_names" slist => getvalues("installed_name");

    # Determine packages that are missing my differencing the list of
    # desired packages, against the list of installed packages
    "missing_list" slist => difference(desired,installed_names);

  reports:
    # Report on packages that are missing, installed
    # and what we were looking for
    "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.

Example:

      "all_packages" data => packagesmatching(".*", ".*", ".*", ".*");

Refresh rules: * inastalled packages cache used by packagesmatching() is refreshed at the end of each agent run in accordance with constraints defined in the relevant package module body. * installed packages cache is refreshed after installing or removing a package. * installed packages cache is refreshed if no local cache exists. This means a reliable way to force a refresh of CFEngine's internal package cache is to simply delete the local cache:

            $(sys.statedir)/packages_installed_<package_module>.lmdb*

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. Can also wrap and unwrap data containers.

The returned data container will have the keys from each of the named data containers, arrays, or lists.

If all the 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).

This function can accept many types of data parameters.

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", "test3" };
}

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_wrap_array_d2" data => mergedata("d1", "[ d2 ]");
      "merged_d1_wrap_map_d2" data => mergedata("d1", '{ "newkey": d2 }');

      "merged_d1_d2_str" string => format("merging %S with %S produced %S", d1, d2, merged_d1_d2);
      "merged_d1_wrap_array_d2_str" string => format("merging %S with wrapped [ %S ] produced %S", d1, d2, merged_d1_wrap_array_d2);
      "merged_d1_wrap_map_d2_str" string => format('merging %S with wrapped { "newkey": %S produced %S', d1, d2, merged_d1_wrap_map_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_wrap_array_d2_str)";
      "$(merged_d1_wrap_map_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";
}

bundle agent test3
{
    vars:
       "dest_files" slist => { "/tmp/default.json", "/tmp/epel.json" };
       "template_file" string => "repository.mustache";

       "process_templates" data => mergedata('{ "$(template_file)" : dest_files }');
       "process__templates_str" string => format("%S", "process_templates");

    reports:
        "$(this.bundle) $(process__templates_str)";
        "$(this.bundle) $(process_templates[$(template_file)])";
}

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 wrapped [ {"b":[4,5,6]} ] produced {"0":{"b":[4,5,6]},"a":[1,2,3],"b":[]}
R: merging {"a":[1,2,3],"b":[]} with wrapped { "newkey": {"b":[4,5,6]} produced {"a":[1,2,3],"b":[],"newkey":{"b":[4,5,6]}}
R: merging {"a":[1,2,3],"b":[]} with [4,5,6] produced {"0":4,"1":5,"2":6,"a":[1,2,3],"b":[]}
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"}
R: test3 {"repository.mustache":["/tmp/default.json","/tmp/epel.json"]}
R: test3 /tmp/default.json
R: test3 /tmp/epel.json

History: Was introduced in CFEngine 3.6.0 (2014). The collecting function behavior was added in 3.9.

See also: getindices(), getvalues(), readjson(), parsejson(), readyaml(), parseyaml(), about collecting functions, 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, interface)

Return type: boolean

Description: Returns whether the current host lies in the range of IP addresses specified, optionally checking only interface.

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");

  "dmz_1_eth0" expression => iprange("128.39.89.10-15", "eth0");
  "lab_1_eth0" expression => iprange("128.39.74.1/23", "eth0");

reports:

  dmz_1::

    "DMZ 1 subnet";

  lab_1::

    "Lab 1 subnet";
}

History: The optional interface parameter was introduced in CFEngine 3.9.


variance

Prototype: variance(list)

Return type: real

Description: Return the variance of the numbers in list.

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*

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

History: Was introduced in version 3.6.0 (2014). The collecting function behavior was added in 3.9.

See also: sort(), mean(), sum(), max(), min(), about collecting functions, and data documentation.


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:

[
   {
      "arch":"default",
      "method":"dpkg",
      "name":"syncthing",
      "version":"0.12.8"
   }
]

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: .*

Argument Descriptions:

  • package_regex - Regular expression matching packge name
  • version_regex - Regular expression matching package version
  • arch_regex - Regular expression matching package architecutre
  • method_regex - Regular expression matching package method (apt-get, rpm, etc ...)

IMPORTANT: Please note that you need to provide package_inventory attribute in body common control in order to be able to use this function. Also depending on the value(s) of package_inventory only packages from selected package modules will be returned. For more information about package_inventory please read package_inventory section.

Example:

      "all_package_updates" data => packageupdatesmatching(".*", ".*", ".*", ".*");

Refresh rules: * updates cache used by packageupdatesmatching() is refreshed at the end of each agent run in accordance with constraints defined in the relevant package module body. * updates cache is refreshed every time repo type package is installed or removed * updates cache is refreshed if no local cache exists. This means a reliable way to force a refresh of CFEngine's internal package cache is to simply delete the local cache:

            $(sys.statedir)/packages_updates_<package_module>.lmdb*

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, tag1, tag2, ...)

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.

You can optionally restrict the search by tags, which you can list after the regular expression.

Example:

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

bundle agent example
{
  vars:
      # this is anchored, so you need .* to match multiple things
      "num" int => countclassesmatching("cfengine");
      "hardcount" int => countclassesmatching(".*", "hardclass");
  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 then the second one is returned, as if you had used the first one in a class expression. So the first item in the pair can be more than just a class name, it's a whole context like Tuesday.linux.!verbose)

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 = {"d":[4,5,6],"list":["element1","element2"],"s":"Hello!"}
R: I have the holder class

See also: getindices(), classesmatching(), variablesmatching(), mergedata(), template_method, mustache, bundlestate()


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.

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*
  • 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

History: The collecting function behavior was added in 3.9.

See also: nth(), filter(), about collecting functions, and data documentation.


reglist

Prototype: reglist(list, regex)

Return type: boolean

Description: Returns whether the anchored regular expression regex matches any item in list.

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*
  • 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.

History: The collecting function behavior was added in 3.9.

See also: getindices(), getvalues(), about collecting functions, and data documentation.


reverse

Prototype: reverse(list)

Return type: slist

Description: Reverses a list.

This is a simple function to reverse a list.

This function can accept many types of data parameters.

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

History: The collecting function behavior was added in 3.9.

See also: filter(), grep(), every(), some(), none(), about collecting functions, and data documentation.


string_mustache

Prototype: string_mustache(template_string, optional_data_container)

Return type: string

Description: Formats a Mustache string template into a string, using either the system datastate() or an explicitly provided data container.

The usual Mustache facilities like conditional evaluation and loops are available, see the example below.

Example:

body common control
{
      bundlesequence => { "config", "example" };
}

bundle agent config
{
  vars:
      "deserts" data => parsejson('{ "deserts": {
  "Africa": "Sahara",
  "Asia": "Gobi"
} }');
}


bundle agent example
{
  vars:
      # {{@}} is the current key during an iteration in 3.7 with Mustache
      "with_data_container" string => string_mustache("from container: deserts = {{%deserts}}
from container: {{#deserts}}The desert {{.}} is in {{@}}. {{/deserts}}", "config.deserts");

      # you can dump an entire data structure with {{%myvar}} in 3.7 with Mustache
      "with_system_state" string => string_mustache("from datastate(): deserts = {{%vars.config.deserts.deserts}}
from datastate(): {{#vars.config.deserts.deserts}}The desert {{.}} is in {{@}}. {{/vars.config.deserts.deserts}}"); # will use datastate()

  reports:
      "With an explicit data container: $(with_data_container)";

      "With the system datastate(): $(with_system_state)";
}

Output:

R: With an explicit data container: from container: deserts = {
  "Africa": "Sahara",
  "Asia": "Gobi"
}
from container: The desert Sahara is in Africa. The desert Gobi is in Asia. 
R: With the system datastate(): from datastate(): deserts = {
  "Africa": "Sahara",
  "Asia": "Gobi"
}
from datastate(): The desert Sahara is in Africa. The desert Gobi is in Asia. 

History: Introduced in CFEngine 3.7

See also: datastate(), readjson(), parsejson(), data.


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 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.

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

See also: file_hash()


data_expand

Prototype: data_expand(data_container)

Return type: data

Description: Transforms a data container to expand all variable references.

This function will take a data container and expand variable references once in all keys and values.

This function can accept many types of data parameters.

Any compound (arrays or maps) data structures will be expanded recursively, so for instance data in a map inside another map will be expanded.

This function is chiefly useful if you want to read data from an external source and it can contain variable references.

Arguments:

  • data_container: string, in the range: .*

Example:

bundle agent main
{
  vars:
      "x" string => "the expanded x";
      "y" string => "the expanded y";

      "read" data => readjson("/tmp/expand.json", inf);
      "expanded" data => data_expand(read);

      "expanded_str" string => format("%S", expanded);

  reports:
      "$(this.bundle): the x and y references expanded to $(expanded_str)";
}

Output:

R: main: the x and y references expanded to {"the expanded x":"the expanded y"}

Notes:

History: Was introduced in version 3.7.0 (2015). The collecting functions behavior was added in 3.9.

See also: readcsv(), readjson(), readyaml(), mergedata(), about collecting functions, and data documentation.


findprocesses

Prototype: findprocesses(regex)

Return type: data

The return value is cached.

Description: Return the list of processes that match the given regular expression regex.

This function searches for the given regular expression in the process table. Use .*sherlock.* to find all the processes that match sherlock. Use .*\bsherlock\b.* to exclude partial matches like sherlock123 (\b matches a word boundary).

Arguments:

  • regex: regular expression, in the range: .*

The returned data container is a list of key-value maps. Each one is guaranteed to have the key pid with the process ID. The key line will also be available with the raw process table contents.

The process table is usually obtained with something like ps -eo user,pid,ppid,pgid,%cpu,%mem,vsize,ni,rss,stat,nlwp,stime,time,args, and the CMD or COMMAND field (args) is used to match against. However the exact data used may change per platform and per CFEngine release.

Example:

    vars:
      "holmes" data => findprocesses(".*sherlock.*");

Output:

    [ { "pid": "2378", "line": "...the ps output here" }, ... ]

History: Introduced in CFEngine 3.9

See also: processes processexists().


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 and class modes with infix option are supported for evaluating traditional math expressions.

All the math is done with the C double type internally. The results are returned as a string. When the mode is math the returned value is a floating-point value formatted to 6 decimal places as a string.

mode and options are optional and default to math and infix, respectively.

Example:

  vars:
    # returns 20.000000
    "result" expression => eval("200/10", "math", "infix");

When the mode is class, the returned string is either false for 0 (!any) or true for anything else (any) so it can be used in a class expression under classes. The == operator (see below) is very convenient for this purpose. The actual accepted values for false allow a tiny margin around 0, just like ==.

Example:

  classes:
    # the class will be set
    "they_are_equal" expression => eval("20 == (200/10)", "class", "infix");

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
    • class
  • 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'

History: - Function added in 3.6.0. - mode and options optional and default to math and infix, respectively as of 3.9.0.


data_regextract

Prototype: data_regextract(regex, string)

Return type: data

Description: Returns a data container filled with backreferences and named captures if the anchored regex matches the string.

This function is significantly better than regextract() because it doesn't create classic CFEngine array variables and supports named captures.

If there are any back reference matches from the regular expression, then the data container will be populated with the values, in the manner:

    $(container[0]) = entire string
    $(container[1]) = back reference 1, etc

Note 0 and 1 are string keys in a map, not offsets.

If named captures are used, e.g. (?<name1>...) to capture three characters under name1, then that will be the key instead of the numeric position of the backreference.

PCRE named captures are described in http://pcre.org/pcre.txt and several syntaxes are supported:

     (?<name>...)    named capturing group (Perl)
     (?'name'...)    named capturing group (Perl)
     (?P<name>...)   named capturing group (Python)

Arguments:

  • regex: regular expression, in the range: .*
  • string: string, in the range: .*

Example:

bundle agent main
{
  vars:
      # the returned data container is a key-value map:

      # the whole matched string is put in key "0"
      # the first three characters are put in key "name1"
      # the next three characters go into key "2" (the capture has no name)
      # the next two characters go into key "3" (the capture has no name)
      # then the dash is ignored
      # then three characters are put in key "name2"
      # then another dash is ignored
      # the next three characters go into key "5" (the capture has no name)
      # anything else is ignored

      "parsed" data => data_regextract("^(?<name1>...)(...)(..)-(?<name2>...)-(..).*", "abcdef12-345-67andsoon");
      "parsed_str" string => format("%S", parsed);

  reports:
      "$(this.bundle): '$(parsed[0])' parses into: $(parsed_str)";
}

Output:

R: main: 'abcdef12-345-67andsoon' parses into: {"0":"abcdef12-345-67andsoon","2":"def","3":"12","5":"67","name1":"abc","name2":"345"}

Notes:

History: Was introduced in version 3.7.0 (2015)

See also: regextract() regex_replace()


makerule

Prototype: makerule(target, sources)

Return type: string

Description: Evaluates whether a target file needs to be built or rebuilt from one or more sources files.

This function can accept many types of data parameters.

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 (indicating a single source) or a list reference or a data container. If the sources argument specifies a list, then the entire list of sources is used to determine whether the target needs rebuilding.

Example:

classes:

 "build_me" expression => makerule("/tmp/target", "/tmp/source.c");
 "build_me_ab" expression => makerule("/tmp/target", '["/tmp/source.a","/tmp/source.b"]' );

commands:

   build_me::

      "/usr/bin/gcc -o /tmp/target /tmp/source.c";

History: The collecting function behavior was added in 3.9.

See also: About collecting functions.


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.

Note: This function only works locally on the hub, but allows the hub to construct custom configuration files for (classes of) hosts. Hosts are selected based on the classes set during the most recently collected agent run.

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.

This function can accept many types of data parameters.

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-- }

History: The collecting function behavior was added in 3.9.

See also: About collecting functions, 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_or_container)

Return type: slist

Description: Returns a list with each array_or_container element modified by a pattern.

This function can accept many types of data parameters.

The $(this.k) and $(this.v) variables expand to the key and value of the current element, similar to the way this is available for maplist.

If the array has two levels, you'll also be able to use the $(this.k[1]) variable for the key at the second level. See the example below for an illustration.

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_or_container: string, in the range: .*

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);
      "map_sorted" slist => sort(map, lex);

      "mycontainer" data => parsejson('
{
  "top":
  {
    "x": 2,
    "y": "big"
  }
}');
      "mapc" slist =>
          maparray("key='$(this.k)', key2='$(this.k[1])', static lookup = '$(static[$(this.v)])', value='$(this.v)'",
          mycontainer);
      "mapc_str" string => format("%S", mapc);

  reports:
      "mapped array: $(map_sorted)";
      "mapped container: $(mapc_str)";
}

Output:

R: mapped array: key='1', static lookup = 'lookup 2', value='2'
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 container: { "key='top', key2='x', static lookup = 'lookup 2', value='2'", "key='top', key2='y', static lookup = 'lookup big', value='big'" }

History: The collecting function behavior was added in 3.9.

See also: maplist(), mapdata(), about collecting functions, and data documentation.


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()).

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*
  • 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

History: Was introduced in version 3.6.0 (2014). canonify mode was introduced in 3.9.0. The collecting function behavior was added in 3.9.

See also: sort(), variance(), sum(), mean(), min(), about collecting functions, and data documentation.


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).


mapdata

Prototype: mapdata(interpretation, pattern, array_or_container)

Return type: data

Description: Returns a data container holding a JSON array. The array is a map across each element of array_or_container, modified by a pattern. The map is either collected literally when interpretation is none, or canonified when interpretation is canonify, or parsed as JSON when interpretation is json, or collected from pattern, invoked as a program, when interpretation is json_pipe.

This function can accept many types of data parameters.

The $(this.k) and $(this.v) variables expand to the key and value of the current element, similar to the way this is available for maplist.

If the array or data container has two levels, you'll also be able to use the $(this.k[1]) variable for the key at the second level. See the example below for an illustration.

The order of the keys is not guaranteed. Use the sort() function if you need order in the resulting output.

Arguments:

  • interpretation: one of
    • none
    • canonify
    • json
    • json_pipe
  • pattern: string, in the range: .*
  • array_or_container: string, in the range: .*

Example:

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

bundle agent run
{
  vars:
      "myarray[lookup][big]" string => "lookup big";
      "myarray[lookup][small]" string => "lookup small";

      # every item must parse as valid JSON when the interpretation is `json`
      "mapa_json" data => mapdata("json", '{ "key": "$(this.k)", "key2": "$(this.k[1])", "value": "$(this.v)" }', myarray);
      "mapa_json_str" string => format("%S", mapa_json);

      # every item is just a string when the interpretation is `none`
      "mapa_none" data => mapdata("none", 'key=$(this.k), level 2 key = $(this.k[1]), value=$(this.v)', myarray);
      "mapa_none_str" string => format("%S", mapa_none);

      "mycontainer" data => parsejson('
{
  "top":
  {
    "x": 100,
    "y": 200
  }
}');

      # every item must parse as valid JSON when the interpretation is `json`
      "mapc_json" data => mapdata("json", '{ "key": "$(this.k)", "key2": "$(this.k[1])", "value": "$(this.v)" }', mycontainer);
      "mapc_json_str" string => format("%S", mapc_json);

      # every item is just a string when the interpretation is `none`
      "mapc_none" data => mapdata("none", 'key=$(this.k), level 2 key = $(this.k[1]), value=$(this.v)', mycontainer);
      "mapc_none_str" string => format("%S", mapc_none);

  reports:
      "mapdata/json on classic CFEngine array result: $(mapa_json_str)";
      "mapdata/none on classic CFEngine array result: $(mapa_none_str)";
      "mapdata/json on data container result: $(mapc_json_str)";
      "mapdata/none on data container result: $(mapc_none_str)";
}

Output:

R: mapdata/json on classic CFEngine array result: [{"key":"lookup","key2":"small","value":"lookup small"},{"key":"lookup","key2":"big","value":"lookup big"}]
R: mapdata/none on classic CFEngine array result: ["key=lookup, level 2 key = small, value=lookup small","key=lookup, level 2 key = big, value=lookup big"]
R: mapdata/json on data container result: [{"key":"top","key2":"x","value":"100"},{"key":"top","key2":"y","value":"200"}]
R: mapdata/none on data container result: ["key=top, level 2 key = x, value=100","key=top, level 2 key = y, value=200"]

json_pipe

The json_pipe interpretation is intended to work with programs that take JSON as input and produce JSON as output. This is a standard tool convention in the Unix world. See the example below for the typical usage.

jq has a powerful programming language that fits the json_pipe interpretation well. It will take JSON input and product JSON output. Please read the jq manual and cookbook to get a feel for the power of this tool. When available, jq will offer tremendous data manipulation power for advanced cases where the built-in CFEngine functions are not enough.

Example with json_pipe:

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

bundle agent run
{
  vars:
      "tester" data => '{ "x": 100, "y": [ true, "a", "b" ] }';

      # "jq ." returns the same thing that was passed in
      "pipe_passthrough" data => mapdata("json_pipe", '$(def.jq) .', tester);
      "pipe_passthrough_str" string => format("%S", pipe_passthrough);

      # "jq .x" returns what was under x wrapped in an array: [100]
      "pipe_justx" data => mapdata("json_pipe", '$(def.jq) .x', tester);
      "pipe_justx_str" string => format("%S", pipe_justx);

      # "jq .y" returns what was under y wrapped in an array: [[true,"a","b"]]
      "pipe_justy" data => mapdata("json_pipe", '$(def.jq) .y', tester);
      "pipe_justy_str" string => format("%S", pipe_justy);

      # "jq .y[]" returns each entry under y *separately*: [true,"a","b"]
      "pipe_yarray" data => mapdata("json_pipe", '$(def.jq) .y[]', tester);
      "pipe_yarray_str" string => format("%S", pipe_yarray);

      # "jq .z" returns null because the key "z" is missing: [null]
      "pipe_justz" data => mapdata("json_pipe", '$(def.jq) .z', tester);
      "pipe_justz_str" string => format("%S", pipe_justz);

      # "jq" can do math too! and much more!
      "pipe_jqmath" data => mapdata("json_pipe", '$(def.jq) 1+2+3', tester);
      "pipe_jqmath_str" string => format("%S", pipe_jqmath);

  reports:
      "mapdata/json_pipe passthrough result: $(pipe_passthrough_str)";
      "mapdata/json_pipe just x result: $(pipe_justx_str)";
      "mapdata/json_pipe just y result: $(pipe_justy_str)";
      "mapdata/json_pipe array under y result: $(pipe_yarray_str)";
      "mapdata/json_pipe just z result: $(pipe_justz_str)";
      "mapdata/json_pipe math expression result: $(pipe_jqmath_str)";
}

Output:

R: mapdata/json_pipe passthrough result: [{"x":100,"y":[true,"a","b"]}]
R: mapdata/json_pipe just x result: [100]
R: mapdata/json_pipe just y result: [[true,"a","b"]]
R: mapdata/json_pipe array under y result: [true,"a","b"]
R: mapdata/json_pipe just z result: [null]
R: mapdata/json_pipe math expression result: [6]

History: Was introduced in 3.7.0. canonify mode was introduced in 3.9.0. The collecting function behavior was added in 3.9. The json_pipe mode was added in 3.9.

See also: maplist(), maparray(), canonify(), about collecting functions, and data documentation.


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.

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*

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). The collecting function behavior was added in 3.9.

See also: sort(), variance(), sum(), max(), min(), about collecting functions, and data documentation.


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.


parseyaml

Prototype: parseyaml(yaml_data)

Return type: data

Description: Parses YAML data directly from an inlined string and returns the result as a data variable

Arguments:

  • yaml_data: string, in the range: .*

Please note that 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 =>  parseyaml('
    - arrayentry1
    - arrayentry2
    - key1: 1
      key2: 2
    ');

      # inline syntax since 3.7
      # note the --- preamble is required with inline data
      "loadthis_inline"

      data =>  '---
    - arrayentry1
    - arrayentry2
    - key1: 1
      key2: 2
    ';

See also: readjson(), readyaml(), mergedata(), Inline YAML and JSON data, and data documentation.


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.

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:

  • 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.

Example:

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

bundle agent run
{
  vars:
      "all" slist      => variablesmatching(".*");
      "v" slist        => variablesmatching("default:sys.cf_version.*");
      "v_sorted" slist => sort(v, lex);
  reports:
      "Variables matching 'default:sys.cf_version.*' = $(v_sorted)";
}

Output:

R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_major
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_minor
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_patch

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.

This function can accept many types of data parameters.

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: .*

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). The collecting function behavior was added in 3.9.

See also: maplist(), maparray(), about collecting functions, and data documentation.


processexists

Prototype: processexists(regex)

Return type: boolean

The return value is cached.

Description: Return whether a process matches the given regular expression regex.

This function searches for the given regular expression in the process table. Use .*sherlock.* to find all the processes that match sherlock. Use .*\bsherlock\b.* to exclude partial matches like sherlock123 (\b matches a word boundary).

Arguments:

  • regex: regular expression, in the range: .*

The process table is usually obtained with something like ps -eo user,pid,ppid,pgid,%cpu,%mem,vsize,ni,rss,stat,nlwp,stime,time,args, and the CMD or COMMAND field (args) is used to match against. However the exact data used may change per platform and per CFEngine release.

Example:

    classes:
      # the class "holmes" will be set if a process line contains the word "sherlock"
      "holmes" expression => processexists(".*sherlock.*");

History: Introduced in CFEngine 3.9

See also: processes findprocesses().


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. Variable references like foo[bar] are also checked, so this is a way to check if a classic CFEngine array or a data container has a specific key or element.

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.

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 rest of the un-split string is thrown away.

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", "two:three" will be thrown away.
      "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.

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: "?(/.*)
  • 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 file filename and returns the result as a data variable. maxbytes is optional, if specified, only the first maxbytes bytes are read from filename.

Arguments:

  • filename: string, in the range: "?(/.*)
  • maxbytes: int, in the range: 0,99999999999

Example:

    vars:

      "loadthis"

         data =>  readjson("/tmp/data.json", 4000);

See also: readdata(), parsejson(), storejson(), parseyaml(), readyaml(), mergedata(), and data documentation.


length

Prototype: length(list)

Return type: int

Description: Returns the length of list.

This function can accept many types of data parameters.

Arguments:

  • list: 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",
      };

      "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

History: The collecting function behavior was added in 3.9.

See also: nth(), mergedata(), about collecting functions, and data documentation.


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().


callstack_callers

Prototype: callstack_callers()

Return type: data

Description: Return the call stack for the current promise.

This is a call stack inspection function and the specific content may be tied to a specific CFEngine version. Using it requires writing code that takes the specific CFEngine version into account.

The returned data container is a list of key-value maps.

The maps all have a type key and a frame key with a counter. For different frames along the stack frame path, the maps have additional keys:

  • whenever possible,
  • bodies: under key body the entry has a full dump of the body policy as JSON, same as what cf-promises -p json would produce, using the internal C function BodyToJson(). This may include the line and sourcePath to locate the exact code line.
  • bundles: under key bundle the entry has a full dump of the bundle policy as JSON, same as what cf-promises -p json would produce, using the internal C function BundleToJson(). This may include the line and sourcePath to locate the exact code line.
  • promise iteration: the iteration_index is recorded
  • promises: the promise_type, promiser, promise_classes, and promise_comment are recorded
  • promise sections (types): the promise_type is recorded

Example:

    vars:
      "stack" data => callstack_callers();

Output:

    [ ... call stack information ... ,
      {
        "depth": 2,
        "frame": 9,
        "promise_classes": "any",
        "promise_comment": "",
        "promise_type": "methods",
        "promiser": "",
        "type": "promise"
      }, ... more call stack information ... ]

History: Introduced in CFEngine 3.9

See also: callstack_promisers()


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.

This function can accept many types of data parameters.

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: .*

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");
      "parameter_name_sorted" slist => sort(parameter_name, lex);

  reports:

      "Found key $(parameter_name_sorted)";
}

Output:

R: Found key default_privs
R: Found key inet_interfaces
R: Found key inet_protocols
R: Found key mydomain
R: Found key relayhost
R: Found key smtp_sasl_auth_enable
R: Found key smtp_sasl_password_maps
R: Found key smtp_sasl_security_options
R: Found key smtp_use_tls

History: The collecting function behavior was added in 3.9.

See also: getvalues(), about collecting functions, and data documentation.


mean

Prototype: mean(list)

Return type: real

Description: Return the mean of the numbers in list.

This function can accept many types of data parameters.

Arguments:

  • list: string, in the range: .*

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

History: Was introduced in version 3.6.0 (2014). The collecting function behavior was added in 3.9.

See also: sort(), variance(), sum(), max(), min(), about collecting functions, and data documentation.


"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 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.

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).

This function can accept many types of data parameters.

Example:

    vars:

      "loadthis"

         data =>  parsejson('{ "key": "value" }');

      # inline syntax since 3.7
      "loadthis_inline"

         data =>  '{ "key": "value" }';

History: The collecting function behavior was added in 3.9.

See also: readjson(), parseyaml(), readyaml(), mergedata(), Inline YAML and JSON data, about collecting functions, 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.

This function should be avoided in favor of data_regextract() because it creates classic CFEngine array variables and does not support named captures.

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

See also: data_regextract() regex_replace()


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.

This function can accept many types of data parameters.

Arguments:

  • glue: string, in the range: .*
  • list: string, in the range: .*

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

History: The collecting function behavior was added in 3.9.

See also: string_split(), about collecting functions.


network_connections

Prototype: network_connections(regex)

Return type: data

Description: Return the list of current network connections.

This function looks in /proc/net to find the current network connections.

The returned data container has four keys:

  • tcp has all the TCP connections over IPv4
  • tcp6 has all the TCP connections over IPv6
  • udp has all the UDP connections over IPv4
  • udp6 has all the UDP connections over IPv6

Under each key, there's an array of connection objects that all look like this:

All the data is collected from the files /proc/net/tcp, /proc/net/tcp6, /proc/net/udp, and /proc/net/udp6.

The address will be either IPv4 or IPv6 as appropriate. The port will be an integer stored as a string. The state will be a string like UNKNOWN.

On Linux, usually a state of UNKNOWN and a remote address 0.0.0.0 or 0:0:0:0:0:0:0:0 with port 0 mean this is a listening IPv4 and IPv6 server. In addition, usually a local address of 0.0.0.0 or 0:0:0:0:0:0:0:0 means the server is listening on every IPv4 or IPv6 interface, while 127.0.0.1 (the IPv4 localhost address) or 0:100:0:0:0:0:0:0 means the server is only listening to connections coming from the same machine.

A state of ESTABLISHED usually means you're looking at a live connection.

Example:

    vars:
      "connections" data => network_connections();

Output:

The SSH daemon:

The printer daemon listening only to local IPv6 connections on port 631:

An established connection on port 2200:

History: Introduced in CFEngine 3.9

See also: sys.inet, sys.inet6.


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.

  • def Variables with some default value that can be defined by augments file or in policy.

  • 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 service_method used by a services type promise, this variable is set to the value of the service_policy promise attribute . For example:

bundle agent example
{
    services:

      "www"
        service_policy => "start";
        service_method => non_standard_services;
}
body service_method non_standard_services
{
  service_bundle => non_standard_services( $(this.service_policy) );
}

This is typically used in the adaptations for custom services bundles in the service methods.

See Also:

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.


def

The context def is populated by the Masterfiles Policy Framework and can also be populated by the augments file.

Note: Variables defined from policy in a bundle named def will override the variables defined by the augments file unless the policy explicitly guards against it.

For example mailto is only defined from policy if it is not yet defined by the augments file.:

bundle common def
{

  vars:

  # ...

      "mailto"
        string => "root@$(def.domain)",
        ifvarclass => not(isvariable("mailto"));

  # ...
}
def.jq

This variable gives a convenient way to invoke jq for the mapdata() function in json_pipe mode and elsewhere. Note the below is the default value defined in the C code that you can override in the vars section of the augments file or in policy as described above.

    # def.jq = jq --compact-output --monochrome-output --ascii-output --unbuffered --sort-keys

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.inet

The available information about the IPv4 network stack, from when the agent initialized. This is currently available only on Linux systems with the special /proc/net/* information files.

From the route table, the default_gateway is extracted. From the list of routes in routes, the default_route is copied to the top level for convenience.

Each route's flags are extracted in a convenient list format.

The stats key contains all the TCP and IP counters provided by the system in /proc/net/netstat.

History: Was introduced in 3.9.0.

See also: sys.inet6, sys.interfaces_data

    % cat /proc/net/route
Iface   Destination Gateway     Flags   RefCnt  Use Metric  Mask        MTU Window  IRTT                                                       
enp4s0  00000000    0102A8C0    0003    0   0   100 00000000    0   0   0                                                                           
enp4s0  0000FEA9    00000000    0001    0   0   1000    0000FFFF    0   0   0                                                                          
enp4s0  0002A8C0    00000000    0001    0   0   100 00FFFFFF    0   0   0                                                                           

    % cat /proc/net/netstat
TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSPassive PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPPrequeued TCPDirectCopyFromBacklog TCPDirectCopyFromPrequeue TCPPrequeueDropped TCPHPHits TCPHPHitsToUser TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPForwardRetrans TCPSlowStartRetrans TCPTimeouts TCPLossProbes TCPLossProbeRecovery TCPRenoRecoveryFail TCPSackRecoveryFail TCPSchedulerFailed TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPRetransFail TCPRcvCoalesce TCPOFOQueue TCPOFODrop TCPOFOMerge TCPChallengeACK TCPSYNChallenge TCPFastOpenActive TCPFastOpenActiveFail TCPFastOpenPassive TCPFastOpenPassiveFail TCPFastOpenListenOverflow TCPFastOpenCookieReqd TCPSpuriousRtxHostQueues BusyPollRxPackets TCPAutoCorking TCPFromZeroWindowAdv TCPToZeroWindowAdv TCPWantZeroWindowAdv TCPSynRetrans TCPOrigDataSent TCPHystartTrainDetect TCPHystartTrainCwnd TCPHystartDelayDetect TCPHystartDelayCwnd TCPACKSkippedSynRecv TCPACKSkippedPAWS TCPACKSkippedSeq TCPACKSkippedFinWait2 TCPACKSkippedTimeWait TCPACKSkippedChallenge TCPWinProbe TCPKeepAlive
TcpExt: 0 0 0 19896 7 0 0 9 0 0 560727 0 0 0 0 3575 2049614 302 313016 0 0 17283401 130554 186252521 0 126381259 21978 34307113 42386136 481 386568 7 175 316 11 822 2028 483 20959 148926 16709 267 271328 38869 512579 72057 281202 375133 561590 150370 23 59420 0 106 391776 9062 174837 4389 211213 13931 0 14556 0 0 0 585 594 65103 100117 0 0 0 0 2199955 0 0 0 0 0 0 0 15 36402752 5236349 0 7020 7132 4057 0 0 0 0 0 0 70 0 17925237 24 30 71 693624 275201738 33 992 2253 49843 136 484 21848 0 25 218 10478 503111
IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets InCsumErrors InNoECTPkts InECT1Pkts InECT0Pkts InCEPkts
IpExt: 0 0 1304886 130589 3784495 6 437612883789 422416538003 334973818 8189234 817859007 284 1 487495405 18258 4804476 543340

    # sys.inet = {
    "default_gateway": "192.168.2.1",
    "default_route": {
      "active_default_gateway": true,
      "dest": "0.0.0.0",
      "flags": [
        "up",
        "net",
        "default",
        "gateway"
      ],
      "gateway": "192.168.2.1",
      "interface": "enp4s0",
      "irtt": 0,
      "mask": "0.0.0.0",
      "metric": 100,
      "mtu": 0,
      "refcnt": 0,
      "use": 0,
      "window": 0
    },
    "routes": [
      {
        "active_default_gateway": true,
        "dest": "0.0.0.0",
        "flags": [
          "up",
          "net",
          "default",
          "gateway"
        ],
        "gateway": "192.168.2.1",
        "interface": "enp4s0",
        "irtt": 0,
        "mask": "0.0.0.0",
        "metric": 100,
        "mtu": 0,
        "refcnt": 0,
        "use": 0,
        "window": 0
      },
      {
        "active_default_gateway": false,
        "dest": "169.254.0.0",
        "flags": [
          "up",
          "net",
          "not_default",
          "local"
        ],
        "gateway": "0.0.0.0",
        "interface": "enp4s0",
        "irtt": 0,
        "mask": "255.255.0.0",
        "metric": 1000,
        "mtu": 0,
        "refcnt": 0,
        "use": 0,
        "window": 0
      },
      {
        "active_default_gateway": false,
        "dest": "192.168.2.0",
        "flags": [
          "up",
          "net",
          "not_default",
          "local"
        ],
        "gateway": "0.0.0.0",
        "interface": "enp4s0",
        "irtt": 0,
        "mask": "255.255.255.0",
        "metric": 100,
        "mtu": 0,
        "refcnt": 0,
        "use": 0,
        "window": 0
      }
    ],
    "stats": {
      "IpExt": {
        "InBcastOctets": "817859007",
        "InBcastPkts": "3784495",
        "InCEPkts": "543340",
        "InCsumErrors": "1",
        "InECT0Pkts": "4804476",
        "InECT1Pkts": "18258",
        "InMcastOctets": "334973818",
        "InMcastPkts": "1304886",
        "InNoECTPkts": "487495405",
        "InNoRoutes": "0",
        "InOctets": "437612883789",
        "InTruncatedPkts": "0",
        "OutBcastOctets": "284",
        "OutBcastPkts": "6",
        "OutMcastOctets": "8189234",
        "OutMcastPkts": "130589",
        "OutOctets": "422416538003"
      },
      "TcpExt": {
        "ArpFilter": "0",
        "BusyPollRxPackets": "0",
        "DelayedACKLocked": "302",
        "DelayedACKLost": "313016",
        "DelayedACKs": "2049614",
        "EmbryonicRsts": "19896",
        "IPReversePathFilter": "0",
        "ListenDrops": "0",
        "ListenOverflows": "0",
        "LockDroppedIcmps": "0",
        "OfoPruned": "0",
        "OutOfWindowIcmps": "9",
        "PAWSActive": "0",
        "PAWSEstab": "3575",
        "PAWSPassive": "0",
        "PruneCalled": "7",
        "RcvPruned": "0",
        "SyncookiesFailed": "0",
        "SyncookiesRecv": "0",
        "SyncookiesSent": "0",
        "TCPACKSkippedChallenge": "218",
        "TCPACKSkippedFinWait2": "0",
        "TCPACKSkippedPAWS": "484",
        "TCPACKSkippedSeq": "21848",
        "TCPACKSkippedSynRecv": "136",
        "TCPACKSkippedTimeWait": "25",
        "TCPAbortFailed": "0",
        "TCPAbortOnClose": "13931",
        "TCPAbortOnData": "211213",
        "TCPAbortOnLinger": "0",
        "TCPAbortOnMemory": "0",
        "TCPAbortOnTimeout": "14556",
        "TCPAutoCorking": "17925237",
        "TCPBacklogDrop": "0",
        "TCPChallengeACK": "7132",
        "TCPDSACKIgnoredNoUndo": "65103",
        "TCPDSACKIgnoredOld": "594",
        "TCPDSACKOfoRecv": "4389",
        "TCPDSACKOfoSent": "9062",
        "TCPDSACKOldSent": "391776",
        "TCPDSACKRecv": "174837",
        "TCPDSACKUndo": "20959",
        "TCPDeferAcceptDrop": "0",
        "TCPDirectCopyFromBacklog": "130554",
        "TCPDirectCopyFromPrequeue": "186252521",
        "TCPFACKReorder": "175",
        "TCPFastOpenActive": "0",
        "TCPFastOpenActiveFail": "0",
        "TCPFastOpenCookieReqd": "0",
        "TCPFastOpenListenOverflow": "0",
        "TCPFastOpenPassive": "0",
        "TCPFastOpenPassiveFail": "0",
        "TCPFastRetrans": "512579",
        "TCPForwardRetrans": "72057",
        "TCPFromZeroWindowAdv": "24",
        "TCPFullUndo": "2028",
        "TCPHPAcks": "42386136",
        "TCPHPHits": "126381259",
        "TCPHPHitsToUser": "21978",
        "TCPHystartDelayCwnd": "49843",
        "TCPHystartDelayDetect": "2253",
        "TCPHystartTrainCwnd": "992",
        "TCPHystartTrainDetect": "33",
        "TCPKeepAlive": "503111",
        "TCPLossFailures": "38869",
        "TCPLossProbeRecovery": "150370",
        "TCPLossProbes": "561590",
        "TCPLossUndo": "148926",
        "TCPLostRetransmit": "16709",
        "TCPMD5NotFound": "0",
        "TCPMD5Unexpected": "0",
        "TCPMemoryPressures": "0",
        "TCPMinTTLDrop": "0",
        "TCPOFODrop": "0",
        "TCPOFOMerge": "7020",
        "TCPOFOQueue": "5236349",
        "TCPOrigDataSent": "275201738",
        "TCPPartialUndo": "483",
        "TCPPrequeueDropped": "0",
        "TCPPrequeued": "17283401",
        "TCPPureAcks": "34307113",
        "TCPRcvCoalesce": "36402752",
        "TCPRcvCollapsed": "106",
        "TCPRenoFailures": "267",
        "TCPRenoRecovery": "481",
        "TCPRenoRecoveryFail": "23",
        "TCPRenoReorder": "11",
        "TCPReqQFullDoCookies": "0",
        "TCPReqQFullDrop": "0",
        "TCPRetransFail": "15",
        "TCPSACKDiscard": "585",
        "TCPSACKReneging": "7",
        "TCPSACKReorder": "316",
        "TCPSYNChallenge": "4057",
        "TCPSackFailures": "271328",
        "TCPSackMerged": "0",
        "TCPSackRecovery": "386568",
        "TCPSackRecoveryFail": "59420",
        "TCPSackShiftFallback": "2199955",
        "TCPSackShifted": "0",
        "TCPSchedulerFailed": "0",
        "TCPSlowStartRetrans": "281202",
        "TCPSpuriousRTOs": "100117",
        "TCPSpuriousRtxHostQueues": "70",
        "TCPSynRetrans": "693624",
        "TCPTSReorder": "822",
        "TCPTimeWaitOverflow": "0",
        "TCPTimeouts": "375133",
        "TCPToZeroWindowAdv": "30",
        "TCPWantZeroWindowAdv": "71",
        "TCPWinProbe": "10478",
        "TW": "560727",
        "TWKilled": "0",
        "TWRecycled": "0"
      }
    }
  }
sys.inet6

The available information about the IPv6 network stack, from when the agent initialized. This is currently available only on Linux systems with the special /proc/net/* information files.

The configured devices with IPv6 addresses from /proc/net/if_inet6 are collected under addresses.

The routes from /proc/net/ipv6_route are collected but not analyzed for default route etc. as with IPv4 routes in sys.inet.

The network statistics from /proc/net/snmp6 are converted to a convenient key-value format under stats.

History: Was introduced in 3.9.0.

See also: sys.inet, sys.interfaces_data

    % cat /proc/net/if_inet6
00000000000000000000000000000001 01 80 10 80       lo
fe80000000000000004249fffebdd7b4 04 40 20 80  docker0
fe80000000000000c27cd1fffe3eada6 02 40 20 80   enp4s0

    % cat /proc/net/ipv6_route
fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000001 00000004 00000001   enp4s0
00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 0007e26c 00200200       lo
00000000000000000000000000000001 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000009 0000020b 80200001       lo
fe80000000000000c27cd1fffe3eada6 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000002 00000004 80200001       lo
ff000000000000000000000000000000 08 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000008 0003ffc5 00000001   enp4s0
00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 0007e26c 00200200       lo

    % cat /proc/net/snmp6
Ip6InReceives                       492189
Ip6InHdrErrors                      0
Ip6InTooBigErrors                   0
Ip6InNoRoutes                       0
Ip6InAddrErrors                     0
Ip6InUnknownProtos                  0
Ip6InTruncatedPkts                  0
Ip6InDiscards                       0
Ip6InDelivers                       490145
Ip6OutForwDatagrams                 0
Ip6OutRequests                      12145
Ip6OutDiscards                      6
Ip6OutNoRoutes                      249070
Ip6ReasmTimeout                     0
Ip6ReasmReqds                       0
Ip6ReasmOKs                         0
Ip6ReasmFails                       0
Ip6FragOKs                          0
Ip6FragFails                        0
Ip6FragCreates                      0
Ip6InMcastPkts                      488766
Ip6OutMcastPkts                     10304
Ip6InOctets                         132343220
Ip6OutOctets                        1522724
Ip6InMcastOctets                    131896014
Ip6OutMcastOctets                   1076616
Ip6InBcastOctets                    0
Ip6OutBcastOctets                   0
Ip6InNoECTPkts                      492196
Ip6InECT1Pkts                       0
Ip6InECT0Pkts                       0
Ip6InCEPkts                         0
Icmp6InMsgs                         275
Icmp6InErrors                       0
Icmp6OutMsgs                        1815
Icmp6OutErrors                      0
Icmp6InCsumErrors                   0
Icmp6InDestUnreachs                 0
Icmp6InPktTooBigs                   0
Icmp6InTimeExcds                    0
Icmp6InParmProblems                 0
Icmp6InEchos                        0
Icmp6InEchoReplies                  0
Icmp6InGroupMembQueries             0
Icmp6InGroupMembResponses           1
Icmp6InGroupMembReductions          1
Icmp6InRouterSolicits               0
Icmp6InRouterAdvertisements         0
Icmp6InNeighborSolicits             5
Icmp6InNeighborAdvertisements       268
Icmp6InRedirects                    0
Icmp6InMLDv2Reports                 0
Icmp6OutDestUnreachs                0
Icmp6OutPktTooBigs                  0
Icmp6OutTimeExcds                   0
Icmp6OutParmProblems                0
Icmp6OutEchos                       0
Icmp6OutEchoReplies                 0
Icmp6OutGroupMembQueries            0
Icmp6OutGroupMembResponses          0
Icmp6OutGroupMembReductions         0
Icmp6OutRouterSolicits              396
Icmp6OutRouterAdvertisements        0
Icmp6OutNeighborSolicits            206
Icmp6OutNeighborAdvertisements      5
Icmp6OutRedirects                   0
Icmp6OutMLDv2Reports                1208
Icmp6InType131                      1
Icmp6InType132                      1
Icmp6InType135                      5
Icmp6InType136                      268
Icmp6OutType133                     396
Icmp6OutType135                     206
Icmp6OutType136                     5
Icmp6OutType143                     1208
Udp6InDatagrams                     486201
Udp6NoPorts                         0
Udp6InErrors                        0
Udp6OutDatagrams                    7273
Udp6RcvbufErrors                    0
Udp6SndbufErrors                    0
Udp6InCsumErrors                    0
Udp6IgnoredMulti                    0
UdpLite6InDatagrams                 0
UdpLite6NoPorts                     0
UdpLite6InErrors                    0
UdpLite6OutDatagrams                0
UdpLite6RcvbufErrors                0
UdpLite6SndbufErrors                0
UdpLite6InCsumErrors                0

    # sys.inet6 = {
    "addresses": {
      "docker0": {
        "address": "d7b4:febd:49ff:42:0:0:0:fe80",
        "device_number": 4,
        "interface": "docker0",
        "prefix_length": 64,
        "raw_flags": "80",
        "scope": 32
      },
      "enp4s0": {
        "address": "ada6:fe3e:d1ff:c27c:0:0:0:fe80",
        "device_number": 2,
        "interface": "enp4s0",
        "prefix_length": 64,
        "raw_flags": "80",
        "scope": 32
      },
      "lo": {
        "address": "1:0:0:0:0:0:0:0",
        "device_number": 1,
        "interface": "lo",
        "prefix_length": 128,
        "raw_flags": "80",
        "scope": 16
      }
    },
    "routes": [
      {
        "dest": "0:0:0:0:0:0:0:0",
        "dest_prefix": "40",
        "flags": [
          "up",
          "net",
          "local"
        ],
        "interface": "enp4s0",
        "metric": 256,
        "next_hop": "0:0:0:0:0:0:0:0",
        "refcnt": 1,
        "source_prefix": "00",
        "use": 4
      },
      {
        "dest": "0:0:0:0:0:0:0:0",
        "dest_prefix": "80",
        "flags": [
          "up",
          "net",
          "local"
        ],
        "interface": "lo",
        "metric": 0,
        "next_hop": "0:0:0:0:0:0:0:0",
        "refcnt": 2,
        "source_prefix": "00",
        "use": 4
      }
    ],
    "stats": {
      "Icmp6InCsumErrors": 0,
      "Icmp6InDestUnreachs": 0,
      "Icmp6InEchoReplies": 0,
      "Icmp6InEchos": 0,
      "Icmp6InErrors": 0,
      "Icmp6InGroupMembQueries": 0,
      "Icmp6InGroupMembReductions": 1,
      "Icmp6InGroupMembResponses": 1,
      "Icmp6InMLDv2Reports": 0,
      "Icmp6InMsgs": 275,
      "Icmp6InNeighborAdvertisements": 268,
      "Icmp6InNeighborSolicits": 5,
      "Icmp6InParmProblems": 0,
      "Icmp6InPktTooBigs": 0,
      "Icmp6InRedirects": 0,
      "Icmp6InRouterAdvertisements": 0,
      "Icmp6InRouterSolicits": 0,
      "Icmp6InTimeExcds": 0,
      "Icmp6InType131": 1,
      "Icmp6InType132": 1,
      "Icmp6InType135": 5,
      "Icmp6InType136": 268,
      "Icmp6OutDestUnreachs": 0,
      "Icmp6OutEchoReplies": 0,
      "Icmp6OutEchos": 0,
      "Icmp6OutErrors": 0,
      "Icmp6OutGroupMembQueries": 0,
      "Icmp6OutGroupMembReductions": 0,
      "Icmp6OutGroupMembResponses": 0,
      "Icmp6OutMLDv2Reports": 1208,
      "Icmp6OutMsgs": 1815,
      "Icmp6OutNeighborAdvertisements": 5,
      "Icmp6OutNeighborSolicits": 206,
      "Icmp6OutParmProblems": 0,
      "Icmp6OutPktTooBigs": 0,
      "Icmp6OutRedirects": 0,
      "Icmp6OutRouterAdvertisements": 0,
      "Icmp6OutRouterSolicits": 396,
      "Icmp6OutTimeExcds": 0,
      "Icmp6OutType133": 396,
      "Icmp6OutType135": 206,
      "Icmp6OutType136": 5,
      "Icmp6OutType143": 1208,
      "Ip6FragCreates": 0,
      "Ip6FragFails": 0,
      "Ip6FragOKs": 0,
      "Ip6InAddrErrors": 0,
      "Ip6InBcastOctets": 0,
      "Ip6InCEPkts": 0,
      "Ip6InDelivers": 490145,
      "Ip6InDiscards": 0,
      "Ip6InECT0Pkts": 0,
      "Ip6InECT1Pkts": 0,
      "Ip6InHdrErrors": 0,
      "Ip6InMcastOctets": 131896014,
      "Ip6InMcastPkts": 488766,
      "Ip6InNoECTPkts": 492196,
      "Ip6InNoRoutes": 0,
      "Ip6InOctets": 132343220,
      "Ip6InReceives": 492189,
      "Ip6InTooBigErrors": 0,
      "Ip6InTruncatedPkts": 0,
      "Ip6InUnknownProtos": 0,
      "Ip6OutBcastOctets": 0,
      "Ip6OutDiscards": 6,
      "Ip6OutForwDatagrams": 0,
      "Ip6OutMcastOctets": 1076616,
      "Ip6OutMcastPkts": 10304,
      "Ip6OutNoRoutes": 249070,
      "Ip6OutOctets": 1522724,
      "Ip6OutRequests": 12145,
      "Ip6ReasmFails": 0,
      "Ip6ReasmOKs": 0,
      "Ip6ReasmReqds": 0,
      "Ip6ReasmTimeout": 0,
      "Udp6IgnoredMulti": 0,
      "Udp6InCsumErrors": 0,
      "Udp6InDatagrams": 486201,
      "Udp6InErrors": 0,
      "Udp6NoPorts": 0,
      "Udp6OutDatagrams": 7273,
      "Udp6RcvbufErrors": 0,
      "Udp6SndbufErrors": 0,
      "UdpLite6InCsumErrors": 0,
      "UdpLite6InDatagrams": 0,
      "UdpLite6InErrors": 0,
      "UdpLite6NoPorts": 0,
      "UdpLite6OutDatagrams": 0,
      "UdpLite6RcvbufErrors": 0,
      "UdpLite6SndbufErrors": 0
    }
  }
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.interfaces_data

The network statistics of the system interfaces, from when the agent initialized. This is currently available only on Linux systems with the special /proc/net/dev file.

History: Was introduced in 3.9.0.

See also: sys.inet6, sys.inet

    % cat /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
enp4s0: 446377831179 492136556    0    0    0     0          0         0 428200856331 499195545    0    0    0     0       0          0
    lo: 1210580426 1049790    0    0    0     0          0         0 1210580426 1049790    0    0    0     0       0          0
wlp3s0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0

    # sys.interfaces_data = {
    "enp4s0": {
      "device": "enp4s0",
      "receive_bytes": "446377831179",
      "receive_compressed": "0",
      "receive_drop": "0",
      "receive_errors": "0",
      "receive_fifo": "0",
      "receive_frame": "0",
      "receive_multicast": "0",
      "receive_packets": "492136556",
      "transmit_bytes": "428200856331",
      "transmit_compressed": "0",
      "transmit_drop": "0",
      "transmit_errors": "0",
      "transmit_fifo": "0",
      "transmit_frame": "0",
      "transmit_multicast": "0",
      "transmit_packets": "499195545"
    },
    "lo": {
      "device": "lo",
      "receive_bytes": "1210580426",
      "receive_compressed": "0",
      "receive_drop": "0",
      "receive_errors": "0",
      "receive_fifo": "0",
      "receive_frame": "0",
      "receive_multicast": "0",
      "receive_packets": "1049790",
      "transmit_bytes": "1210580426",
      "transmit_compressed": "0",
      "transmit_drop": "0",
      "transmit_errors": "0",
      "transmit_fifo": "0",
      "transmit_frame": "0",
      "transmit_multicast": "0",
      "transmit_packets": "1049790"
    },
    "wlp3s0": {
      "device": "wlp3s0",
      "receive_bytes": "0",
      "receive_compressed": "0",
      "receive_drop": "0",
      "receive_errors": "0",
      "receive_fifo": "0",
      "receive_frame": "0",
      "receive_multicast": "0",
      "receive_packets": "0",
      "transmit_bytes": "0",
      "transmit_compressed": "0",
      "transmit_drop": "0",
      "transmit_errors": "0",
      "transmit_fifo": "0",
      "transmit_frame": "0",
      "transmit_multicast": "0",
      "transmit_packets": "0"
    }
  }
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.ip2iface

A map of full IPv4 addresses (key) to the system interface (value), e.g. $(sys.ip2iface[1.2.3.4]).

    # If the IPv4 address on the interfaces are
    #    le0 = 192.168.1.101
    #    xr1 = 10.12.7.254
    #
    # Then you will have
    # sys.ip2iface[192.168.1.101] = le0
    # sys.ip2iface[10.12.7.254] = xr1

Note:

The list of addresses may be acquired with getindices("sys.ip2iface") (or from any of the other associative arrays). Only those interfaces which are marked as "up" and have an IP address will have entries.

History: Was introduced in 3.9.

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. $(sys.ipv4[le0]) or $(sys.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
    # sys.ipv4_1[le0] = 192
    # sys.ipv4_2[le0] = 192.168
    # sys.ipv4_3[le0] = 192.168.1
    #   sys.ipv4[le0] = 192.168.1.101
    # sys.ipv4_1[xr1] = 10
    # sys.ipv4_2[xr1] = 10.12
    # sys.ipv4_3[xr1] = 10.12.7
    #   sys.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

History: Introduced in CFEngine 3.6, version based sub directory removed in CFEngine 3.8.

sys.local_libdir

The name of the directory where CFEngine looks for its libraries, without any prefixes.

    # local_libdir = lib

History: Introduced in CFEngine 3.6, version based sub directory removed in CFEngine 3.8.

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.statedir

The name of the state directory where CFEngine looks for its embedded database files.

    # statedir = /var/cfengine/state

History: Introduced in CFEngine 3.7

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

Language Concepts

There is only one grammatical form for statements in the language:

    bundle bundle_type name
    {
    promise_type:

      classes::

        "promiser" -> { "promisee1", "promisee2", ... }

            attribute_1 => value_1,
            attribute_2 => value_2,
            ...
            attribute_n => value_n;
    }

In addition, CFEngine bodies can be defined and used as attribute values. Here's a real-life example of a body and its usage.

body edit_defaults no_backup
{
      edit_backup => "false";
}

... and elsewhere, noting the attribute name matches the body type ...

  files:
    "myfile" edit_defaults => no_backup;

You can recognize everything in CFEngine from just those few concepts.

A declaration about the state we desire to maintain (e.g., the permissions or contents of a file, the availability or absence of a service, the (de)installation of a package).

A collection of promises.

A part of a promise which details and constrains its nature, possibly in separate and re-usable parts. Effectively a body is like a promise attribute that has several parameters.

CFEngine's boolean classifiers that describe context.

An association of the form "LVALUE represents RVALUE", where RVALUE may be a scalar value or a list of scalar values: a string, integer or real number.

This documentation about the language concepts introduces in addition

Syntax, identifiers and names

The CFEngine 3 language has a few simple rules:

  • CFEngine built-in words, names of variables, bundles, body templates and classes may only contain the usual alphanumeric and underscore characters (a-zA-Z0-9_)
  • All other 'literal' data must be quoted.
  • Declarations of promise bundles in the form:

    bundle agent-type identifier
    {
    ...
    }
    

    where agent-type is the CFEngine component responsible for maintaining the promise.

  • Declarations of promise body-parts in the form:

    body constraint_type template_identifier
    {
    ...
    }
    

    matching and expanding on a reference inside a promise of the form constraint_type => template_identifier

  • attribute expressions in the body of a promise take the form

    left-hand-side (CFEngine_word) => right-hand-side (user defined data).
    

    This can take several forms:

    cfengine_word => user_defined_template(parameters)
                  user_defined_template
                  builtin_function()
                  "quoted literal scalar"
                  { list }
    

    In each of these cases, the right hand side is a user choice.

    CFEngine uses many `constraint expressions' as part of the body of a promise. These take the form: left-hand-side (cfengine word) ‘=>’ right-hand-side (user defined data). This can take several forms:

    cfengine_word => user_defined_template(parameters)
        user_defined_template
        builtin_function()
        "quoted literal scalar"
        { list }
    

    In each of these cases, the right hand side is a user choice.

Filenames and Paths

Filenames in Unix-like operating systems use the forward slash '/' character for their directory separator. All references to file locations must be absolute pathnames in CFEngine, i.e. they must begin with a complete specification of which directory they are in or with a variable reference that resolves to that. For example:

/etc/passwd
/var/cfengine/masterfiles/distfile
$(sys.masterdir)/distfile # usually the same thing in 3.6

The only place where it makes sense to refer to a file without a complete directory specification is when searching through directories for different kinds of file, e.g. in pattern matching

leaf_name => { "tmp_.*", "output_file", "core" };

Here, one can write core without a path, because one is looking for any file of that name in a number of directories.

The Windows operating systems traditionally use a different filename convention. The following are all valid absolute file names under Windows:

c:\winnt
"c:\spaced name"
c:/winnt
/var/cfengine/inputs
//fileserver/share2/dir

The 'drive' name "C:" in Windows refers to a partition or device. Unlike Unix, Windows does not integrate these seamlessly into a single file-tree. This is not a valid absolute filename:

\var\cfengine\inputs

Paths beginning with a backslash are assumed to be win32 paths. They must begin with a drive letter or double-slash server name.

Note that in many cases, you have sys.inputdir and other Special Variables that work equally well on Windows and non-Windows system.

Note in recent versions of Cygwin you can decide to use the /cygdrive to specify a path to windows file E.g /cygdrive/c/myfile means c:\myfile or you can do it straight away in CFEngine as c:\myfile.


Bundles

A bundle is a collection of promises. They allow to group related promises together into named building blocks that can be thought of as "subroutines" in the CFEngine promise language. A bundle that groups a number of promises related to configuring a web server or a file system would be named "webserver" or "filesystem," respectively.

Most promise types are specific to a particular kind of interpretation that requires a typed interpreter - the bundle type. Bundles belong to the agent that is used to keep the promises in the bundle. So cf-agent has bundles declared as:

    bundle agent my_name
    {
    }

while cf-serverd has bundles declared as:

    bundle server my_name
    {
    }

and cf-monitord has bundles declared as

    bundle monitor my_name
    {
    }

A number of promises can be made in any kind of bundle since they are of a generic input/output nature. These are vars, classes, defaults, meta and reports promises.

Common Bundles

Bundles of type common may only contain the promise types that are common to all bodies. Their main function is to define cross-component global definitions.

     bundle common globals
     {
     vars:

       "global_var" string = "value";

     classes:

       "global_class" expression = "value";
     }

Common bundles are observed by every agent, whereas the agent specific bundle types are ignored by components other than the intended recipient.

Rules for evaluation of common bundles

These are the specific evaluation differences between common and agent bundles:

  • common bundles are automatically evaluated even if they are not in the bundlesequence, as long as they have no parameters
  • auto-evaluated common bundles (not in the bundlesequence explicitly) don't evaluate their reports promises, so their reports won't be printed.
  • when common bundles define a class, it's global (scope is namespace) by default; the classes in agent bundles are local (scope is bundle) by default.
  • common bundles can only contain meta, default, vars, classes, and reports promises
Bundle Parameters

Bundles can be parameterized, allowing for code re-use. If you need to do the same thing over and over again with slight variations, using a promise bundle is an easy way to avoid unnecessary duplication in your promises.

    bundle agent hello_world
    {
      vars:
          "myfiles"     => "/tmp/world.txt";
          "desired_content" string => "hello";
          "userinfo" data => parsejson('{ "mark": 10, "jeang":20, "jonhenrik":30, "thomas":40, "eben":-1 }');

      methods:
          "Hello World"
            usebundle => ensure_file_has_content("$(myfiles)", "$(desired_content)");

          "report" usebundle => subtest_c(@(userinfo));

    }

    bundle agent ensure_file_has_content(file, content)
    {
      files:

          "$(file)"
            handle => "$(this.bundle)_file_content",
            create => "true",
            edit_defaults => empty,
            edit_line => append_if_no_line("$(content)"),
            comment => "Ensure that the given parameter for file '$(file)' has only
                        the contents of the given parameter for content '$(content)'";

    }

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

You can pass slist and data variables to other bundles with the @(var) notation. You do NOT need to qualify the variable name with the current bundle name.

Scope

All variables in CFEngine are globally accessible. If you refer to a variable by ‘$(unqualified)’, then it is assumed to belong to the current bundle. To access any other (scalar) variable, you must qualify the name, using the name of the bundle in which it is defined:

$(bundle_name.qualified)

The value of the variable depends on evaluation order, which is not controllable by the user. Thus you should not assume that you can evaluate a bundle twice with different variables and get variables from it that correspond to the second evaluation. In other words, if you have:

bundle agent mybundle(x)
{
  vars:
  "y" string => $(x);
}

and call mybundle(1) and mybundle(2), the variable y could be 1 or 2.

Classes defined inside agent bundles are not visible outside those bundles. Classes defined in common bundles have global scope, so they are visible everywhere.

Note that namespaced bundles work exactly the same way as non-namespaced bundles (which are actually in the default namespace). You just say namespace:bundle_name instead of bundle_name. See Namespaces for more details.


Bodies

While the idea of a promise is very simple, the definition of a promise can grow complicated. Complex promises are best understood by breaking them down into independent, re-usable components. The CFEngine reserved word body is used to encapsulate the details of complex promise attribute values. Bodies can optionally have parameters.

    bundle agent example
    {
      files:
        !windows::
          "/etc/passwd"
            handle => "example_files_not_windows_passwd",
            perms => system;

          "/home/bill/id_rsa.pub"
            handle => "example_files_not_windows_bills_priv_ssh_key",
            perms => mog("600", "bill", "sysop"),
            create => "true";
    }

The promisers in this example are the files /etc/passwd and /home/bill/id_rsa.pub. The promise is that the perms attribute type is associated with a named, user-defined promise body system and mog respectively.

    body perms system
    {
      mode => "644";
      owners => { "root" };
      groups => { "root" };
    }

    body perms mog(mode,user,group)
    {
      owners => { "$(user)" };
      groups => { "$(group)" };
      mode   => "$(mode)";
    }

Like bundles, bodies have a type. The type of the body has to match the left-hand side of the promise attribute in which it is used. In this case, files promises have an attribute perms that can be associated with any body of type perms.

The attributes within the body are then type specific. Bodies of type perms consist of the file permissions, the file owner, and the file group, which the instance system sets to 644, root and root, respectively.

Such bodies can be reused in multiple promises. Like bundles, bodies can have parameters. The body mog also consists of the file permissions, file owner, and file group, but the values of those attributes are passed in as parameters.

Body Inheritance

CFEngine 3.8 introduced body inheritance via the inherit_from attribute. It's a parameterized single-inheritance system, so a body can only inherit from one other body, and it can apply parameters. The two bodies must have the same type.

Let's see it with the system and mog bodies from earlier:

    body perms system
    {
      mode => "644";
      owners => { "root" };
      groups => { "root" };
    }

    body perms system_inherited
    {
      inherit_from => mog("644", "root", "root");
    }

The earlier system body and this system_inherited body have the same effect, eventually applying mode 644, owner root, and group root. But they are created differently: the first by explicitly listing the parameters; the other by applying parameters to the inherit_from chain of inheritance.

Which one is better? Usually, inheriting from a more generic specification is considered a better design pattern because it reduces horizontal complexity. But it's less explicit and some users and sites will prefer a more explicit listing of body attributes and their values, as in the system body. CFEngine will accomodate either.

Body parameters can be used in the inheritance chain. Here's another body that inherits from mog but takes a mode. All its other parameters are specified inside the body. So system_inherited_mode("234") is exactly like mog("234", "root", "root").

    body perms system_inherited_mode(mode)
    {
      inherit_from => mog($(mode), "root", "root");
    }

Again, whether you prefer this or directly calling the mog body is your choice. Keep in mind that if you want to maintain compatibility with 3.7 and earlier, inherit_from is not available.

Body inheritance simply copies attributes down the chain to the newest body. The latest wins. Let's see an example with a chain of inheritance from the system body from earlier:

    body perms system
    {
      mode => "644";
      owners => { "root" };
      groups => { "root" };
    }

    body perms system_once(x)
    {
      inherit_from => system;
      owners => { $(x) };
      mode => "645";
    }

    body perms system_twice
    {
      inherit_from => system_once("mark");
      mode => "646";
    }

The inheritance chain goes from system to system_once to system_twice. The owners attribute in system_once will be whatever $(x) is, overwriting the value from system. Then system_twice will inherit that same owners value which is now "mark".

The mode attribute will be overwritten to 645 in system_once and then overwritten to 646 in system_twice.

If this gets complicated, just think "latest wins".

Implicit, Control Bodies

A special case for bodies are the implicit promises that configure the basic operation of CFEngine. These are hard-coded to CFEngine and control the basic operation of the agents, such as cf-agent and cf-serverd. Each agent has a special body whose name is control.

    body agent control
    {
        bundlesequence => { "test" };
    }

This promise bodies configures the bundlesequence to execute on a cf-agent.

    body server control
    {
        allowconnects         => { "127.0.0.1" , "::1", @(def.acl) };
    }

This promise body defines the clients allowed to connect to a cf-serverd. For more information, see the reference documentation about the CFEngine Agents

Default bodies

CFEngine 3.9 introduced a way to create default bodies. It allows defining, for given promise and body types, a body that will be used each time no body is defined. To use a body as default, name it <promise_type>_<body_type> and put it in the bodydefault namespace. For example, a default action body for files promises will be named files_action, and in each files promise, if no action attribute is set, the files_action action will be used.

Note: The default bodies only apply to promises in the default namespace.

In the following example, we define a default action body for files promises, that specifies an action_policy => "warn" to prevent actually modifying files and to only warn about considered modifications. We define it once, and don't have to explicitly put this body in all our files promises.

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

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

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

Promises

One concept in CFEngine should stand out from the rest as being the most important: promises. Everything else is just an abstraction that allows us to declare promises and model the various actors in the system.

Everything is a Promise

Everything in CFEngine 3 can be interpreted as a promise. Promises can be made about all kinds of different subjects, from file attributes, to the execution of commands, to access control decisions and knowledge relationships. If you are managing a system that serves web pages you may define a promise that port 80 needs to be open on a web server. This same web server may also define a promise that a particular directory has a particular set of permissions and the proper owner to serve web pages via Apache.

This simple but powerful idea allows a very practical uniformity in CFEngine syntax.

Promise Types

The promise_type defines what kind of object is making the promise. The type dictates how CFEngine interprets the promise body. These promise types are straightforward: The files promise type deals with file permissions and file content, and the packages promise type allows you to work with packaging systems such as rpm and apt.

Some promise types are common to all CFEngine components, while others can only be executed by one of them. cf-serverd cannot keep packages promises, and cf-agent cannot keep access promises. See the Promise Type reference for a comprehensive list of promise types.

The Promiser

The promiser is an object affected by a promise, and this can be anything: a file, a port on a network. It is the entity that is making a promise that a certain fact will be true. These facts are listed in the form of attributes and values. A file could promise that a permission attribute has a particular value (i.e. 775 permission value) and that an owner attribute has another value (i.e. "root").

When a promise is made in CFEngine it is made to another entity - a promisee. A promisee is an optional part of a promise declaration. The promisee can help provide insight into the system's configuration, and may become relevant as your system grows in complexity.

The classes in a promise control the conditions that make the promise valid. Examples are the operating system on which the policy is executed, or the day of the week. More about that in the classes and decision making section.

Not all of these elements are necessary every time, but when you combine them they enable a wide range of behavior.

Promise Example
     # Promise type
     files:
         "/home/mark/tmp/test_plain" -> "system blue team",
             create  => "true",
             perms   => owner("@(usernames)"),
             comment => "Hello World";

In this example, the promise is about a file named test_plain in the directory /home/mark/tmp, and the promise is made to some entity named system blue team. The create attribute instructs CFEngine to create the file if it doesn't exist. It has a list of owners that is defined by a variable named "usernames" (see the documentation about Bodies for more details on this last expression).

The comment attribute in this example can be added to any promise. It has no actual function other than to provide more information to the user in error tracing and auditing.

This is a promise that will affect the state of a file on the filesystem. In CFEngine you can do this without having to execute the touch, chmod, and chown commands. CFEngine is declarative: you declare a contract (or a promise) that you want CFEngine to keep and you leave the details up to the tool.

Promise Attributes

Promise attributes have a type and a value. The type can be any of the datatypes that are allowed for variables, and in addition

  • Boolean - allowed input values are

    • "true"/"false"
    • "on"/"off"
    • "yes"/"no"
  • irange[min, max] and rrange[min, max] - a range of integer or real values, created via the irange() and rrange() functions

  • clist - a list of classes or class expressions. Note that these attributes can take both strings (which are evaluated as class expressions) and functions that return type class

  • Menu option - one value from a list of values

  • body type - a complex set of attributes expressed in a separate, reusable block

  • bundle type - a separate bundle that is used as a sub-routine or a sub-set of promises

Implicit Promises

Some promise types can have implicit behavior. For example, the following promise simply prints out a log message "hello world".

   reports:
     "hello world";

The same promise could be implemented using the commands type, invoking the echo command:

   commands:
     "/bin/echo hello world";

These two promises have default attributes for everything except the `promiser'. Both promises simply cause CFEngine to print a message.


Normal Ordering

CFEngine takes a pragmatic point of view to ordering. When promising scalar attributes and properties, ordering is irrelevant and should not be considered. More complex patterned data structures require ordering to be preserved, e.g. editing in files. CFEngine solves this in a two-part strategy:

CFEngine maintains a default order of promise-types. This is based on a simple logic of what needs to come first, e.g. it makes no sense to create something and then delete it, but it could make sense to delete and then create (an equilibrium). This is called normal ordering and is described below. You can override normal ordering in exceptional circumstances by making a promise in a class context and defining that class based on the outcome of another promise, or using the depends_on promise attribute.

Agent normal ordering

CFEngine tries to keep variable and class promises before starting to consider any other kind of promise. In this way, global variables and classes can be set.

If you set variables based on classes that are determined by other variables, then you introduce an order dependence to the resolution that might be non-unique. Since CFEngine starts trying to converge values as soon as possible, it is best to define variables in bundles before using them, i.e. as early as possible in your configuration. In order to make sure all global variables and classes are available early enough policy pre-evaluation step was introduced.

Policy evaluation overview

CFEngine policy evaluation is done in several steps:

  1. Classes provided as a command line argument (-D option) are read and set.
  2. Environment detection and hard classes discovery is done.
  3. Persistent classes are loaded.
  4. Policy sanity check using cf-promises -c (full-check) is performed.
  5. Pre-evaluation step is taking place.
  6. Exact policy evaluation is done.

For more information regarding each step please see the detailed description below.

Policy evaluation details

Before exact evaluation of promises takes place first command line parameters are read and all classes defined using -D parameter are set. Next, environment detection takes place and hard classes are discovered. When environment detection is complete all the persistent classes are loaded and a policy sanity check is performed using cf-promises.

cf-promises policy validation step

In this step policy is validated and classes and vars promises are evaluated. Note that cached functions are executed here, and then again during the normal agent execution. Variables and classes resolved in this step do not persist into the following evaluation step, so all functions will run again during Agent pre-evaluation.

Agent pre-evaluation step

In order to support expansion of variables in body common control inputs and make sure all needed classes and variables are determined before they are needed in normal evaluation, pre-evaluation takes place immediately before policy evaluation.

In pre-evaluation files are loaded based on ordering in body common control (first) and body file control (after body common control). This means that files included in body common control are loaded and parsed before files placed in body file control. This is important from a common bundles evaluation perspective as bundles placed in files included in body common control inputs will be evaluated before bundles from file control inputs.

While pre-evaluating policy files common bundles are evaluated first (only classes and variables promises) and then agent bundles (variables only). This is caused by the fact that both variables and classes placed in common bundles are global whereas classes placed in agent bundles are local (by default) to bundles where those are defined. This means that during agent bundle pre-evaluation dependencies between variables and classes will not be resolved.

After all policy files are parsed one extra step of pre-evaluation is done in order to help resolve dependencies between classes and variables placed in different bundles. In this step first classes and variables from common bundles are resolved (in the same order that the policy was parsed) followed by variables in agent bundles.

Agent evaluation step

After pre-evaluation is complete normal evaluation begins.

In this step CFEngine executes agent promise bundles in the strict order defined by the bundlesequence (possibly overridden by the -b or --bundlesequence command line option). If the bundlesequence is not provided via command line argument or is not present in body common control agent will attempt to execute a bundle named main. If bundle main is not defined, the agent will error and exit.

Within a bundle, the promise types are executed in a round-robin fashion according to so-called normal ordering (essentially deletion first, followed by creation). The actual sequence continues for up to three iterations of the following, converging towards a final state:

meta
vars
defaults
classes
users
files
packages
guest_environments
methods
processes
services
commands
storage
databases
reports

Within edit_line bundles in files promises, the normal ordering is:

meta
vars
defaults
classes
delete_lines
field_edits
insert_lines
replace_patterns
reports

The order of promises within one of the above types follows their top-down ordering within the bundle itself. The order may be overridden by making a promise depend on a class that is set by another promise, or by using the depends_on attribute in the promise.

Note: The evaluation order of common bundles are classes, then variables and finally reports. All common bundles are evaluated regardless if they are placed in bundlesequence or not. Placing common bundles in bundlesequence will cause classes and variables to be evaluated again, and is generally good practice to make sure evaluation works properly.

Server normal ordering

As with the agent, common bundles are executed before any server bundles; following this all server bundles are executed (the bundlesequence is only used for cf-agent). Within a server bundle, the promise types are unambiguous. Variables and classes are resolved in the same way as the agent. On connection, access control must be handled first, then a role request might be made once access has been granted. Thus ordering is fully constrained by process with no additional freedoms.

Within a server bundle, the normal ordering is:

vars
classes
roles
access
Monitor normal ordering

As with the agent, common bundles are executed before any monitor bundles; following this all monitor bundles are executed (the bundlesequence is only used for cf-agent). Variables and classes are resolved in the same way as the agent.

Within a monitor bundle, the normal ordering is:

vars
classes
measurements
reports

Classes and Decisions

Classes are used to apply promises only to particular environments, depending on context. A promise might only apply to Linux systems, or should only be applied on Sundays, or only when a variable has a certain value.

Classes are simply facts that represent the current state or context of a system. The list of set classes classifies the environment at time of execution.

Classes are either set or not set, depending on context. Classes fall into hard classes that are discovered by CFEngine, and soft classes that are user-defined. Refer to Hard and Soft Classes in the Reference section for more information.

In CFEngine Enterprise, the list of set classes is reported to the CFEngine Database Server and can be used there for reporting, grouping of hosts and inventory management.

Hard Classes

Hard classes are discovered by CFEngine. Each time it wakes up, it discovers and reads properties of the environment or context in which it runs.It turns these properties of the environment into classes. This information is effectively cached and may be used to make decisions about configuration.

You can see all of the classes defined on a particular host by running the following command as a privileged user.

$ cf-promises --show-classes|grep hardclass

These are classes that describe your operating system, the time of day, the week of the year, etc. Time-varying classes (tagged with time_based) will change if you do this a few times over the course of a week.

Soft Classes

Soft classes are user-defined classes which you can use to implement your own classifications. These classes are defined in bundles and are evaluated when the bundle is evaluated. They can be based on test functions or on other classes.

    bundle agent myclasses
    {
    classes:
      "always";
      "always2" expression => "any";
      "solinux" expression => "linux||solaris";
      "alt_class" or => { "linux", "solaris", fileexists("/etc/fstab") };
      "oth_class" and => { fileexists("/etc/shadow"), fileexists("/etc/passwd") };

    reports:
      alt_class::
        # This will only report "Boo!" on linux, solaris, or any system
        # on which the file /etc/fstab exists
        "Boo!";
    }

This example defines a few soft classes local to the myclasses bundle.

  • The always and always2 soft classes are always defined.

  • The solinux soft class is defined as a combination of the linux or the solaris hard classes. This class will be set if the operating system family is either of these values.

  • The alt_class soft class is defined as a combination of linux, solaris, or the presence of a file named /etc/fstab. If one of the two hard classes evaluate to true, or if there is a file named /etc/fstab, the alt_class class will also be set.

  • The oth_class soft class is defined as the combination of two fileexists functions - /etc/shadow and /etc/passwd. If both of these files are present the oth_class class will also be set.

Negative Knowledge

If a class is set, then it is certain that the corresponding fact is true. However, that a class is not set could mean that something is not the case, or that something is simply not known. This is only a problem with soft classes, where the state of a class can change during the execution of a policy, depending on the order in which bundles and promises are evaluated.

Making Decisions based on classes

The easiest way to limit the application of a promise to certain conditions is to use the following notation:

    bundle agent greetings
    {
     reports:
       Morning::
         "Good morning!";

       Evening::
         "Good evening!";

       "! any"::
         "This report won't ever be seen.";

       # whitespace allowed only in 3.8 and later
       Friday . Evening::
         "It's Friday evening, TGIF!";

       "Monday . Evening"::
         "It's Monday evening.";
    }

In this example, the report "Good morning!" is only printed if the class Morning is set, while the report "Good evening!" is only printed when the class Evening is set.

The "! any" context will never be evaluated. Note that since CFEngine 3.8 context expressions can contain spaces for legibility.

The "Monday . Evening" context will only be true on Monday evenings. The Friday . Evening context will only be true on Friday evenings. See below for more on context operators.

Sometimes it's convenient to put class names in variables. This example shows two ways to execute code conditionally based on such variables:

    bundle agent greetings
    {
     vars:
      "myclassname" string => "Evening";

      reports:
       "$(myclassname)"::
         "Good evening!";

       "any"::
         "Good evening too!" ifvarclass => "$(myclassname)";
    }

As you saw above, the class predicate ifvarclass (aliased to if; unless is also available) can be used if variable class expressions are required. It is ANDed with the normal class expression, and is evaluated together with the promise. Both may contain variables as long as the resulting expansion is a legal class expression.

    bundle agent example
    {
      vars:
              "french_cities"  slist => { "toulouse", "paris" };
              "german_cities"  slist => { "berlin" };
              "italian_cities" slist => { "milan" };
              "usa_cities"     slist => { "lawrence" };

              "all_cities" slist => { @(french_cities), @(german_cities), @(italian_cities), @(usa_cities) };

      classes:
          "italy"   or => { @(italian_cities) };
          "germany" or => { @(german_cities) };
          "france"  or => { @(french_cities) };

      reports:
        "It's $(sys.date) here";

        Morning.italy::
          "Good morning from Italy",
            ifvarclass => "$(all_cities)";

        Afternoon.germany::
          "Good afternoon from Germany",
            ifvarclass => "$(all_cities)";

        france::
          "Hello from France",
            ifvarclass => "$(all_cities)";

        france::
          "IMPOSSSIBLE!  THIS WILL NOT PRINT!!!",
            unless => "france";

        "$(all_cities)"::
          "Hello from $(all_cities)";

        "Hello from $(all_cities), ifvarclass edition",
          ifvarclass => "$(all_cities)";
    }

Example Output:

    cf-agent -Kf example.cf -D lawrence -b example
    R: It's Tue May 28 16:47:33 2013 here
    R: Hello from lawrence
    R: Hello from lawrence, ifvarclass edition

    cf-agent -Kf example.cf -D paris -b example
    R: It's Tue May 28 16:48:18 2013 here
    R: Hello from France
    R: Hello from paris
    R: Hello from paris, ifvarclass edition

    cf-agent -Kf example.cf -D milan -b example
    R: It's Tue May 28 16:48:40 2013 here
    R: Hello from milan
    R: Hello from milan, ifvarclass edition

    cf-agent -Kf example.cf -D germany -b example
    R: It's Tue May 28 16:49:01 2013 here

    cf-agent -Kf example.cf -D berlin -b example
    R: It's Tue May 28 16:51:53 2013 here
    R: Good afternoon from Germany
    R: Hello from berlin
    R: Hello from berlin, ifvarclass edition

In this example, lists of cities are defined in the vars section and these lists are combined into a list of all cities. These variable lists are used to qualify the greetings and to make the policy more concise. In the classes section a country class is defined if a class described on the right hand side evaluates to true. In the reports section the current time is always reported but only agents found to have the Morning and italy classes defined will report "Good morning from Italy", this is further qualified by ensuring that the report is only generated if one of the known cities also has a class defined.

Operators and Precedence

Classes promises define new classes based on combinations of old ones. This is how to make complex decisions in CFEngine, with readable results. It is like defining aliases for class combinations. Such class 'aliases' may be specified in any kind of bundle.

Since CFEngine 3.8, whitespace is allowed between operators. It was not allowed up to 3.7.

For example a . b is equivalent to a.b and perhaps more readable.

Classes may be combined with the operators listed here in order from highest to lowest precedence:

  • ‘()':: ~ The parenthesis group operator.

  • ‘!’:: ~ The NOT operator.

  • ‘.’:: ~ The AND operator.

  • ‘&’:: ~ The AND operator (alternative).

  • ‘|’:: ~ The OR operator.

  • ‘||’:: ~ The OR operator (alternative).

These operators can be combined to form complex expressions. For example, the following expression would be only true on Mondays or Wednesdays from 2:00pm to 2:59pm on Windows XP systems:

(Monday|Wednesday).Hr14.WinXP::
Operands that are functions

If an operand is another function and the return value of the function is undefined, the result of the logical operation will also be undefined. For this reason, when using functions as operators, it is safer to collapse the functions down to scalar values and to test if the values are either true or false before using them as operands in a logical expression.

e.g.

    ...
    classes:
            "variable_1"
            expression => fileexists("/etc/aliases.db");
    ...

    "result"
    or => { isnewerthan("/etc/aliases", "/etc/aliases.db"),
    "!variable_1" };

The function, isnewerthan can return "undefined" if one or other of the files does not exist. In that case, result would also be undefined. By checking the validity of the return value before using it as an operand in a logical expression, unpredictable results are avoided. i.e negative knowledge does not necessarily imply that something is not the case, it could simply be unknown. Checking if each file exists before calling isnewerthan would avoid this problem.

Operands that are JSON booleans

If an operand is true it will succeed, even though there doesn't have to be a class named true. If an operand is false it will fail, even though there may be a class named false. This allows JSON booleans from data containers to be used in context expressions:

bundle agent main
{
    vars:
      "checks" data => '[true, false]';
      # find all classes named
      "classes_named_true" slist => classesmatching('true');

  classes:
      # always defined
      "first_check" expression => "$(checks[0])";
      # never defined
      "second_check" expression => "$(checks[1])";

  reports:
      # prints nothing, there are no classes named 'true'
      "Classes named 'true': $(classes_named_true)";

    first_check::
      "The class was defined from '$(checks[0])'";
    !first_check::
      "The class was NOT defined from '$(checks[0])'";
    second_check::
      "The class was defined from '$(checks[1])'";
    !second_check::
      "The class was NOT defined from '$(checks[1])'";
}

Output:

R: The class was defined from 'true'
R: The class was NOT defined from 'false'
Global and Local classes

Classes defined in bundles of type common are global in scope, whereas classes defined in all other bundle types are local. Classes are evaluated when the bundle is evaluated (and the bundles are evaluated in the order specified in the bundlesequence).

Note that any class promise must have one - and only one - value constraint. That is, you might not leave 'expression' in the example above or add both 'and' and 'xor' constraints to the single promise.

Another type of class definition uses the body classes. This allows setting of classes based on the outcome of a promise. To set a class if a promise is repaired, one might write:

     "promiser..."
        ...
        classes => if_repaired("signal_class");

These classes are global in scope, but the scope attribute can be used to make them local to the bundle.

Finally, restart_class classes in processes are global.

Canceling classes

You can cancel a class with a classes body. See the cancel_kept, cancel_notkept, and cancel_repaired attributes.

Class Scopes: A More Complex Example
    body common control
    {
        bundlesequence => { "global","local_one", "local_two" };
    }

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

    bundle common global
    {
        classes:
            # The soft class "zero" is always satisfied,
            # and is global in scope
            "zero" expression => "any";
    }

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

    bundle agent local_one
    {
        classes:
            # The soft class "one" is always satisfied,
            # and is local in scope to local_one
            "one" expression => "any";
    }

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

    bundle agent local_two
    {
        classes:
            # The soft class "two" is always satisfied,
            # and is local in scope to ls_2
            "two" expression => "any";

        reports:
            zero.!one.two::
                # This report will be generated
                "Success";
    }

In this example, there are three bundles. One common bundle named global with a global scope. Two agent bundles define classes one and two which are local to those bundles.

The local_two bundle promises a report "Success" which applies only if zero.!one.two evaluates to true. Within the local_two scope this evaluates to true because the one class is not set.


Variables

Just like classes are defined as promises, variables (or "variable definitions") are also promises. Variables can be defined in any promise bundle. This bundle name can be used as a context when using variables outside of the bundle they are defined in.

CFEngine variables have three high-level types: scalars, lists, and data containers.

  • A scalar is a single value,
  • a list is a collection of scalars.
  • a data container is a lot like a JSON document, it can be a key-value map or an array or anything else allowed by the JSON standard with unlimited nesting.
Scalar Variables

Each scalar may have one of three types: string, int or real. String scalars are sequences of characters, integers are whole numbers, and reals are float pointing numbers.

    vars:
      "my_scalar" string => "String contents...";
      "my_int" int       => "1234";
      "my_real" real     => "567.89";

Integer constants may use suffixes to represent large numbers. The following suffixes can be used to create integer values for common powers of 1000.

  • 'k' = value times 1000
  • 'm' = value times 10002
  • 'g' = value times 10003

Since computing systems such as storage and memory are based on binary values, CFEngine also provide the following uppercase suffixes to create integer values for common powers of 1024.

  • 'K' = value times 1024.
  • 'M' = value times 10242
  • 'G' = value times 10243

However, the values must have an integer numeric part (e.g. 1.5M is not allowed).

In some contexts, % can be used a special suffix to denote percentages.

Lastly, there is a reserved value which can be used to specific a parameter as having no limit at all.

  • 'inf' = a constant representing an unlimited value.

CFEngine typing is mostly dynamic, and CFEngine will try to coerce string values into int and real types, and if it cannot it will report an error. However, arguments to built-in functions check the defined argument type for consistency.

Scalar Referencing and Expansion

Scalar variables are referenced by $(my_scalar) (or ${my_scalar}) and expand to the single value they hold at that time. If you refer to a variable by $(unqualified), then it is assumed to belong to the current bundle. To access any other (scalar) variable, you must qualify the name, using the name of the bundle in which it is defined:

$(bundle_name.qualified)
Scalar Size Limitations

At the moment, up to 4095 bytes can fit into a scalar variable. This limitation may be removed in the future.

If you try to expand strings in a variable or string context that add up to more that 4095 bytes, you will notice this limitation as well. The functions eval() to do math, string_head() and string_tail() to extract a certain number of characters from either end of a string, and string_length() to find a string's length may be helpful.

See readfile() for more detail on reading values from a file.

See data_readstringarray() and data_readstringarrayidx() for a way to read large files' contents into a data container without going through scalar variables or arrays.

Lists

List variables can be of type slist, ilist or rlist to hold lists of strings, integers or reals, respectively.

Every element of a list is subject to the same size limitations as a regular scalar.

They are declared as follows:

     vars:
         "my_slist" slist => { "list", "of", "strings" };
         "my_ilist" ilist => { "1234", "5678" };
         "my_rlist" rlist => { "567.89" };
List Substitution and Expansion

An entire list is referenced with the symbol ‘@’ and can be passed in their entirety in any context where a list is expected as @(list). For example, the following variable definition references a list named "shortlist":

    vars:
        "shortlist" slist => { "you", "me" };
        "longlist" slist => { @(shortlist), "plus", "plus" };

The declaration order does not matter – CFEngine will understand the dependency, and execute the promise to assign the variable @(shortlist) before the promise to assign the variable @(longlist).

Using the @ symbol in a string scalar will not result in list substitution. For example, the string value "My list is @(mylist)" will not expand this reference.

Using the scalar reference to a local list variable, will cause CFEngine to iterate over the values in the list. E.g. suppose we have local list variable @(list), then the scalar $(list) implies an iteration over every value of the list.

In some function calls, listname instead of @(listname) is expected. See the specific function's documentation to be sure.

Data Container Variables

The data containers can contain several levels of data structures, e.g. list of lists of key-value arrays. They are used to store structured data, such as data read from JSON or YAML files. The variable type is data.

Data containers are obtained from functions that return data types, such as readjson() or parsejson(), readyaml() or parseyaml(), or from merging existing containers.

They can NOT be modified, once created.

Data containers do not have the size limitations of regular scalar variables.

TODO: More, and examples

Associative Arrays

Note that associative arrays are being deprecated in favor of the data variable type. It is recommended to use the data variable type instead whenever possible to ensure future compatibility of your CFEngine policy.

Every value in an associative array is subject to the same size limitations as a regular scalar.

Associative array variables are written with [ and ] brackets that enclose an arbitrary key. These keys are associated with values

    bundle agent example
    {
        vars:

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

            "array[cf-monitord]" string => "The monitor";
            "array[cf-serverd]" string => "The server";
            "array[cf-execd]" string => "The executor, not executioner";

        commands:

            "/bin/echo $(component) is"
                args => "$(array[$(component)])";
    }

This example defines three values in an associative array under the keys cf-monitord, cf-serverd, and cf-execd. They and are sequently printed with the echo command.

Arrays are associative and may be of type scalar or list. Enumerated arrays are simply treated as a special case of associative arrays, since there are no numerical loops in CFEngine. Special functions exist to extract lists of keys from array variables for iteration purposes.

Here is an example of using the function getindices() which extracts all of the keys from an associative array. If this series of promises were executed it would print out two messages, one for each key.

    bundle agent array
    {
      vars:

          "v[index_1]" string => "value_1";
          "v[index_2]" string => "value_2";

          "parameter_name" slist => getindices("v");

      reports:
          "Found index: $(parameter_name)";
    }

Augments

An augments file can be used to define variables and classes to the execution of all CFEngine components before any parsing or evaluation happen. It's a JSON data file, so you should view and edit it with a JSON-aware editor if possible.This is a convenient way to override defaults defined in the Masterfiles Policy Framework without modifying the shipped policy itself.

The file def.json is found like the policy file to be run:

  • with no arguments, it's in $(sys.inputdir)/def.json because $(sys.inputdir)/promises.cf is used
  • with -f /dirname/myfile.cf, it's in /dirname/def.json
  • with -f myfile.cf, it's in ./def.json

Values will be expanded, so you can use the variables from Special Variables.

Currently the augments file can contain three keys:

  • inputs: any filenames you put here will appear in the def.augments_inputs variable. The standard set of masterfiles refers to this variable and will autoload those files.

  • vars: any variables you put here will be put in the def scope. Thus:

    "vars":
    {
    "phone": "22-333-4444",
    "myplatform": "$(sys.os)",
    }
    

    results in the variable def.phone with value 22-333-4444 being defined, and def.myplatform with the value of your current OS. Again, note that this happens before policy is parsed or evaluated.

    You can see the list of variables thus defined in the output of cf-promises --show-vars (see Components and Common Control). They will be tagged with the tag source=augments_file. For instance, the above two variables (assuming you placed the data in $(sys.inputdir)/def.json) result in

    cf-promises --show-vars
    ...
    default:def.myplatform                   linux                                                        source=augments_file
    default:def.phone                        22-333-4444                                                  source=augments_file
    
  • classes: any class names you put here will be evaluated and installed as hard classes if they match as a class name or a regular expression. Thus:

    "classes":
    {
    "my_always": "any",
    "my_other_apache": [ "server[34]", "debian.*" ],
    }
    

    results in my_always being always defined. my_other_apache will be defined if the classes server3 or server4 are defined, or if any class starting with debian is defined. You can use any hard classes listed in Hard and Soft Classes with the exception of am_policy_hub and policy_server.

    You can see the list of classes thus defined through def.json in the output of cf-promises --show-classes (see Components and Common Control). They will be tagged with the tags source=augments_file,hardclass. For instance, the above two classes result in:

    % cf-promises --show-classes
    ...
    my_always                                                    source=augments_file,hardclass
    my_other_apache                                              source=augments_file,hardclass
    

History: Introduced into the Masterfiles Policy Framework with CFEngine 3.7.0, the file def.json in the root of the policy directory was processed by policy. In CFEngine 3.8.1 def.json parsing was moved from a policy level feature into the core agent to address usability issues. It was also possible to use inputs to autoload inputs without referencing the def.augments_inputs variable. This would happen before all evaluation stages. However, this functionality turned out to be problematic and was removed in later versions. Pre-parsing of def.cf was introduced to the 3.7.x series in 3.7.3. In 3.7.3, 3.8.2 and later def.json is looked for next to the policy entry file.


Loops

There are no explicit loops in CFEngine, instead there are lists. To make a loop, you simply refer to a list as a scalar and CFEngine will assume a loop over all items in the list.

It's as if you said "I know three colors: red green blue. Let's talk about color."

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

    bundle agent color_example
    {
        vars:
            "color" slist => { "red", "green", "blue" };

        reports:
            "Let's talk about $(color)";
    }

CFEngine will implicitly loop over each $(color):

% cf-agent -K -f ./test_colors.cf

R: Let's talk about red
R: Let's talk about green
R: Let's talk about blue

Here's a more complex example.

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

    bundle agent example
    {
        vars:
            "component" slist => { "cf-monitord", "cf-serverd", "cf-execd" };

            "array[cf-monitord]" string => "The monitor";
            "array[cf-serverd]" string => "The server";
            "array[cf-execd]" string => "The executor, not executionist";

        reports:
            "$(component) is $(array[$(component)])";
    }

In this example, the list component has three elements. The list as a whole may be referred to as @(component), in order to pass the whole list to a promise where a list is expected. However, if we write $(component), i.e. the scalar variable, then CFEngine will substitute each scalar from the list in turn, and thus iterate over the list elements using a loop.

The output looks something like this:

$ cf-agent unit_loops.cf

2013-06-12T18:56:01+0200   notice: R: cf-monitord is The monitor
2013-06-12T18:56:01+0200   notice: R: cf-serverd is The server
2013-06-12T18:56:01+0200   notice: R: cf-execd is The executor, not executionist

You see from this that, if we refer to a list variable using the scalar reference operator $(), CFEngine interprets this to mean “please iterate over all values of the list”. Thus, we have effectively a `foreach' loop, without the attendant syntax.

If a variable is repeated, its value is tied throughout the expression; so the output of:

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

    bundle agent example
    {
    vars:
      "component" slist => { "cf-monitord", "cf-serverd", "cf-execd" };

      "array[cf-monitord]" string => "The monitor";
      "array[cf-serverd]" string => "The server";
      "array[cf-execd]" string => "The executor, not executioner";

    commands:
       "/bin/echo $(component) is"
                args => "$(array[$(component)])";
    }

is as follows:

2013-06-12T18:57:34+0200   notice: Q: ".../bin/echo cf-mo": cf-monitord is The monitor
2013-06-12T18:57:34+0200   notice: Q: ".../bin/echo cf-se": cf-serverd is The server
2013-06-12T18:57:34+0200   notice: Q: ".../bin/echo cf-ex": cf-execd is The executor, not executioner
Iterating Across Multiple Lists

CFEngine can iterate across multiple lists simultaneously.

    bundle agent iteration
    {
    vars:
        "stats"   slist => { "value", "av", "dev" };

        "monvars" slist => {
                           "rootprocs",
                           "otherprocs",
                           "diskfree",
                           "loadavg"
                           };
    reports:
        "mon.$(stats)_$(monvars) is $(mon.$(stats)_$(monvars))";
    }

This example uses two lists, stats and monvars. We can now iterate over both lists in the same promise. The reports that we thus generate will report on value_rootprocs, av_rootprocs, and dev_rootprocs, followed next by value_otherprocs, av_otherprocs, etc, ending finally with dev_loadavg.

The order of iteration is an implementation detail and should not be expected to be consistent. Use the sort() function if you need to sort a list in a predictable way.


Pattern Matching and Referencing

One of the strengths of CFEngine 3 is the ability to recognize and exploit patterns. All string patterns in CFEngine 3 are matched using PCRE regular expressions.

CFEngine has the ability to extract back-references from pattern matches. This makes sense in two cases. Back references are fragments of a string that match parenthetic expressions. For instance, suppose we have the string:

Mary had a little lamb ...

and apply the regular expression

"Mary ([^l]+)little (.*)"

The pattern matches the entire string, and it contains two parenthesized subexpressions, which respectively match the fragments had a and lamb .... The regular expression libraries assign three matches to this result, labelled 0, 1 and 2.

The zeroth value is the entire string matched by the total expression. The first value is the fragment matched by the first parenthesis, and so on.

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.

Consider the examples below:

    bundle agent testbundle
    {
    files:

      # This might be a dangerous pattern - see explanation in the next section

      # on "Runaway change warning"


      "/home/mark/tmp/cf([23])?_(.*)"
           edit_line => myedit("second backref: $(match.2)");
    }

There are other filenames that could match this pattern, but if, for example, there were to exist a file /home/mark/tmp/cf3_test, then we would have:

‘$(match.0)’
equal to `/home/mark/tmp/cf3_test'
‘$(match.1)’
equal to `3'
‘$(match.2)’
equal to `test'

Note that because the pattern allows for an optional '2' or '3' to follow the letters cf, it is possible that $(match.1) would contain the empty string. For example, if there was a file named /home/mark/tmp/cf_widgets, then we would have

‘$(match.0)’
equal to `/home/mark/tmp/cf_widgets'
‘$(match.1)’
equal to `'
‘$(match.2)’
equal to `widgets'

Now look at the edit bundle. This takes a parameter (which is the back-reference from the filename match), but it also uses back references to replace shell comment lines with C comment lines (the same approach is used to hash-comment lines in files). The back-reference variables $(match.n) refer to the most recent pattern match, and so in the C_comment body, they do not refer to the filename components, but instead to the hash-commented line in the replace_patterns promise.

    bundle edit_line myedit(parameter)
    {
      vars:

       "edit_variable" string => "private edit variable is $(parameter)";

      insert_lines:

         "$(edit_variable)";

      replace_patterns:

      # replace shell comments with C comments

       "#(.*)"

          replace_with => C_comment,
         select_region => MySection("New section");
      }

    ########################################
    # Bodies
    ########################################

    body replace_with C_comment
    {
    replace_value => "/* $(match.1) */"; # backreference from replace_patterns
    occurrences => "all";  # first, last, or all
    }

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

    body select_region MySection(x)
    {
        select_start => "\[$(x)\]";
        select_end => "\[.*\]";
    }

Try this example on the file

[First section]
one
two
three

[New section]
four
#five

six

[final]
seven
eleven

The resulting file is edited like this:

[First section]

one
two
three

[New section]

four
/* five */
six

[final]

seven
eleven

private edit variable is second backref: test
Runaway change warning

Be careful when using patterns to search for files that are altered by CFEngine if you are not using a file repository. Each time CFEngine makes a change it saves an old file into a copy like cf3_test.cf-before-edit. These new files then get matched by the same expression above – because it ends in the generic.*), or does not specify a tail for the expression. Thus CFEngine will happily edit backups of the edit file too, and generate a recursive process, resulting in something like the following:

 cf3_test                  cf3_test.cf-before-edit
 cf3_test~                 cf3_test~.cf-before-edit.cf-before-edit
 cf3_test~.cf-before-edit  cf3_test~.cf-before-edit.cf-before-edit.cf-before-edit

Always try to be as specific as possible when specifying patterns. A lazy approach will often come back to haunt you.

Commenting lines

The following example shows how you would hash-comment lines in a file using CFEngine.

    ######################################################################
    #
    # HashCommentLines implemented in CFEngine 3
    #
    ######################################################################


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

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

    bundle agent testbundle
    {
    files:
      "/home/mark/tmp/comment_test"
           create    => "true",
           edit_line => comment_lines_matching;
    }

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


    bundle edit_line comment_lines_matching
      {
      vars:

        "regexes" slist => { "one.*", "two.*", "four.*" };

      replace_patterns:

       "^($(regexes))$"
          replace_with => comment("# ");
      }

    ########################################
    # Bodies
    ########################################


    body replace_with comment(c)
    {
        replace_value => "$(c) $(match.1)";
        occurrences => "all";
    }
Regular expressions in paths

When applying regular expressions in paths, the path will first be split at the path separators, and each element matched independently. For example, this makes it possible to write expressions like /home/.*/file to match a single file inside a lot of directories — the .* does not eat the whole string.

Note that whenever regular expressions are used in paths, the / is always used as the path separator, even on Windows. However, on Windows, if the pathname is interpreted literally (no regular expressions), then the backslash is also recognized as the path separator. This is because the backslash has a special (and potentially ambiguous) meaning in regular expressions (a \d means the same as [0-9], but on Windows it could also be a path separator and a directory named d).

The pathtype attribute allows you to force a specific behavior when interpreting pathnames. By default, CFEngine looks at your pathname and makes an educated guess as to whether your pathname contains a regular expression. The values literal and regex explicitly force CFEngine to interpret the pathname either one way or another. (see the pathtype attribute).

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

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


    bundle agent wintest
    {
    files:
      "c:/tmp/file/f.*"     # "best guess" interpretation
        delete => nodir;


      "c:\tmp\file"
        delete => nodir,
        pathtype => "literal";  # force literal string interpretation


      "C:/windows/tmp/f\d"
        delete => nodir,
        pathtype => "regex";    # force regular expression interpretation
    }

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


    body delete nodir
    {
        rmdirs => "false";
    }

Note 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 (you can't use it in a character class like [^/] or in a parenthesized or repeated regular expression component.

This means that regular expressions which include "optional directory components" won't work. You can't have a files promise to tidy the directory (/usr)?/tmp. Instead, you need to be more verbose and specify /usr/tmp|/tmp, or even better, think declaratively and 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 like /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 degenerate case of /tmp//something (or /tmp/something).

Anchored vs. unanchored regular expressions

CFEngine uses the full power of regular expressions, but there are two “flavors” of regex. Because they behave somewhat differently (while still utilizing the same syntax), it is important to know which one is used for a particular component of CFEngine:

An “anchored” regular expression will only successfully match an entire string, from start to end. An anchored regular expression behaves as if it starts with ^ and ends with $, whether you specify them yourself or not. Furthermore, an anchored regular expression cannot have these automatic anchors removed.

An “unanchored” regular expression may successfully match anywhere in a string. An unanchored regex may use anchors (such as ^, $, \A, \Z, \b, etc.) to restrict where in the string it may match. That is, an unanchored regular expression may be easily converted into a partially- or fully-anchored regex.

For example, the comment parameter in readstringarray() is an unanchored regex. If you specify the regular expression as #.*, then on any line which contains a pound sign, everything from there until the end of the line will be removed as a comment. However, if you specify the regular expression as ^#.* (note the ^ anchor at the start of the regex), then only lines which start with a # will be removed as a comment! If you want to ignore C-style comment in a multi-line string, then you have to a bit more clever, and use this regex: (?s)/\*.*?\*/

Conversely, delete_lines promises use anchored regular expressions to delete lines. If our promise uses bob:\d* as a line-matching regex, then only the second line of this file will be deleted (because only the second line starts with bob: and is then followed exclusively by digits, all the way to the end of the string).

bobs:your:uncle
bob:111770
thingamabob:1234
robert:bob:xyz
i:am:not:bob

If CFEngine expects an unanchored regular expression, then finding every line that contains the letters bob is easy. You just use the regex bob. But if CFEngine expects an anchored regular expression, then you must use .*bob.*.

If you want to find every line that has a field which is exactly bob with no characters before or after, then it is only a little more complicated if CFEngine expects an unanchored regex: (^|:)bob(:|$). But if CFEngine expects an anchored regular expression, then it starts getting ugly, and you'd need to use bob:.*|.*:bob:.*|.*:bob.

Special topics on Regular Expressions

Regular expressions are a complicated subject, and really are beyond the scope of this document. However, it is worth mentioning a couple of special topics that you might want to know of when using regular expressions.

The first is how to not get a back reference. If you want to have a parenthesized expression that does not generate a back reference, there is a special PCRE syntax to use. Instead of using () to bracket the piece of a regular expression, use (?:) instead. For example, this will match the filenames foolish, foolishly, bearish, bearishly, garish, and garishly in the /tmp directory. The variable $match.0 will contain the full filename, and $match.1 will either contain the string ly or the empty string. But the (?:expression) which matches foo, bear, or gar does not create a back-reference:

    files:
        "/tmp/(?:foo|bear|gar)ish(ly)?"

Note that sometimes multi-line strings are subject to be matched by regular expressions. CFEngine internally matches all regular expressions using PCRE_DOTALL option, so . matches newlines. If you want to match any character except newline you could use \N escape sequence.

Another thing you might want to do is ignore capitalization. CFEngine is case-sensitive (in all things), so the files promise /tmp/foolish will not match the files /tmp/Foolish or /tmp/fOoLish, etc. There are two ways to achieve case-insensitivity. The first is to use character classes:

    files:
        "/tmp/[Ff][Oo][Oo][Ll][Ii][Ss][Hh]"

While this is certainly correct, it can also lead to unreadability. The PCRE patterns in CFEngine have another way of introducing case-insensitivity into a pattern:

    files:
        "/tmp/(?i:foolish)"

The (?i:) brackets impose case-insensitive matching on the text that it surrounds, without creating a sub-expression. You could also write the regular expression like this (but be aware that the two expressions are different, and work slightly differently, so check the documentation for the specifics):

    files:
        "/tmp/(?i)foolish"

The /s, /m, and /x switches from PCRE are also available, but use them with great care!


Namespaces

Namespaces are private bundle and body "playgrounds", allowing multiple files to define the bundles and bodies with the same name in different namespaces without conflict. They are key to writing reusable policies.

Everything in CFEngine lives in a namespace (it's the default namespace if not set).

Specifying a namespace

To isolate a file into its own namespace, you add a file control promise to the file before the relevant bundles or bodies. All bundles and bodies start off in the default namespace if you don't explicitly set this. Once set, this applies until the end of the file or the next namespace change.

    body file control
    {
       namespace => "myspace";
    }
Accessing syntax elements between namespaces and the default namespace

To distinguish the bundle mymethod in the default namespace from one in another namespace, you prefix the bundle name with the namespace, separated by a colon.

    methods:

      "namespace demo" usebundle => myspace:mymethod("arg1");
      "namespace demo" usebundle => mymethod("arg1","arg2");

To distinguish a body from one in another namespace, you can prefix the body name with the namespace, separated by a colon.

    files:
       "/file"
          create => "true",
           perms => name1:settings;

If you don't make any namespace declarations, you'll be in the default namespace. Bundles, bodies, classes, and variables from the default namespace can be accessed like any other:

    files:
      "/file"
         create => "true",
          perms => default:settings;

If you use the standard library from your own namespace, remember to specify this default: prefix.

To access classes, variables, or meta-data in bundles in a different namespace, use the colon as a namespace prefix:

$(namespace:bundle.variable)
$(namespace:bundle_meta.variable)

Note that this means that if you are in a namespace that's not default, you must qualify classes from default fully:

default:myclass::
"do something" ifvarclass => "default:myotherclass";
Namespacing of classes and variables created in policy

In policy, you can't create classes outside your own namespace. So the following, for example, will create the class mynamespace:done if it runs in the namespace mynamespace.

    files:
      "/file"
         create => "true",
         action => if_repaired("done");

Similarly, variables you create in a namespaced bundle have to be prefixed like mynamespace:mybundle.myvar from outside your namespace, but can use mybundle.myvar inside the namespace and myvar inside mybundle.

As a workaround, you could have a helper bundle in another namespace to create classes and variables as needed.

Exceptions to namespacing rules

Exceptions to the rules above:

  • All hard classes can be used as-is from any namespace, without a namespace prefix. These are classes like linux. They will have the tag hardclass.

  • All special variable contexts, as documented in Special Variables, are always accessible without a namespace prefix. For example, this, mon, sys, and const fall in this category.


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.

Note: When updating objects via the REST API the behavior is to overwrite existing objects. Any missing keys are reset to default values. For example if you have custom LDAP settings and want to update the blueHostHorizon you should first query to get the current settings, and then post the complete settings that you desire else the customized LDAP settings will be reset to defaults.

This example shows using JQ to preserve existing setting when updating an individual key value.

[root@hub]# curl -s -u admin:admin http://localhost:80/api/settings \
| jq '.data[0] + {"blueHostHorizon": 2222, "logLevel": "warning"}' \
| curl -s -u admin:admin http://localhost:80/api/settings -X POST -d @-

[root@hub]# curl -s -u admin:admin http://localhost:80/api/settings | jq '.data[0]'
{
  "blueHostHorizon": 2222,
  "hostIdentifier": "default.sys.fqhost",
  "ldapEnabled": false,
  "ldapEncryption": "plain",
  "ldapHost": "localhost",
  "ldapLoginAttribute": "uid",
  "ldapPort": 389,
  "ldapPortSSL": 636,
  "logLevel": "warning",
  "rbacEnabled": true,
  "sketchActivationAlertTimeout": 60
}
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 /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.


Status and Settings REST API

REST API for managing settings, checking hub status.

Get server status

URI: https://hub.cfengine.com/api

Method: GET

Example response:

{
  "meta": {
    "page": 1,
    "count": 1,
    "total": 1,
    "timestamp": 1437396760
  },
  "data": [
    {
      "apiName": "CFEngine Enterprise API",
      "apiVersion": "v1",
      "enterpriseVersion": "3.6.4",
      "uiVersion": "ed2766c",
      "coreVersion": "3.6.5",
      "authenticated": "internal",
      "userId": "admin",
      "license": {
        "expires": "2222-12-25 00:00:00+00",
        "owner": "FREE ENTERPRISE - http://cfengine.com/terms for terms",
        "licenseType": "Enterprise Free",
        "granted": 25
      }
    }
  ]
}

Output:

  • apiName Human-friendly API name.
  • apiVersion API version string.
  • enterpriseVersion Version of the CFEngine Enterprise build.
  • uiVersion The internal build number of the Enterprise UI.
  • coreVersion The version of CFEngine Core (Community) the Enterprise version was built against.
  • authenticated ("internal", "external") Whether the request was authenticated using the internal users table or an external source.
  • license.expires Time when the license expires.
  • license.owner The name of the license owner.
  • license.granted Host number capacity granted by the license.
  • license.licenseType License description.

Example usage: Checking Status

Get settings

URI: https://hub.cfengine.com/api/settings

Method: GET

Check all settings of Mission Portal and REST API. API call allowed only for administrator.

Example response:

{
  "meta": {
    "page": 1,
    "count": 1,
    "total": 1,
    "timestamp": 1350992335
  },
  "data": [
    {
      "ldapPort": 389,
      "ldapPortSSL": 636,
      "hostIdentifier": "default.sys.fqhost",
      "rbacEnabled": true,
      "logLevel": "error",
      "ldapEnabled": true,
      "ldapUsername": "",
      "ldapPassword": "",
      "ldapEncryption": "ssl",
      "ldapLoginAttribute": "uid",
      "ldapHost": "ldap.example.com",
      "ldapBaseDN": "ou=people,dc=example,dc=com",
      "ldapFilter": "(objectClass=inetOrgPerson)",
      "blueHostHorizon": 900,
      "sketchActivationAlertTimeout": 60
    }
  ]
}

Output:

  • 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.
  • 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).
  • 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.

Example usage: Example: Viewing settings

Update settings

URI: https://hub.cfengine.com/api/settings

Method: POST

Update settings for Mission Portal and API's. API call allowed only for administrator.

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.
  • 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).
  • 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.
  • blueHostHorizon (900) Threshold in minutes that hosts are unreachable before they are considered a health issue.

Example Request Body:

{
  "ldapPort": 389,
  "ldapPortSSL": 636,
  "hostIdentifier": "default.sys.fqhost",
  "rbacEnabled": false,
  "logLevel": "error",
  "ldapEnabled": true,
  "ldapUsername": "",
  "ldapPassword": "",
  "ldapEncryption": "ssl",
  "ldapLoginAttribute": "uid",
  "ldapHost": "ldap.example.com",
  "ldapBaseDN": "ou=people,dc=example,dc=com",
  "ldapFilter": "(objectClass=inetOrgPerson)",
  "blueHostHorizon": 900,
  "sketchActivationAlertTimeout": 60
}

Example usage: Example: Configuring LDAP, Example: Changing The Log Level


Design Center REST API

Please see The Design Center API for the Design Center API commands that are wrapped by the following Enterprise API commands.

List of sketches

URI: https://hub.cfengine.com/api/dc/sketch

Method: GET

Example response:

{
  "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
            }
          }
        }
      }
    }
  ]
}
Information about specific sketch

URI: https://hub.cfengine.com/api/dc/sketch/:sketchName

Method: GET

Example response:

{
  "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"
          }
        ]
      }
    }
  ]
}
Install sketch in the system

URI: https://hub.cfengine.com/api/dc/sketch/:sketchName

Method: PUT

Example usage: Sample API call to Install sketch

List of available definitions

URI: https://hub.cfengine.com/api/dc/definition

Method: GET

Example response:

{
  "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"
        }
      }
    }
  ]
}
Create new definition

URI: https://hub.cfengine.com/api/dc/definition/:definitionName

Method: PUT

Example Request Body:

{
  "sketchName": "test",
  "params": {
    "param_1": "value"
  }
}

Example usage: Sample API call to Define sketch parameters

List of available environments

URI: https://hub.cfengine.com/api/dc/environment

Method: GET

Example response:

{
  "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": []
        }
      }
    }
  ]
}
Create new environment

URI: https://hub.cfengine.com/api/dc/environment/:name

Method: PUT

Example Request Body:

{
  "environment": [
    "cfengine3"
  ]
}

Example usage: Sample API call to Define environment

List of available activations

URI: https://hub.cfengine.com/api/dc/activation

Method: GET

Parameters:

  • sketch Name of the sketch
  • details 1 or 0 for extended details

Example response:

{
  "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
          }
        }
      ]
    }
  ]
}
Activation details

URI: https://hub.cfengine.com/api/dc/activation/:activation_id/:sketchName

Method: GET

Parameters:

  • sketchName Name of the sketch
  • details 1 or 0 for host and other details

Example response:

{
  "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": []
        }
      }
    ]
  ]
}
Create new activation

URI: https://hub.cfengine.com/api/dc/activation/:id

Method: PUT

Example Request Body:

{
  "environmentName": "092b04a40fdd4cb8bfdb685f2c4a0328",
  "paramName": "c53db12b79d5b2b74f319b91caf7e88f",
  "bundleName": "installed"
}

Example usage: Sample API call to Activate sketch

Delete the activation

URI: https://hub.cfengine.com/api/dc/activation/:id

Method: DELETE

List of validations

URI: https://hub.cfengine.com/api/dc/validation

Method: GET

Get validation details

URI: https://hub.cfengine.com/api/dc/validation/:id

Method: GET

Set validation type

URI: https://hub.cfengine.com/api/dc/validate/:validationType

Method: POST

Example Request Body:

{
  "validationData": [
    "asdasd"
  ]
}
Get workspace

URI: https://hub.cfengine.com/api/dc/workspace

Method: GET

Checks for the workspace and returns the path.

Post the commits

URI: https://hub.cfengine.com/api/dc/workspace/commit

Method: POST

Example Request Body:

{
  "message": "some message",
  "userEmail": "email.com"
}

Example usage: Sample API call to Commit changes

Reset the user workspace

URI: https://hub.cfengine.com/api/dc/workspace/reset

Method: POST

List workspace settings

URI: https://hub.cfengine.com/api/dc/workspace/settings

Method: GET

Returns the settings of the workspace (VCS settings), 404 if not found.

Create settings

URI: https://hub.cfengine.com/api/dc/workspace/settings

Method: POST

Content-Type header should be multipart/form-data.

Example Request Body:

{
  "gitServer": "serverurl",
  "gitEmail": "email.com",
  "gitBranch": "gitbranch name",
  "gitAuthor": "author name",
  "gitPrivateKey": "@filepath"
}
curl -F "gitServer=servername" -F "gitEmail=mail" -F "gitPrivateKey=@/home/user1/Desktop/id_rsa" http://server
Delete settings

URI: https://hub.cfengine.com/api/dc/workspace/settings

Method: DELETE


Query REST API

In case of a need for full flexibility, Query API allow users to execute SQL queries on CFEngine Database.

Database schema available can be found here.

Execute SQL query

URI: https://hub.cfengine.com/api/query

Method: POST

Execute user SQL query. Accepts SQL compatible with PostgreSQL database. Query is a subject to Role Base Access Control and will include data for hosts that issuing user have permissions to access. Read-only SQL is allowed.

API performance depend on the query result size, to achieve fastest results consider narrowing result set at much as possible.

Parameters:

  • query (string) SQL query string.
  • sortColumn (string) Column name on which to sort results. Optional parameter.
  • sortDescending (boolean) Sorting order. Optional parameter.
  • skip (integer) Number of results to skip for the processed query. The Mission Portal uses this for pagination. Optional parameter.
  • limit (integer) Limit the number of results in the query.
  • hostContextInclude (string) Includes only results that concern hosts which have specified CFEngine context (class) set. Optional parameter.
  • hostContextExclude (string) Excludes results that concern hosts which have specified CFEngine context (class) set. Optional parameter.

Example Request Body:

{
  "query": "select hostname, ipaddress from hosts",
  "limit": 2,
  "hostContextExclude": "policy_server"
}

Example response:

{
  "data": [
    {
      "header": [
        {
          "columnName": "hostname",
          "columnType": "STRING"
        },
        {
          "columnName": "ipaddress",
          "columnType": "STRING"
        }
      ],
      "query": "select hostname, ipaddress from hosts",
      "queryTimeMs": 152,
      "rowCount": 1001,
      "rows": [
        [
          "ab84e58e4287",
          "172.17.16.251"
        ],
        [
          "293b3c9647fb",
          "172.17.16.6"
        ]
      ]
    }
  ],
  "meta": {
    "count": 1,
    "page": 1,
    "timestamp": 1437051092,
    "total": 1
  }
}

Example usage: Synchronous Example: Listing Hostname and IP for Ubuntu Hosts

Schedule SQL query as long running job

URI: https://hub.cfengine.com/api/query/async

Method: POST

Execute user SQL query as a async job. Result is available as file to download within specified format after job is finished.

Accepts SQL compatible with PostgreSQL database. Query is a subject to Role Base Access Control and will include data for hosts that issuing user have permissions to access. Read-only SQL is allowed.

Returns JOB ID which can be used to check query status and get query results.

API returns entire query result. Make sure that result size is sensible.

Parameters:

  • query (string) SQL query string.
  • outputType (string) Supported types: 'csv' (default). Optional parameter.
  • hostContextInclude (string) Includes only results that concern hosts which have specified CFEngine context (class) set. Optional parameter.
  • hostContextExclude (string) Excludes results that concern hosts which have specified CFEngine context (class) set. Optional parameter.

Example Request Body:

{
  "query": "select hostname, ipaddress from hosts",
  "outputType": "csv",
  "hostContextExclude": "policy_server"
}

Example response:

{
  "data": [
    {
      "id": "7b7de87ade18f337d62df26881ff39b1",
      "query": "select hostname, ipaddress from hosts limit 10"
    }
  ],
  "meta": {
    "count": 1,
    "page": 1,
    "timestamp": 1437054235,
    "total": 1
  }
}

Value of ID field is a unique job identifier that can be used to check job status and retrieve query results.

Check async query status

URI: https://hub.cfengine.com/api/query/async/:id

Method: GET

Check the status of async scheduled job. When the query is finished it will return a URI to file available to download as a href field in the response.

Example response:

{
  "data": [
    {
      "href": "https://hub.cfengine.com/api/static/7b7de87ade18f337d62df26881ff39b1.csv",
      "id": "7b7de87ade18f337d62df26881ff39b1",
      "percentageComplete": 100
    }
  ],
  "meta": {
    "count": 1,
    "page": 1,
    "timestamp": 1437054427,
    "total": 1
  }
}
Cancel async query

URI: https://hub.cfengine.com/api/query/async/:id

Method: DELETE


SQL Schema

CFEngine allows standardized SQL SELECT queries to be used with REST 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 environment"}
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 received 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

Host REST API

Host API allows to access host specific information.

List hosts

URI: https://hub.cfengine.com/api/host

Method: GET

Parameters:

  • context-include (comma delimited string of regular expression strings) Includes hosts having context matching the expression.
  • context-exclude (comma delimited string of regular expression strings) Excludes hosts having context matching the expression.
  • page (integer) Number of the page with results. By default 1.
  • count (integer) Size of the page. By default 50 results.

Example response:

{
  "meta": {
    "page": 1,
    "count": 2,
    "total": 2,
    "timestamp": 1437142156
  },
  "data": [
    {
      "id": "SHA=27b88b8a92f1b10b1839ac5b26d022c98d48629bd761c4324d1f1fb0f04f17ba",
      "hostname": "host001",
      "ip": "192.168.33.151",
      "lastreport": "1437141907",
      "firstseen": "1437138906"
    },
    {
      "id": "SHA=4a18877bbb7b79f4dde4b03d3ba05bcd66346124cbcd9373590416a90177fcaa",
      "hostname": "hub",
      "ip": "192.168.33.65",
      "lastreport": "1437141907",
      "firstseen": "1437138666"
    }
  ]
}

Output:

  • id Unique host identifier.
  • hostname Host name. Can be reconfigured globally to represent variable set in the policy using hostIdentifier setting.
  • ip IP address of the host. If host have multiple network interfaces, IP belongs to the interface that is used to communicate with policy server.
  • lastreport Time of receiving last report from the client, successfully. Represented as UNIX TIMESTAMP.
  • firstseen Time of receiving the first status report from the client. It is equivalent to the time when the client have been bootstrapped to the server for the first time. Represented as UNIX TIMESTAMP.

Example usage: Example: Listing Hosts With A Given Context, Example: Looking Up Hosts By Hostname, Example: Looking Up Hosts By IP

Host Details

URI: https://hub.cfengine.com/api/host/:host-id

Method: GET

Example response:

{
  "meta": {
    "page": 1,
    "count": 1,
    "total": 1,
    "timestamp": 1437144171
  },
  "data": [
    {
      "id": "SHA=27b88b8a92f1b10b1839ac5b26d022c98d48629bd",
      "hostname": "host001",
      "ip": "192.168.33.151",
      "lastreport": "1437144007",
      "firstseen": "1437138906"
    }
  ]
}

Output:

  • id Unique host identifier.
  • hostname Host name. Can be reconfigured globally to represent variable set in the policy using hostIdentifier setting.
  • ip IP address of the host. If host have multiple network interfaces, IP belongs to the interface that is used to communicate with policy server.
  • lastreport Time of receiving last report from the client, successfully. Represented as UNIX TIMESTAMP.
  • firstseen Time of receiving the first status report from the client. It is equivalent to the time when the client have been bootstrapped to the server for the first time. Represented as UNIX TIMESTAMP.
Remove host from the hub

URI: https://hub.cfengine.com/api/host/:host-id

Method: DELETE

Remove data about the host from reporting database and stop collecting reports from the host. API call schedules a job for purging authentication keys exchanged during bootstrap which prevents host from being collected in the future. Key purging usually take an effect within 5-10 minutes.

Deleted host need to be re-bootstrapped if it was deleted by accident.

List monitoring attributes for host

URI: https://hub.cfengine.com/api/host/:host-id/vital

Method: GET

List all available vital attributes monitored by CFEngine on the client.

Note: Collecting monitoring data by default is disabled.

Example response:

{
  "meta": {
    "page": 1,
    "count": 24,
    "total": 24,
    "timestamp": 1437144887
  },
  "data": [
    {
      "id": "mem_free",
      "timestamp": 1437144300,
      "description": "Free system memory",
      "units": "megabytes"
    },
    {
      "id": "mem_total",
      "timestamp": 1437144300,
      "description": "Total system memory",
      "units": "megabytes"
    },
    {
      "id": "loadavg",
      "timestamp": 1437144300,
      "description": "Kernel load average utilization",
      "units": "jobs"
    },
    {
      "id": "diskfree",
      "timestamp": 1437144300,
      "description": "Free disk on / partition",
      "units": "percent"
    }
  ]
}

Output:

  • id Unique vital identifier.
  • timestamp Last measurement time. Represented as UNIX TIMESTAMP.
  • description Vital short description.
  • units Units for the samples.

Example usage: Example: Listing Available Vital Signs For A Host

Get samples from vital

URI: https://hub.cfengine.com/api/host/:host-id/vital/:vital-id

Method: GET

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.

Example response:

{
  "meta": {
    "page": 1,
    "count": 1,
    "total": 1,
    "timestamp": 1437146605
  },
  "data": [
    {
      "id": "mem_free",
      "description": "Free system memory",
      "units": "megabytes",
      "timestamp": 1437146100,
      "values": [
        [
          1437140700,
          1229.8600
        ],
        [
          1437141000,
          1216.4500
        ],
        [
          1437141300,
          1218.3800
        ]
      ]
    }
  ]
}

Output:

  • id ID of vital sign.
  • description Description of vital sign.
  • units Measurement unit of vital sign.
  • timestamp Timestamp of the last received data point.
  • values Vital sign data. (array of [ t, y ], where t is the sample timestamp)

Example usage: Example: Retrieving Vital Sign Data


Changes REST API

Changes API allows to track changes performed by CFEngine agent in the infrastructure.

Count changes performed by agent

URI: https://hub.cfengine.com/api/v2/changes/policy/count

Method: GET

Count changes performed by CFEngine to the infrastructure. Count can be narrowed down to specific groups of hosts, period of time or operation characteristics.

Note: In the environments with extensive policy and large number of clients it is recommended to narrow down the results as much as possible to achieve more precise results and faster response times. This can be done by specifying filtering parameters listed below.

  • from (integer) Include changes performed within interval. Starting from unix timestamp. If not specified default value is last 24 hours.
  • to (integer) Include changes performed within interval. Ending at to unix timestamp. If not specified default value is NOW.
  • nodegroup (string) Include only nodes that have set specified context (cfengine class). Defaults to include all nodes.
  • hostkey (string) Search results for nodes matching specified unique hostkey.
  • stackpath (string) Search results matching specified stack path which is execution stack of the promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • promisetype (string) Search results matching specified promise type - such as commands, processes etc. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • promisehandle (string) Search results matching specified promise handle. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • bundlename (string) Search results matching specified bundle name. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • policyfile (string) Search results matching specified path for policy file where promise is defined. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • logmessages (string) Search results matching any of the messages logged for the promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • promisees (string) Search results matching any of the promisees specified for promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.

Example response:

{
    "count": 49
}

Output:

  • count Total count of changes performed by cf-agent that match specified filtering criteria.

Example usage: Example: Count changes

List changes performed by agent

URI: https://hub.cfengine.com/api/v2/changes/policy

Method: GET

List changes performed by CFEngine to the infrastructure. List can be narrowed down to specific groups of hosts, period of time or operation characteristics. In case of checking only for presence of the changes it is recommended to use Count changes performed by agent API.

Note: In the environments with extensive policy and large number of clients it is recommended to narrow down the results as much as possible to achieve more precise results and faster response times. This can be done by specifying filtering parameters listed below.

Parameters:

  • from (integer) Include changes performed within interval. Starting from unix timestamp. If not specified default value is last 24 hours.
  • to (integer) Include changes performed within interval. Ending at to unix timestamp. If not specified default value is NOW.
  • nodegroup (string) Include only nodes that have set specified context (cfengine class). Defaults to include all nodes.
  • hostkey (string) Search results for nodes matching specified unique hostkey.
  • stackpath (string) Search results matching specified stack path which is execution stack of the promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • promisetype (string) Search results matching specified promise type - such as commands, processes etc. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • promisehandle (string) Search results matching specified promise handle. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • bundlename (string) Search results matching specified bundle name. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • policyfile (string) Search results matching specified path for policy file where promise is defined. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • logmessages (string) Search results matching any of the messages logged for the promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • promisees (string) Search results matching any of the promisees specified for promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
  • sort (string) Sort results by specified direction and attribute. By default sort direction is ascending, to sort as descending add '-' before attribute name. Result can be sorted by all returned fields. If not specified results are not sorted. Examples: sort=bundlename - sort ascending by bundlename, sort=-promisehandle - sort descending by promise handle.
  • count (integer) Page size. Default 50 items.
  • page (integer) Page number. Default 1st page.

Example response:

{
  "data": [
    {
      "bundlename": "maintain_cfe_hub_process",
      "changetime": 1430127161,
      "hostkey": "SHA=de6ba9f406a2358e9169fb27e5459687d7107a001bb0abd4dd06485a63c2e50b",
      "hostname": "hub",
      "logmessages": [
        "Unable to make file belong to an unknown user",
        "Owner of '/var/log/postgresql.log' was 0, setting to 4294967295",
        "Unknown user 'cfpostgres' in promise",
        "Unable to make file belong to an unknown user",
        "Owner of '/var/log/postgresql.log' was 0, setting to 4294967295"
      ],
      "policyfile": "/var/cfengine/inputs/update/update_processes.cf",
      "promisees": [],
      "promisehandle": "cfe_internal_maintain_cfe_hub_process_files_create_postgresql_log",
      "promiser": "/var/log/postgresql.log",
      "promisetype": "files",
      "stackpath": "/default/cfe_internal_update_processes/methods/'TAKING CARE CFE HUB PROCESSES'/default/maintain_cfe_hub_process/files/'/var/log/postgresql.log'[0]"
    },
    {
      "bundlename": "generate_repairs",
      "changetime": 1437566606,
      "hostkey": "SHA=a5c09762c561f78ee16097c0524e9efc1a2181c910cefae533f9013acd888b9f",
      "hostname": "e63dc85f0e3e",
      "logmessages": [
        "Executing 'no timeout' ... '/bin/echo 123'",
        "Completed execution of '/bin/echo 123'"
      ],
      "policyfile": "/var/cfengine/inputs/promises.cf",
      "promisees": [],
      "promisehandle": "",
      "promiser": "/bin/echo 123",
      "promisetype": "commands",
      "stackpath": "/default/generate_repairs/commands/'/bin/echo 123'[0]"
    }
  ],
  "total": 382723,
  "next": "https://hub.cfengine.com/api/v2/changes/policy/?page=2&count=2",
  "previous": null
}

Output:

  • total Total number of results.
  • next Link for fetching next page. Set to NULL if current page is last.
  • previous Link for previous page. Set to NULL if the current page if the first.
  • data.bundlename Bundle name where the promise is executed.
  • data.changetime Time of performing change by cf-agent to the system. Expressed as UNIT TIMESTAMP.
  • data.hostkey Unique host identifier.
  • data.hostname Host name locally detected on the host, configurable as hostIdentifier option in Settings API and Mission Portal settings UI.
  • data.logmessages List of 5 last messages generated during promise execution. Log messages can be used for tracking specific changes made by CFEngine while repairing or failing promise execution.
  • data.policyfile Path to the file where the promise is located in.
  • data.promisees List of promisees defined for the promise.
  • data.promisehandle A unique id-tag string for referring promise.
  • data.promiser Object affected by a promise.
  • data.promisetype Type of the promise.
  • data.stackpath Call stack of the promise.

Example usage: Example: Show vacuum command executions


Users and Access-Control REST API

This REST API allows to manage users allowed to use Mission Portal as also Role Based Access Control settings.

List users

URI: https://hub.cfengine.com/api/user

Method: GET

List all users. API call allowed only for administrator.

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.

Example response:

{
  "meta": {
    "page": 1,
    "count": 3,
    "total": 3,
    "timestamp": 1437383957
  },
  "data": [
    {
      "id": "CFE_ROBOT",
      "email": "admin@organisation.com",
      "roles": [
        "admin",
        "cf_vcs",
        "cf_remoteagent"
      ],
      "external": false
    },
    {
      "id": "admin",
      "name": "admin",
      "email": "admin@organisation.com",
      "roles": [
        "admin",
        "cf_remoteagent"
      ],
      "external": false
    },
    {
      "id": "user_1",
      "email": "user_1@example.com",
      "roles": [
        "linux_team"
      ],
      "external": false
    }
  ]
}

Output:

  • id User name.
  • email Email address.
  • roles List of assigned RBAC roles.
  • external Is user from external source (LDAP/AD).

Example usage: Example: Listing Users

Get user data

URI: https://hub.cfengine.com/api/user/:username

Method: GET

Get info for a specified user. API call allowed only for administrator.

Example response:

{
  "meta": {
    "page": 1,
    "count": 1,
    "total": 1,
    "timestamp": 1437385581
  },
  "data": [
    {
      "id": "user_1",
      "name": "",
      "email": "user_1@example.com",
      "roles": [
        "linux_team"
      ],
      "external": false
    }
  ]
}

Output:

  • id User name.
  • email Email address.
  • roles List of assigned RBAC roles.
  • external Is user from external source (LDAP/AD).

Example usage: Example: Retrieving a User

Create new user

URI: https://hub.cfengine.com/api/user/:username

Method: PUT

Create a new user. API call allowed only for administrator.

Example Request Body:

{
  "email": "user_1@example.com",
  "roles": [
    "linux_team"
  ]
}

Example usage: Example: Creating a New User

Update user

URI: https://hub.cfengine.com/api/user/:username

Method: POST

Update user information. API call allowed only for administrator.

Example Request Body:

{
  "email": "user_1@example.com",
  "roles": [
    "linux_team"
  ]
}

Example usage: Example: Updating an Existing User, Example: Adding a User to a Role

Delete user

URI: https://hub.cfengine.com/api/user/:username

Method: DELETE

Remove internal user. API call allowed only for administrator.

Example usage: Example: Deleting a User

List RBAC roles

URI: https://hub.cfengine.com/api/role

Method: GET

List defined roles for Role Based Access Control. API call allowed only for administrator.

Example response:

{
  "meta": {
    "page": 1,
    "count": 3,
    "total": 3,
    "timestamp": 1437391879
  },
  "data": [
    {
      "id": "admin",
      "description": "Admin role"
    },
    {
      "id": "cf_remoteagent",
      "description": "Allow execution of cf-runagent"
    },
    {
      "id": "linux_team",
      "description": "Linux team is responsible for all linux test servers.",
      "includeContext": "linux,test_env",
      "excludeContext": "dev_env|production_env",
      "sketches": [
        "Packages::installed"
      ]
    }
  ]
}

Output:

  • id Unique role name.
  • description Role description.
  • includeContext Permit access to hosts that have class set.
  • excludeContext Permit access to hosts that have class not set.
  • sketches List of allowed sketches to use in MP.
Get RBAC role

URI: https://hub.cfengine.com/api/role/:role_id

Method: GET

Get role definition. API call allowed only for administrator.

Example response:

{
  "meta": {
    "page": 1,
    "count": 1,
    "total": 1,
    "timestamp": 1437392992
  },
  "data": [
    {
      "id": "linux_team",
      "description": "Linux team is responsible for all linux servers.",
      "includeContext": "linux",
      "sketches": [
        "Packages::installed"
      ]
    }
  ]
}

Output:

  • id Unique role name.
  • description Role description.
  • includeContext Permit access to hosts that have class set.
  • excludeContext Permit access to hosts that have class not set.
  • sketches List of allowed sketches to use in MP.
Create RBAC role

URI: https://hub.cfengine.com/api/role/:role_id

Method: PUT

Create a new role definition. API call allowed only for administrator.

Fields:

  • description Role description.
  • includeContext Permit access to hosts that have class set.
  • excludeContext Permit access to hosts that have class not set.
  • sketches List of allowed sketches to use in MP.

Example Request Body:

{
  "description": "Linux team is responsible for all linux servers.",
  "includeContext": "linux",
  "excludeContext": "product_a"
  "sketches": [
    "Packages::installed"
  ]
}
Update RBAC role

URI: https://hub.cfengine.com/api/role/:role_id

Method: POST

Update role definition. API call allowed only for administrator.

Fields:

  • description Role description.
  • includeContext Permit access to hosts that have class set.
  • excludeContext Permit access to hosts that have class not set.
  • sketches List of allowed sketches to use in MP.

Example Request Body:

{
  "description": "Linux team is responsible for all linux servers.",
  "includeContext": "linux",
  "excludeContext": "product_a"
  "sketches": [
    "Packages::installed"
  ]
}
Delete RBAC role

URI: https://hub.cfengine.com/api/role/:role_id

Method: DELETE

Remove role definition. API call allowed only for administrator. <!--- End include: /home/jenkins/workspace/build-documentation-3.9/label/DOCUMENTATION_x86_64_linux_debian_6/documentation/reference/enterprise-api-ref/users-rbac.markdown -->


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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(this.bundle): $(on) => SET class '$(extract_$(on)[2]) for '$(extract_$(on)[1])'"
      ifvarclass => "parsed_$(on)";

      "DEBUG $(this.bundle): $(off) => UNSET class '$(extract_$(off)[1])'"
      ifvarclass => "parsed_$(off)";

      "DEBUG $(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
      "DEBUG $(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";

      "DEBUG $(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
      "DEBUG $(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";
      "path[virtualenv]" string => "/usr/bin/virtualenv";

    !(freebsd|darwin)::
      "path[getfacl]"  string => "/usr/bin/getfacl";

    freebsd|darwin::
      "path[npm]"      string => "/usr/local/bin/npm";
      "path[pip]"      string => "/usr/local/bin/pip";
      "path[virtualenv]" string => "/usr/local/bin/virtualenv";
      "path[automount]" string => "/usr/sbin/automount";

    _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";
      "path[pgrep]"         string => "/usr/bin/pgrep";
      "path[getent]"        string => "/usr/bin/getent";

    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[service]"  string => "/usr/sbin/service";
      "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[dpkg_divert]"         string => "/usr/bin/dpkg-divert";
      "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[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";
      "path[userdel]"             string => "/usr/sbin/userdel";
      "path[usermod]"             string => "/usr/sbin/usermod";

    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)_reached" };
      repair_denied => { "repair_denied_$(x)", "$(x)_denied", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_reached" };
      repair_timeout => { "repair_timeout_$(x)", "$(x)_timeout", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_reached" };
      promise_kept => { "promise_kept_$(x)", "$(x)_kept", "$(x)_ok", "$(x)_reached" };
}
results

Prototype: results(scope, class_prefix)

Description: Define classes prefixed with class_prefix and suffixed with appropriate outcomes: _kept, _repaired, _not_kept, _error, _failed, _denied, _timeout, _reached

Arguments:

  • scope: The scope in which the class should be defined (bundle or namespace)
  • class_prefix: The prefix for the classes defined

This body can be applied to any promise and sets global (namespace) or local (bundle) classes based on its outcome. For instance, with class_prefix set to abc:

  • if the promise is to change a file's owner to nick and the file was already owned by nick, the classes abc_reached and abc_kept will be set.

  • if the promise is to change a file's owner to nick and the file was owned by adam and the change succeeded, the classes abc_reached and abc_repaired will be set.

This body is a simpler, more consistent version of the body scoped_classes_generic, which see. The key difference is that fewer classes are defined, and only for outcomes that we can know. For example this body does not define "OK/not OK" outcome classes, since a promise can be both kept and failed at the same time.

It's important to understand that promises may do multiple things, so a promise is not simply "OK" or "not OK." The best way to understand what will happen when your specific promises get this body is to test it in all the possible combinations.

Suffix Notes:

  • _reached indicates the promise was tried. Any outcome will result in a class with this suffix being defined.

  • _kept indicates some aspect of the promise was kept

  • _repaired indicates some aspect of the promise was repaired

  • _not_kept indicates some aspect of the promise was not kept. error, failed, denied and timeout outcomes will result in a class with this suffix being defined

  • _error indicates the promise repair encountered an error

  • _failed indicates the promise failed

  • _denied indicates the promise repair was denied

  • _timeout indicates the promise timed out

Example:

bundle agent example
{
  commands:
    "/bin/true"
      classes => results("bundle", "my_class_prefix");

  reports:
    my_class_prefix_kept::
      "My promise was kept";

    my_class_prefix_repaired::
      "My promise was repaired";
}

See also: scope, scoped_classes_generic, classes_generic

Implementation:

body classes results(scope, class_prefix)
{
  scope => "$(scope)";

  promise_kept => { "$(class_prefix)_reached",
                    "$(class_prefix)_kept" };

  promise_repaired => { "$(class_prefix)_reached",
                        "$(class_prefix)_repaired" };

  repair_failed => { "$(class_prefix)_reached",
                     "$(class_prefix)_error",
                     "$(class_prefix)_not_kept",
                     "$(class_prefix)_failed" };

  repair_denied => { "$(class_prefix)_reached",
                     "$(class_prefix)_error",
                     "$(class_prefix)_not_kept",
                     "$(class_prefix)_denied" };

  repair_timeout => { "$(class_prefix)_reached",
                      "$(class_prefix)_error",
                      "$(class_prefix)_not_kept",
                      "$(class_prefix)_timeout" };
}
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)_reached" };
      repair_denied => { "repair_denied_$(x)", "$(x)_denied", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_reached" };
      repair_timeout => { "repair_timeout_$(x)", "$(x)_timeout", "$(x)_not_ok", "$(x)_error", "$(x)_not_kept", "$(x)_reached" };
      promise_kept => { "promise_kept_$(x)", "$(x)_kept", "$(x)_ok", "$(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";
}
collect_vars

Prototype: collect_vars(name, tag, flatten)

Description: bundle to collect tagged variables into a data container

Arguments:

  • name: the variable name to create inside collect_vars
  • tag: the tag regex string to match e.g. "beta,gamma=.*"
  • flatten: to flatten variable values, set to "any" or "true" or "1"

The result will be a map in collect.$(name). You can also use cmerge.$(name)_str for a string version of the merged containers (if it fits in a CFEngine string).

The name is variable so you can call this bundle for more than one collection.

Every found variable will be a key in the map, unless you specify flatten, in which case they'll be flattened into a top-level array of data.

The flatten parameter can be "any" or "true" or "1" to be true.

Example:

body common control
{
      inputs => { "$(sys.libdir)/stdlib.cf" };
}

bundle agent main
{
  vars:
      # the "mymerge" tag is user-defined
      "a" data  => parsejson('{ "mark": "burgess" }'), meta => { "mymerge" };
      "b" data  => parsejson('{ "volker": "hilsheimer" }'), meta => { "mymerge" };

  methods:
      # merge a, b into container collect_vars.all
      "go" usebundle => collect_vars("all", "mymerge", "");
      # flatten a, b into container collect_vars.flattened
      "go_flatten" usebundle => collect_vars("flattened", "mymerge", "true");

  reports:
      # merged = {"default:main.a":{"mark":"burgess"},"default:main.b":{"volker":"hilsheimer"}}
      "merged = $(collect_vars.all_str)";
      # flattened = {"mark":"burgess","volker":"hilsheimer"}
      "flattened = $(collect_vars.flattened_str)";
}

Implementation:

bundle agent collect_vars(name, tag, flatten)
{
  classes:
      "flatten" expression => strcmp($(flatten), "any");
      "flatten" expression => strcmp($(flatten), "1");
      "flatten" expression => strcmp($(flatten), "true");

  vars:
      "todo_$(name)"    slist => variablesmatching(".*", $(tag));

    !flatten::
      "$(name)"
        data => parsejson('{}'),
        policy => "free";

      # this iterates!
      "$(name)"
        data => mergedata($(name), '{ "$(todo_$(name))": $(todo_$(name)) }'),
        policy => "free";

    flatten::
      "$(name)"
        data => parsejson('[]'),
        policy => "free";

      # this iterates!
      "$(name)"
        data => mergedata($(name), "$(todo_$(name))"),
        policy => "free";

    any::
      "$(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);

  methods:
      "any"
      usebundle  => $(bundlesfound),
      ifvarclass => strcmp(1, $(count));

  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.(DEBUG|DEBUG_$(this.bundle))"::
      "DEBUG $(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";
}
contains_literal_string

Prototype: contains_literal_string(string)

Description: Ensure the literal string is present in the promised file

Arguments:

  • string: The string (potentially multiline) to ensure exists in the promised file.

Implementation:

bundle edit_line contains_literal_string(string)
{

   insert_lines:
     "$(string)"
       insert_type => "preserve_block",
       expand_scalars => "false",
       whitespace_policy => { "exact_match" };
}
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 => results("bundle", "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)])_kept|manage_variable_values_ini_not_$(cindex[$(index)])_repaired).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 => results("bundle", "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)])_kept|set_variable_values_ini_not_$(cindex[$(index)])_repaired).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    => results("bundle", "$(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_kept|$(cindex[$(index)])_in_file_repaired)";
}
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_variable_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 => results("bundle", "$(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_kept|$(cv)_$(cindex[$(index)])_in_file_repaired)";
}
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)"),
        scope => "bundle";

      # 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"),
        scope => "bundle";

  replace_patterns:
      # If the line is commented out, uncomment and replace with
      # the correct value
      "^\s*#\s*($(index)\s+.*|$(index))$"
        comment => "If we find a single commented entry we can uncomment it to
                    keep the settings near any inline documentation. If there
                    are multiple comments, then we don't try to replace them and
                    instead will later append the new value after the first
                    commented occurance of $(index).",
        handle => "set_config_values_replace_commented_line",
        replace_with => value("$(index) $($(v)[$(index)])"),
          ifvarclass => "!line_exists_$(cindex[$(index)]).!replace_attempted_$(cindex[$(index)])_reached.!multiple_comments_$(cindex[$(index)])",
             classes => results("bundle", "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 => results("bundle", "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)])_reached.multiple_comments_$(cindex[$(index)])";

      # If the line doesn't exist and there are no occurrences
      # 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)])_reached.!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)])_reached.!multiple_comments_$(ci[$(i)])",
             classes => results("bundle", "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 => results("bundle", "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)])_reached.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)])_reached.!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 => results("bundle", "replace_attempted_$(cindex[$(index)])");

  insert_lines:
      "$(index) $($(v)[$(index)])"
      ifvarclass => "replace_attempted_$(cindex[$(index)])_reached";

}
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 => results("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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(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)
{
  vars:
      "len" int => string_length($(str));
    summarize::
      "summary" string => format("%s...%s",
                                string_head($(str), 18),
                                string_tail($(str), 18));
  classes:
      "summarize" expression => isgreaterthan($(len), 40);

  files:
      "$(file)"
      create => "true",
      edit_line => insert_lines($(str)),
      edit_defaults => empty;

  reports:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(this.bundle): creating $(file) with contents '$(str)'"
      ifvarclass => "!summarize";

      "DEBUG $(this.bundle): creating $(file) with contents '$(summary)'"
      ifvarclass => "summarize";
}
file_make_mog

Prototype: file_make_mog(file, str, mode, owner, group)

Description: Make a file from a string with mode, owner, group

Arguments:

  • file: target
  • str: the string data
  • mode: the file permissions in octal
  • owner: the file owner as a name or UID
  • group: the file group as a name or GID

Example:

methods:
     "" usebundle => file_make_mog("/tmp/z.txt", "Some text
and some more text here", "0644", "root", "root");

Implementation:

bundle agent file_make_mog(file, str, mode, owner, group)
{
  vars:
      "len" int => string_length($(str));
    summarize::
      "summary" string => format("%s...%s",
                                string_head($(str), 18),
                                string_tail($(str), 18));
  classes:
      "summarize" expression => isgreaterthan($(len), 40);

  files:
      "$(file)"
      create => "true",
      edit_line => insert_lines($(str)),
      perms => mog($(mode), $(owner), $(group)),
      edit_defaults => empty;

  reports:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(this.bundle): creating $(file) with contents '$(str)', mode '$(mode)', owner '$(owner)' and group '$(group)'"
      ifvarclass => "!summarize";

      "DEBUG $(this.bundle): creating $(file) with contents '$(summary)', mode '$(mode)', owner '$(owner)' and group '$(group)'"
      ifvarclass => "summarize";
}
file_make_mustache

Prototype: file_make_mustache(file, template, data)

Description: Make a file from a mustache template

Arguments:

  • file: Target file to render
  • template: Path to mustache template
  • data: Data container to use

Example:

vars:
  "state" data => datastate();

methods:
     "" usebundle => file_make_mustache( "/tmp/z.txt", "/tmp/z.mustache", @(state) );

Implementation:

bundle agent file_make_mustache(file, template, data)
{
  files:
      "$(file)"
        create => "true",
        edit_template => "$(template)",
        template_method => "mustache",
        template_data => @(state);

  reports:
      "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(this.bundle): rendering $(file) with template '$(template)'";
}
file_make_mustache_with_perms

Prototype: file_make_mustache_with_perms(file, template, data, mode, owner, group)

Description: Make a file from a mustache template

Arguments:

  • file: Target file to render
  • template: Path to mustache template
  • data: Data container to use
  • mode: File permissions
  • owner: Target file owner
  • group: Target file group

Example:

vars:
  "state" data => datastate();

methods:
     "" usebundle => file_make_mustache( "/tmp/z.txt", "/tmp/z.mustache", @(state),
                                         600, "root", "root" );

Implementation:

bundle agent file_make_mustache_with_perms(file, template, data, mode, owner, group)
{
  files:
      "$(file)"
        create => "true",
        edit_template => "$(template)",
        template_method => "mustache",
        perms => mog( $(mode), $(owner), $(group) ),
        template_data => @(state);

  reports:
      "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(this.bundle): rendering $(file) with template '$(template)'";
}
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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(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

See Also: local_dcp()

Implementation:

body copy_from remote_dcp(from,server)
{
      servers     => { "$(server)" };
      source      => "$(from)";
      compare     => "digest";
}
local_cp

Prototype: local_cp(from)

Description: Copy a file if the modification time or creation time of the source file is newer (the default comparison mechanism).

Arguments:

  • from: The path to the source file.

Example:

bundle agent example
{
  files:
      "/tmp/file.bak"
        copy_from => local_cp("/tmp/file");
}

See Also: local_dcp()

Implementation:

body copy_from local_cp(from)
{
      source      => "$(from)";
}
local_dcp

Prototype: local_dcp(from)

Description: Copy a local file if the hash on the source file differs.

Arguments:

  • from: The path to the source file.

Example:

bundle agent example
{
  files:
      "/tmp/file.bak"
      copy_from => local_dcp("/tmp/file");
}

See Also: local_cp(), remote_dcp()

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.

Example:

bundle agent home_dir_init
{
  files:
      "/home/mark.burgess/."
      copy_from => seed_cp("/etc/skel"),
      depth_search => recurse(inf),
      file_select => all,
      comment => "We want to be sure that the home directory has files that are
                  present in the skeleton.";
}

Implementation:

body copy_from seed_cp(from)
{
      source      => "$(from)";
      compare     => "exists";
}
sync_cp

Prototype: sync_cp(from, server)

Description: Synchronize a file with a remote server.

  • If the file does not exist on the remote server then it should be purged.
  • Allow types to change (directories to files and vice versa).
  • The mode of the remote file should be preserved.
  • Files are compared using the default comparison (mtime or ctime).

Arguments:

  • from: The location of the file on the remote server
  • server: The hostname or IP of the server from which to download

Example:

files:
  "/tmp/masterfiles/."
    copy_from => sync_cp( "/var/cfengine/masterfiles", $(sys.policy_server) ),
    depth_search => recurse(inf),
    file_select => all,
    comment => "Mirror masterfiles from the hub to a temporary directory";

See Also: dir_sync(), copyfrom_sync()

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";
}
symlinked_to

Prototype: symlinked_to(target)

Description: Select symlinks that point to $(target)

Arguments:

  • target: The file the symlink should point to in order to be selected

Implementation:

body file_select symlinked_to(target)
{
  file_types  => { "symlink" };
  issymlinkto => { "$(target)" };
  file_result => "issymlinkto";
}
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";
}

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)";
}

Package Modules

See the packages promises documentation for a comprehensive reference on the body types and attributes used here.

Package Module API

This section describes how to create a package module for the CFEngine package promise. Package modules are backends that enable the package promise to work with different types of platform package managers.

The CFEngine side

CFEngine never calls any package manager commands, it only ever calls the package module. The information that CFEngine deals in is:

  • Which packages are currently installed:
    • Name
    • Version
    • Architecture
  • Which of the installed packages have updates available:
    • Name
    • Version
    • Architecture

These two lists are everything that CFEngine needs to know to decide whether its package promises are fulfilled or not. In addition to this it will carry out package operations by asking the package module to do certain tasks, but in the end, the result of the operation is always determined by comparing the promise against those two lists. Therefore it is very important that the package module has a robust and deterministic way of returning these lists.

To take one example: When a package is installed, CFEngine does not really care what the return code of the operation is, the reason being that not all package managers can be trusted to return the correct return code (yum has a tendency to always report success, for instance). Instead, it ignores the return code, and instead asks for the list of currently installed packages after the installation, and if the package we tried to install has not appeared, it knows that the promise failed.

In the same fashion, if we try to install an update, that update is expected to disappear from the list of available updates after the operation.

The package module

The package module itself is simply an executable, and can be in any executable format, whether than is Python, Perl, Shell script or native binary.

The package module resides in the /var/cfengine/modules/packages both on the hub and the clients, and out of the box CFEngine is set up to synchronize this directory among its clients. For this reason it is advised to avoid native binary formats for package modules, to reduce the burden of distribution to different platforms, but the API does not forbid it, and it may be useful in some circumstances.

The API

The API consists of commands which are passed in the module arguments, combined with a simple text protocol that will be fed on the module's standard input, and replies are expected on its standard output.

In the examples below we use simple "Here Documents" to show how standard input can be passed to a package module in order to produce the indented output or effect. If you don't know about Here Documents, the man page for bash is a good place to read about them. During actual execution, the input will come from CFEngine itself.

The API commands are listed roughly in the order that they should be implemented, in order to facilitate a nice debugging cycle during development.

options attribute

All the API commands listed below, except supports-api-version, support the options attribute. This attribute will contain the contents from the options attribute in the promise, or the default_options in the package module body, if the former is unspecified. It may appear more than once if the options list has more than one element.

This attribute has no inherent meaning to CFEngine, and will be passed verbatim. It is meant as a mechanism to communicate special attributes to the package module that are not covered by the main API. For example, for certain package modules it may be used to pass a repository URL.

The options attribute will not be explicitly listed in the examples below, but it is valid in all of them except supports-api-version, even when the description reads "no input".

supports-api-version

The very first command that any package module must implement is supports-api-version. This command takes no input, and is expected to print a single digit followed by a newline. This is simply a way for CFEngine and the package module to agree on which version of the protocol to use. For now there is only one such version, and the expected output is simply "1".

This is the only command which does not support the options attribute.

Example:

$ ./package-module supports-api-version < /dev/null
1
$
get-package-data

CFEngine uses this command to determine what kind of promise has been made. Currently two types are supported: "file" and "repo".

The input is expected to be a triplet of File/Version/Architecture, where File is the promiser string from the promise, and Version and Architecture contain the strings from the corresponding promise attributes, if they were specified. This implies that either one of Version and Architecture may not be included, so some entries may only contain File or File with one more attribute.

What the module should do is figure out whether the string passed in File is referring to a file based or a repository based package. Exactly what identifies each one is up to the package module, but generally it means that file based packages should refer to actual files on the file system, whereas repository based packages should refer to package names that a "smart" package manager can resolve, such as for instance "apt". There are exceptions to this rule however, for example if a string is a URL referring to a downloadable package file, the type of package would still be file based, since it refers to a single package file which is not part of a repository.

The module should start by returning one attribute PackageType, which should be either file or repo. Next, it should return the proper name of the package in a Name attribute. Proper name means the name that will be displayed in package listings, so for example, /home/johndoe/zip-3.0-4.el5.x86_64.rpm would resolve to simply zip. For repository based package name, in most cases the returned Name will be the same as what what passed in, but this may not be the case for all package managers.

Next, for file based package name it should return Version and Architecture if it is able to determine these, but it is allowed to omit them if the module doesn't know (if the resource is remote, for instance).

For repository based package names the module should not return Version and Architecture, since they are often ambiguous in repository situations, and any discrepancies will be handled at the install stage instead.

Example 1:

$ ./package-module get-package-data <<EOF
File=zip
Version=3.0-4
Architecture=amd64
EOF
PackageType=repo
Name=zip
$

Example 2:

$ ./package-module get-package-data <<EOF
File=zip
EOF
PackageType=repo
Name=zip
$

Example 3:

$ ./package-module get-package-data <<EOF
File=/home/johndoe/zip-3.0-4.el5.x86_64.rpm
Version=3.0-4
Architecture=amd64
EOF
PackageType=file
Name=zip
Version=3.0-4
Architecture=amd64
$

Example 4:

$ ./package-module get-package-data <<EOF
File=/home/johndoe/zip-3.0-4.el5.x86_64.rpm
EOF
PackageType=file
Name=zip
Version=3.0-4
Architecture=amd64
$
list-installed

This command is expected to return a list of all currently installed packages on the system. It takes no input, and the output is expected to be a list of triplets of Name/Version/Architecture.

Example:

$ ./package-module list-installed < /dev/null
Name=zip
Version=3.0-4
Architecture=amd64
Name=libc6
Version=2.15
Architecture=amd64
Name=libc6
Version=2.15
Architecture=i386
...
$
list-updates

This command is expected to return a list of all the available updates for currently installed updates. The command takes no input, and the output is expected to be a list of triplets of Name/Version/Architecture.

It is not an error to include updates to packages that are not installed, but this information will not be used, and it is therefore recommended to omit it for performance purposes.

If the available updates come from an external source, such as an online repository service or a remote file server, this command is expected to fetch the information from there. CFEngine will make sure that this command is not called too often, so there is no need to try to limit the online resource usage in this command. See more about caching and list-updates-local below.

Example:

$ ./package-module list-updates < /dev/null
Name=zip
Version=3.0-4
Architecture=amd64
Name=libc6
Version=2.15
Architecture=amd64
Name=libc6
Version=2.15
Architecture=i386
...
$
list-updates-local

This command is expected to return a list of all the available updates for currently installed updates. The command takes no input, and the output is expected to be a list of triplets of Name/Version/Architecture.

It is not an error to include updates to packages that are not installed, but this information will not be used, and it is therefore recommended to omit it for performance purposes.

Unlike list-updates, this command is not expected to use the network to fetch information from external sources, but should fetch all the information from local storage. This command exists precisely to limit such expensive operations.

Example:

$ ./package-module list-updates-local < /dev/null
Name=zip
Version=3.0-4
Architecture=amd64
Name=libc6
Version=2.15
Architecture=amd64
Name=libc6
Version=2.15
Architecture=i386
...
$
repo-install

This command is used by CFEngine to ask the package module to install packages from the package repository. Note that CFEngine itself has no notion of which package repository it should come from. This is up to the package module, and may either be a platform configured default, such as is the case for for example yum, or a specific repository which is passed in via the options attribute. The command will be called for promises where get-package-data returned PackageType=repo.

The command takes a list of triplets, Name, Version and Architecture, where the last two may be omitted. In this case the module is expected to provide some default, which is usually the latest version and the native platform architecture.

No output is expected.

Example:

$ ./package-module repo-install <<EOF
Name=zip
Name=libc6
Version=2.15
Architecture=amd64
Name=libc6
Version=2.15
Architecture=i386
EOF
$
file-install

This command is used by CFEngine to ask the package module to install a specific package file. The command will be called for promises where get-package-data returned PackageType=file.

The command takes a list of triplets, File, Version and Architecture, where the last two may be omitted. For package files that can contain more than one package, the last two attributes may be used to select the correct one. The command should never be called with attributes that are not present in the package, since this will already have been detected after querying get-package-data.

No output is expected.

Example:

$ ./package-module file-install <<EOF
File=/mnt/storage/zip-3.0-4.el5.x86_64.rpm
File=/mnt/storage/libc6-2.15.el5.i386.rpm
Version=2.15
Architecture=i386
EOF
$
remove

To remove packages, CFEngine will call the package module with the remove command.

The command takes a list of triplets, Name, Version and Architecture, where the last two may be omitted. If so, the module is expected to remove all packages matching the other attribute(s). Note that Name is the basename of the package, the same format that get-package-data returns in Name.

No output is expected.

Example:

$ ./package-module remove <<EOF
Name=zip
Name=libc6
Version=2.15
Architecture=amd64
Name=libc6
Version=2.15
Architecture=i386
EOF
$
Error messages

All of the package module commands except supports-api-version have the option of returning error messages. The error messages are simply an attribute ErrorMessage with a string, which may optionally be preceded by whatever Name or File triplet was given to the command initially, in order to tie it to a specific promise.

Example:

$ ./package-module file-install <<EOF
File=/mnt/storage/zip-3.0-4.el5.x86_64.rpm
File=/mnt/storage/libc6-2.15.el5.i386.rpm
Version=2.15
Architecture=i386
EOF
File=/mnt/storage/zip-3.0-4.el5.x86_64.rpm
ErrorMessage=File not found
File=/mnt/storage/libc6-2.15.el5.i386.rpm
Version=2.15
Architecture=x86_64
ErrorMessage=Doesn't contain architecture 'x86_64'
$
Other output

CFEngine does not expect any other output on the package module's standard output, so the module should make sure it silences the output from its sub commands. Alternatively, it may redirect their output to standard error instead, but this will not be formatted using CFEngine's normal log formatting and is not recommended.

Caching

For performance reasons, CFEngine will cache the list of packages returned from list-packages and the list of updates from either of list-updates or list-updates-local. The exact circumstances where each is called is:

Whenever one is called its result is cached by CFEngine and will be used internally. It is a good idea to set the two policy attributes, query_installed_ifelapsed and query_updates_ifelapsed to zero during module development to avoid any issues with caching during debugging, but they should be set back when deploying in production.


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 bodies
package_module_knowledge

Prototype: package_module_knowledge

Description: common package_module_knowledge bundle

This common bundle defines which package modules are the defaults on different platforms.

Implementation:

bundle common package_module_knowledge
{
  vars:
    debian::
      "platform_default" string => "apt_get";

    freebsd::
      "platform_default" string => "pkg";

    redhat::
      "platform_default" string => "yum";

    aix::
      "platform_default" string => "nimclient";
}
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:
    "(DEBUG|DEBUG_$(this.bundle)).filebased.!suse.!debian.!redhat.!aix.!solaris_pkgadd"::
      "DEBUG $(this.bundle): sorry, can't do file-based installs on $(sys.os)";
}
package_module bodies
apt_get

Prototype: apt_get

Implementation:

body package_module apt_get
{
    query_installed_ifelapsed => "60";
    query_updates_ifelapsed => "1440";
    #default_options =>  {};
}
nimclient

Prototype: nimclient

Description: Define details used when interfacing with nimclient package module

Example:

bundle agent example_nimclient
{
  packages:
      "expect.base"
        policy => "present",
        options => { "lpp_source=lppaix71034" },
        package_module => nimclient;
}

Implementation:

body package_module nimclient
{
    query_installed_ifelapsed => "60";
    query_updates_ifelapsed => "14400";

    # This would likey be customized based on your infrastructure specifics
    # you may for example want to default the lpp_source based on something
    # like `oslevel -s` output.
    #default_options =>  {};
}
pkgsrc

Prototype: pkgsrc

Description: Define details used when interfacing with the pkgsrc package module.

Example: cf3 bundle agent main { packages: "vim" policy => "present", package_module => pkgsrc; }

Implementation:

body package_module pkgsrc
{
    query_installed_ifelapsed => "60";
    query_updates_ifelapsed => "14400";
}
yum

Prototype: yum

Implementation:

body package_module yum
{
    query_installed_ifelapsed => "60";
    query_updates_ifelapsed => "1440";
    #default_options => {};
}
pkg

Prototype: pkg

Implementation:

body package_module pkg
{
    query_installed_ifelapsed => "60";
    query_updates_ifelapsed => "1440";
    #default_options => {};
}
freebsd_ports

Prototype: freebsd_ports

Description: Define details used when interfacing with the freebsd ports package module.

Note: Ports are expected to be setup prior to trying to use the packages promise. You may need to ensure that portsnap extract has been run, e.g. fileexists("/usr/ports/Mk/bsd.port.mk")

Example: cf3 bundle agent main { packages: freebsd:: "vim" policy => "present", package_module => freebsd_ports; }

Implementation:

body package_module freebsd_ports
{
    query_installed_ifelapsed => "60";
    query_updates_ifelapsed => "1440";
}
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";
}
smartos_pkg_add

Prototype: smartos_pkg_add(repo)

Description: SmartOS pkg_add installation package method

This package method interacts with SmartOS pkg_add to install from local or remote repositories. It is slightly different than the FreeBSD pkg_add.

This example installs "perl5" from a remote repository:






vars:
  environment => { "PACKAGESITE=http://repo.example.com/private/8_STABLE/" };
packages:
  "perl5"
    package_policy   =>  "add",
    package_method   =>  freebsd;

Implementation:

body package_method freebsd
{
      package_changes => "individual";

      # Could use rpm for this
      package_list_command => "/usr/sbin/pkg info";

      # Remember to escape special characters like |

      package_list_name_regex    => "([^\s]+)-.*";
      package_list_version_regex => "[^\s]+-([^\s]+).*";

      package_name_regex    => "([^\s]+)-.*";
      package_version_regex => "[^\s]+-([^\s]+).*";

      package_installed_regex => ".*";

      package_name_convention => "$(name)-$(version)";

      package_add_command => "/usr/sbin/pkg install -y";
      package_delete_command => "/usr/sbin/pkg delete -y";
}
freebsd_portmaster

Prototype: freebsd_portmaster

Description: FreeBSD portmaster package installation method

This package method interacts with portmaster to build and install packages.

Note that you must use the complete package name as it appears in /usr/ports/*/name, such as 'perl5.14' rather than 'perl5'. Repositories are hard-coded to /usr/ports; alternate locations are unsupported at this time. This method supports both pkg_* and pkgng systems.

Example:

packages:
  "perl5.14"
    package_policy   =>  "add",
    package_method   =>  freebsd_portmaster;

Implementation:

body package_method freebsd_portmaster
{
      package_changes => "individual";

      package_list_command => "/usr/sbin/pkg info";

      package_list_name_regex    => "([^\s]+)-.*";
      package_list_version_regex => "[^\s]+-([^\s]+).*";

      package_installed_regex => ".*";

      package_name_convention => "$(name)";
      package_delete_convention => "$(name)-$(version)";

      package_file_repositories => {
                                     "/usr/ports/accessibility/",
                                     "/usr/port/arabic/",
                                     "/usr/ports/archivers/",
                                     "/usr/ports/astro/",
                                     "/usr/ports/audio/",
                                     "/usr/ports/benchmarks/",
                                     "/usr/ports/biology/",
                                     "/usr/ports/cad/",
                                     "/usr/ports/chinese/",
                                     "/usr/ports/comms/",
                                     "/usr/ports/converters/",
                                     "/usr/ports/databases/",
                                     "/usr/ports/deskutils/",
                                     "/usr/ports/devel/",
                                     "/usr/ports/dns/",
                                     "/usr/ports/editors/",
                                     "/usr/ports/emulators/",
                                     "/usr/ports/finance/",
                                     "/usr/ports/french/",
                                     "/usr/ports/ftp/",
                                     "/usr/ports/games/",
                                     "/usr/ports/german/",
                                     "/usr/ports/graphics/",
                                     "/usr/ports/hebrew/",
                                     "/usr/ports/hungarian/",
                                     "/usr/ports/irc/",
                                     "/usr/ports/japanese/",
                                     "/usr/ports/java/",
                                     "/usr/ports/korean/",
                                     "/usr/ports/lang/",
                                     "/usr/ports/mail/",
                                     "/usr/ports/math/",
                                     "/usr/ports/mbone/",
                                     "/usr/ports/misc/",
                                     "/usr/ports/multimedia/",
                                     "/usr/ports/net/",
                                     "/usr/ports/net-im/",
                                     "/usr/ports/net-mgmt/",
                                     "/usr/ports/net-p2p/",
                                     "/usr/ports/news/",
                                     "/usr/ports/packages/",
                                     "/usr/ports/palm/",
                                     "/usr/ports/polish/",
                                     "/usr/ports/ports-mgmt/",
                                     "/usr/ports/portuguese/",
                                     "/usr/ports/print/",
                                     "/usr/ports/russian/",
                                     "/usr/ports/science/",
                                     "/usr/ports/security/",
                                     "/usr/ports/shells/",
                                     "/usr/ports/sysutils/",
                                     "/usr/ports/textproc/",
                                     "/usr/ports/ukrainian/",
                                     "/usr/ports/vietnamese/",
                                     "/usr/ports/www/",
                                     "/usr/ports/x11/",
                                     "/usr/ports/x11-clocks/",
                                     "/usr/ports/x11-drivers/",
                                     "/usr/ports/x11-fm/",
                                     "/usr/ports/x11-fonts/",
                                     "/usr/ports/x11-servers/",
                                     "/usr/ports/x11-themes/",
                                     "/usr/ports/x11-toolkits/",
                                     "/usr/ports/x11-wm/",
      };

      package_add_command => "/usr/local/sbin/portmaster -D -G --no-confirm";
      package_update_command => "/usr/local/sbin/portmaster -D -G --no-confirm";
      package_delete_command => "/usr/local/sbin/portmaster --no-confirm -e";
}
alpinelinux

Prototype: alpinelinux

Description: Alpine Linux apk package installation method

This package method interacts with apk to manage packages.

Example:

packages:
  "vim"
    package_policy   =>  "add",
    package_method   =>  alpinelinux;

Implementation:

body package_method alpinelinux
{
      package_changes => "bulk";
      package_list_command => "/sbin/apk info -v";
      package_list_name_regex    => "([^\s]+)-.*";
      package_list_version_regex => "[^\s]+-([^\s]+).*";
      package_name_regex    => ".*";
      package_installed_regex => ".*";
      package_name_convention => "$(name)";
      package_add_command => "/sbin/apk add";
      package_delete_command => "/sbin/apk del";
}
emerge

Prototype: emerge

Description: Gentoo emerge package installation method

This package method interacts with emerge to build and install packages.

Example:

packages:
  "zsh"
    package_policy   =>  "add",
    package_method   =>  emerge;

Implementation:

body package_method emerge
{
      package_changes => "individual";
      package_list_command => "/bin/sh -c '/bin/ls -d /var/db/pkg/*/* | cut -c 13-'";
      package_list_name_regex => ".*/([^\s]+)-\d.*";
      package_list_version_regex => ".*/[^\s]+-(\d.*)";
      package_installed_regex => ".*";                          # all reported are installed
      package_name_convention => "$(name)";
      package_list_update_command => "/bin/true";               # I prefer manual syncing
      #package_list_update_command => "/usr/bin/emerge --sync"; # if you like automatic
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_add_command => "/usr/bin/emerge -q --quiet-build";
      package_delete_command => "/usr/bin/emerge --depclean";
      package_update_command => "/usr/bin/emerge --update";
      package_patch_command => "/usr/bin/emerge --update";
      package_verify_command => "/usr/bin/emerge -s";
      package_noverify_regex => ".*(Not Installed|Applications found : 0).*";
}
pacman

Prototype: pacman

Description: Arch Linux pacman package management method

Implementation:

body package_method pacman
{
      package_changes => "bulk";

      package_list_command => "/usr/bin/pacman -Q";
      package_verify_command  => "/usr/bin/pacman -Q";
      package_noverify_regex  => "error:\b.*\bwas not found";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_list_name_regex    => "(.*)\s+.*";
      package_list_version_regex => ".*\s+(.*)";
      package_installed_regex => ".*";

      package_name_convention => "$(name)";
      package_add_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
      package_delete_command => "/usr/bin/pacman -Rs --noconfirm";
      package_update_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
}
zypper

Prototype: zypper

Description: SUSE installation method

This package method interacts with the SUSE Zypper package manager

Example:

packages:
    "mypackage" package_method => zypper, package_policy => "add";

Implementation:

body package_method zypper
{
      package_changes => "bulk";

      package_list_command => "$(paths.path[rpm]) -qa --queryformat \"$(rpm_knowledge.rpm_output_format)\"";

      # set it to "0" to avoid caching of list during upgrade
      package_list_update_command => "$(suse_knowledge.call_zypper) list-updates";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_patch_list_command => "$(suse_knowledge.call_zypper) patches";
      package_installed_regex => "i.*";
      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_patch_installed_regex => ".*Installed.*|.*Not Applicable.*";
      package_patch_name_regex    => "[^|]+\|\s+([^\s]+).*";
      package_patch_version_regex => "[^|]+\|[^|]+\|\s+([^\s]+).*";

      package_name_convention => "$(name)";
      package_add_command => "$(suse_knowledge.call_zypper) --non-interactive install";
      package_delete_command => "$(suse_knowledge.call_zypper) --non-interactive remove --force-resolution";
      package_update_command => "$(suse_knowledge.call_zypper) --non-interactive update";
      package_patch_command => "$(suse_knowledge.call_zypper) --non-interactive patch$"; # $ means no args
      package_verify_command => "$(suse_knowledge.call_zypper) --non-interactive verify$";
}
generic

Prototype: generic

Description: Generic installation package method

This package method attempts to handle all platforms.

The Redhat section is a verbatim insertion of yum_rpm(), which was contributed by Trond Hasle Amundsen.

Example:

packages:
    "mypackage" package_method => generic, package_policy => "add";

Implementation:

body package_method generic
{
    suse::
      package_changes => "bulk";
      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 => "$(suse_knowledge.call_zypper) list-updates";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
      package_patch_list_command => "$(suse_knowledge.call_zypper) patches";
      package_installed_regex => "i.*";
      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_patch_installed_regex => ".*Installed.*|.*Not Applicable.*";
      package_patch_name_regex    => "[^|]+\|\s+([^\s]+).*";
      package_patch_version_regex => "[^|]+\|[^|]+\|\s+([^\s]+).*";
      package_name_convention => "$(name)";
      package_add_command => "$(suse_knowledge.call_zypper) --non-interactive install";
      package_delete_command => "$(suse_knowledge.call_zypper) --non-interactive remove --force-resolution";
      package_update_command => "$(suse_knowledge.call_zypper) --non-interactive update";
      package_patch_command => "$(suse_knowledge.call_zypper) --non-interactive patch$"; # $ means no args
      package_verify_command => "$(suse_knowledge.call_zypper) --non-interactive verify$";

    redhat::
      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)";

    debian::
      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)";
      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)";

    debian.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 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)";

    debian.!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 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)";

    freebsd::
      package_changes => "individual";
      package_list_command => "/usr/sbin/pkg info";
      package_list_name_regex    => "([^\s]+)-.*";
      package_list_version_regex => "[^\s]+-([^\s]+).*";
      package_name_regex    => "([^\s]+)-.*";
      package_version_regex => "[^\s]+-([^\s]+).*";
      package_installed_regex => ".*";
      package_name_convention => "$(name)-$(version)";
      package_add_command => "/usr/sbin/pkg install -y";
      package_delete_command => "/usr/sbin/pkg delete";

    alpinelinux::
      package_changes => "bulk";
      package_list_command => "/sbin/apk info -v";
      package_list_name_regex    => "([^\s]+)-.*";
      package_list_version_regex => "[^\s]+-([^\s]+).*";
      package_name_regex    => ".*";
      package_installed_regex => ".*";
      package_name_convention => "$(name)";
      package_add_command => "/sbin/apk add";
      package_delete_command => "/sbin/apk del";

    gentoo::
      package_changes => "individual";
      package_list_command => "/bin/sh -c '/bin/ls -d /var/db/pkg/*/* | cut -c 13-'";
      package_list_name_regex => "([^/]+/(?:(?!-\d).)+)-\d.*";
      package_list_version_regex => "[^/]+/(?:(?!-\d).)+-(\d.*)";
      package_installed_regex => ".*";                          # all reported are installed
      package_name_convention => "$(name)";
      package_list_update_command => "/bin/true";               # I prefer manual syncing
      #package_list_update_command => "/usr/bin/emerge --sync"; # if you like automatic
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";

      package_add_command => "/usr/bin/emerge -q --quiet-build";
      package_delete_command => "/usr/bin/emerge --depclean";
      package_update_command => "/usr/bin/emerge --update";
      package_patch_command => "/usr/bin/emerge --update";
      package_verify_command => "/usr/bin/emerge -s";
      package_noverify_regex => ".*(Not Installed|Applications found : 0).*";

    archlinux::
      package_changes => "bulk";
      package_list_command => "/usr/bin/pacman -Q";
      package_verify_command  => "/usr/bin/pacman -Q";
      package_noverify_regex  => "error:\b.*\bwas not found";
      package_list_name_regex    => "(.*)\s+.*";
      package_list_version_regex => ".*\s+(.*)";
      package_installed_regex => ".*";
      package_name_convention => "$(name)";
      package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
      package_add_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
      package_delete_command => "/usr/bin/pacman -Rs --noconfirm";
      package_update_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
}

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");

    sysvservice.have_init::
      "running" expression => returnszero("$(paths.service) $(service) status > /dev/null", "useshell");

    chkconfig.SuSE::
      "onboot"
        expression => returnszero("$(paths.chkconfig) $(service) | $(paths.grep) 'on$' >/dev/null", "useshell"),
        comment => "SuSE chkconfig outputs current state to stdout rather than as an exit code";

    chkconfig.!SuSE::
      "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.start.!running::
      "$(paths.service) $(service) start"
      handle => "standard_services_sysvservice_not_running_start",
      classes => kept_successful_command,
      comment => "If the service should be running and it is not
                  currently running then we should issue the standard service
                  command to start the service.";

    sysvservice.restart::
      "$(paths.service) $(service) restart"
      handle => "standard_services_sysvservice_restart",
      classes => kept_successful_command,
      comment => "If the service should be restarted we issue the
                  standard service command to restart or reload the service.
                  There is no restriction based on the services current state as
                  restart can start a service that was not already
                  running.";

    sysvservice.reload.running::
      "$(paths.service) $(service) reload"
      handle => "standard_services_sysvservice_reload",
      classes => kept_successful_command,
      comment => "If the service should be reloaded we issue the
                  standard service command to reload the service.
                  It is restricted to when the service is running as a reload
                  should not start services that are not already running. This
                  may not be triggered as service state parameters are limited
                  and translated to the closest meaning.";

    sysvservice.((stop|disable).running)::
      "$(paths.service) $(service) stop"
      handle => "standard_services_sysvservice_stop",
      classes => kept_successful_command,
      comment => "If the service should be stopped or disabled and it is
                  currently running then we should issue the standard service
                  command to stop the service.";

    smf::
      "$(paths.svcadm) $(svcadm_mode) $(service)"
      classes => kept_successful_command;

  methods:
    fallback::
      "classic" usebundle => classic_services($(service), $(state));

  reports:
    verbose_mode.systemd::
      "$(this.bundle): using systemd layer to $(state) $(service)";
    verbose_mode.systemd.!service_loaded::
      "$(this.bundle): Service $(service) unit file is not loaded; doing nothing";
    verbose_mode.chkconfig::
      "$(this.bundle): using chkconfig layer to $(state) $(service) (chkconfig mode $(chkconfig_mode))"
        ifvarclass => "!chkconfig_$(c_service)_unregistered.((start.!onboot)|(stop.onboot))";
    verbose_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";
    verbose_mode.sysvservice::
      "$(this.bundle): using System V service / Upstart layer to $(state) $(service)";
    verbose_mode.smf::
      "$(this.bundle): using Solaris SMF to $(state) $(service) (svcadm mode $(svcadm_mode))";
    verbose_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.*";

      "baseinit[quagga]"          string => "quagga";
      "pattern[quagga]"           string => "quagga/.*";

    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:
    "DEBUG|DEBUG_$(this.bundle)"::
      "DEBUG $(this.bundle): Using init system $(inits)"
      ifvarclass => and(canonify("$(inits)_set"));

      "DEBUG $(this.bundle): No init system is set, using $(default[init])"
      ifvarclass => "no_inits_set";

      "DEBUG $(this.bundle): The service $(service) needs to be started"
      ifvarclass => and(canonify("start_$(service)"));

      "DEBUG $(this.bundle): The service pattern is provided: $(pattern[$(service)])"
      ifvarclass => and(isvariable("pattern[$(service)]"));

      "DEBUG $(this.bundle): The default service pattern was used: $(default[pattern])"
      ifvarclass => not(isvariable("pattern[$(service)]"));

      "DEBUG $(this.bundle): The stopcommand is provided: $(stopcommand[$(service)])"
      ifvarclass => and(isvariable("stopcommand[$(service)]"));

      "DEBUG $(this.bundle): The stopcommand is NOT provided, using default"
      ifvarclass => not(isvariable("stopcommand[$(service)]"));

      "DEBUG $(this.bundle): The startcommand is provided: $(startcommand[$(service)])"
      ifvarclass => and(isvariable("startcommand[$(service)]"));

      "DEBUG $(this.bundle): The startcommand is NOT provided, using default"
      ifvarclass => not(isvariable("startcommand[$(service)]"));

      "DEBUG $(this.bundle): The restartcommand is provided: $(restartcommand[$(service)])"
      ifvarclass => and(isvariable("restartcommand[$(service)]"));

      "DEBUG $(this.bundle): The restartcommand is NOT provided, using default"
      ifvarclass => not(isvariable("restartcommand[$(service)]"));

      "DEBUG $(this.bundle): The reloadcommand is provided: $(reloadcommand[$(service)])"
      ifvarclass => and(isvariable("reloadcommand[$(service)]"));

      "DEBUG $(this.bundle): The reloadcommand is NOT provided, using default"
      ifvarclass => not(isvariable("reloadcommand[$(service)]"));

      "DEBUG $(this.bundle): The baseinit is provided: $(baseinit[$(service)])"
      ifvarclass => and(isvariable("baseinit[$(service)]"));

      "DEBUG $(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";
}
standard_services

Prototype: standard_services

Description: Default services_method for when you wan't to call it explicitly

By default this service_method is not used. The call for standard_services is within the core and not here. In case you use a promise like:

services:
    "ssh"
      service_policy => "start";

Then this method is skipped and CFEngine calls standard_services bundle directly. This is here as a helper in case you wan't to be explicit with your service promise and point it to standard_services (for readability, documentation, etc).

Do note that any options defined in this method does not apply to service promises without explicit template_method call for standard_services.

Example:

services:
    "ssh"
      service_policy => "start",
      service_method => standard_services;

Implementation:

body service_method standard_services
{
        service_bundle => standard_services( $(this.promiser), $(this.service_policy) );
}

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 list 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 DEBUG or DEBUG_git (from the command line, -D DEBUG_git) 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";

      # We get the passwd entry from the user that owns the repo so
      # that we can extract the home directory for later use.
      "repo_uid_passwd_ent"
        string => execresult("$(paths.getent) passwd $(repo_uid)", noshell),
        comment => "We need to extract the home directory of the repo
                    owner so that it can be used to avoid errors from
                    unprivledged execution trying to access the root
                    users git config.";

  classes:
      "am_root" expression => strcmp($(this.promiser_uid), "0");

      # $(repo_uid) must be defined before we try to test this or we will end up
      # having at least one pass during evaluation the agent will not know it
      # needs to drop privileges, leading to some files like .git/index being
      # created with elevated privileges, and subsequently causing the agent to
      # not be able to commit as a normal user.
      "need_to_drop"
        not => strcmp($(this.promiser_uid), $(repo_uid)),
        ifvarclass => isvariable( repo_uid );

    am_root.need_to_drop::
      # This regular expression could be tightened up
      # Extract the home directory from the owner of the repository
      # into $(repo_uid_passwd[1])
      "extracted_repo_uid_home"
        expression => regextract( ".*:.*:\d+:\d+:.*:(.*):.*",
                                  $(repo_uid_passwd_ent),
                                  "repo_uid_passwd" ),
        ifvarclass => isvariable("repo_uid_passwd_ent");

  commands:
    am_root.need_to_drop::
      # Because cfengine does not inherit the shell environment when
      # executing commands, git will look for the root users git
      # config and error when the executing user does not have
      # access. So we need to set the home directory of the executing
      # user.
      "$(paths.env) HOME=$(repo_uid_passwd[1]) $(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:
    "DEBUG|DEBUG_$(this.bundle).am_root.need_to_drop"::
      "DEBUG $(this.bundle): with dropped privileges to uid '$(repo_uid)' and gid '$(repo_gid)', in directory '$(repo_path)', running Git command '$(paths.env) HOME=\"$(repo_uid_passwd[1])\" $(oneliner) $(subcmd) $(args)'"
        ifvarclass => isvariable("repo_uid_passwd[1]");

    "DEBUG|DEBUG_$(this.bundle).(!am_root|!need_to_drop)"::
      "DEBUG $(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
  • inherit_from: body inherit_from
  • meta: slist
  • 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
package_module
password
perms
printfile
process_count
process_select
rename
replace_with
report_data_select
runagent
select_region
server
service_method
volume