The Complete CFEngine Reference
Table of Content
- Macros
- Components and Common Control
- Promise Types and Attributes
- Functions
- regex_replace
- usemodule
- readstringarrayidx
- canonifyuniquely
- readdata
- strcmp
- and
- "parse[int|real|string]array"
- peers
- sort
- concat
- ago
- some
- "on"
- filestat
- execresult
- remoteclassesmatching
- filesize
- splayclass
- ip2host
- readcsv
- storejson
- unique
- getvalues
- intersection
- regarray
- dirname
- string_tail
- readyaml
- callstack_promisers
- countlinesmatching
- returnszero
- ldaparray
- ldapvalue
- isplain
- bundlesmatching
- file_hash
- sum
- format
- regcmp
- classmatch
- data_readstringarrayidx
- classify
- isnewerthan
- lsdir
- lastnode
- string_length
- hubknowledge
- userexists
- selectservers
- filesexist
- nth
- string_split
- randomint
- getfields
- hostsseen
- regline
- bundlestate
- none
- min
- hostrange
- host2ip
- changedbefore
- or
- not
- groupexists
- fileexists
- diskfree
- read[int|real|string]list
- url_get
- expandrange
- ldaplist
- every
- data_readstringarray
- getuid
- escape
- grep
- shuffle
- registryvalue
- difference
- remotescalar
- packagesmatching
- readtcp
- mergedata
- hostinnetgroup
- iprange
- variance
- packageupdatesmatching
- string_head
- islessthan
- countclassesmatching
- islink
- ifelse
- datastate
- getclassmetatags
- strftime
- sublist
- reglist
- reverse
- string_mustache
- accumulated
- peerleader
- hash
- data_expand
- findprocesses
- regldap
- eval
- data_regextract
- makerule
- hostswithclass
- isdir
- isgreaterthan
- filter
- hashmatch
- maparray
- max
- laterthan
- getusers
- mapdata
- findfiles
- isexecutable
- product
- accessedbefore
- translatepath
- parseyaml
- parsestringarrayidx
- canonify
- string_reverse
- variablesmatching
- getvariablemetatags
- maplist
- processexists
- string_downcase
- isvariable
- classesmatching
- getgid
- splitstring
- peerleaders
- readfile
- rrange
- readjson
- length
- string_upcase
- callstack_callers
- now
- getindices
- mean
- "read[int|real|string]array"
- getenv
- parsejson
- regextract
- irange
- join
- network_connections
- Hard and Soft Classes
- Special Variables
- Language Concepts
- Enterprise API Reference
- Standard Library
- Common Bodies and Bundles
- Commands Bundles and Bodies
- Databases Bundles and Bodies
- Files Bundles and Bodies
- Monitor Bundles and Bodies
- Package Modules
- Packages Bundles and Bodies
- Processes Bundles and Bodies
- Users Bundles and Bodies
- Services Bundles and Bodies
- Storage Bundles and Bodies
- Version Control Bodies and Bundles
- Design Center
- All Promise and Body Types
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.
- Components and Common Control
- Promise Types and Attributes
- Functions
- Hard and Soft Classes
- Special Variables
- Enterprise API Reference
- Syntax, identifiers and names
- Standard Library
- Design Center
- All Promise and Body Types
- Macros
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 common
and 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.
- users: Users logged in
- rootprocs: Privileged system processes
- otherprocs: Non-privileged process
- diskfree: Free disk on / partition
- loadavg: % kernel load utilization
- netbiosns_in: netbios name lookups (in)
- netbiosns_out: netbios name lookups (out)
- netbiosdgm_in: netbios name datagrams (in)
- netbiosdgm_out: netbios name datagrams (out)
- netbiosssn_in: netbios name sessions (in)
- netbiosssn_out: netbios name sessions (out)
- irc_in: IRC connections (in)
- irc_out: IRC connections (out)
- cfengine_in: CFEngine connections (in)
- cfengine_out: CFEngine connections (out)
- nfsd_in: nfs connections (in)
- nfsd_out: nfs connections (out)
- smtp_in: smtp connections (in)
- smtp_out: smtp connections (out)
- www_in: www connections (in)
- www_out: www connections (out)
- ftp_in: ftp connections (in)
- ftp_out: ftp connections (out)
- ssh_in: ssh connections (in)
- ssh_out: ssh connections (out)
- wwws_in: wwws connections (in)
- wwws_out: wwws connections (out)
- icmp_in: ICMP packets (in)
- icmp_out: ICMP packets (out)
- udp_in: UDP dgrams (in)
- udp_out: UDP dgrams (out)
- dns_in: DNS requests (in)
- dns_out: DNS requests (out)
- tcpsyn_in: TCP sessions (in)
- tcpsyn_out: TCP sessions (out)
- tcpack_in: TCP acks (in)
- tcpack_out: TCP acks (out)
- tcpfin_in: TCP finish (in)
- tcpfin_out: TCP finish (out)
- tcpmisc_in: TCP misc (in)
- tcpmisc_out: TCP misc (out)
- webaccess: Webserver hits
- weberrors: Webserver errors
- syslog: New log entries (Syslog)
- messages: New log entries (messages)
- temp0: CPU Temperature core 0
- temp1: CPU Temperature core 1
- temp2: CPU Temperature core 2
- temp3: CPU Temperature core 3
- cpu: %CPU utilization (all)
- cpu0: %CPU utilization core 0
- cpu1: %CPU utilization core 1
- cpu2: %CPU utilization core 2
- 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,
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.
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 atransformer
-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 atransformer
-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 atransformer
-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) andfalse
(never defined) that allow JSON booleans to be used inside expressionsthe logical and operation, expressed as
a&b
ora.b
, which is true if botha
andb
are truethe logical or operation, expressed as
a|b
, which is true if eithera
orb
are truethe logical not operation, expressed as
!a
, which is true ifa
is not true. Note again here thata
could become true during the execution. So if you have"myclass" expression => "!x"
andx
starts undefined but is defined later, you could have bothx
andmyclass
defined!parenthesis
(whatever)
which operate as expected to prioritize expression evaluationthe 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 slist
s, 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 ilist
s, 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 rlist
s, 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]
, usemergedata("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()
andgetvalues()
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
andz
. 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 get50
, and if you ask for$(c[z])
you'll get implicit iteration on1,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 finallyreplace_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:
literal
orstring
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 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 forhash
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";
}
copylink_patterns
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.
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
dirlinks
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.
depth_search
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.*" };
}
rmdeadlinks
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";
}
traverse_links
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
link_from
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" };
}
link_children
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.
link_type
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 Portsnimclient
- AIX NIM clientpkg
- FreeBSD pkgpkgsrc
- 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 toxyz
instead of the default^meta=a,b,c
sets the class and variable tags for any following definitions toa
,b
, andc
^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
%
aredata
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:
execresult()
andreturnszero()
for shell executionregldap()
,ldapvalue()
,ldaparray()
, andldaplist()
for LDAP querieshost2ip()
andip2host()
for DNS queriesreadtcp()
for TCP interactionshubknowledge()
,selectservers()
,remoteclassesmatching()
, andremotescalar()
for hub queries
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
ormyarray
ormyslist
), 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 thething
in a JSON array; then the contents ofotherthing[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
Functions by Return Type
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-insensitivem
: 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:
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 ofCSV
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:
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.
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 range0,99999999999
maxbytes
: Maximum bytes to read, in the range0,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 oflex
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 range0,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 range0,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 range0,1000
Days of runtime (one day equals 86,400 seconds)
hours
, in the range0,1000
Hours of runtime
minutes
, in the range0,1000
Minutes of runtime 0-59
seconds
, in the range0,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 bytesgid
: group IDuid
: owner IDino
: inode numbernlink
: number of hard linksctime
: creation time in Unix epoch formatatime
: last access time in Unix epoch formatmtime
: last modification time in Unix epoch formatmode
: file mode as a decimal numbermodeoct
: 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
, orunknown
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 directorydirname
: the directory portion of the file namelinktarget
: if the file is asymlink
, 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 asymlink
, its first target. On Windows, this returns the file name itself.xattr
: a string with newline-separated extended attributes and SELinux contexts inkey=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. Uselinktarget_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 ofnoshell
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 oftrue
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 ofdaily
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:
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:
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:
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 ofnoshell
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 ofsubtree
onelevel
base
security
: one ofnone
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 ofsubtree
onelevel
base
security
: one ofnone
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 ofmd5
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:
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 oftrue
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:
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:
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 oflastseen
notseen
field
: one ofname
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 oflex
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:
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:
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.
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.
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 range0,99999999999
maxbytes
: Maximum bytes to read, in the range0,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 contenturl.timeout
: if present,libcurl
will time out the request after that many secondsurl.referer
: if present,libcurl
will set the Referer to thisurl.user-agent
: if present,libcurl
will set the User-Agent to thisurl.headers
: an array of strings in the formatFoo: bar
specifying headers for the request
The returned data container will have the following keys:
returncode
: the HTTP response code, e.g.200
.rc
: thelibcurl
integer result code, either0
for success or something else for failureerror_message
: when present, indicates the request was unsuccessful and explains whysuccess
: a boolean. Whensuccess
isfalse
, the result code was not0
and the request was unsuccessful.content
: the response content as a stringheaders
: the response headers as a string
Arguments:
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:
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 ofsubtree
onelevel
base
security
: one ofnone
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:
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:
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:
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 oftrue
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 nameversion_regex
- Regular expression matching package versionarch_regex
- Regular expression matching package architecutremethod_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 nameversion_regex
- Regular expression matching package versionarch_regex
- Regular expression matching package architecutremethod_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:
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:
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
islink
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 ofgmtime
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 ofhead
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 escape
d,
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 range0,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 range0,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 range0,1000
Days of runtime (one day equals 86,400 seconds)
hours
, in the range0,1000
Hours of runtime
minutes
, in the range0,1000
Minutes of runtime 0-59
seconds
, in the range0,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 ofmd5
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 ofsubtree
onelevel
base
regex
: regular expression, in the range:.*
security
: one ofnone
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.7182818284590452354log2e
: 1.4426950408889634074log10e
: 0.43429448190325182765ln2
: 0.69314718055994530942ln10
: 2.30258509299404568402pi
: 3.14159265358979323846pi_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
andfloor
: the next highest or the previous highest integerlog10
,log2
,log
sqrt
sin
,cos
,tan
,asin
,acos
,atan
abs
: absolute valuestep
: 0 if the argument is negative, 1 otherwise
Arguments:
mode
:string
, in the range:.*
options
: one ofmath
class
expression
: one ofinfix
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:
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 ofname
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:
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 range0,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 ofmd5
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:
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 oflex
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:
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 ofnone
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 froma
toz
{x,y,anything}
will matchx
ory
oranything
.
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:
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:
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:
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 to0
.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:
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 whatcf-promises -p json
would produce, using the internal C functionBodyToJson()
. This may include theline
andsourcePath
to locate the exact code line. - bundles: under key
bundle
the entry has a full dump of the bundle policy as JSON, same as whatcf-promises -p json
would produce, using the internal C functionBundleToJson()
. This may include theline
andsourcePath
to locate the exact code line. - promise iteration: the
iteration_index
is recorded - promises: the
promise_type
,promiser
,promise_classes
, andpromise_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 range0,99999999999
maxbytes
: Maximum bytes to read, in the range0,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:
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:
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 IPv4tcp6
has all the TCP connections over IPv6udp
has all the UDP connections over IPv4udp6
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
setsdebug_mode
,-v
setsverbose_mode
, and-I
setsinform_mode
. Another useful option,-n
, setsopt_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 rancf-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 thatselectservers()
sets or the variables thatregextract()
sets. These classes or variables will also have afunction=FUNCTIONNAME
tag.source=promise
: this soft class was created from policy.inventory
: related to the system inventory, e.g. the network interfacesattribute_name=none
: has no visual attribute name (ignored by Mission Portal)attribute_name=X
: has visual attribute nameX
(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 variablesys.fqhost
isxyz
, the resulting classxyz
will have the tagderived-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 ameasurements
system observation and will also have themonitoring
tag.
Hard Classes
- CFEngine-specific classes
any
: this class is always setam_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 hostinform_mode
,verbose_mode
,debug_mode
: log verbosity levels in order of noisinessopt_dry_run
: set when the--dry-run
option is givenfailsafe_fallback
: set when the base policy is invalid and the built-infailsafe.cf
(seebootstrap.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 usingcf-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-favoriteunknown_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
.
- Operating System Architecture -
- Network Classes
- Unqualified Name of Host. CFEngine truncates it at the first dot.
Note:
www.sales.company.com
andwww.research.company.com
have the same unqualified name –www
- The IP address octets of any active interface (in the form
ipv4_192_0_0_1
,ipv4_192_0_0
,ipv4_192_0
,ipv4_192
) - User-defined Group of Hosts
mac_unknown
: set when the MAC address can't be found- See also:
sys.domain
,sys.hardware_addresses
,sys.sys.host
,sys.interface
,sys.interfaces
,sys.interface_flags
,sys.ipv4
,sys.ip_addresses
,sys.fqhost
,sys.uqhost
.
- Unqualified Name of Host. CFEngine truncates it at the first dot.
Note:
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
andHr0, Hr1,... Hr23
- Hour of the Day in GMT -
GMT_Hr00, GMT_Hr01, ...GMT_Hr23
andGMT_Hr0, GMT_Hr1, ...GMT_Hr23
. - Minutes of the Hour -
Min00, Min17,... Min45,...
andGMT_Min00, GMT_Min17,... GMT_Min45,...
- Five Minute Interval of the Hour -
Min00_05, Min05_10,... Min55_00
andGMT_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
andGMT_Q1, GMT_Q2, GMT_Q3, GMT_Q4
- An expression of the current quarter hour -
Hr12_Q3
andGMT_Hr12_Q3
- Day of the Month -
Day1, Day2,... Day31
andGMT_Day1, GMT_Day2,... GMT_Day31
- Month -
January, February,... December
andGMT_January, GMT_February,... GMT_December
- Year -
Yr1997, Yr2004
andGMT_Yr1997, GMT_Yr2004
- Period of the Day -
Night, Morning, Afternoon, Evening
andGMT_Night, GMT_Morning, GMT_Afternoon, GMT_Evening
(six hour blocks starting at 00:00 hours). - Lifecycle Index -
Lcycle_0, Lcycle_1, Lcycle_2
andGMT_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 namesiu.hio.no
,hio.no
, andno
.- See also:
sys.fqhost
,sys.uqhost
.
- See also:
An arbitrary user-defined string (as specified in the
-D
command line option, or defined in aclasses
promise promise orclasses
body,restart_class
in aprocesses
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 fileWORKDIR/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:
transformer
edit_template
source
incopy_from
exec_program
infile_select
- class names in
body classes
- logging attributes in
body action
- promised service name in
service_method
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:
Services Bundles and Bodies
in theMasterfiles Policy Framework standard library
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.
% 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
isnamespace
) by default; the classes in agent bundles are local (scope
isbundle
) by default. - common bundles can only contain
meta
,default
,vars
,classes
, andreports
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]
andrrange[min, max]
- a range of integer or real values, created via theirange()
andrrange()
functionsclist
- 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 typeclass
Menu option - one value from a list of values
body
type - a complex set of attributes expressed in a separate, reusable blockbundle
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:
- Classes provided as a command line argument (-D option) are read and set.
- Environment detection and hard classes discovery is done.
- Persistent classes are loaded.
- Policy sanity check using cf-promises -c (full-check) is performed.
- Pre-evaluation step is taking place.
- 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
andalways2
soft classes are always defined.The
solinux
soft class is defined as a combination of thelinux
or thesolaris
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 oflinux
,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
, thealt_class
class will also be set.The
oth_class
soft class is defined as the combination of twofileexists
functions -/etc/shadow
and/etc/passwd
. If both of these files are present theoth_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 AND
ed 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 thedef.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 thedef
scope. Thus:"vars": { "phone": "22-333-4444", "myplatform": "$(sys.os)", }
results in the variable
def.phone
with value22-333-4444
being defined, anddef.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 tagsource=augments_file
. For instance, the above two variables (assuming you placed the data in$(sys.inputdir)/def.json
) result incf-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 classesserver3
orserver4
are defined, or if any class starting withdebian
is defined. You can use any hard classes listed in Hard and Soft Classes with the exception ofam_policy_hub
andpolicy_server
.You can see the list of classes thus defined through
def.json
in the output ofcf-promises --show-classes
(see Components and Common Control). They will be tagged with the tagssource=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 taghardclass
.All special variable contexts, as documented in Special Variables, are always accessible without a namespace prefix. For example,
this
,mon
,sys
, andconst
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 3xAgentExecutionInterval
from last execution, status will be set toFAIL
. 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.
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
orBundle
, 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 asMetaTags
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 asVariableValue
orMetaTags
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.
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 asPromiseOutcome
,LogMessages
orReleaseId
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 bundlespaths.cf
: standard place to find the path for many common tools and system filesfeature.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 evaluationsexpire
: 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 messagesmessage
: 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 repairedno
: 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 repairedno
: 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 repaircl
: 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
ornamespace
)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 bynick
, the classesabc_reached
andabc_kept
will be set.if the promise is to change a file's owner to
nick
and the file was owned byadam
and the change succeeded, the classesabc_reached
andabc_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 definedx
: 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 withmXC_
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 runuser
: The owner of crontabhours
: The hours at which the job should runmins
: 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 namedepth
: 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 rotationmax_size
: minimum size in bytes that the file will grow to before being rotatedrotate_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 filesmax_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 namemethod
: the HTTP method (HEAD or GET)port
: the port number, e.g. 80uri
: 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 createvarlist
: 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 insidecollect_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 asgroup
: groupname or gid to run command asdir
: 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 asgroup
: 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 asjail_root
: path that will be the root directory for the processdir
: 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 asumask
: 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 asgid
: group name or gid to run command asumask
: 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 connectionpassword
: 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 connectionpassword
: 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 whichstring
will be inserted beforestring
: 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 matchcomment
: 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 aftercomment
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 matchcomment
: 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 aftercomment
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 withend
: 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 interestend
: 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 expressionreplace
: 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 spacelist
: 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 spacelist
: An slist of nameserver addressesoptions
: 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 containingtab[sectionName][LHS]="RHS"
. The value is not changed when theRHS
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 containingtab[sectionName][LHS]="RHS"
. The value is not changed when theRHS
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 sectionconfig
: The fully-qualified name of an associative array containingv[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 containingv[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 containingv[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 containingv[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 containingv[LHS]="rhs"
sep
: The separator to insert, e.g.for space-separated
bp
: The key-value separation regex, e.g.\s+
for space-separatedkp
: The keys to select from v, use.*
for allcp
: 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 ofv
that match the regexpat
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 arrayv[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 arrayv[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 matchfield
: The field to be modifiedval
: The new value offield
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 modifiedfield
: The field that should be modifiedval
: The value forfield
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 modifiedfield
: The field where users should be addedallusers
: The list of users to add tofield
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 ofpattern
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 containmarker
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 applymount
: the mount pointoption
: 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 templatejson_file
: a file with JSON datatarget_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 templatejson_string
: a string with JSON datatarget_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 directoryto
: 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 fileto
: 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
: targetstr
: 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
: targetstr
: the string datamode
: the file permissions in octalowner
: the file owner as a name or UIDgroup
: 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 rendertemplate
: Path to mustache templatedata
: 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 rendertemplate
: Path to mustache templatedata
: Data container to usemode
: File permissionsowner
: Target file ownergroup
: 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";
}
file_hardlink
Prototype: file_hardlink(target, link)
Description: Make a hard link to a file
Arguments:
target
: of linklink
: 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)";
}
file_link
Prototype: file_link(target, link)
Description: Make a symlink to a file
Arguments:
target
: of symlinklink
: 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:
newval
: the new optionmethod
:field_operation
to apply
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 valuemethod
: 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 columnscol
: The (1-based) index of the value to changenewval
: The new valuemethod
: 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 columnscol
: The (1-based) index of the value to changenewval
: The new valuemethod
: 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 serverserver
: 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 serverserver
: 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 serverserver
: 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 serverserver
: 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 serverserver
: 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";
}
link_from bodies
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 thistype
: the link's type (symlink
orhardlink
)
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 modeuser
: 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 modeuser
: The username of the new ownergroup
: 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 ownerg
: 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 depthlist
: 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 namedays
: 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 sizeto
: 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 yearsmonths
: Number of monthsdays
: Number of dayshours
: Number of hoursminutes
: Number of minutesseconds
: 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 selectdays
: 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 typesdays
: 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 foundextract_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:
list-packages
: When either the system is changed, orquery_installed_ifelapsed
in the policy has expired.list-updates
: Only whenquery_updates_ifelapsed
in the policy has expired.list-updates-local
: Only when the system is changed.
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:
packageorfile
: the package or full filename to addpackage_version
: thepackage_version
desiredpackage_arch
: a string determining thepackage_architectures
desired
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:
packageorfile
: the package or full filename to deletepackage_version
: thepackage_version
desiredpackage_arch
: a string determining thepackage_architectures
desired
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:
packageorfile
: the package or full filename to add or updatepackage_version
: thepackage_version
desiredpackage_arch
: a string determining thepackage_architectures
desired
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 desiredpackage_policy
, add or delete or addupdatepackage_version
: the desiredpackage_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 topip
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:
repo
: the specific repository forpackage_file_repositories
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 inyum --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 rangeupper
: 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 controlstate
: 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 controlstate
: 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:
- Your init script basename =
$(service)
- Your init script argument =
$(state)
- Your init script lives in
/etc/init.d/
(for non-*bsd), or/etc/rc.d/
(for *bsd) - Your process regex pattern =
\b$(service)\b
- 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 serversource
: Path of remote file system to mount
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 serversource
: Path of remote file system to mountperm
: 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.
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 repositoryfile
: 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 repositorybranch
: 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 repositorynew_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 clonestash_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 repositorymessage
: 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 repositorysubcmd
: any valid git sub-commandargs
: 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:
sketch.json
main.cf
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
eitherstring
orlist
(support for more types will be added in the future) validation
must be a validation that has been defined in the API (living either inconstdata.conf
orvardata.conf
)- the referenced validation can use
minimum_value
,maximum_value
, orvalid_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 likeapi_ok
promises, e.g.warnings
orsuccess
, 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
andwarnings
: 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": {}
}
}
search
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'srepolist
. Optional; when not given, the first element of therepolist
will be used.source
: the sketch source repository. Must be in the API'srecognized_sources
. Optional; when not given, every element of therecognized_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'srepolist
.
{ 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 givenA
orB
to pass validation.minimum_value
and thenmaximum_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 thenvalid_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
andvalid_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
andarray_v
are almost exactly likelist
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
action
: bodyaction
classes
: bodyclasses
comment
:string
depends_on
:slist
handle
:string
if
:string
ifvarclass
:string
meta
:slist
unless
:string
access
admit
:slist
admit_hostnames
:slist
admit_ips
:slist
admit_keys
:slist
deny
:slist
deny_hostnames
:slist
deny_ips
:slist
deny_keys
:slist
ifencrypted
:boolean
maproot
:slist
report_data_select
: bodyreport_data_select
resource_type
: one ofpath
,literal
,context
,query
,variable
,bundle
shortcut
:string
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 range0,99999999999
scope
: one ofnamespace
,bundle
select_class
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
xor
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
commands
databases
database_columns
:slist
in range.*
database_operation
: one ofcreate
,delete
,drop
,cache
,verify
,restore
database_rows
:slist
in range.*,.*
database_server
: bodydatabase_server
database_type
: one ofsql
,ms_registry
registry_exclude
:slist
defaults
delete_attribute
delete_lines
delete_select
: bodydelete_select
not_matching
:boolean
select_region
: bodyselect_region
delete_text
delete_tree
field_edits
edit_field
: bodyedit_field
select_region
: bodyselect_region
files
acl
: bodyacl
changes
: bodychanges
copy_from
: bodycopy_from
create
:boolean
delete
: bodydelete
depth_search
: bodydepth_search
edit_defaults
: bodyedit_defaults
edit_line
:bundle
edit_template
:string
in range"?(/.*)
edit_xml
:bundle
file_select
: bodyfile_select
file_type
: one ofregular
,fifo
link_from
: bodylink_from
move_obstructions
:boolean
pathtype
: one ofliteral
,regex
,guess
perms
: bodyperms
rename
: bodyrename
repository
:string
in range"?(/.*)
template_data
:data
template_method
: one ofcfengine
,mustache
touch
:boolean
transformer
:string
in range"?(/.*)
guest_environments
environment_host
:string
in range[a-zA-Z0-9_]+
environment_interface
: bodyenvironment_interface
environment_resources
: bodyenvironment_resources
environment_state
: one ofcreate
,delete
,running
,suspended
,down
environment_type
: one ofxen
,kvm
,esx
,vbox
,test
,xen_net
,kvm_net
,esx_net
,test_net
,zone
,ec2
,eucalyptus
insert_lines
expand_scalars
:boolean
insert_select
: bodyinsert_select
insert_type
: one ofliteral
,string
,file
,file_preserve_block
,preserve_block
,preserve_all_lines
location
: bodylocation
select_region
: bodyselect_region
whitespace_policy
:olist
in rangeignore_leading,ignore_trailing,ignore_embedded,exact_match
insert_text
insert_tree
measurements
data_type
: one ofcounter
,int
,real
,string
,slist
history_type
: one ofweekly
,scalar
,static
,log
match_value
: bodymatch_value
stream_type
: one ofpipe
,file
units
:string
meta
methods
packages
additional_packages
:slist
architecture
:string
options
:slist
package_architectures
:slist
package_method
: bodypackage_method
package_module
: bodypackage_module
package_policy
: one ofadd
,delete
,reinstall
,update
,addupdate
,patch
,verify
package_select
: one of>
,<
,==
,!=
,>=
,<=
package_version
:string
policy
: one ofabsent
,present
version
:string
processes
process_count
: bodyprocess_count
process_select
: bodyprocess_select
process_stop
:string
in range"?(/.*)
restart_class
:string
in range[a-zA-Z0-9_$(){}\[\].:]+
signals
:olist
in rangehup,int,trap,kill,pipe,cont,abrt,stop,quit,term,child,usr1,usr2,bus,segv
replace_patterns
replace_with
: bodyreplace_with
select_region
: bodyselect_region
reports
bundle_return_value_index
:string
in range[a-zA-Z0-9_$(){}\[\].:]+
friend_pattern
:string
intermittency
:real
in range0,1
lastseen
:int
in range0,99999999999
printfile
: bodyprintfile
report_to_file
:string
in range"?(/.*)
showstate
:slist
roles
services
service_dependencies
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
service_method
: bodyservice_method
service_policy
: one ofstart
,stop
,enable
,disable
,restart
,reload
set_attribute
set_text
storage
users
description
:string
group_primary
:string
groups_secondary
:slist
in range.*
home_bundle
:bundle
home_bundle_inherit
:boolean
home_dir
:string
in range"?(/.*)
password
: bodypassword
policy
: one ofpresent
,absent
,locked
shell
:string
in range"?(/.*)
uid
:int
in range-99999999999,99999999999
vars
data
:data
ilist
:ilist
in range-99999999999,99999999999
int
:int
in range-99999999999,99999999999
policy
: one offree
,overridable
,constant
,ifdefined
real
:real
in range-9.99999E100,9.99999E100
rlist
:rlist
in range-9.99999E100,9.99999E100
slist
:slist
string
:string
All Body Types
acl
aces
:slist
in range((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
acl_default
: one ofnochange
,access
,specify
,clear
acl_directory_inherit
deprecated: one ofnochange
,parent
,specify
,clear
acl_inherit
: one oftrue
,false
,yes
,no
,on
,off
,nochange
acl_method
: one ofappend
,overwrite
acl_type
: one ofgeneric
,posix
,ntfs
inherit_from
: bodyinherit_from
meta
:slist
specify_default_aces
:slist
in range((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
specify_inherit_aces
deprecated:slist
in range((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
action
action_policy
: one offix
,warn
,nop
audit
:boolean
background
:boolean
expireafter
:int
in range0,99999999999
ifelapsed
:int
in range0,99999999999
inherit_from
: bodyinherit_from
log_failed
:string
in rangestdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)
log_kept
:string
in rangestdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)
log_level
: one ofinform
,verbose
,error
,log
log_priority
: one ofemergency
,alert
,critical
,error
,warning
,notice
,info
,debug
log_repaired
:string
in rangestdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)
log_string
:string
measurement_class
:string
meta
:slist
report_level
: one ofinform
,verbose
,error
,log
agent
abortbundleclasses
:slist
in range.*
abortclasses
:slist
in range.*
addclasses
:slist
in range.*
agentaccess
:slist
in range.*
agentfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
allclassesreport
:boolean
alwaysvalidate
:boolean
bindtointerface
:string
in range.*
checksum_alert_time
:int
in range0,60
childlibpath
:string
in range.*
default_repository
:string
in range"?(/.*)
default_timeout
:int
in range0,99999999999
defaultcopytype
: one ofmtime
,atime
,ctime
,digest
,hash
,binary
dryrun
:boolean
editbinaryfilesize
:int
in range0,99999999999
editfilesize
:int
in range0,99999999999
environment
:slist
in range[A-Za-z0-9_]+=.*
expireafter
:int
in range0,99999999999
files_auto_define
:slist
files_single_copy
:slist
hashupdates
:boolean
hostnamekeys
:boolean
ifelapsed
:int
in range0,99999999999
inform
:boolean
intermittency
:boolean
max_children
:int
in range0,99999999999
maxconnections
:int
in range0,99999999999
mountfilesystems
:boolean
nonalphanumfiles
:boolean
refresh_processes
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repchar
:string
in range.
report_class_log
:boolean
secureinput
:boolean
select_end_match_eof
:boolean
sensiblecount
:int
in range0,99999999999
sensiblesize
:int
in range0,99999999999
skipidentify
:boolean
suspiciousnames
:slist
timezone
:slist
verbose
:boolean
changes
hash
: one ofmd5
,sha1
,sha224
,sha256
,sha384
,sha512
,best
inherit_from
: bodyinherit_from
meta
:slist
report_changes
: one ofall
,stats
,content
,none
report_diffs
:boolean
update_hashes
:boolean
classes
cancel_kept
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
cancel_notkept
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
cancel_repaired
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
failed_returncodes
:slist
in range[-0-9_$(){}\[\].]+
inherit_from
: bodyinherit_from
kept_returncodes
:slist
in range[-0-9_$(){}\[\].]+
meta
:slist
persist_time
:int
in range0,99999999999
promise_kept
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
promise_repaired
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repair_denied
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repair_failed
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repair_timeout
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repaired_returncodes
:slist
in range[-0-9_$(){}\[\].]+
scope
: one ofnamespace
,bundle
timer_policy
: one ofabsolute
,reset
common
bundlesequence
:slist
in range.*
bwlimit
:real
in range0,99999999999
cache_system_functions
:boolean
domain
:string
in range.*
fips_mode
:boolean
goal_patterns
:slist
ignore_missing_bundles
:boolean
ignore_missing_inputs
:boolean
inputs
:slist
in range.*
lastseenexpireafter
:int
in range0,99999999999
output_prefix
:string
package_inventory
:slist
in range.*
package_module
:string
in range.*
protocol_version
: one of0
,undefined
,1
,classic
,2
,latest
require_comments
:boolean
site_classes
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
syslog_host
:string
in range[a-zA-Z0-9_$(){}.:-]+
syslog_port
:int
in range0,99999999999
tls_ciphers
:string
tls_min_version
:string
version
:string
contain
chdir
:string
in range"?(/.*)
chroot
:string
in range"?(/.*)
exec_group
:string
exec_owner
:string
exec_timeout
:int
in range1,3600
inherit_from
: bodyinherit_from
meta
:slist
no_output
:boolean
preview
:boolean
umask
:string
useshell
: one ofnoshell
,useshell
,powershell
,true
,false
,yes
,no
,on
,off
copy_from
check_root
:boolean
collapse_destination_dir
:boolean
compare
: one ofatime
,mtime
,ctime
,digest
,hash
,exists
,binary
copy_backup
: one oftrue
,false
,timestamp
copy_size
:irange
in range0,inf
copylink_patterns
:slist
encrypt
:boolean
findertype
: one ofMacOSX
force_ipv4
:boolean
force_update
:boolean
inherit_from
: bodyinherit_from
link_type
: one ofsymlink
,hardlink
,relative
,absolute
linkcopy_patterns
:slist
meta
:slist
portnumber
:string
preserve
:boolean
protocol_version
: one of0
,undefined
,1
,classic
,2
,latest
purge
:boolean
servers
:slist
in range[A-Za-z0-9_.:\-\[\]]+
source
:string
in range.+
stealth
:boolean
timeout
:int
in range1,3600
trustkey
:boolean
type_check
:boolean
verify
:boolean
database_server
db_server_connection_db
:string
db_server_host
:string
db_server_owner
:string
db_server_password
:string
db_server_type
: one ofpostgres
,mysql
inherit_from
: bodyinherit_from
meta
:slist
delete
dirlinks
: one ofdelete
,tidy
,keep
inherit_from
: bodyinherit_from
meta
:slist
rmdirs
:boolean
delete_select
delete_if_contains_from_list
:slist
in range.*
delete_if_match_from_list
:slist
in range.*
delete_if_not_contains_from_list
:slist
in range.*
delete_if_not_match_from_list
:slist
in range.*
delete_if_not_startwith_from_list
:slist
in range.*
delete_if_startwith_from_list
:slist
in range.*
inherit_from
: bodyinherit_from
meta
:slist
depth_search
depth
:int
in range0,99999999999
exclude_dirs
:slist
in range.*
include_basedir
:boolean
include_dirs
:slist
in range.*
inherit_from
: bodyinherit_from
meta
:slist
rmdeadlinks
:boolean
traverse_links
:boolean
xdev
:boolean
edit_defaults
edit_backup
: one oftrue
,false
,timestamp
,rotate
empty_file_before_editing
:boolean
inherit
:boolean
inherit_from
: bodyinherit_from
max_file_size
:int
in range0,99999999999
meta
:slist
recognize_join
:boolean
rotate
:int
in range0,99
edit_field
allow_blank_fields
:boolean
extend_fields
:boolean
field_operation
: one ofprepend
,append
,alphanum
,delete
,set
field_separator
:string
in range.*
field_value
:string
in range.*
inherit_from
: bodyinherit_from
meta
:slist
select_field
:int
in range0,99999999
start_fields_from_zero
:boolean
value_separator
:string
in range^.$
environment_interface
env_addresses
:slist
env_name
:string
env_network
:string
inherit_from
: bodyinherit_from
meta
:slist
environment_resources
env_baseline
:string
in range"?(/.*)
env_cpus
:int
in range0,99999999999
env_disk
:int
in range0,99999999999
env_memory
:int
in range0,99999999999
env_spec
:string
in range.*
inherit_from
: bodyinherit_from
meta
:slist
executor
agent_expireafter
:int
in range0,10080
exec_command
:string
in range"?(/.*)
executorfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
mailfilter_exclude
:slist
mailfilter_include
:slist
mailfrom
:string
in range.*@.*
mailmaxlines
:int
in range0,1000
mailsubject
:string
mailto
:string
in range.*@.*
schedule
:slist
smtpserver
:string
in range.*
splaytime
:int
in range0,99999999999
file
file_select
atime
:irange
in range0,2147483647
ctime
:irange
in range0,2147483647
exec_program
:string
in range"?(/.*)
exec_regex
:string
in range.*
file_result
:string
in range[!*(leaf_name|path_name|file_types|mode|size|owner|group|atime|ctime|mtime|issymlinkto|exec_regex|exec_program|bsdflags)[|&.]*]*
file_types
:olist
in rangeplain,reg,symlink,dir,socket,fifo,door,char,block
inherit_from
: bodyinherit_from
issymlinkto
:slist
leaf_name
:slist
meta
:slist
mtime
:irange
in range0,2147483647
path_name
:slist
in range"?(/.*)
search_bsdflags
:slist
in range[+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+
search_groups
:slist
search_mode
:slist
in range[0-7augorwxst,+-=]+
search_owners
:slist
search_size
:irange
in range0,inf
hub
client_history_timeout
:int
in range1,65535
exclude_hosts
:slist
hub_schedule
:slist
port
:int
in range1,65535
insert_select
inherit_from
: bodyinherit_from
insert_if_contains_from_list
:slist
in range.*
insert_if_match_from_list
:slist
in range.*
insert_if_not_contains_from_list
:slist
in range.*
insert_if_not_match_from_list
:slist
in range.*
insert_if_not_startwith_from_list
:slist
in range.*
insert_if_startwith_from_list
:slist
in range.*
meta
:slist
link_from
copy_patterns
:slist
inherit_from
: bodyinherit_from
link_children
:boolean
link_type
: one ofsymlink
,hardlink
,relative
,absolute
meta
:slist
source
:string
in range.+
when_linking_children
: one ofoverride_file
,if_no_such_file
when_no_source
: one offorce
,delete
,nop
location
before_after
: one ofbefore
,after
first_last
: one offirst
,last
inherit_from
: bodyinherit_from
meta
:slist
select_line_matching
:string
in range.*
match_value
extraction_regex
:string
inherit_from
: bodyinherit_from
meta
:slist
select_line_matching
:string
in range.*
select_line_number
:int
in range0,99999999999
select_multiline_policy
: one ofaverage
,sum
,first
,last
track_growing_file
:boolean
monitor
forgetrate
:real
in range0,1
histograms
:boolean
monitorfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
tcpdump
:boolean
tcpdumpcommand
:string
in range"?(/.*)
mount
edit_fstab
:boolean
inherit_from
: bodyinherit_from
meta
:slist
mount_options
:slist
mount_server
:string
mount_source
:string
in range"?(/.*)
mount_type
: one ofnfs
,nfs2
,nfs3
,nfs4
unmount
:boolean
package_method
inherit_from
: bodyinherit_from
meta
:slist
package_add_command
:string
in range.+
package_arch_regex
:string
package_changes
: one ofindividual
,bulk
package_commands_useshell
:boolean
package_default_arch_command
:string
in range"?(/.*)
package_delete_command
:string
in range.+
package_delete_convention
:string
package_file_repositories
:slist
package_installed_regex
:string
package_list_arch_regex
:string
package_list_command
:string
in range.+
package_list_name_regex
:string
package_list_update_command
:string
package_list_update_ifelapsed
:int
in range-99999999999,99999999999
package_list_version_regex
:string
package_multiline_start
:string
package_name_convention
:string
package_name_regex
:string
package_noverify_regex
:string
package_noverify_returncode
:int
in range-99999999999,99999999999
package_patch_arch_regex
:string
package_patch_command
:string
in range.+
package_patch_installed_regex
:string
package_patch_list_command
:string
in range.+
package_patch_name_regex
:string
package_patch_version_regex
:string
package_update_command
:string
in range.+
package_verify_command
:string
in range.+
package_version_equal_command
:string
in range.+
package_version_less_command
:string
in range.+
package_version_regex
:string
package_module
default_options
:slist
query_installed_ifelapsed
:int
in range-99999999999,99999999999
query_updates_ifelapsed
:int
in range-99999999999,99999999999
password
data
:string
format
: one ofplaintext
,hash
inherit_from
: bodyinherit_from
meta
:slist
perms
bsdflags
:slist
in range[+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+
groups
:slist
in range[a-zA-Z0-9_$.-]+
inherit_from
: bodyinherit_from
meta
:slist
mode
:string
in range[0-7augorwxst,+-=]+
owners
:slist
in range[a-zA-Z0-9_$.-]+
rxdirs
:boolean
printfile
file_to_print
:string
in range"?(/.*)
inherit_from
: bodyinherit_from
meta
:slist
number_of_lines
:int
in range0,99999999999
process_count
in_range_define
:slist
inherit_from
: bodyinherit_from
match_range
:irange
in range0,99999999999
meta
:slist
out_of_range_define
:slist
process_select
command
:string
inherit_from
: bodyinherit_from
meta
:slist
pgid
:irange
in range0,99999999999
pid
:irange
in range0,99999999999
ppid
:irange
in range0,99999999999
priority
:irange
in range-20,+20
process_owner
:slist
process_result
:string
in range[(process_owner|pid|ppid||pgid|rsize|vsize|status|command|ttime|stime|tty|priority|threads)[|&!.]*]*
rsize
:irange
in range0,99999999999
status
:string
stime_range
:irange
in range0,2147483647
threads
:irange
in range0,99999999999
ttime_range
:irange
in range0,99999999999
tty
:string
vsize
:irange
in range0,99999999999
rename
disable
:boolean
disable_mode
:string
in range[0-7augorwxst,+-=]+
disable_suffix
:string
inherit_from
: bodyinherit_from
meta
:slist
newname
:string
rotate
:int
in range0,99
replace_with
inherit_from
: bodyinherit_from
meta
:slist
occurrences
: one ofall
,first
replace_value
:string
in range.*
report_data_select
inherit_from
: bodyinherit_from
meta
:slist
metatags_exclude
:slist
in range.*
metatags_include
:slist
in range.*
monitoring_exclude
:slist
in range.*
monitoring_include
:slist
in range.*
promise_handle_exclude
:slist
in range.*
promise_handle_include
:slist
in range.*
runagent
background_children
:boolean
encrypt
:boolean
force_ipv4
:boolean
hosts
:slist
max_children
:int
in range0,99999999999
output_directory
:string
in range"?(/.*)
output_to_file
:boolean
port
:int
in range1,65535
timeout
:int
in range1,9999
trustkey
:boolean
select_region
include_end_delimiter
:boolean
include_start_delimiter
:boolean
inherit_from
: bodyinherit_from
meta
:slist
select_end
:string
in range.*
select_end_match_eof
:boolean
select_start
:string
in range.*
server
allowallconnects
:slist
allowciphers
:string
allowconnects
:slist
allowlegacyconnects
:slist
allowtlsversion
:string
allowusers
:slist
auditing
:boolean
bindtointerface
:string
call_collect_interval
:int
in range0,99999999999
cfruncommand
:string
in range.+
collect_window
:int
in range0,99999999999
denybadclocks
:boolean
denyconnects
:slist
dynamicaddresses
:slist
hostnamekeys
:boolean
listen
:boolean
logallconnections
:boolean
logencryptedtransfers
:boolean
maxconnections
:int
in range0,99999999999
port
:int
in range1,65535
serverfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
skipverify
deprecated:slist
trustkeysfrom
:slist
service_method
inherit_from
: bodyinherit_from
meta
:slist
service_args
:string
service_autostart_policy
: one ofnone
,boot_time
,on_demand
service_bundle
:bundle
service_dependence_chain
: one ofignore
,start_parent_services
,stop_child_services
,all_related
service_type
: one ofwindows
,generic
volume
check_foreign
:boolean
freespace
:string
in range[0-9]+[MBkKgGmb%]
inherit_from
: bodyinherit_from
meta
:slist
scan_arrivals
:boolean
sensible_count
:int
in range0,99999999999
sensible_size
:int
in range0,99999999999