The Complete CFEngine Reference
Table of Content
- Macros
- Components and Common Control
- Promise Types and Attributes
- Functions
- remotescalar
- difference
- usemodule
- randomint
- min
- accumulated
- maplist
- isgreaterthan
- changedbefore
- ldaplist
- datastate
- now
- escape
- accessedbefore
- groupexists
- classmatch
- regldap
- readdata
- ifelse
- parseyaml
- classesmatching
- maparray
- classify
- ip2host
- translatepath
- bundlesmatching
- rrange
- hostinnetgroup
- regarray
- hubknowledge
- canonify
- string_reverse
- peerleader
- readstringarrayidx
- lastnode
- dirname
- irange
- remoteclassesmatching
- hostrange
- every
- makerule
- diskfree
- getuid
- isvariable
- splayclass
- regline
- getusers
- eval
- laterthan
- canonifyuniquely
- hashmatch
- file_hash
- readjson
- strftime
- variance
- parsejson
- data_readstringarray
- getfields
- none
- ldapvalue
- filestat
- string_head
- string_tail
- regextract
- lsdir
- reglist
- readcsv
- ago
- islink
- mergedata
- sum
- sublist
- bundlestate
- hostswithclass
- getvalues
- isdir
- execresult
- packageupdatesmatching
- sort
- readyaml
- grep
- peerleaders
- filesize
- concat
- string_downcase
- "on"
- unique
- readtcp
- join
- isexecutable
- parsestringarrayidx
- getenv
- getclassmetatags
- or
- reverse
- max
- string_length
- regcmp
- not
- isnewerthan
- nth
- registryvalue
- read[int|real|string]list
- ldaparray
- fileexists
- selectservers
- length
- some
- isplain
- and
- host2ip
- string_split
- iprange
- returnszero
- "parse[int|real|string]array"
- islessthan
- mean
- shuffle
- splitstring
- data_readstringarrayidx
- readfile
- getindices
- hostsseen
- product
- findfiles
- data_expand
- expandrange
- filesexist
- getgid
- peers
- filter
- format
- intersection
- countlinesmatching
- packagesmatching
- getvariablemetatags
- hash
- string_mustache
- userexists
- variablesmatching
- mapdata
- countclassesmatching
- "read[int|real|string]array"
- data_regextract
- string_upcase
- strcmp
- storejson
- Hard and Soft Classes
- Special Variables
- 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
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: Introduced in CFEngine 3.7.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
Note: This value affects the hostsseen()
function and license counting by
cf-hub
in the Enterprise edition.
Example:
body common control
{
lastseenexpireafter => "72";
}
See Also: hostsseen(), cf-hub
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: localhost
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
Default value: 514
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
--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. This option is overridden by FILE if supplied as argument.
--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 classes which if defined
lead to termination of cf-agent.
Regular expressions are used for classes that cf-agent
will watch out
for. If any matching class becomes defined, it will cause the
current execution of cf-agent
to be aborted. This may be used for
validation, for example. To handle class expressions, simply create
an alias for the expression with a single name.
Type: slist
Allowed input range: .*
Example:
body agent control
{
abortclasses => { "danger.*", "should_not_continue" };
}
abortbundleclasses
Description: The abortbundleclasses
slist contains classes which
if defined lead to termination of current bundle.
Regular expressions are used for classes, or class expressions
that cf-agent
will watch out for. If any of these classes becomes
defined, it will cause the current bundle to be aborted. This may
be used for validation, for example.
Type: slist
Allowed input range: .*
Example: This example shows how to use the feature to validate input to a method bundle.
body common control
{
bundlesequence => { "testbundle" };
version => "1.2.3";
}
#################################
body agent control
{
abortbundleclasses => { "invalid.*" };
}
#################################
bundle agent testbundle
{
vars:
"userlist" slist => { "xyz", "mark", "jeang", "jonhenrik", "thomas", "eben" };
methods:
"any" usebundle => subtest("$(userlist)");
}
#################################
bundle agent subtest(user)
{
classes:
"invalid" not => regcmp("[a-z]{4}","$(user)");
reports:
!invalid::
"User name $(user) is valid at exactly 4 letters";
# abortbundleclasses will prevent this from being evaluated
invalid::
"User name $(user) is invalid";
}
addclasses
Description: The addclasses
slist contains classes to be defined
always in the current context.
This adds global, literal classes. The only predicates available during the control section are hard-classes.
Type: slist
Allowed input range: .*
Example:
any::
addclasses => { "My_Organization" }
solaris::
addclasses => { "some_solaris_alive", "running_on_sunshine" };
Notes:
Another place to make global aliases for system hardclasses. Classes here are added unequivocally to the system. If classes are used to predicate definition, then they must be defined in terms of global hard classes.
agentaccess
Description: A agentaccess
slist contains user names that are
allowed to execute cf-agent.
This represents a list of user names that will be allowed to attempt execution of the current configuration. This is mainly a sanity check rather than a security measure.
Type: slist
Allowed input range: .*
Example:
agentaccess => { "mark", "root", "sudo" };
agentfacility
Type: (menu option)
Allowed input range:
LOG_USER
LOG_DAEMON
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
Default value: LOG_USER
Description: The agentfacility
menu option policy sets the agent's
syslog facility level.
Example:
agentfacility => "LOG_USER";
Notes:
This is ignored on Windows, as CFEngine Enterprise creates event logs.
See Also: Manual pages for syslog.
allclassesreport
Description: The allclassesreport
menu option policy determines
whether to generate the allclasses.txt
report.
If set to true, the state/allclasses.txt
file will be written to disk
during agent execution.
Type: boolean
Default value: false
Example:
body agent control
{
allclassesreport => "true";
}
Notes:
This functionality is retained only for CFEngine 2 compatibility. As of
CFEngine 3.5, the classesmatching()
function provides
a more convenient way to retrieve a list of set classes at execution time.
History: Was introduced in 3.2.4, Enterprise 2.1.4 (2011)
alwaysvalidate
Description: The alwaysvalidate
menu option policy is a true/false
flag to determine whether configurations will always be checked before
executing, or only after updates.
Type: boolean
Example:
body agent control
{
Min00_05::
# revalidate once per hour, regardless of change in configuration
alwaysvalidate => "true";
}
Notes:
The agents cf-agent
and cfserverd
can run cf-promises
to
validate inputs before attempting to execute a configuration. As of
version 3.1.2 core, this only happens if the configuration file has
changed to save CPU cycles. When this attribute is set, cf-agent
will force a revalidation of the input.
History: Was introduced in version 3.1.2,Enterprise 2.0.1 (2010)
auditing
Deprecated: This menu option policy is deprecated, does nothing and is kept for backward compatibility.
binarypaddingchar
Deprecated: This attribute was deprecated in 3.6.0.
bindtointerface
Description: The bindtointerface
string describes the interface
to be used for outgoing connections.
On multi-homed hosts, the server and client can bind to a specific interface for server traffic. The IP address of the interface must be given as the argument, not the device name.
Type: string
Allowed input range: .*
Example:
bindtointerface => "192.168.1.1";
hashupdates
Description: The hashupdates
determines whether stored hashes are
updated when change is detected in source.
If 'true' the stored reference value is updated as soon as a warning message has been given. As most changes are benign (package updates etc) this is a common setting.
Type: boolean
Default value: false
Example:
body agent control
{
hashupdates => "true";
}
childlibpath
Description: The childlibpath
string contains the LD_LIBRARY_PATH
for child processes.
This string may be used to set the internal LD_LIBRARY_PATH
environment
of the agent.
Type: string
Allowed input range: .*
Example:
body agent control
{
childlibpath => "/usr/local/lib:/usr/local/gnu/lib";
}
checksum_alert_time
Description: The value of checksum_alert_time represents the persistence time for the checksum_alert class.
When checksum changes trigger an alert, this is registered as a persistent class. This value determines the longevity of that class.
Type: int
Allowed input range: 0,60
Default value: 10 mins
Example:
body agent control
{
checksum_alert_time => "30";
}
defaultcopytype
Description: The defaultcopytype
menu option policy sets the global
default policy for comparing source and image in copy transactions.
Type: (menu option)
Allowed input range:
mtime
atime
ctime
digest
hash
binary
Example:
body agent control
{
#...
defaultcopytype => "digest";
}
default_repository
Description: The default_repository
string contains the path to the
default file repository.
If defined the default repository is the location where versions of files altered by CFEngine are stored. This should be understood in relation to the policy for 'backup' in copying, editing etc. If the backups are time-stamped, this becomes effective a version control repository.
Type: string
Allowed input range: "?(/.*)
Default value: unset
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
. If
unset then backups are stored in the same directory as the original file with an
identifying suffix.
See also: edit_backup
in body edit_defaults
, copy_backup
in body copy_from
default_timeout
Description: The value of default_timeout
represents the maximum
time a network connection should attempt to connect or read from server.
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
}
See Also: Promise locking, ifelapsed action body attribute
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";
}
See Also: background
in action bodies
maxconnections
Description: The value of maxconnections
represents the maximum
number of outgoing connections to cf-serverd
.
Type: int
Allowed input range: 0,99999999999
Default value: 30 remote queries
Example:
# client side
body agent control
{
maxconnections => "1000";
}
Notes:
Watch out for kernel limitations for maximum numbers of open file descriptors which can limit this.
mountfilesystems
Description: The mountfilesystems
menu option policy determines
whether to mount any filesystems promised.
It issues the generic command to mount file systems defined in the file system table.
Type: boolean
Default value: false
Example:
body agent control
{
mountfilesystems => "true";
}
nonalphanumfiles
Description: The nonalphanumfiles
menu option policy determines
whether to warn about filenames with no alphanumeric content.
This test is applied in all recursive/depth searches.
Type: boolean
Default value: false
Example:
body agent control
{
nonalphanumfiles => "true";
}
repchar
Description: The repchar
string represents a character used to
canonize pathnames in the file repository.
Type: string
Allowed input range: .
Default value: _
Example:
body agent control
{
repchar => "_";
}
Notes:
refresh_processes
Description: The refresh_processes
slist contains bundles to reload
the process table before verifying the bundles named in this list
(lazy evaluation).
If this list of regular expressions is non-null and an existing bundle is mentioned or matched in this list, CFEngine will reload the process table at the start of the named bundle, each time is is scheduled. If the list is null, the process list will be reloaded at the start of every scheduled bundle.
Type: slist
Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+
Example:
body agent control
{
refresh_processes => { "mybundle" };
#refresh_processes => { "none" };
}
This examples uses a non-empty list with the name 'none'. This is not a reserved word, but as long as there are no bundles with the name 'none' this has the effect of never reloading the process table. This keeps improves the efficiency of the agent.
History: Was introduced in version 3.1.3, Enterprise 2.0.2 (2010)
secureinput
Description: The secureinput
menu option policy checks whether
input files are writable by unauthorized users.
If this is set, the agent will not accept an input file that is not owned by a privileged user.
Type: boolean
Default value: false
Example:
body agent control
{
secureinput => "true";
}
sensiblecount
Description: The value of sensiblecount
represents the minimum
number of files a mounted filesystem is expected to have.
Type: int
Allowed input range: 0,99999999999
Default value: 2 files
Example:
body agent control
{
sensiblecount => "20";
}
sensiblesize
Description: The value of sensiblesize
represents the minimum
number of bytes a mounted filesystem is expected to have.
Type: int
Allowed input range: 0,99999999999
Default value: 1000 bytes
Example:
body agent control
{
sensiblesize => "20K";
}
skipidentify
Description: The skipidentify
menu option policy determines whether
to send an IP/name during server connection because address resolution is
broken.
Hosts that are not registered in DNS cannot supply reasonable credentials for a secondary confirmation of their identity to a CFEngine server. This causes the agent to ignore its missing DNS credentials.
Type: boolean
Default value: false
Example:
body agent control
{
skipidentify => "true";
}
suspiciousnames
Description: The suspiciousnames
slist contains names to warn about
if found during any file search.
If CFEngine sees these names during recursive (depth) file searches it will warn about them.
Type: slist
Allowed input range: (arbitrary string)
Example:
body agent control
{
suspiciousnames => { ".mo", "lrk3", "rootkit" };
}
syslog
Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.
track_value
Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.
timezone
Description: The timezone
slist contains allowed timezones this
machine must comply with.
Type: slist
Allowed input range: (arbitrary string)
Example:
body agent control
{
timezone => { "MET", "CET", "GMT+1" };
}
verbose
Description: The verbose
menu option policy determines whether to
switch on verbose standard output.
It is equivalent to (and when present, overrides) the command line option '-v'. Sets the default output level 'permanently' for this promise.
Type: boolean
Default value: false
Example:
body agent control
{
verbose => "true";
}
cf-serverd
cf-serverd
is a socket listening daemon providing two services: it acts as a
file server for remote file copying and it allows an authorized
cf-runagent
to start a cf-agent
run. cf-agent
typically connects to a cf-serverd
instance to request updated policy code,
but may also request additional files for download. cf-serverd
employs
role based access control (defined in policy code) to authorize
requests.
cf-serverd
keeps the promises made in common
and server
bundles, and is
affected by common
and server
control bodies.
Note: This daemon reloads it's config when the SIGHUP signal is received.
History:
- SIGHUP behavior added in 3.7.0
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. This option is overridden by FILE if supplied as argument.
--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" , ".*\.example\.org" };
allowallconnects => { "127.0.0.1" , "::1" , ".*\.example\.org" };
# Uncomment me under controlled circumstances
#trustkeysfrom => { "127.0.0.1" , "::1" , ".*\.example\.org" };
}
allowallconnects
Description: List of IP addresses that may have more than one connection to the server port
This list of regular expressions matches hosts that are allowed to
connect an unlimited number of times up to the maximum connection
limit. Without this, a host may only connect once (which is a very
strong constraint, as the host must wait for the TCP FIN_WAIT
to
expire before reconnection can be attempted).
Note that 127.0.0.1
is a regular expression (i.e., "127 any
character 0 any character 0 any character 1"), but this will only
match the IP address 127.0.0.1
. Take care with IP addresses and
domain names, as the hostname regular expression www.domain.com
will potentially match more than one hostname (e.g.,
wwwxdomain.com
, in addition to the desired hostname
www.domain.com
).
Type: slist
Allowed input range: (arbitrary string)
Examples:
allowallconnects => {
"127.0.0.1",
"::1",
"200\.1\.10\..*",
"host\.domain\.tld",
"host[0-9]+\.domain\.com"
};
allowconnects
Description: List of IP addresses that may connect to the server port.
If a client's identity matches an entry in this list it is granted to permission to send data to the server port. Clients who are not in this list may not connect or send data to the server.
See also the warning about regular expressions in
allowallconnects
.
Type: slist
Allowed input range: (arbitrary string)
Examples:
allowconnects => {
"127.0.0.1",
"::1",
"200\.1\.10\..*",
"host\.domain\.tld",
"host[0-9]+\.domain\.com"
};
allowlegacyconnects
Description: List of hosts from which the server accepts connections that are not using the latest protocol.
Set this attribute to an empty list to not allow any incoming connections using legacy protocol versions:
allowlegacyconnects => { }
To define subnets or address ranges, use CIDR notation:
allowlegacyconnects => { "192.168.1.0/24", "192.168.2.123" }
Absence of this attribute means that connections from all hosts are accepted, 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" , "::1" };
allowallconnects => { "10.10.10" , "::1" };
trustkeysfrom => { "10.10.10" , "::1" };
call_collect_interval => "5";
}
#########################################################
bundle server access_rules()
{
access:
policy_server::
"collect_calls"
resource_type => "query",
admit => { "10.10.10.10" };
satellite_hosts::
"delta"
comment => "Grant access to cfengine hub to collect report deltas",
resource_type => "query",
admit => { "policy_hub" };
"full"
comment => "Grant access to cfengine hub to collect full report dump",
resource_type => "query",
admit => { "policy_hub" };
}
History: Was introduced in Enterprise 3.0.0 (2012)
collect_window
CFEngine Enterprise only.
Description: A time in seconds that a collect-call tunnel remains open to a hub to attempt a report transfer before it is closed
Type: int
Allowed input range: 0,99999999999
collect_window => "15";
Default value: 30
History: Was introduced in Enterprise 3.0.0 (2012). Default value changed from 10 to 30 at 3.7.2.
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
Deprecated: This attribute was deprecated in 3.7.0.
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 we accept public keys on trust
If connecting hosts' public keys have not already been trusted, this allows us to accept the keys on trust. Normally this should be an empty list except in controlled circumstances.
See also the warning about regular expressions in
allowallconnects
.
Type: slist
Allowed input range: (arbitrary string)
Example:
body server control
{
trustkeysfrom => { "10\.0\.1\.1", "192\.168\..*"};
}
listen
Description: true/false enable server daemon to listen on defined port
This attribute allows to disable cf-serverd
from listening on any
port. Should be used in conjunction with call_collect_interval
.
This setting only applies to CFEngine clients, the policy hub will
not be affected. Changing this setting requires a restart of
cf-serverd
for the change to take effect.
Type: boolean
Default value: true
Example:
body server control
{
listening_host_context::
listen => "true";
!listening_host_context::
listen => "false";
}
History: Was introduced in 3.4.0, Enterprise 3.0 (2012)
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.
Note: This daemon reloads it's config when the SIGHUP signal is received.
History:
- SIGHUP behavior added in 3.7.0
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. This option is overridden by FILE if supplied as argument.
--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";
mailfrom
Description: Email-address cfengine mail appears to come from
Type: string
Allowed input range: .*@.*
Example:
body executor control
{
mailfrom => "mrcfengine@example.org";
}
mailmaxlines
Description: Maximum number of lines of output to send by email
This limit prevents anomalously large outputs from clogging up a system
administrator's mailbox. The output is truncated in the email report, but the
complete original transcript is stored in WORKDIR/outputs/*
where it can be
viewed on demand. A reference to the appropriate file is given.
Type: int
Allowed input range: 0,1000
Default value: 30
Example:
body executor control
{
mailmaxlines => "100";
}
mailsubject
Description: The subject in the mail sent by CFEngine.
The subject can contain system variables, like for example IP address or architecture.
Type: string
Allowed input range: .*
Example:
body executor control
{
mailsubject => "CFEngine report ($(sys.fqhost))";
}
mailto
Description: Email-address cfengine mail is sent to
The address to whom email is sent if an smtp host is configured.
Type: string
Allowed input range: .*@.*
Example:
body executor control
{
mailto => "cfengine_alias@example.org";
}
schedule
Description: The class schedule used by cf-execd for activating cf-agent
The list should contain class expressions comprised of classes
which are visible to the cf-execd
daemon. In principle, any
defined class expression will cause the daemon to wake up and
schedule the execution of the cf-agent
. In practice, the classes
listed in the list are usually date- and time-based.
The actual execution of cf-agent
may be delayed by splaytime
,
and may be deferred by promise caching and the value of
ifelapsed
. Note also that the effectiveness of the splayclass
function may be affected by changing the schedule
.
Type: slist
Allowed input range: (arbitrary string)
Default value:
schedule => { "Min00", "Min05", "Min10", "Min15", "Min20", "Min25",
"Min30", "Min35", "Min40", "Min45", "Min50", "Min55" };
Example:
body executor control
{
schedule => { "Min00", "(Evening|Night).Min15_20", "Min30", "(Evening|Night).Min45_50" };
}
smtpserver
Description: Name or IP of a willing smtp server for sending email
This should point to a standard port 25 server without encryption. If you are running secured or encrypted email then you should run a mail relay on localhost and point this to localhost.
Type: string
Allowed input range: .*
Example:
body executor control
{
smtpserver => "smtp.example.org";
}
splaytime
Description: Time in minutes to splay this host based on its name hash
Whenever any class listed in the schedule
attribute is present,
cf-execd
can schedule an execution of cf-agent
. The actual
execution will be delayed an integer number of seconds between
0-splaytime
minutes. The specific amount of delay for "this" host
is based on a hash of the hostname. Thus a collection of hosts will
all execute at different times, and surges in network traffic can
be avoided.
A general rule for scaling of small updates is to set the splay time to
runinterval-1 minutes for up a few thousand hosts. For example, the default
schedule executes once every 5 minutes, so the splay time should be set to no
more than 4 minutes. The splaytime
should be set to a value less than the
cf-execd
scheduling interval, else multiple clients might contend for data.
In other words, splaytime
+ cf-agent
run time should be less than the
scheduling interval.
Type: int
Allowed input range: 0,99999999999
Default value: 0
The CFEngine default policy sets splaytime
to 1.
Example:
body executor control
{
splaytime => "2";
}
See also: The splayclass()
function for a task-specific
means for setting splay times.
cf-promises
cf-promises
is a tool for checking CFEngine policy code. It operates by
first parsing policy code checking for syntax errors. Second, it validates the
integrity of policy consisting of multiple files. Third, it checks for
semantic errors, e.g. specific attribute set rules. Finally, cf-promises
attempts to expose errors by partially evaluating the policy, resolving as
many variable and classes promise statements as possible. At no point does
cf-promises
make any changes to the system.
In 3.6.0 and later, cf-promises
will not evaluate function calls
either. This may affect customers who use execresult
for instance.
Use the new --eval-functions yes
command-line option (default is
no
) to retain the old behavior from 3.5.x and earlier.
cf-agent
calls cf-promises
to validate the policy before running
it. In that case --eval-functions
is not specified, so functions
are not evaluated prematurely (as you would expect).
Command reference
--eval-functions value - Evaluate functions during syntax checking (may catch more run-time errors). Possible values: 'yes', 'no'. Default is 'yes'
--show-classes - Show discovered classes, including those defined in common bundles in policy
--show-vars - Show discovered variables, including those defined without dependency to user-defined classes in policy
--help , -h - Print the help message
--bundlesequence, -b value - Use the specified bundlesequence for verification
--debug , -d - Enable debugging output
--verbose , -v - Output verbose information about the behaviour of the agent
--dry-run , -n - All talk and no action mode - make no changes, only inform of promises not kept
--version , -V - Output the version of the software
--file , -f value - Specify an alternative input file than the default. This option is overridden by FILE if supplied as argument.
--define , -D value - Define a list of comma separated classes to be defined at the start of execution
--negate , -N value - Define a list of comma separated classes to be undefined at the start of execution
--inform , -I - Print basic information about changes made to the system, i.e. promises repaired
--diagnostic , -x - Activate internal diagnostics (developers only)
--reports , -r - Generate reports about configuration and insert into CFDB
--policy-output-format, -p value - Output the parsed policy. Possible values: 'none', 'cf', 'json'. Default is 'none'. (experimental)
--syntax-description, -s value - Output a document describing the available syntax elements of CFEngine. Possible values: 'none', 'json'. Default is 'none'.
--full-check , -c - Ensure full policy integrity checks
--warn , -W value - Pass comma-separated <warnings>|all to enable non-default warnings, or error=<warnings>|all
--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. This option is overridden by FILE if supplied as argument.
--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 input file than the default. This option is overridden by FILE if supplied as argument.
--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
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. This option is overridden by FILE if supplied as argument.
--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 - Activate internal diagnostics (developers only)
--hail , -H value - Hail the following comma-separated lists of hosts, overriding default list
--interactive , -i - Enable interactive mode for key trust
--timeout , -t value - Connection timeout, seconds
--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-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";
}
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: List of classes indicating when pull collection round should be initiated.
Type: slist
Allowed input range: (arbitrary string)
Default value: { "Min00", "Min05", "Min10", "Min15", "Min20", "Min25", "Min30", "Min35", "Min40", "Min45", "Min50", "Min55" }
Example:
body hub control
{
# Collect reports every at the top and half of the hour. Additionally collect
# reports during the evening or night between Minute 45 and 50.
hub_schedule => { "Min00", "Min30", "(Evening|Night).Min45_50" };
}
History:
- 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.
Deprecated attributes
export_zenoss
History:
- deprecated in 3.6.0
- introduced in version 3.1.0, Enterprise 2.0.0 (2010)
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.
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.
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: body agent control ifelapsed value
Example:
#local
body action example
{
ifelapsed => "120"; # 2 hours
expireafter => "240"; # 4 hours
}
# global
body agent control
{
ifelapsed => "180"; # 3 hours
}
See Also: promise locking, ifelapsed in body agent control
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";
}
Note: This attribute can not make the logging for an individual promise less
verbose than specified by an agent option ( -v
, --verbose
, -I
, --inform
,
-d
, --debug
).
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
Deprecated: This menu option policy is deprecated as of 3.6.0. It performs no action and is kept for backward compatibility.
background
Description: A true/false switch for parallelizing the promise repair.
If possible, perform the verification of the current promise in the background (up to max_children
in body agent control).
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:
bundle agent main
{
commands:
"/bin/sleep 10"
action => background;
"/bin/sleep"
args => "20",
action => background;
}
body action background
{
background => "true";
}
See Also: max_children
in body agent control
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.
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
See also: Common Body Attributes
env_addresses
Description: env_addresses
is the IP addresses of the environment's
network interfaces
The IP addresses of the virtual machine can be overridden here at run time.
Type: slist
Allowed input range: (arbitrary string)
Example:
body environment_interface vnet(primary)
{
env_name => "$(this.promiser)";
env_addresses => { "$(primary)" };
host1::
env_network => "default_vnet1";
host2::
env_network => "default_vnet2";
}
env_name
Description: env_name
is the hostname of the virtual environment.
The 'hostname' of a virtual guest may or may not be the same as the identifier used as 'promiser' by the virtualization manager.
Type: string
Allowed input range: (arbitrary string)
Example:
body environment_interface vnet(primary)
{
env_name => "$(this.promiser)";
env_addresses => { "$(primary)" };
host1::
env_network => "default_vnet1";
host2::
env_network => "default_vnet2";
}
env_network
Description: The hostname of the virtual network
Type: string
Allowed input range: (arbitrary string)
Example:
body environment_interface vnet(primary)
{
env_name => "$(this.promiser)";
env_addresses => { "$(primary)" };
host1::
env_network => "default_vnet1";
host2::
env_network => "default_vnet2";
}
environment_resources
Type: body enviornment_resources
See also: Common Body Attributes
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";
}
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.
See also: Common Body Attributes
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.
insert_lines
This promise type 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 in CFEngine, but an error of logic in the policy itself.
To add multiple ordered lines after the marker, a single correlated promise should be used:
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
See also: Common Body Attributes
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: Common Body Attributes, 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
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)
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";
}
See also: Common Body Attributes
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 => ",";
}
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
See also: Common Body Attributes
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
See also: Common Body Attributes
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";
access
Access promises are conditional promises made by resources living on the server.
The promiser is the name of the resource affected and is interpreted to be a path, unless a
different resource_type
is specified. Access is then granted to hosts listed in admit_ips
,
admit_keys
and admit_hostnames
, or denied using the counterparts deny_ips
, deny_keys
and deny_hostnames
.
You layer the access policy by denying all access and then allowing it only to selected clients, then denying to an even more restricted set.
bundle server access_rules()
{
access:
"/source/directory"
comment => "Access to file transfer",
admit_ips => { "192.168.0.1/24" };
}
For file copy requests, the file becomes transferable to the remote client according to the
conditions specified in the access promise. Use ifencrypted
to grant access only if the
transfer is encrypted in the "classic" CFEngine protocol (the TLS protocol is always encrypted).
When access is granted to a directory, the promise is automatically made about all of its contents and sub-directories.
Use the maproot
attribute (like its NFS counterpart) to control
which hosts can see file objects not owned by the server process
owner.
File resources are specified using an absolute filepath, but can set a shortcut
through
which clients can access the resource using a logical name, without having any detailed
knowledge of the filesystem layout on the server. Specifically in access promises about
files, a special variable context connection
is available with variables ip
, key
and hostname
, containing information about the connection through which access is attempted.
"/var/cfengine/cmdb/$(connection.key).json"
shortcut => "me.json",
admit_keys => { "$(connection.key)" };
In this example, requesting the file me.json
will transfer the file stored on the
server under the name /var/cfengine/cmdb/SHA=....json
to the requesting host,
where it will be received as me.json
.
Note that the usage of the $(connection.*)
variables is strictly
limited to literal strings within the promiser and admit/deny lists; they cannot be
passed to functions or stored in other variables.
With CFEngine Enteprise, access promises can be made about additional query data for reporting and orchestration.
# Grant orchestration communication
"did.*"
comment => "Access to class context (enterprise)",
resource_type => "context",
admit_ips => { "127.0.0.1" };
"value of my test_scalar, can expand variables here - $(sys.host)"
comment => "Grant access to the string in quotes, by name test_scalar",
handle => "test_scalar",
resource_type => "literal",
admit_ips => { "127.0.0.1" };
"XYZ"
comment => "Grant access to contents of persistent scalar variable XYZ",
resource_type => "variable",
admit_ips => { "127.0.0.1" };
# Client grants access to CFEngine hub access
"delta"
comment => "Grant access to cfengine hub to collect report deltas",
resource_type => "query",
report_data_select => default_data_select_host,
admit_ips => { "127.0.0.1" };
"full"
comment => "Grant access to cfengine hub to collect full report dump",
resource_type => "query",
report_data_select => default_data_select_host,
admit_ips => { "127.0.0.1" };
policy_server::
"collect_calls"
comment => "Grant access to cfengine client to request the collection of its reports",
resource_type => "query",
admit_ips => { "10.1.2.*" };
}
Using the built-in report_data_select
body default_data_select_host
:
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 => { "127.0.0.1", @(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 contains host names or IP addresses
to grant access to file objects.
Admit promises grant access to file objects on the server. Arguments may be IP addresses or hostnames, provided DNS name resolution is active. In order to reach this stage, a client must first have passed all of the standard connection tests in the control body.
The lists may contain network addresses in CIDR notation to match the IP address or name of the connecting host.
Type: slist
Allowed input range: (arbitrary string)
Example:
access:
"/var/share/scripts"
admit => { "127.0.0.1", "192.168.0.1/24", ".cfengine.com" };
Notes:
admit
will be deprecated in CFEngine 3.7 in favor of admit_ips
,
admit_hostnames
, and admit_keys
.
deny
Description: The deny
slist contains host names or IP addresses
to deny access to file objects.
Denial is for special exceptions. A better strategy is always to grant on a need to know basis. A security policy based on exceptions is a weak one.
Type: slist
Allowed input range: (arbitrary string)
Example:
bundle server access_rules()
{
access:
"/path"
admit => { ".example.org" },
deny => { "badhost_1.example.org", "badhost_1.example.org" };
}
Notes: Only regular expressions or exact matches are allowed in this list, as non-specific matches are too greedy for denial.
deny
will be deprecated in CFEngine 3.7 in favor of deny_ips
,
deny_hostnames
, and deny_keys
.
maproot
Description: The maproot
slist contains host names or IP addresses
to grant full read-privilege on the server.
Normally users authenticated by the server are granted access only to
files owned by them and no-one else. Even if the cf-serverd
process
runs with root privileges on the server side of a client-server
connection, the client is not automatically granted access to download
files owned by non-privileged users. If maproot
is true then remote
root
users are granted access to all files.
A typical case where mapping is important is in making backups of many user files.
Type: slist
Allowed input range: (arbitrary string)
Example:
access:
"/home"
admit_hostnames => { "backup_host.example.org" },
ifencrypted => "true",
# Backup needs to have access to all users
maproot => { "backup_host.example.org" };
Notes:
On Windows, cf-serverd
, maproot
is required to read files if the
connecting user does not own the file on the server.
ifencrypted
Description: The ifencrypted
menu option determines whether the
current file access promise is conditional on the connection from the
client being encrypted.
This option has no effect with the TLS CFEngine protocol, where encryption is always enabled.
If this flag is true a client cannot access the file object unless its connection is encrypted.
Type: boolean
Default value: false
Example:
access:
"/path/file"
admit_hostnames => { ".example.org" },
ifencrypted => "true";
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 => { "127.0.0.1", @(def.policy_servers) };
}
report_data_select => default_data_select_policy_hub,
admit => { "127.0.0.1", @(def.policy_servers) };
}
See also: Common Body Attributes
History: Introduced in Enterprise 3.5.0
metatags_exclude
Description: List of anchored regular expressions matching metatags of classes or vars 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 of classes or vars to include in reporting.
Classes and variables with metatags matching any entry of that list will be reported to the CFENgine Enterprise server.
When combined with metatags_exclude
, the exclude list is applied to the subset
from this list.
Type: slist
Allowed input range: .*
See also: metatags_exclude
, promise_handle_include
, monitoring_include
History: Introduced in CFEngine 3.6.0
promise_handle_exclude
Description: List of anchored regular expressions matching promise handles to exclude from reporting.
Information about promises with handles that match any entry in that list will not be reported to the CFEngine Enterprise server.
When combined with promise_handle_include
, this list is applied to the
selected subset.
Type: slist
Allowed input range: .*
See also: promise_handle_include
, metatags_exclude
, monitoring_exclude
History: Introduced in CFEngine 3.6.0
promise_handle_include
Description: List of anchored regular expressions matching promise handles to include in reporting.
Information about promises with handles that match any entry in that list will be reported to the CFEngine Enterprise server.
When combined with promise_handle_exclude
, the exclude list is applied to the
subset from this list.
Type: slist
Allowed input range: .*
See also: promise_handle_exclude
, metatags_include
, monitoring_include
History: Introduced in CFEngine 3.6.0
monitoring_include
Description: List of anchored regular expressions matching monitoring objects to include in reporting.
Monitoring objects with names matching any entry in that list will be reported to the CFEngine Enterprise server.
When combined with monitoring_exclude
, the exclude list is applied to the
subset from this list.
Type: slist
Allowed input range: .*
See also: monitoring_exclude
, promise_handle_include
, metatags_include
History: Introduced in Enterprise 3.5.0
monitoring_exclude
Description: List of anchored regular expressions matching monitoring objects to exclude from reporting.
Monitoring objects with names matching any entry in that list will not be reported to the CFEngine Enterprise server.
When combined with monitoring_include
, this list is applied to the selected
subset.
Type: slist
Allowed input range: .*
See also: monitoring_include
, promise_handle_exclude
, metatags_exclude
History: Introduced in Enterprise 3.5.0
classes_include
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.
See also: metatags_include
, metatags_exclude
classes_exclude
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.
See also: metatags_include
, metatags_exclude
variables_include
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.
See also: metatags_include
, metatags_exclude
variables_exclude
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by meta-tags instead.
See also: metatags_include
, metatags_exclude
promise_notkept_log_include
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.
See also: promise_handle_exclude
promise_notkept_log_exclude
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.
See also: promise_handle_exclude
promise_repaired_log_include
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.
See also: promise_handle_exclude
promise_repaired_log_exclude
Deprecated: This attribute is deprecated as of CFEngine 3.6.0. It performs no action and is kept for backwards compatibility. Filter data by handle instead.
See also: promise_handle_exclude
resource_type
Description: The resource_type
is the type of object being granted
access.
By default, access to resources granted by the server are files.
However, sometimes it is useful to cache literal
strings, hints and
data on the server for easy access (e.g. the contents of variables or
hashed passwords). In the case of literal data, the promise handle
serves as the reference identifier for queries. Queries are instigated
by function calls by any agent.
Type: (menu option)
Allowed input range:
path
literal
context
query
variable
If the resource type is literal
, CFEngine will grant access to a
literal data string. This string is defined either by the promiser
itself, but the name of the variable is the identifier given by the
promise handle of the access promise, since the promiser string might be
complex.
If the resource type is variable
then the promiser is the name of a
persistent scalar variable defined on the server-host. Currently
persistent scalars are only used internally by Enterprise CFEngine to
hold enumerated classes for orchestration purposes.
If you want to send the value of a policy defined variable in the server host (which for some reason is not available directly through policy on the client, e.g. because they have different policies), then you could use the following construction:
access:
"$(variable_name)"
handle => "variable_name",
resource_type => "literal";
If the resource type is context
, the promiser is treated as a regular
expression to match persistent classes defined on the server host. If
these are matched by the request from the client, they will be
transmitted (See remoteclassesmatching()
).
The term query
may also be used in CFEngine Enterprise to query the server
for data from embedded databases. This is currently for internal use only, and
is used to grant access to report 'menus'. If the promiser of a query request
is called collect_calls
, this grants access to server peering collect-call
tunneling (see also call_collect_interval
).
Example:
bundle server access_rules()
{
access:
"value of my test_scalar, can expand variables here - $(sys.host)"
handle => "test_scalar",
comment => "Grant access to contents of test_scalar VAR",
resource_type => "literal",
admit_ips => { "127.0.0.1" };
"XYZ"
resource_type => "variable",
handle => "XYZ",
admit_ips => { "127.0.0.1" };
# On the policy hub
"collect_calls"
resource_type => "query",
admit_ips => { "127.0.0.1" };
# On the isolated client in the field
"delta"
comment => "Grant access to cfengine hub to collect report deltas",
resource_type => "query",
admit_ips => { "127.0.0.1" };
"full"
comment => "Grant access to cfengine hub to collect full report dump",
resource_type => "query",
admit_ips => { "127.0.0.1" };
}
shortcut
Description: For file promisers, the server will give access to the file under its shortcut name.
Type: string
Allowed input range: (arbitrary string)
Example:
"/var/cfengine/cmdb/$(connection.key).json"
shortcut => "me.json",
admit_keys => { "$(connection.key)" };
In this example, requesting the file me.json
will transfer the file stored on the
server under the name /var/cfengine/cmdb/SHA=....json
to the requesting host,
where it will be received as me.json
.
History: Introduced in CFEngine 3.6.0
processes
Process promises refer to items in the system process table, i.e., a command in some state of execution (with a Process Control Block). Promiser objects are patterns that are unanchored, meaning that they match line fragments in the system process table.
processes:
"regex contained in process line"
process_select = process_filter_body,
restart_class = "activation class for process",
..;
Note: Process table formats differ between 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
).
For example, this is a sample of $(sys.workdir)/state/cf_rootprocs
on a linux system:
USER PID PPID PGID %CPU %MEM VSZ NI RSS NLWP STIME ELAPSED TIME COMMAND
root 1 0 1 0.0 0.2 19232 0 1096 1 Sep14 1-21:41:52 00:00:00 /sbin/init
root 2 0 0 0.0 0.0 0 0 0 1 Sep14 1-21:41:52 00:00:00 [kthreadd]
root 3 2 0 0.0 0.0 0 - 0 1 Sep14 1-21:41:52 00:00:00 [migration/0]
This is an example showing how to restart splunk when a splunkd process owned by root is using 80% or more of the CPU.
bundle agent example
{
processes:
# Reference process table in $(sys.workdir)/state/cf_procs
# Find lines in the process table starting with root (USER column)
# followed by one or more spaces, followed by a digit (PID column),
# followed by one or more spaces, followed by a digit (PGID column),
# followed by one or more spaces, followed by 8 or 9 followed by a number
# in the range 0-9 (to match numbers greater than 80), followed by a dot,
# followed by anything, and containing splunkd (expected to match the
# COMMAND column).
"^root\s+\d+\s+\d+\s+\d+\s+[89][0-9]\..*splunkd"
handle => "example_splunk_high_cpu_stop_gracefully",
process_stop => "/opt/splunkforwarder/bin/splunk stop",
comment => "Find splunkd processes owned by root that are consuming more
than 80% of a CPU and restart it with it's preferred
utility. Stop it gracefully with the internal splunk binary.";
"^root\s.*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";
}
Getting complex regular expressions just right can be difficult, so for most
sophisticated matches, users should use a simple pattern match such as program
names combinded with a process_select
body before delving into complex regular
expressions.
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).
To restart a process, you should set a class to activate and then describe a
command
in that class.
commands:
restart_me::
"/path/executable" ... ;
This rationalizes complex restart-commands and avoids unnecessary overlap
between processes
and commands
.
The process_stop
is also arguably a command, but it should be an
ephemeral command that does not lead to a persistent process. It is
intended only for commands of the form /etc/inetd service stop, not for
processes that persist. Processes are restarted at the end of a bundle's
execution, but stop commands are executed immediately.
Commands and Processes
CFEngine distinguishes between processes
and commands
so that
there is a clean separation between detection (promises about the process
table) and certain repairs (promises to execute commands that start
processes).
Command executions are about jobs, services, scripts etc. They are properties of an executable file, and the referring 'promiser' is a file object. On the other hand a process is a property of a "process identifier" which is a kernel instantiation, a quite different object altogether. For example:
- A "PID" (which is not an executable) promises to be reminded of a signal, e.g.
kill signal pid
- An "command" promises to start or stop itself with a parameterized specification.
exec command argument1 argument2 ...
Neither the file nor the pid necessarily promise to respond to these activations, but they are nonetheless physically meaningful phenomena or attributes associated with these objects.
- Executable files do not listen for signals as they have no active state.
- PIDs do not run themselves or stop themselves with new arguments, but they can use signals as they are running.
Executions lead to processes for the duration of their lifetime, so these two issues are related, although the promises themselves are not.
Platform notes
Process promises depend on the ps
native tool, which by default truncates
lines at 128 columns on HP-UX. It is recommended to edit the file
/etc/default/ps
and increase the DEFAULT_CMD_LINE_WIDTH
setting to 1024 to
guarantee that process promises will work smoothly on that platform.
Attributes
Common Attributes
Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:
action
classes
comment
depends_on
handle
ifvarclass
meta
process_count
Type: body process_count
See also: Common Body Attributes
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
See also: Common Body Attributes
command
Description: Regular expression matching the command/cmd field of a process
This expression should match the entire COMMAND
field of the process
table, not just a fragment. This field is usually the last field on the
line, so it thus starts with the first non-space character and ends with
the end of line.
Type: string
Allowed input range: (arbitrary string)
Example:
body process_select example
{
command => "cf-.*";
process_result => "command";
}
pid
Description: Range of integers matching the process id of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
pid => irange("1","10");
process_result => "pid";
}
pgid
Description: Range of integers matching the parent group id of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
pgid => irange("1","10");
process_result => "pgid";
}
ppid
Description: Range of integers matching the parent process id of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
ppid => irange("407","511");
process_result => "ppid";
}
priority
Description: Range of integers matching the priority field (PRI/NI) of a process
Type: irange[int,int]
Allowed input range: -20,+20
Example:
body process_select example
{
priority => irange("-5","0");
}
process_owner
Description: List of regexes matching the user of a process
The regular expressions should match a legal user name on the system. The regex is anchored, meaning it must match the entire name.
Type: slist
Allowed input range: (arbitrary string)
Example:
body process_select example
{
process_owner => { "wwwrun", "nobody" };
}
process_result
Description: Boolean class expression with the logical combination of process selection criteria
A logical combination of the process selection classifiers. The syntax
is the same as that for class expressions. If process_result
is not
specified, then all set attributes in the process_select
body are AND'ed
together.
Type: string
Allowed input range:
[(process_owner|pid|ppid||pgid|rsize|vsize|status|command|ttime|stime|tty|priority|threads)[|!.]*]*
Example:
body process_select proc_finder(p)
{
process_owner => { "avahi", "bin" };
command => "$(p)";
pid => irange("100","199");
vsize => irange("0","1000");
process_result => "command.(process_owner|vsize).!pid";
}
See also: file_result
rsize
Description: Range of integers matching the resident memory size of a process, in kilobytes
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select
{
rsize => irange("4000","8000");
}
status
Description: Regular expression matching the status field of a process
For instance, characters in the set NRSsl+..
. Windows processes do not
have status fields.
Type: string
Allowed input range: (arbitrary string)
Example:
body process_select example
{
status => "Z";
}
stime_range
Description: Range of integers matching the start time of a process
The calculation of time from process table entries is sensitive to Daylight Savings Time (Summer/Winter Time) so calculations could be an hour off. This is for now a bug to be fixed.
Type: irange[int,int]
Allowed input range: 0,2147483647
Example:
body process_select example
{
stime_range => irange(ago(0,0,0,1,0,0),now);
}
ttime_range
Description: Range of integers matching the total elapsed time of a process.
This is total accumulated time for a process.
Type: irange[int,int]
Allowed input range: 0,2147483647
Example:
body process_select example
{
ttime_range => irange(0,accumulated(0,1,0,0,0,0));
}
tty
Description: Regular expression matching the tty field of a process
Windows processes are not regarded as attached to any terminal, so they all have tty '?'.
Type: string
Allowed input range: (arbitrary string)
Example:
body process_select example
{
tty => "pts/[0-9]+";
}
threads
Description: Range of integers matching the threads (NLWP) field of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
threads => irange(1,5);
}
vsize
Description: Range of integers matching the virtual memory size of a process, in kilobytes.
On Windows, the virtual memory size is the amount of memory that cannot be shared with other processes. In Task Manager, this is called Commit Size (Windows 2008), or VM Size (Windows XP).
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
vsize => irange("4000","9000");
}
process_stop
Description: A command used to stop a running process
As an alternative to sending a termination or kill signal to a process, one may call a 'stop script' to perform a graceful shutdown.
Type: string
Allowed input range: "?(/.*)
Example:
processes:
"snmpd"
process_stop => "/etc/init.d/snmp stop";
restart_class
Description: A class to be defined globally if the process is not
running, so that a command:
rule can be referred to restart the process
This is a signal to restart a process that should be running, if it is not running. Processes are signaled first and then restarted later, at the end of bundle execution, after all possible corrective actions have been made that could influence their execution.
Windows does not support having processes start themselves in the
background, like Unix daemons usually do; as fork off a child process.
Therefore, it may be useful to specify an action
body that sets
background
to true in a commands promise that is invoked by the class
set by restart_class
. See the commands
promise type for more
information.
Type: string
Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+
Example:
processes:
"cf-serverd"
restart_class => "start_cfserverd";
commands:
start_cfserverd::
"/var/cfengine/bin/cf-serverd";
signals
Description: A list of menu options representing signals to be sent to a process.
Signals are presented as an ordered list to the process. On Windows, only the kill signal is supported, which terminates the process.
Type: (option list)
Allowed input range:
hup
int
trap
kill
pipe
cont
abrt
stop
quit
term
child
usr1
usr2
bus
segv
Example:
processes:
cfservd_out_of_control::
"cfservd"
signals => { "stop" , "term" },
restart_class => "start_cfserv";
any::
"snmpd"
signals => { "term" , "kill" };
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" };
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
.
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)
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
See also: Common Body Attributes
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";
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
See also: Common Body Attributes
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";
}
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)
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";
(Promises within the same bundle are evaluated top to bottom, so vars promises further down in a bundle can overwrite previous values of a variable. See normal ordering for more information).
services
Services are registered in the operating system in some way, and get a unique name.
Service promises abstracts the mechanism for interacting with services
on the given operating system, making it as uniform and easy as possible
to work with services cross-platform. The exact mechanism CFEngine uses
vary depending on availability at the OS, but it could be System V scripts,
systemd units, tools such as chkconfig
, or the Windows API.
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).
CFEngine supports 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";
}
Notes:
Services promises for Windows are only available in CFEngine Enterprise. Note
that the name of a service in Windows may be different from its Display
name
. CFEngine Enterprise policies use the name, not the display name,
due to the need for uniqueness.
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
service_method
bodies have access to $(this.promiser)
(the promised service)
and $(this.service_policy)
(the policy state the service should have).
Notes: service_bundle
is not used when service_type
is windows
.
See also: Common Body Attributes
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.
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
See also: Common Body Attributes
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
See also: Common Body Attributes
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";
}
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
See also: Common Body Attributes
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.
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 for CFEngine versions before 3.7.5 and 3.10.0 (see this
ticket) 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";
}
See also: Common Body Attributes
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
A valid username identifier for the system and cannot be empty. However,
user
can be set to*
as a synonym for the entity that owns the file system object (e.g.user:*:r
).Notes:
- The user id is not a valid alternative.
- This ACL is required when
acl_method
is set tooverwrite
.
-
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
).Note: The username is not a valid alternative.
group
A valid group identifier for the system and cannot be empty. However,
group
can be set to*
as a synonym for the group that owns the POSIX file system object (group:*:rwx
).Notes:
- The group id is not a valid alternative.
- This ACL is required when
acl_method
is set tooverwrite
.
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).Note: The group name is not a valid alternative.
all
Indicates that the line applies to every user.
Note: This ACL is required when
acl_method
is set tooverwrite
.mask
A valid mask identifier (e.g.
mask:rwx
). In essence the mask is an upper bound of the permissions that any entry in the group class will grant. Whenacl_method
isoverwrite
if mask is not supplied, it will default tomask:rwx
).-
One or more strings
op
|perms
|(nperms
); a concatenation ofop
,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 inacl_type
. For example,nperms
will be ignored ifacl_type:
ntfs
and the object is stored on a file system not supporting NTFS ACLs. Valid values fornperms
varies with different ACL types, and is defined in subsequent sections.perm_type
(optional)Can be set to either
allow
ordeny
, and defaults toallow
.deny
is only valid ifacl_type
is set to an ACL type that support deny permissions. Adeny
ACE will only be enforced if the file object is stored on a file system supporting the acl type set inacl_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 Notes
- The
r
permission is not necessary to read an object's permissions and attributes in all file systems. For example, in POSIX, havingx
on its containing directory is sufficient. - Capital
X
which is supported by thesetfacl
command is not supported by the acl library, and thus not supported by the acl body.
- The
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.
Note: When acl_method
is set to overwrite
the acl must include the system
owner, group and all. For example user:*:rwx
, group:*:rx
, and all:---
.
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
See also: Common Body Attributes
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.
See also: Common Body Attributes
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 default_repository in body agent control, 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";
}
See also: Common Body Attributes, default_repository
in body agent control
, edit_backup
in body edit_defaults
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 this attribute only works when combined with depth_search
and 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
See also: Common Body Attributes
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. For an example
see bundle agent rm_rf_depth
in the standard library.
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
See also: Common Body Attributes
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
See also: Common Body Attributes
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:
A value of true
(the default behavior) will result in the agent retaining the
previous version of the file suffixed with .cf-before-edit
.
body edit_defaults backup( edit_backup )
{
edit_backup => "$(edit_backup)";
}
bundle agent main
{
files:
"/tmp/example_edit_backup_true"
create => "true";
"/tmp/example_edit_backup_true"
edit_line => insert_lines("Hello World"),
edit_defaults => backup("true");
vars:
"example_files" slist => sort(lsdir( "/tmp/", "example_edit_backup_true.*", false), lex);
reports:
"$(example_files)";
}
Outputs:
R: example_edit_backup_true
R: example_edit_backup_true.cf-before-edit
A value of timestamp
will result in the original file be suffixed with the
epoch and the canonified form of the date when the file was changed followed by
.cf-before-edit
. For example
_1511292441_Tue_Nov_21_13_27_22_2017.cf-before-edit
.
body edit_defaults backup( edit_backup )
{
edit_backup => "$(edit_backup)";
}
bundle agent main
{
files:
"/tmp/example_edit_backup_timestamp"
create => "true";
"/tmp/example_edit_backup_timestamp"
edit_line => insert_lines("Hello World"),
edit_defaults => backup("timestamp");
vars:
"example_files" slist => lsdir( "/tmp/", "example_edit_backup_timestamp.*", false);
reports:
"$(example_files)";
}
Outputs:
R: example_edit_backup_timestamp
R: example_edit_backup_timestamp_1511300904_Tue_Nov_21_15_48_25_2017.cf-before-edit
A value of false
will result in no retention of the original file.
A value of rotate
will result in the original file be suffixed with
.cf-before-edit
followed by an integer representing the nth previous version
of the file. The number of rotations is managed by the rotate
attribute in
edit_defaults
.
body edit_defaults backup( edit_backup )
{
edit_backup => "$(edit_backup)";
rotate => "2";
}
bundle agent main
{
files:
"/tmp/example_edit_backup_rotate"
create => "true";
"/tmp/example_edit_backup_rotate"
edit_line => insert_lines("Hello World"),
edit_defaults => backup("rotate");
"/tmp/example_edit_backup_rotate"
handle => "edit_2",
edit_line => insert_lines("Goodbye"),
edit_defaults => backup("rotate");
vars:
"example_files" slist => lsdir( "/tmp/", "example_edit_backup_rotate.*", false);
reports:
"$(example_files)";
}
Outputs:
R: example_edit_backup_rotate
R: example_edit_backup_rotate.cf-before-edit.1
R: example_edit_backup_rotate.cf-before-edit.2
See also: default_repository
in body agent control
, copy_backup
in body copy_from
, rotate
in body edit_defaults
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";
}
See also: edit_backup
in body edit_defaults
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.
The 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
See also: Common Body Attributes
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
link_from
Type: body link_from
See also: Common Body Attributes
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:
body link_from example
{
link_children => "true";
}
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:
body link_from example
{
link_type => "symlink";
source => "/tmp/source";
}
Notes: On Windows, hard links are the only supported type.
source
Description: The source file to which the link should point
For remote copies this refers to the file name on the remote server.
Type: string
Allowed input range: .+
Example:
body link_from example
{
source => "/path/to/source";
}
when_linking_children
Description: Policy for overriding existing files when linking directories of children
The options refer to what happens if the directory already exists, and is already partially populated with files. If the directory being copied from contains a file with the same name as that of a link to be created, it must be decided whether to override the existing destination object with a link, or simply omit the automatic linkage for files that already exist. The latter case can be used to make a copy of one directory with certain fields overridden.
Type: (menu option)
Allowed input range:
override_file
if_no_such_file
Example:
body link_from example
{
when_linking_children => "if_no_such_file";
}
when_no_source
Description: Behavior when the source file to link to does not exist
This describes how CFEngine should respond to an attempt to create a link to a file that does not exist. The options are to force the creation to a file that does not (yet) exist, delete any existing link, or do nothing.
Type: (menu option)
Allowed input range:
force
delete
nop
Default value: nop
Example:
body link_from example
{
when_no_source => "force";
}
move_obstructions
Description: true/false whether to move obstructions to file-object creation
If we have promised to make file X a link, but it already exists as a file, or vice-versa, or if a file is blocking the creation of a directory, then normally CFEngine will report an error. If this is set, existing objects will be moved aside to allow the system to heal without intervention. Files and directories are saved/renamed, but symbolic links are deleted.
Note that symbolic links for directories are treated as directories, not links. This behavior can be discussed, but the aim is to err on the side of caution.
Type: boolean
Default value: false
Example:
files:
"/tmp/testcopy"
copy_from => mycopy("/tmp/source"),
move_obstructions => "true",
depth_search => recurse("inf");
Notes: Some operating systems (Solaris) use symbolic links in path names. Copying to a directory could then result in renaming of the important link, if the behavior is different.
pathtype
Description: Menu option for interpreting promiser file object
By default, CFEngine makes an educated guess as to whether the promise pathname involves a regular expression or not. This guesswork is needed due to cross-platform differences in filename interpretation.
If CFEngine guesses (or is told) that the pathname uses a regular expression pattern, it will undertake a file search to find possible matches. This can consume significant resources, and so the guess option will always try to optimize this. Guesswork is, however, imperfect, so you have the option to declare your intention.
Type: (menu option)
Allowed input range:
literal
regex
guess
If the keyword literal
is invoked, a path will be treated as a literal
string regardless of what characters it contains. If it is declared
regex
, it will be treated as a pattern to match.
Note that CFEngine splits the promiser up into path links before
matching, so that each link in the path chain is matched separately.
Thus it it meaningless to have a /
in a regular expression, as the
comparison will never see this character.
Default value: guess
Example:
files:
"/var/lib\d"
pathtype => "guess", # best guess (default)
perms => system;
"/var/lib\d"
pathtype => "regex", # force regex interpretation
perms => system;
"/var/.*/lib"
pathtype => "literal", # force literal interpretation
perms => system;
In these examples, at least one case implies an iteration over all files/directories matching the regular expression, while the last case means a single literal object with a name composed of dots and stars.
Notes:
On Windows paths using regex
must use the forward slash (/
) as path
separator, since the backward slash has a special meaning in a regular
expression. Literal paths may also use backslash (\
) as a path
separator.
perms
Type: body perms
See also: Common Body Attributes
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
See also: Common Body Attributes
disable
Description: true/false automatically rename and remove permissions
Disabling a file means making it unusable. For executables this means preventing execution, for an information file it means making the file unreadable.
Type: boolean
Default value: false
Example:
body rename example
{
disable => "true";
disable_suffix => ".nuked";
}
disable_mode
Description: The permissions to set when a file is disabled
To disable an executable it is not enough to rename it, you should also remove the executable flag.
Type: string
Allowed input range: [0-7augorwxst,+-]+
Example:
body rename example
{
disable_mode => "0600";
}
disable_suffix
Description: The suffix to add to files when disabling
To disable files in a particular manner, use this string suffix.
Type: string
Allowed input range: (arbitrary string)
Default value: .cfdisabled
Example:
body rename example
{
disable => "true";
disable_suffix => ".nuked";
}
newname
Description: The desired name for the current file
Type: string
Allowed input range: (arbitrary string)
Example:
body rename example(s)
{
newname => "$(s)";
}
rotate
Description: Maximum number of file rotations to keep
Used for log rotation. If the file is named foo
and the rotate attribute
is set to 4, as above, then initially foo
is copied to foo.1
and the old
file foo
is zeroed out (that is, the inode of the original logfile does
not change, but the original log file will be empty after the rotation
is complete).
The next time the promise is executed, foo.1
will be renamed foo.2
, foo
is again copied to foo.1
and the old file foo
is again zeroed out.
Each time the promise is executed (and typically, the promise would be executed as guarded by time-based or file-size-based classes), the files are copied/zeroed or rotated as above until there are rotate numbered files plus the one "main" file.
Type: int
Allowed input range: 0,99
Example:
body rename example
{
rotate => "4";
}
In the example above, the file foo.3
will be renamed foo.4
, but the old
version of the file foo.4
will be deleted (that is, it "falls off the end"
of the rotation).
repository
Description: Name of a repository for versioning
A local repository for this object, overrides the default.
Note that when a repository is specified, the files are stored using the
canonified directory name of the original file, concatenated with the
name of the file. So, for example, /usr/local/etc/postfix.conf
would
ordinarily be stored in an alternative repository as
_usr_local_etc_postfix.conf.cfsaved
.
Type: string
Allowed input range: "?(/.*)
Example:
files:
"/path/file"
copy_from => source,
repository => "/var/cfengine/repository";
template_data
Description: The data to be passed to the template (Mustache only)
Type: data
Allowed input range: (arbitrary string)
Example:
files:
"/path/file"
...
edit_template => "mytemplate.mustache",
template_data => parsejson('{"message":"hello"}'),
template_method => "mustache";
If this is omitted, the result of the datastate()
function call is
used instead. See edit_template
for how you can use the data state
in Mustache.
See also: edit_template
, template_method
, datastate()
template_method
Description: The template type.
By default cfengine
requests the native CFEngine template
implementation, but you can use mustache
as well.
Type: (menu option)
Allowed input range:
cfengine
mustache
Default value: cfengine
files:
"/path/file"
...
edit_template => "mytemplate.mustache",
template_data => parsejson('{"message":"hello"}'),
template_method => "mustache";
See also: edit_template
, template_data
, datastate()
touch
Description: true/false whether to touch time stamps on file
Type: boolean
Example:
files:
"/path/file"
touch => "true";
transformer
Description: Command (with full path) used to transform current file (no shell wrapper used)
A command to execute, usually for the promised file to transform it to something else (but possibly to create the promised file based on a different origin file).
The promiser file must exist in order to effect the transformer.
Note also that if you use the $(this.promiser)
variable or other
variable in this command, and the file object contains spaces, then you
should quote the variable. For example:
transformer => "/usr/bin/gzip \"$(this.promiser)\"",
Note also that the transformer does not actually need to change the file. You can, for example, simply report on the existence of files with:
transformer => "/bin/echo I found a file named $(this.promiser)",
The file streams stdout
and stderr
are redirected by CFEngine, and
will not appear in any output unless you run cf-agent
with the -v
switch.
It is possible to set classes based on the return code of a
transformer-command in a very flexible way. See the kept_returncodes
,
repaired_returncodes
and failed_returncodes
attributes.
Finally, you should note that the command is not run in a shell. This means that you cannot perform file redirection or create pipelines.
Type: string
Allowed input range: "?(/.*)
Example:
These examples show both types of promises.
files:
"/home/mark/tmp/testcopy"
file_select => pdf_files,
transformer => "/usr/bin/gzip $(this.promiser)",
depth_search => recurse("inf");
In the first example, the promise is made on the file that we wish to transform. If the promised file exists, the transformer will change the file to a compressed version (and the next time CFEngine runs, the promised file will no longer exist, because it now has the .gz extension).
classes:
"do_update" expression => isnewerthan("/etc/postfix/alias",
"/etc/postfix/alias.cdb");
files:
"/etc/postfix/alias.cdb"
create => "true", # Must have this!
transformer => "/usr/sbin/postalias /etc/postfix/alias",
ifvarclass => "do_update";
In the second example, the promise is made on the file resulting from the transformation (and the promise is conditional on the original file being newer than the result file). In this case, we must specify create = true. If we do not, then if the promised file is removed the transformer will not be executed.
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 environmnet variables set in
environmnet
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
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";
}
See also: Common Body Attributes
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 for any following definitions^meta=a,b,c
sets the class and variable tags for any following definitions toa
,b
, andc
- lines which begin with a
+
are treated as classes to be defined (like -D). NOTE: classes are defined with thenamespace
scope. - 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.
NOTE: All variables and classes defined by the module protocol are defined
in the default
namespace. It is not possible to define variables and
classes in any other namespace. Protocol extensions ( lines that start with ^
) apply until they are explicitly reset, or until the end of the modules
execution.
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"
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.
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.
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
.
See also: Common Body Attributes
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
Package modules out-of-the-box
yum
Manage packages using yum
.
Example:
Example showing file based package source.
packages:
"/mnt/nfs/packages/httpd-2.2.22.x86_64.rpm"
policy => "present",
package_module => yum;
Example showing repository based package source.
packages:
"httpd"
policy => "present",
package_module => yum,
version => "2.2.22";
Example showing how to enable a specific repository for a specific promise.
bundle agent example
{
packages:
"git"
policy => "present",
package_module => yum,
options => { "enablerepo=updates" };
}
Notes:
- Supports file path and repository sourced packages.
- Requires Python version 2 to be installed on the host.
History:
- Added in CFEngine 3.7.0
enablerepo
anddisablerepo
option support added in 3.7.8, 3.10.4, 3.12.0
apt_get
Manage packages using apt-get
.
Example:
Example showing file based package source.
packages:
"/mnt/nfs/packages/apache2-2.2.22.x86_64.deb"
policy => "present",
package_module => apt_get;
Example showing repository based package source.
packages:
"apache2"
policy => "present",
package_module => apt_get,
version => "2.2.22",
options => { "-o", "APT::Install-Recommends=0" };
Notes:
- Requires Python version 2 to be installed on the host.
- Supports
options
attribute. Each space separate option must be added as a separate list element. The options are passed directly through to the package manager.
History:
- Added in CFEngine 3.7.0
freebsd_ports
Manage packages using FreeBSD Ports.
History:
- Added in CFEngine 3.9.0
nimclient
Manage packages using nimclient
on AIX.
Example:
packages:
aix::
"expect.base"
policy => "present",
package_module => nimclient,
options => { "lpp_source=lppaix710304" };
Notes:
options
attribute support to specifylpp_source
. Please note it is REQUIRED to specify anlpp_source
when using this package module.
History:
- Added in CFEngine 3.9.0
pkg
Manage packages using FreeBSD pkg.
Example:
packages:
freebsd::
"emacs-nox11"
policy => "present",
package_module => pkg;
"emacs"
policy => "absent",
package_module => pkg;
History:
- Added in CFEngine 3.9.0
pkgsrc
Manage packages using pkgsrc.
History:
- Added in CFEngine 3.9.0
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
See also: Common Body Attributes
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).
If a column value has a comma you can escape the comma with backslash \,
.
bundle agent main
# @brief Configure system variables for hosts that should not use a proxy
{
databases:
windows::
"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
database_operation => "create",
database_rows =>
{
"NO_PROXY,REG_SZ,localhost\,127.0.0.1\,localaddress\,.localdomain\,169.254.169.254\,.cfengine.com"
},
database_type => "ms_registry";
}
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";
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). User presence is determined by
the NetUserGetInfo
function on Windows and reading /etc/passwd
on Unix,
Linux and similar platforms nix External/non-local for example LDAP are ignored.
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"
}
See also: Common Body Attributes
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";
classes
Classes promises may be made in any
bundle. Classes that are set in common
bundles are global in scope,
while classes in all other bundles are local.
Note: The term class and context are sometimes used interchangeably.
bundle common g
{
classes:
"one" expression => "any";
"client_network" expression => iprange("128.39.89.0/24");
}
Note: You must use at least one of the following attributes to make a complete promise.
- and
- expression
- dist
- or
- not
- xor
For example, the following promise is incomplete:
bundle agent example
{
classes:
"web"
if => fileexists("/etc/httpd/httpd.conf");
}
The following are complete promsies:
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.
Type: class
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
classes:
"class_name" expression => "solaris|(linux.specialclass)";
"has_toor" expression => userexists("toor");
or
Description: Combine class sources with inclusive OR
The class on the left-hand side will be set if any one (or more) of the class expressions on the right-hand side are true.
Type: clist
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
classes:
"compound_test"
or => { classmatch("linux_x86_64_2_6_22.*"), "suse_10_3" };
Notes:
This is useful construction for writing expressions that contain functions.
persistence
Description: Make the class persistent to avoid re-evaluation
The value specifies time in minutes.
Type: int
Allowed input range: 0,99999999999
Example:
bundle common setclasses
{
classes:
"cached_classes"
or => { "any" },
persistence => "1";
"cached_class"
expression => "any",
persistence => "1";
}
Notes:
This feature can be used to avoid recomputing expensive classes calculations on each invocation. This is useful if a class discovered is essentially constant or only slowly varying, such as a hostname or alias from a non-standard naming facility.
For example, to create a conditional inclusion of costly class evaluations,
put them into a separate bundle in a file classes.cf.
# promises.cf
body common control
{
persistent_classes::
bundlesequence => { "test" };
!persistent_classes::
bundlesequence => { "setclasses", "test" };
!persistent_classes::
inputs => { "classes.cf" };
}
bundle agent test
{
reports:
!my_persistent_class::
"no persistent class";
my_persistent_class::
"persistent class defined";
}
Then create classes.cf
# classes.cf
bundle common setclasses
{
classes:
"persistent_classes" # timer flag
expression => "any",
persistence => "480";
"my_persistent_class"
or => { ...long list or heavy function... } ,
persistence => "480";
}
History: Was introduced in CFEngine 3.3.0
not
Description: Evaluate the negation of string expression in normal form
The class on the left-hand side will be set if the class expression on the right-hand side evaluates to false.
Type: class
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
classes:
"others" not => "linux|solaris";
"no_toor" not => userexists("toor");
Notes:
Knowing that something is not the case is not the same as not knowing whether something is the case. That a class is not set could mean either. See the note on Negative Knowledge.
scope
Description: Scope of the class set by this promise.
Type: (menu option)
Allowed input range:
namespace
bundle
Default value: bundle
in agent bundles, namespace
in common bundles
Example:
classes:
"namespace_context"
scope => "namespace";
"bundle_or_namespace_context"; # without an explicit scope, depends on bundle type
"bundle_context"
scope => "bundle";
See also: scope
in body classes
select_class
Description: Select one of the named list of classes to define based on host's fully qualified domain name, the primary IP address and the UID that cf-agent is running under.
The class is chosen deterministically (not randomly) but it is not possible to say which host will end up in which class in advance. Only that hosts will always end up in the same class every time.
Type: clist
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
bundle common g
{
classes:
"selection" select_class => { "one", "two" };
reports:
one::
"One was selected";
two::
"Two was selected";
selection::
"A selection was made";
}
Notes:
This feature is similar to the splayclass
function. However,
instead of selecting a class for a moment in time, it always chooses one class
in the list; the same class each time for a given host. This allows hosts to
be distributed across a controlled list of classes (e.g for load balancing
purposes).
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
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()
, andldaplist()
for LDAP querieshost2ip()
andip2host()
for DNS queriesreadtcp()
for TCP interactionshubknowledge()
, andremotescalar()
for hub queries
When enabled cached functions are not executed on every pass of convergence. Instead, the function will only be executed once during the agent evaluation step and its result will be cached until the end of that agent execution.
Note: Function caching is per-process, so results will not be cached between
separate components e.g. cf-agent
, cf-serverd
and cf-promises
.
Additionally functions are cached by hashing the function arguments. If you have
the exact same function call in two different promises (it does not matter if
they are in the same bundle or not) only the first executed function will be
cached. That cached result will be re-used for other identical function
occurrences.
Function caching can be disabled by setting cache_system_functions
in body
common control to false
.
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.
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
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()
difference
Prototype: difference(list1, list2)
Return type: slist
Description: Returns the unique elements in list1
that are not in
list2
.
Arguments:
list1
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
list2
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"a" slist => { 1,2,3,"x" };
"b" slist => { "x" };
# normal usage
"diff_between_a_and_b" slist => difference(a, b);
"diff_between_a_and_b_str" string => join(",", diff_between_a_and_b);
# NOTE: advanced usage!
"mylist1" slist => { "a", "b" };
"mylist2" slist => { "a", "b" };
"$(mylist1)_str" string => join(",", $(mylist1));
# Here we're going to really call difference(a,a) then difference(a,b) then difference(b,a) then difference(b,b)
# We create a new variable for each difference!!!
"diff_$(mylist1)_$(mylist2)" slist => difference($(mylist1), $(mylist2));
"diff_$(mylist1)_$(mylist2)_str" string => join(",", "diff_$(mylist1)_$(mylist2)");
reports:
# normal usage
"The difference between lists a and b is '$(diff_between_a_and_b_str)'";
# NOTE: advanced usage results!
"The difference of list '$($(mylist1)_str)' with '$($(mylist2)_str)' is '$(diff_$(mylist1)_$(mylist2)_str)'";
}
Output:
R: The difference between lists a and b is '1,2,3'
R: The difference of list '1,2,3,x' with '1,2,3,x' is ''
R: The difference of list '1,2,3,x' with 'x' is '1,2,3'
R: The difference of list 'x' with '1,2,3,x' is ''
R: The difference of list 'x' with 'x' is ''
See also: intersection()
.
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)";
}
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'
min
Prototype: min(list, sortmode)
Return type: string
Description: Return the minimum of the items in list
according to sortmode
(same sort modes as in sort()
).
list
can be a data container or a regular list.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
sortmode
: one 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
Notes:
History: Was introduced in version 3.6.0 (2014)
See also: sort()
, variance()
, sum()
, max()
, mean()
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.
maplist
Prototype: maplist(pattern, list)
Return type: slist
Description: Return a list with each element in list
modified by a
pattern.
The $(this)
variable expands to the currently processed entry from list
.
This is essentially like the map() function in Perl, and applies to
lists.
Arguments:
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)
See also: maparray()
, mapdata()
, and data
documentation.
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
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";
}
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"
);
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()
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";
}
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)
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
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
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
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"
);
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.
ifelse
Prototype: ifelse(...)
Return type: string
Description: Evaluate each pair of arguments up to the last one as a (class
, value
) tuple, returning value
if class
is set.
If none are set, returns the last argument.
Arguments:
The ifelse
function is like a multi-level if-else statement.It was
inspired by Oracle's DECODE
function. It must have an odd number of
arguments (from 1 to N). The last argument is the default value, like
the else
clause in standard programming languages. Every pair of
arguments before the last one are evaluated as a pair. If the first
one evaluates true (as if you had used it in a class expression
, so
it can be more than just a class name, it's a whole context like
Tuesday.linux.!verbose
) then the second one is returned.
Generally, if ifelse
were called with arguments (a1, a2, b1,
b2, c)
, the behavior expressed as pseudo-code is:
if a1 then return a2
else-if b1 then return b2
else return c
(But again, note that any odd number of arguments is supported.)
The ifelse
function is extremely useful when you want to avoid
explicitly stating the negative of all the expected cases; this
problem is commonly seen like so:
class1.class2::
"myvar" string => "x";
class3.!class2::
"myvar" string => "y";
!((class1.class2)||class3.!class2)::
"myvar" string => "z";
That's hard to read and error-prone (do you know how class2
will
affect the default case?). Here's the alternative with ifelse
:
"myvar" string => ifelse("class1.class2", "x",
"class3.!class2", "y",
"z");
Example:
bundle agent example
{
classes:
"myclass" expression => "any";
"myclass2" expression => "any";
"secondpass" expression => "any";
vars:
# we need to use the secondpass class because on the first pass,
# myclass and myclass2 are not defined yet
secondpass::
# result: { "1", "single string parameter", "hardclass OK", "bundle class OK", "5 parameters OK" }
"mylist" slist => {
ifelse(1),
ifelse("single string parameter"),
ifelse("cfengine", "hardclass OK", "hardclass broken"),
ifelse("myclass.myclass2", "bundle class OK", "bundle class broken"),
ifelse("this is not true", "5 parameters broken",
"this is also not true", "5 parameters broken 2",
"5 parameters OK"),
};
reports:
"ifelse result list: $(mylist)";
}
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.
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 anchored 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
anchored 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
maparray
Prototype: maparray(pattern, array_or_container)
Return type: slist
Description: Returns a list with each array_or_container
element
modified by a pattern
.
array_or_container
can be a data container.
The $(this.k)
and $(this.v)
variables expand to the key and value
of the current element, similar to the way this
is available for
maplist
.
If the array has two levels, you'll also be able to use the
$(this.k[1])
variable for the key at the second level. See the
example below for an illustration.
If a value in the array is an slist
, you'll get one result for each
value (implicit looping).
The order of the array keys is not guaranteed. Use the sort
function if you need order in the resulting output.
Arguments:
pattern
:string
, in the range:.*
array_or_container
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "run" };
}
bundle agent run
{
vars:
"static[2]" string => "lookup 2";
"static[two]" string => "lookup two";
"static[big]" string => "lookup big";
"static[small]" string => "lookup small";
"todo[1]" string => "2";
"todo[one]" string => "two";
"todo[3999]" slist => { "big", "small" };
"map" slist => maparray("key='$(this.k)', static lookup = '$(static[$(this.v)])', value='$(this.v)'", todo);
"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)";
"mapped container: $(mapc_str)";
}
Output:
R: mapped array: key='3999', static lookup = 'lookup big', value='big'
R: mapped array: key='3999', static lookup = 'lookup small', value='small'
R: mapped array: key='one', static lookup = 'lookup two', value='two'
R: mapped array: key='1', static lookup = 'lookup 2', value='2'
R: mapped container: { "key='top', key2='x', static lookup = 'lookup 2', value='2'", "key='top', key2='y', static lookup = 'lookup big', value='big'" }
See also: maplist()
, mapdata()
, and data
documentation.
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()
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
See Also: host2ip()
, iprange()
History: Was introduced in version 3.1.3, Nova 2.0.2 (2010)
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.
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 anchored 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()
.
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.
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");
regarray
Prototype: regarray(array, regex)
Return type: boolean
Description: Returns whether array
contains elements matching the
anchoredregular expression regex
.
Arguments:
array
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
regex
: regular expression, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"myarray[0]" string => "bla1";
"myarray[1]" string => "bla2";
"myarray[3]" string => "bla";
classes:
"ok" expression => regarray("myarray","b.*2");
reports:
ok::
"Found in list";
!ok::
"Not found in list";
}
Output:
R: Found in list
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()
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()
.
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
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
...
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()
.
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()
.
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");
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()
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";
}
every
Prototype: every(regex, list)
Return type: boolean
Description: Returns whether every element in the variable list
matches
the unanchored regex
.
Arguments:
regex
: Regular expression to find, in the range.*
list
: The name of the list variable to check, in the range[a-zA-Z0-9_$(){}\[\].:]+
. It can be a data container or a regular list.
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
classes:
"every_dot_star" expression => every(".*", test);
"every_dot" expression => every(".", test);
"every_number" expression => every("[0-9]", test);
"every2_dot_star" expression => every(".*", test2);
"every2_dot" expression => every(".", test2);
"every2_number" expression => every("[0-9]", test2);
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
reports:
"The test list is $(test)";
every_dot_star::
"every() test passed: every element matches '.*'";
!every_dot_star::
"every() test failed: not every element matches '.*'";
every_number::
"every() test failed: every element matches '[0-9]'";
!every_number::
"every() test passed: not every element matches '[0-9]'";
every_dot::
"every() test failed: every element matches '.'";
!every_dot::
"every() test passed: not every element matches '.'";
"The test2 list is $(test2)";
every2_dot_star::
"every() test2 passed: every element matches '.*'";
!every2_dot_star::
"every() test2 failed: not every element matches '.*'";
every2_number::
"every() test2 failed: every element matches '[0-9]'";
!every2_number::
"every() test2 passed: not every element matches '[0-9]'";
every2_dot::
"every() test2 failed: every element matches '.'";
!every2_dot::
"every() test2 passed: not every element matches '.'";
}
Output:
R: The test list is 1
R: The test list is 2
R: The test list is 3
R: The test list is one
R: The test list is two
R: The test list is three
R: The test list is long string
R: The test list is four
R: The test list is fix
R: The test list is six
R: every() test passed: every element matches '.*'
R: every() test passed: not every element matches '[0-9]'
R: every() test passed: not every element matches '.'
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: every() test2 passed: every element matches '.*'
R: every() test2 passed: not every element matches '[0-9]'
R: every() test2 passed: not every element matches '.'
See also: filter()
, some()
, and none()
.
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.
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 or a list reference. If
the sources
argument is the name of a list variable, then the entire
list of sources is used to determine whether the target needs rebuilding.
Example:
classes:
"build_me" expressions => makerule("/tmp/target", "/tmp/source.c");
commands:
build_me::
"/usr/bin/gcc -o /tmp/target /tmp/source.c";
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
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.
isvariable
Prototype: isvariable(var)
Return type: boolean
Description: Returns whether a variable named var
is defined.
The variable need only exist. This says nothing about its value. Use
regcmp
to check variable values.
Arguments:
var
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"bla" string => "xyz..";
classes:
"exists" expression => isvariable("bla");
reports:
exists::
"Variable exists: \"$(bla)\"..";
}
Output:
R: Variable exists: "xyz.."..
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";
}
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";
}
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).
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.
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 an expression. 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:
expression
:string
, in the range:.*
mode
: one ofmath
class
options
: 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'
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()
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()).
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";
}
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()
readjson
Prototype: readjson(filename, maxbytes)
Return type: data
Description: Parses JSON data from the first maxbytes
bytes of
file filename
and returns the result as a data
variable.
Arguments:
Example:
vars:
"loadthis"
data => readjson("/tmp/data.json", 4000);
See also: readdata()
, parsejson()
, storejson()
, parseyaml()
, readyaml()
, mergedata()
, and data
documentation.
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.
variance
Prototype: variance(list)
Return type: real
Description: Return the variance of the numbers in list
.
list
can be a data container or a regular list.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Use the eval()
function to easily get the standard deviation (square root of the variance).
This is not part of a full statistical package but a convenience function.
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
# the behavior will be the same whether you use a data container or a list
# "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
"mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
"mylist_str" string => format("%S", mylist);
"max_int" string => max(mylist, "int");
"max_lex" string => max(mylist, "lex");
"max_ip" string => max(mylist, "ip");
"min_int" string => min(mylist, "int");
"min_lex" string => min(mylist, "lex");
"min_ip" string => min(mylist, "ip");
"mean" real => mean(mylist);
"variance" real => variance(mylist);
reports:
"my list is $(mylist_str)";
"mean is $(mean)";
"variance is $(variance) (use eval() to get the standard deviation)";
"max int is $(max_int)";
"max IP is $(max_ip)";
"max lexicographically is $(max_lex)";
"min int is $(min_int)";
"min IP is $(min_ip)";
"min lexicographically is $(min_lex)";
}
Output:
R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1
Notes:
History: Was introduced in version 3.6.0 (2014)
See also: sort()
, mean()
, sum()
, max()
, min()
parsejson
Prototype: parsejson(json_data)
Return type: data
Description: Parses JSON data directly from an inlined string and
returns the result as a data
variable
Arguments:
json_data
:string
, in the range:.*
Please note that because JSON uses double quotes, it's usually most convenient to use single quotes for the string (CFEngine allows both types of quotes around a string).
Example:
vars:
"loadthis"
data => parsejson('{ "key": "value" }');
# inline syntax since 3.7
"loadthis_inline"
data => '{ "key": "value" }';
See also: readjson()
, parseyaml()
, readyaml()
, mergedata()
, Inline YAML and JSON data
, and data
documentation.
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
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.
none
Prototype: none(regex, list)
Return type: boolean
Description: Returns whether no element in list
matches the regular
expression regex
.
list
can be a data container or a regular list.
Arguments:
regex
: regular expression, in the range:.*
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
The regular expression is unanchored.
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
classes:
"none11" expression => none("jebadiah", test1);
"none12" expression => none("2", test1);
"none21" expression => none("jebadiah", test2);
"none22" expression => none("2", test2);
vars:
"test1" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
reports:
"The test1 list is $(test1)";
none11::
"none() test1 1 passed";
!none11::
"none() test1 1 failed";
none12::
"none() test1 2 failed";
!none12::
"none() test1 2 passed";
"The test2 list is $(test2)";
none21::
"none() test2 1 passed";
!none21::
"none() test2 1 failed";
none22::
"none() test2 2 failed";
!none22::
"none() test2 2 passed";
}
Output:
R: The test1 list is 1
R: The test1 list is 2
R: The test1 list is 3
R: The test1 list is one
R: The test1 list is two
R: The test1 list is three
R: The test1 list is long string
R: The test1 list is four
R: The test1 list is fix
R: The test1 list is six
R: none() test1 1 passed
R: none() test1 2 passed
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: none() test2 1 passed
R: none() test2 2 passed
See also: filter()
, every()
, and some()
.
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"
);
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:
The list of fields may be extended as needed by CFEngine.
History:
- function introduced in version 3.5.0.
linktarget
andlinktarget_shallow
field options added in 3.6.0.
See also: dirname()
, fileexists()
, isdir()
, islink()
, isplain()
, lastnode()
, returnszero()
, splitstring()
.
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()
.
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()
.
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()
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
Tips:
- Filter out the current (
.
) and parent (..
)directories with a negative look ahead.lsdir( "/tmp", "^(?!(\.|\.\.)).*", false )
.
Notes:
History: Was introduced in 3.3.0, Nova 2.2.0 (2011)
reglist
Prototype: reglist(list, regex)
Return type: boolean
Description: Returns whether the anchored regular expression
regex
matches any item in list
.
Arguments:
list
:string
, in the range:@[(][a-zA-Z0-9_$(){}\[\].:]+[)]
regex
: regular expression, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"nameservers" slist => {
"128.39.89.10",
"128.39.74.16",
"192.168.1.103",
"10.132.51.66"
};
classes:
"am_name_server" expression => reglist(@(nameservers), "127\.0\.0\.1");
reports:
am_name_server::
"127.0.0.1 is currently set as a nameserver";
!am_name_server::
"127.0.0.1 is NOT currently set as a nameserver";
}
Output:
R: 127.0.0.1 is NOT currently set as a nameserver
In the example above, the IP address in $(sys.ipv4[eth0])
must be escape
d,
so that the (.) characters in the IP address are not interpreted as the
regular expression "match any" characters.
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.
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
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.
Notes:
islink()
does not resolve symlinks as part of it's test. If a broken symlink exists, the file is still seen to be a symlink. Usefilestat("myfile", "link target")
to see if a file resolves to a the expected target, and check if the link target exists. Alternatively usetest
withreturnszero()
, for examplereturnszero("/bin/test -f myfile")
.
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.
See Also: fileexists()
, filestat()
, isdir()
, isplain()
, returnszero()
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).
If a key inside a data container is specified (container[key]
), the
value under that key is extracted and merged. The key can be a string
for JSON objects or a number for JSON arrays. Note that a single data
container, CFEngine array, or slist can be named, in which case you're
simply extracting the contents of the data container into a new data
container.
If any list (slist, ilist, or rlist) is named, it's first converted to a JSON array, then merged as above.
If any CFEngine "classic" array (array[key]
) is named, it's first
converted to a JSON object, then merged as above.
mergedata()
is thus a convenient way, together with getindices()
and
getvalues()
, to bridge the gap between data container and the
traditional list and array data types in CFEngine.
If any of the above-mentioned variables are named inside brackets like
[ thing ]
then the thing
will be wrapped in a JSON array and
then merged..
If any of the above-mentioned variables are named inside braces with a
key like { "newkey": thing }
then the thing
will be wrapped
in a JSON object under key newkey
and then merged.
Example:
body common control
{
bundlesequence => { "test", "test2" };
}
bundle agent test
{
vars:
"d1" data => parsejson('{ "a": [1,2,3], "b": [] }');
"d2" data => parsejson('{ "b": [4,5,6] }');
"d3" data => parsejson('[4,5,6]');
"list1" slist => { "element1", "element2" };
"array1[mykey]" slist => { "array_element1", "array_element2" };
"array2[otherkey]" string => "hello";
"merged_d1_d2" data => mergedata("d1", "d2");
"merged_d1_d3" data => mergedata("d1", "d3");
"merged_d3_list1" data => mergedata("d3", "list1");
"merged_d1_array1" data => mergedata("d1", "array1");
"merged_d2_array2" data => mergedata("d2", "array2");
"merged_d1_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";
}
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"}
See also: getindices()
, getvalues()
, readjson()
, parsejson()
, readyaml()
, parseyaml()
, and data
documentation.
sum
Prototype: sum(list)
Return type: real
Description: Return the sum of the reals in list
.
This function might be used for simple ring computation. Of course, you could
easily combine sum
with readstringarray
or readreallist
etc., to collect
summary information from a source external to CFEngine.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"adds_to_six" ilist => { "1", "2", "3" };
"six" real => sum("adds_to_six");
"adds_to_zero" rlist => { "1.0", "2", "-3e0" };
"zero" real => sum("adds_to_zero");
reports:
"six is $(six), zero is $(zero)";
}
Output:
R: six is 6.000000, zero is 0.000000
Because $(six)
and $(zero)
are both real numbers, the report that is
generated will be:
six is 6.000000, zero is 0.000000
Notes:
History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010)
sublist
Prototype: sublist(list, head_or_tail, max_elements)
Return type: slist
Description: Returns list of up to max_elements
of list
, obtained from head or tail depending on head_or_tail
.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
head_or_tail
: one 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
Notes:
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()
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()
getvalues
Prototype: getvalues(varref)
Return type: slist
Description: Returns the list of values in varref
which can be
the name of an array or container.
If the array contains list values, then all of the list elements are flattened into a single list to make the return value a list.
If the data container contains non-scalar values (e.g. nested
containers) they are skipped. The special values true
, false
, and
null
are serialized to their string representations. Numerical
values are serialized to their string representations.
You can specify a path inside the container. For example, below you'll
look at the values of d[k]
, not at the top level of d
.
Make sure you specify the correct scope when supplying the name of the variable.
Arguments:
varref
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"v[index_1]" string => "value_1";
"v[index_2]" string => "value_2";
"values" slist => getvalues("v");
# works with data containers too
"d" data => parsejson('{ "k": [ 1, 2, 3, "a", "b", "c" ] }');
"cvalues" slist => getvalues("d[k]");
reports:
"Found values: $(values)";
"Found container values: $(cvalues)";
}
Output:
R: Found values: value_1
R: Found values: value_2
R: Found container values: 1
R: Found container values: 2
R: Found container values: 3
R: Found container values: a
R: Found container values: b
R: Found container values: c
Notes:
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..
See Also: fileexists()
, filestat()
, islink()
, isplain()
, returnszero()
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.
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()
.
sort
Prototype: sort(list, mode)
Return type: slist
Description: Returns list
sorted according to mode
.
Lexicographical, integer, real, IP, and MAC address sorting is supported currently. The example below will show each sorting mode in action.
Note IPv6 addresses can not use uppercase hexadecimal characters
(A-Z
) but must use lowercase (a-z
) instead.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
mode
: one 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'
See also: shuffle()
.
readyaml
Prototype: readyaml(filename, maxbytes)
Return type: data
Description: Parses YAML data from the first maxbytes
bytes of
file filename
and returns the result as a data
variable.
Arguments:
Example:
vars:
"loadthis"
data => readyaml("/tmp/data.yaml", 4000);
See also: readdata()
, parsejson()
, parseyaml()
, storejson()
, mergedata()
, and data
documentation.
grep
Prototype: grep(regex, list)
Return type: slist
Description: Returns the sub-list if items in list
matching the
anchored regular expression regex
.
list
can be a data container or a regular list.
Arguments:
regex
: regular expression, in the range:.*
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"mylist" slist => { "One", "Two", "Three", "Four", "Five" };
"Tlist" slist => grep("T.*","mylist");
"empty_list" slist => grep("ive","mylist");
"datalist" data => parsejson('[1,2,3, "Tab", "chive"]');
"data_Tlist" slist => grep("T.*","datalist");
"data_empty_list" slist => grep("ive","datalist");
"todo" slist => { "mylist", "Tlist", "empty_list", "datalist", "data_Tlist", "data_empty_list" };
"$(todo)_str" string => format("%S", $(todo));
reports:
"$(todo): $($(todo)_str)";
}
Output:
R: mylist: { "One", "Two", "Three", "Four", "Five" }
R: Tlist: { "Two", "Three" }
R: empty_list: { --empty-list-- }
R: datalist: [1,2,3,"Tab","chive"]
R: data_Tlist: { "Tab" }
R: data_empty_list: { --empty-list-- }
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
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).
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)
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()
.
"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.
unique
Prototype: unique(list)
Return type: slist
Description: Returns list of unique elements from list
.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test_str" string => join(",", "test");
"test_unique" slist => unique("test");
"unique_str" string => join(",", "test_unique");
reports:
"The test list is $(test_str)";
"The unique elements of the test list: $(unique_str)";
}
Output:
R: The test list is 1,2,3,one,two,three,long string,four,fix,six,one,two,three
R: The unique elements of the test list: 1,2,3,one,two,three,long string,four,fix,six
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.
join
Prototype: join(glue, list)
Return type: string
Description: Join the items of list
into a string, using the conjunction in glue
.
Converts a list or data container into a scalar variable using the join string in first argument.
Arguments:
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
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)
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)
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)
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()
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)
reverse
Prototype: reverse(list)
Return type: slist
Description: Reverses a list.
This is a simple function to reverse a list.
Arguments:
- list : The name of the list variable to check, in the range
[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"one", "two", "three",
};
"reversed" slist => reverse("test");
reports:
"Original list is $(test)";
"The reversed list is $(reversed)";
}
Output:
R: Original list is 1
R: Original list is 2
R: Original list is 3
R: Original list is one
R: Original list is two
R: Original list is three
R: Original list is long string
R: The reversed list is three
R: The reversed list is two
R: The reversed list is one
R: The reversed list is long string
R: The reversed list is 3
R: The reversed list is 2
R: The reversed list is 1
See also: filter()
, grep()
, every()
, some()
, and none()
.
max
Prototype: max(list, sortmode)
Return type: string
Description: Return the maximum of the items in list
according to sortmode
(same sort modes as in sort()
).
list
can be a data container or a regular list.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
sortmode
: one 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
Notes:
History: Was introduced in version 3.6.0 (2014)
See also: sort()
, variance()
, sum()
, mean()
, min()
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()
.
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.
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)
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
nth
Prototype: nth(list_or_container, position_or_key)
Return type: string
Description: Returns the element of list_or_container
at zero-based position_or_key
.
If an invalid position (below 0 or above the size of the list minus 1) or missing key is requested, this function does not return a valid value.
list_or_container
can be an slist or a data container. If it's a
slist, the offset is simply the position in the list. If it's a data
container, the meaning of the position_or_key
depends on its
top-level contents: for a list like [1,2,3,4]
you will get the list
element at position_or_key
. For a key-value map like
{ a: 100, b: 200 }
, a position_or_key
of a
returns 100
.
Arguments:
list_or_container
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
position_or_key
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test_str" string => format("%S", test);
"test2" data => parsejson("[1, 2, 3, null]");
"test2_str" string => format("%S", test2);
"test3" data => parsejson('{ "x": true, "y": "z" }');
"test3_str" string => format("%S", test3);
"nth" slist => { 1, 2, 6, 10, 11, 1000 };
"nth2" slist => getindices(test2);
"nth3" slist => getindices(test3);
"access[$(nth)]" string => nth(test, $(nth));
"access[0]" string => nth(test, 0);
"access2[$(nth2)]" string => nth(test2, $(nth2));
"access3[$(nth3)]" string => nth(test3, $(nth3));
reports:
"The test list is $(test_str)";
"element #$(nth) of the test list: $(access[$(nth)])";
"element #0 of the test list: $(access[0])";
"The test2 data container is $(test2_str)";
"element #$(nth2) of the test2 data container: $(access2[$(nth2)])";
"The test3 data container is $(test3_str)";
"element #$(nth3) of the test3 data container: $(access3[$(nth3)])";
}
Output:
R: The test list is { "1", "2", "3", "one", "two", "three", "long string", "four", "fix", "six", "one", "two", "three" }
R: element #1 of the test list: 2
R: element #2 of the test list: 3
R: element #6 of the test list: long string
R: element #10 of the test list: one
R: element #11 of the test list: two
R: element #1000 of the test list: $(access[1000])
R: element #0 of the test list: 1
R: The test2 data container is [1,2,3,null]
R: element #0 of the test2 data container: 1
R: element #1 of the test2 data container: 2
R: element #2 of the test2 data container: 3
R: element #3 of the test2 data container: null
R: The test3 data container is {"x":true,"y":"z"}
R: element #x of the test3 data container: true
R: element #y of the test3 data container: z
See also: length()
.
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.
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
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");
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.
Notes:
fileexists()
does not resolve symlinks. If a broken symlink exists, the file is seen to exist. For this functionality usefilestat("myfile", "link target")
to see if a file resolves to a the expected target, and check if the link target exists. Alternatively usetest
withreturnszero()
, for examplereturnszero("/bin/test -f myfile")
.
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!
See Also: filestat()
, isdir()
, islink()
, isplain()
, returnszero()
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.
length
Prototype: length(list)
Return type: int
Description: Returns the length of list
.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"length" int => length("test");
"test_str" string => join(",", "test");
reports:
"The test list is $(test_str)";
"The test list has $(length) elements";
}
Output:
R: The test list is 1,2,3,one,two,three,long string,four,fix,six,one,two,three
R: The test list has 13 elements
See also: nth()
.
some
Prototype: some(regex, list)
Return type: boolean
Description: Return whether any element of list
matches the
Unanchored regular expression regex
.
list
can be a data container or a regular list.
Arguments:
regex
: regular expression, in the range:.*
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
some()
will return as soon as any element matches.
It's convenient to set a class to not => some(".*", mylist)
in order
to check if mylist
is empty. Since some()
returns as soon as
possible, that is better than using length()
or every()
or
none()
which all must traverse the entire list.
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
classes:
# This is an easy way to check if a list is empty, better than
# expression => strcmp(length(x), "0")
# Note that if you use length() or none() or every() they will
# go through all the elements!!! some() returns as soon as any
# element matches.
"empty_x" not => some(".*", x);
"empty_y" not => some(".*", y);
"some11" expression => some("long string", test1);
"some12" expression => some("none", test1);
"some21" expression => some("long string", test2);
"some22" expression => some("none", test2);
vars:
"x" slist => { "a", "b" };
"y" slist => { };
"test1" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
reports:
empty_x::
"x has no elements";
empty_y::
"y has no elements";
any::
"The test1 list is $(test1)";
some11::
"some() test1 1 passed";
!some11::
"some() test1 1 failed";
some12::
"some() test1 2 failed";
!some12::
"some() test1 2 passed";
"The test2 list is $(test2)";
some21::
"some() test2 1 passed";
!some21::
"some() test2 1 failed";
some22::
"some() test2 2 failed";
!some22::
"some() test2 2 passed";
}
Output:
R: y has no elements
R: The test1 list is 1
R: The test1 list is 2
R: The test1 list is 3
R: The test1 list is one
R: The test1 list is two
R: The test1 list is three
R: The test1 list is long string
R: The test1 list is four
R: The test1 list is fix
R: The test1 list is six
R: some() test1 1 passed
R: some() test1 2 passed
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: some() test2 1 passed
R: some() test2 2 passed
See also: filter()
, every()
, and none()
.
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..
See Also: fileexists()
, filestat()
, isdir()
, islink()
, returnszero()
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)
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")) };
}
See Also: ip2host()
, iprange()
History: This function was introduced in CFEngine version 3.0.4 (2010)
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()
iprange
Prototype: iprange(range)
Return type: boolean
Description: Returns whether the current host lies in the range of IP addresses specified.
Pattern matching based on IP addresses.
Arguments:
range
:string
, in the range:.*
Example:
bundle agent example
{
classes:
"dmz_1" expression => iprange("128.39.89.10-15");
"lab_1" expression => iprange("128.39.74.1/23");
reports:
dmz_1::
"DMZ 1 subnet";
lab_1::
"Lab 1 subnet";
}
See Also: host2ip()
, ip2host()
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()
.
"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)
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()
.
mean
Prototype: mean(list)
Return type: real
Description: Return the mean of the numbers in list
.
list
can be a data container or a regular list.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
# the behavior will be the same whether you use a data container or a list
# "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
"mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
"mylist_str" string => format("%S", mylist);
"max_int" string => max(mylist, "int");
"max_lex" string => max(mylist, "lex");
"max_ip" string => max(mylist, "ip");
"min_int" string => min(mylist, "int");
"min_lex" string => min(mylist, "lex");
"min_ip" string => min(mylist, "ip");
"mean" real => mean(mylist);
"variance" real => variance(mylist);
reports:
"my list is $(mylist_str)";
"mean is $(mean)";
"variance is $(variance) (use eval() to get the standard deviation)";
"max int is $(max_int)";
"max IP is $(max_ip)";
"max lexicographically is $(max_lex)";
"min int is $(min_int)";
"min IP is $(min_ip)";
"min lexicographically is $(min_lex)";
}
Output:
R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1
Notes:
History: Was introduced in version 3.6.0 (2014)
See also: sort()
, variance()
, sum()
, max()
, min()
shuffle
Prototype: shuffle(list, seed)
Return type: slist
Description: Return list
shuffled with seed
.
The same seed will produce the same shuffle every time. For a random shuffle,
provide a random seed with the randomint
function.
Arguments:
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"mylist" slist => { "b", "c", "a" };
"seeds" slist => { "xx", "yy", "zz" };
"shuffled_$(seeds)" slist => shuffle(mylist, $(seeds));
"joined_$(seeds)" string => join(",", "shuffled_$(seeds)");
reports:
"shuffled RANDOMLY by $(seeds) = '$(joined_$(seeds))'";
}
Output:
R: shuffled RANDOMLY by xx = 'b,a,c'
R: shuffled RANDOMLY by yy = 'a,c,b'
R: shuffled RANDOMLY by zz = 'c,b,a'
See also: sort()
.
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()
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
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
getindices
Prototype: getindices(varref)
Return type: slist
Description: Returns the list of keys in varref
which can be
the name of an array or container.
Make sure you specify the correct scope when supplying the name of the variable.
Note the list which getindices returns is not guaranteed to be in any specific order.
In the case of a doubly-indexed array (such as parsestringarrayidx and
friends produce), the primary keys are returned; i.e. if
varref[i][j]
exist for various i
, j
and you ask for the keys of
varref
, you get the i
values. For each such i
you can then ask
for getindices("varref[i]")
to get a list of the j
values (and so
on, for higher levels of indexing).
Arguments:
varref
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"ps[relayhost]" string => "[mymailrelay]:587";
"ps[mydomain]" string => "iu.hio.no";
"ps[smtp_sasl_auth_enable]" string => "yes";
"ps[smtp_sasl_password_maps]" string => "hash:/etc/postfix/sasl-passwd";
"ps[smtp_sasl_security_options]" string => "";
"ps[smtp_use_tls]" string => "yes";
"ps[default_privs]" string => "mailman";
"ps[inet_protocols]" string => "all";
"ps[inet_interfaces]" string => "127.0.0.1";
"parameter_name" slist => getindices("ps");
reports:
"Found key $(parameter_name)";
}
Output:
R: Found key inet_protocols
R: Found key mydomain
R: Found key smtp_use_tls
R: Found key smtp_sasl_password_maps
R: Found key smtp_sasl_security_options
R: Found key relayhost
R: Found key default_privs
R: Found key inet_interfaces
R: Found key smtp_sasl_auth_enable
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)";
}
See Also: lastseenexpireafter in body common control
product
Prototype: product(list)
Return type: real
Description: Returns the product of the reals in list
.
This function might be used for simple ring computation. Of course, you could
easily combine product
with readstringarray
or readreallist
etc., to
collect summary information from a source external to CFEngine.
Arguments:
list
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"series" rlist => { "1.1", "2.2", "3.3", "5.5", "7.7" };
"prod" real => product("series");
"sum" real => sum("series");
reports:
"Product result: $(prod) > $(sum)";
}
Output:
R: Product result: 338.207100 > 19.800000
History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010)
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()
.
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.
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:[a-zA-Z0-9_$(){}\[\].:]+
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)
See also: readcsv()
, readjson()
, readyaml()
, 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
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
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.
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
filter
Prototype: filter(filter, list, is_regex, invert, max_return)
Return type: slist
Description: Transforms a list or data container into a list subset thereof.
This is a generic filtering function that returns a list of up to max_return
elements in list
that match the filtering rules specified in filter
,
is_regex
and invert
.
Arguments:
- filter : Anchored regular expression or static string to find, in the range
.*
- list : The name of the list variable or data container to check, in the range
[a-zA-Z0-9_$(){}\[\].:]+
- is_regex_ : Boolean
Treat filter
as a regular expression or as a static string.
invert
: Boolean
Invert filter.
max_return
: Maximum number of elements to return in the 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-- }
See also: grep()
, every()
, some()
, and
none()
.
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.
intersection
Prototype: intersection(list1, list2)
Return type: slist
Description: Returns the unique elements in list1 that are also in list2.
Arguments:
list1
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
list2
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"a" slist => { 1,2,3,"x" };
"b" slist => { "x" };
"mylist1" slist => { "a", "b" };
"mylist2" slist => { "a", "b" };
"$(mylist1)_str" string => join(",", $(mylist1));
"int_$(mylist1)_$(mylist2)" slist => intersection($(mylist1), $(mylist2));
"int_$(mylist1)_$(mylist2)_str" string => join(",", "int_$(mylist1)_$(mylist2)");
reports:
"The intersection of list '$($(mylist1)_str)' with '$($(mylist2)_str)' is '$(int_$(mylist1)_$(mylist2)_str)'";
}
Output:
R: The intersection of list '1,2,3,x' with '1,2,3,x' is '1,2,3,x'
R: The intersection of list '1,2,3,x' with 'x' is 'x'
R: The intersection of list 'x' with '1,2,3,x' is 'x'
R: The intersection of list 'x' with 'x' is 'x'
See also: difference()
.
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
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()
.
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()
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()
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
.
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
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.*");
reports:
"Variables matching 'default:sys.cf_version.*' = $(v)";
}
Output:
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_major
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_patch
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_minor
History: Introduced in CFEngine 3.6
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 parsed as JSON when interpretation
is json
.
array_or_container
can be a data container.
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
json
pattern
:string
, in the range:.*
array_or_container
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
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"]
See also: maplist()
, maparray()
, and data
documentation.
History: Was introduced in 3.7.0
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
"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
...
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()
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()
.
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
storejson
Prototype: storejson(data_container)
Return type: string
Description: Converts a data container to a JSON string.
Arguments:
data_container
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
vars:
"loadthis"
data => readjson("/tmp/data.json", 4000);
"andback"
string => storejson(loadthis);
reports:
"Converted /tmp/data.json to '$(andback)'";
See also: readjson()
, readyaml()
, parsejson()
, parseyaml()
, and data
documentation.
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 IPv4 address octets of any active interface (in the form
ipv4_192_0_0_1
,ipv4_192_0_0
,ipv4_192_0
,ipv4_192
) - The IPv6 addresses of all active interfaces (with dots replaced by
underscores, e.g.
ipv6_fe80__a410_6072_21eb_d3fa
) added in 3.7.8, 3.10.3, 3.12.0 - 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:
connection Variables defined for embedding unprintable values or values with special meanings in strings.
const Variables defined for embedding unprintable values or values with special meanings in strings.
edit Variables used to access information about editing promises during their execution.
match Variable used in string matching.
mon Variables defined in a monitoring context.
sys Variables defined in order to automate discovery of system values.
this Variables used to access information about promises during their execution.
edit
This context is used to access information about editing promises during their execution. It is context dependent and not universally meaningful or available.
bundle agent testbundle
{
files:
"/tmp/testfile"
edit_line => test;
}
#
bundle edit_line test
{
classes:
"ok" expression => regline(".*mark.*","$(edit.filename)");
reports:
ok::
"File matched $(edit.filename)";
}
edit.filename
This variable points to the filename of the file currently making an edit promise. If the file has been arrived at through a search, this could be different from the files promiser.
sys
System variables are derived from CFEngine's automated discovery of system values. They are provided as variables in order to make automatically adaptive rules for configuration.
files:
"$(sys.resolv)"
create => "true",
edit_line => doresolv("@(this.list1)","@(this.list2)"),
edit_defaults => reconstruct;
sys.arch
The variable gives the kernel's short architecture description.
# arch = x86_64
sys.bindir
The name of the directory where CFEngine looks for its binaries..
# bindir = /var/cfengine/bin
History: Introduced in CFEngine 3.6
sys.cdate
The date of the system in canonical form, i.e. in the form of a class, from when the agent initialized.
# cdate = Sun_Dec__7_10_39_53_2008_
sys.cf_promises
A variable containing the path to the CFEngine syntax analyzer
cf-promises
on the platform you are using.
classes:
"syntax_ok" expression => returnszero("$(sys.cf_promises)");
sys.cf_version
The variable gives the version of the running CFEngine Core.
# cf_version = 3.0.5
sys.cf_version_major
The variable gives the major version of the running CFEngine Core.
# cf_version = 3.0.5
# cf_version_major = 3
History: Was introduced in 3.5.1, Enterprise 3.5.1.
sys.cf_version_minor
The variable gives the minor version of the running CFEngine Core.
# cf_version = 3.0.5
# cf_version_minor = 0
History: Was introduced in 3.5.1, Enterprise 3.5.1.
sys.cf_version_patch
The variable gives the patch version of the running CFEngine Core.
# cf_version = 3.0.5
# cf_version_patch = 5
History: Was introduced in 3.5.1, Enterprise 3.5.1.
sys.class
This variable contains the name of the hard-class category for this host (i.e. its top level operating system type classification).
# class = linux
See also: sys.os
sys.cpus
A variable containing the number of CPU cores detected. On systems which
provide virtual cores, it is set to the total number of virtual, not
physical, cores. In addition, on a single-core system the class 1_cpu
is set, and on multi-core systems the class n_cpus
is set, where
n is the number of cores identified.
reports:
"Number of CPUS = $(sys.cpus)";
8_cpus::
"This system has 8 processors.";
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2012)
sys.crontab
The variable gives the location of the current users's master crontab directory.
# crontab = /var/spool/crontab/root
sys.date
The date of the system as a text string, from when the agent initialized.
# date = Sun Dec 7 10:39:53 2008
sys.doc_root
A scalar variable containing the default path for the document root of the standard web server package.
History: Was introduced in 3.1.0, Enterprise 2.0.
sys.domain
The domain name as discovered by CFEngine. If the DNS is in use, it could
be possible to derive the domain name from its DNS registration, but in
general there is no way to discover this value automatically. The
common control
body permits the ultimate specification of this value.
# domain = example.org
sys.enterprise_version
The variable gives the version of the running CFEngine Enterprise Edition.
# enterprise_version = 3.0.0
History: Was introduced in 3.5.0, Enterprise 3.0.0.
sys.expires
History:
- Removed 3.5.0
- Introduced in version 3.1.4, Enterprise 2.0.2 (2011).
sys.exports
The location of the system NFS exports file.
# exports = /etc/exports
# exports = /etc/dfs/dfstab
sys.failsafe_policy_path
The name of the failsafe policy file.
# failsafe_policy_path = /var/cfengine/inputs/failsafe.cf
History: Introduced in CFEngine 3.6
sys.flavor, sys.flavour
A variable containing an operating system identification string that is used to determine the current release of the operating system in a form that can be used as a label in naming. This is used, for instance, to detect which package name to choose when updating software binaries for CFEngine.
These two variables are synonyms for each other.
History: Was introduced in 3.2.0, Enterprise 2.0
See also: sys.ostype
sys.fqhost
The fully qualified name of the host. In order to compute this value properly, the domain name must be defined.
# fqhost = host.example.org
See also: sys.uqhost
sys.fstab
The location of the system filesystem (mount) table.
# fstab = /etc/fstab
sys.hardware_addresses
This is a list variable containing a list of all known MAC addresses for system interfaces.
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.hardware_mac[interface_name]
This contains the MAC address of the named interface. For example:
reports:
"Tell me $(sys.hardware_mac[eth0])";
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.host
The name of the current host, according to the kernel. It is undefined whether this is qualified or unqualified with a domain name.
# host = myhost
sys.inputdir
The name of the inputs directory where CFEngine looks for its policy files.
# inputdir = /var/cfengine/inputs
History: Introduced in CFEngine 3.6
sys.interface
The assumed (default) name of the main system interface on this host.
# interface = eth0
sys.interfaces
Displays a system list of configured interfaces currently active in use by the system. This list is detected at runtime and it passed in the variables report to the CFEngine Enterprise Database.
To use this list in a policy, you will need a local copy since only local variables can be iterated.
bundle agent test
{
vars:
# To iterate, we need a local copy
"i1" slist => { @(sys.ip_addresses)} ;
"i2" slist => { @(sys.interfaces)} ;
reports:
"Addresses: $(i1)";
"Interfaces: $(i2)";
"Addresses of the interfaces: $(sys.ipv4[$(i2)])";
}
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.interface_flags
Contains a space separated list of the flags of the named interface. e.g.
reports:
"eth0 flags: $(sys.interface_flags[eth0])";
Outputs:
R: eth0 flags: up broadcast running multicast
The following device flags are supported:
- up
- broadcast
- debug
- loopback
- pointopoint
- notrailers
- running
- noarp
- promisc
- allmulti
- multicast
History: Was introduced in 3.5.0 (2013)
sys.ip_addresses
Displays a system list of IP addresses currently in use by the system. This list is detected at runtime and passed in the variables report to the CFEngine Enterprise Database.
To use this list in a policy, you will need a local copy since only local variables can be iterated.
bundle agent test
{
vars:
# To iterate, we need a local copy
"i1" slist => { @(sys.ip_addresses)} ;
"i2" slist => { @(sys.interfaces)} ;
reports:
"Addresses: $(i1)";
"Interfaces: $(i2)";
"Addresses of the interfaces: $(sys.ipv4[$(i2)])";
}
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.ipv4
All four octets of the IPv4 address of the first system interface.
Note:
If your system has a single ethernet interface, $(sys.ipv4)
will contain
your IPv4 address. However, if your system has multiple interfaces, then
$(sys.ipv4)
will simply be the IPv4 address of the first interface in the
list that has an assigned address, Use $(sys.ipv4[interface_name])
for
details on obtaining the IPv4 addresses of all interfaces on a system.
sys.ipv4[interface_name]
The full IPv4 address of the system interface named as the associative
array index, e.g. $(ipv4[le0])
or $(ipv4[xr1])
.
# If the IPv4 address on the interfaces are
# le0 = 192.168.1.101
# xr1 = 10.12.7.254
#
# Then the octets of all interfaces are accessible as an associative array
# ipv4_1[le0] = 192
# ipv4_2[le0] = 192.168
# ipv4_3[le0] = 192.168.1
# ipv4[le0] = 192.168.1.101
# ipv4_1[xr1] = 10
# ipv4_2[xr1] = 10.12
# ipv4_3[xr1] = 10.12.7
# ipv4[xr1] = 10.12.7.254
Note:
The list of interfaces may be acquired with getindices("sys.ipv4")
(or
from any of the other associative arrays). Only those interfaces which
are marked as "up" and have an IP address will be listed.
sys.ipv4_1[interface_name]
The first octet of the IPv4 address of the system interface named as the
associative array index, e.g. $(ipv4_1[le0])
or $(ipv4_1[xr1])
.
sys.ipv4_2[interface_name]
The first two octets of the IPv4 address of the system interface named as the associative array index, e.g. $(ipv4_2[le0])
or $(ipv4_2[xr1])
.
sys.ipv4_3[interface_name]
The first three octets of the IPv4 address of the system interface named as the associative array index, e.g. $(ipv4_3[le0])
or $(ipv4_3[xr1])
.
sys.key_digest
The digest of the host's cryptographic public key.
# sys.key_digest = MD5=bc230448c9bec14b9123443e1608ac07
sys.last_policy_update
Timestamp when last policy change was seen by host
sys.libdir
The name of the directory where CFEngine looks for its libraries.
# libdir = /var/cfengine/inputs/lib/3.6
History: Introduced in CFEngine 3.6
sys.local_libdir
The name of the directory where CFEngine looks for its libraries, without any prefixes.
# local_libdir = lib/3.6
History: Introduced in CFEngine 3.6
sys.logdir
The name of the directory where CFEngine log files are saved
# logdir = /var/cfengine/
History: Introduced in CFEngine 3.6
sys.license_owner
History:
- Removed 3.5.0
- Introduced in version 3.1.4, Enterprise 2.0.2 (2011).
sys.licenses_granted
History:
- Removed 3.5.0
- 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
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";
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.
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.
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"));
# ...
}
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.
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_ports
All of the TCP and UDP ports for both IPV4 and IPv6 that cf-monitord
has
observed listening.
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).
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.
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 (array) Includes only results that concern hosts which have all specified CFEngine contexts (class) set. Optional parameter.
- hostContextExclude (array) Excludes results that concern hosts which have specified CFEngine context (class) set. Hosts that have at lest one of the specified contexts set will be excluded from the results. 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 (array) Includes only results that concern hosts which have all specified CFEngine contexts (class) set. Optional parameter.
- hostContextExclude (array) Excludes results that concern hosts which have specified CFEngine context (class) set. Hosts that have at lest one of the specified contexts set will be excluded from the results. 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
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 cf-agent execution when the change have been made. Changes made by the same cf-agent execution will have the same change time. Expressed at unix 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
<!--- End include: /home/jenkins/workspace/build-documentation-3.7/label/DOCUMENTATION_x86_64_linux_ubuntu_16/documentation/reference/enterprise-api-ref/changes.markdown
-->
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 derived from the lastseen database (this is expected to be the IP address from which connections come from, beware NAT will cause multiple hosts to appear to have the same IP address).
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 enviroment"}
changetimestamp | 2015-03-11 09:50:11+00
-[ RECORD 3 ]---|-------------------------------------------------------
hostkey | SHA=a4dd5...
contextname | enterprise_edition
metatags | {inventory,attribute_name=none,source=agent,hardclass}
changetimestamp | 2015-03-11 09:50:11+00
Table: Variables
Variables and their values set on hosts at their last reported cf-agent execution.
Columns:
HostKey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.NameSpace (text) Namespace within which the variable is set. If no namespace is set then it is set as:
default
.Bundle (text) Bundle name where the variable is set.
VariableName (text) Name of the variable.
VariableValue (text) Variable value serialized to string.
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. Effectively a snapshot
of each hosts lastseen database (cf_lastseen.lmdb
, cf-key -s
) at the time of
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.LastSeenDirection (
INCOMING
/OUTGOING
) Direction within which the connection was established.INCOMING
- host recieved incoming connection.OUTGOING
- host opened connection to remote host.
RemoteHostKey (text)
HostKey
of the remote host.RemoteHostIP (text) IP address of the remote host.
LastSeenTimeStamp (timestamp) Time when the connection was established.
LastSeenInterval (real) Average time period (seconds) between connections for the given
LastSeenDirection
with the host.
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 | 299
-[ RECORD 2 ]-----|------------------------
hostkey | SHA=3b94d...
lastseendirection | INCOMING
remotehostkey | SHA=a4dd5...
remotehostip | 192.168.33.151
lastseentimestamp | 2015-03-13 12:22:06+00
lastseeninterval | 298
-[ RECORD 3 ]-----|------------------------
hostkey | SHA=2aab8...
lastseendirection | INCOMING
remotehostkey | SHA=3b94d...
remotehostip | 192.168.33.65
lastseentimestamp | 2015-03-13 12:20:45+00
lastseeninterval | 299
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
This table was deprecated in 3.7.0. It is no longer used.
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
This table was deprecated in 3.7.0. It is no longer used.
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
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
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.7/label/DOCUMENTATION_x86_64_linux_ubuntu_16/documentation/reference/enterprise-api-ref/users-rbac.markdown
-->
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
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)"::
"$(this.bundle): $(on) => SET class '$(extract_$(on)[2]) for '$(extract_$(on)[1])'"
ifvarclass => "parsed_$(on)";
"$(this.bundle): $(off) => UNSET class '$(extract_$(off)[1])'"
ifvarclass => "parsed_$(off)";
"$(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
"$(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";
"$(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
"$(this.bundle): have no $(extract_$(off)[1])" ifvarclass => "!$(extract_$(off)[1])";
}
feature_test
Prototype: feature_test
Description: Finds feature_set_X and feature_unset_X classes and reports X
Note that this bundle is intended to be used exactly like feature
and just show what's defined or undefined.
Example:
Check classes xyz
, qpr
, ijk
, and abc
.
cf-agent -I -f ./feature.cf -b feature_test -Dfeature_set_10_xyz,feature_set_100_qpr,feature_set_90m_ijk,feature_unset_abc
Implementation:
bundle agent feature_test
{
classes:
"parsed_$(on)" expression => regextract("feature_set_([0-9]+[kmgKMG]?)_(.*)",
$(on),
"extract_$(on)");
"parsed_$(off)" expression => regextract("feature_unset_(.*)",
$(off),
"extract_$(off)");
vars:
"on" slist => classesmatching("feature_set_.*");
"off" slist => classesmatching("feature_unset_.*");
reports:
"$(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
"$(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";
"$(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
"$(this.bundle): have no $(extract_$(off)[1])" ifvarclass => "!$(extract_$(off)[1])";
}
Paths
To use these bundles, add the following to your policy:
body file control
{
inputs => { "paths.cf" }
}
common bodies
paths
Prototype: paths
Description: Defines an array path
with common paths to standard binaries,
and classes for defined and existing paths.
If the current platform knows that binary XYZ should be present,
_stdlib_has_path_XYZ
is defined. Furthermore, if XYZ is actually present
(i.e. the binary exists) in the expected location, _stdlib_path_exists_XYZ
is
defined.
Example:
bundle agent repair_newlines(filename)
{
commands:
_stdlib_path_exists_sed::
"$(path[sed])"
args => "-i 's/^M//' $(filename)"
}
Implementation:
bundle common paths
{
vars:
#
# Common full pathname of commands for OS
#
enterprise.(am_policy_hub|policy_server)::
"path[git]"
string => "$(sys.workdir)/bin/git",
comment => "CFEngine Enterprise Hub ships with its own git which is used internally";
!(enterprise.(am_policy_hub|policy_server))::
"path[git]" string => "/usr/bin/git";
!(freebsd|darwin|smartos)::
"path[npm]" string => "/usr/bin/npm";
"path[pip]" string => "/usr/bin/pip";
"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";
_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 => "/usr/bin/systemctl";
linux::
"path[lsattr]" string => "/usr/bin/lsattr";
"path[tar]" string => "/bin/tar";
"path[mailx]" string => "/usr/bin/mailx";
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[oslevel]" string => "/usr/bin/oslevel";
"path[ping]" string => "/usr/bin/ping";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[tr]" string => "/usr/bin/tr";
archlinux::
"path[awk]" string => "/usr/bin/awk";
"path[bc]" string => "/usr/bin/bc";
"path[cat]" string => "/usr/bin/cat";
"path[cksum]" string => "/usr/bin/cksum";
"path[crontab]" string => "/usr/bin/crontab";
"path[cut]" string => "/usr/bin/cut";
"path[dc]" string => "/usr/bin/dc";
"path[df]" string => "/usr/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[dmidecode]" string => "/usr/bin/dmidecode";
"path[echo]" string => "/usr/bin/echo";
"path[egrep]" string => "/usr/bin/egrep";
"path[ethtool]" string => "/usr/bin/ethtool";
"path[find]" string => "/usr/bin/find";
"path[free]" string => "/usr/bin/free";
"path[grep]" string => "/usr/bin/grep";
"path[hostname]" string => "/usr/bin/hostname";
"path[init]" string => "/usr/bin/init";
"path[iptables]" string => "/usr/bin/iptables";
"path[iptables_save]" string => "/usr/bin/iptables-save";
"path[iptables_restore]" string => "/usr/bin/iptables-restore";
"path[ls]" string => "/usr/bin/ls";
"path[lsof]" string => "/usr/bin/lsof";
"path[netstat]" string => "/usr/bin/netstat";
"path[ping]" string => "/usr/bin/ping";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[test]" string => "/usr/bin/test";
"path[top]" string => "/usr/bin/top";
"path[tr]" string => "/usr/bin/tr";
#
"path[pacman]" string => "/usr/bin/pacman";
"path[yaourt]" string => "/usr/bin/yaourt";
"path[useradd]" string => "/usr/bin/useradd";
"path[groupadd]" string => "/usr/bin/groupadd";
"path[ip]" string => "/usr/bin/ip";
"path[ifconfig]" string => "/usr/bin/ifconfig";
"path[journalctl]" string => "/usr/bin/journalctl";
"path[netctl]" string => "/usr/bin/netctl";
freebsd|netbsd|openbsd::
"path[awk]" string => "/usr/bin/awk";
"path[bc]" string => "/usr/bin/bc";
"path[cat]" string => "/bin/cat";
"path[crontabs]" string => "/var/cron/tabs";
"path[cut]" string => "/usr/bin/cut";
"path[dc]" string => "/usr/bin/dc";
"path[df]" string => "/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[echo]" string => "/bin/echo";
"path[egrep]" string => "/usr/bin/egrep";
"path[find]" string => "/usr/bin/find";
"path[grep]" string => "/usr/bin/grep";
"path[ls]" string => "/bin/ls";
"path[netstat]" string => "/usr/bin/netstat";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[tr]" string => "/usr/bin/tr";
freebsd.!(freebsd_9_3|freebsd_10|freebsd_11)|netbsd|openbsd::
"path[ping]" string => "/usr/bin/ping";
freebsd_9_3|freebsd_10|freebsd_11::
"path[ping]" string => "/sbin/ping";
freebsd|netbsd::
"path[cksum]" string => "/usr/bin/cksum";
"path[realpath]" string => "/bin/realpath";
freebsd::
"path[getfacl]" string => "/bin/getfacl";
"path[dtrace]" string => "/usr/sbin/dtrace";
"path[zpool]" string => "/sbin/zpool";
"path[zfs]" string => "/sbin/zfs";
openbsd::
"path[cksum]" string => "/bin/cksum";
smartos::
"path[npm]" string => "/opt/local/bin/npm";
"path[pip]" string => "/opt/local/bin/pip";
solaris::
"path[awk]" string => "/usr/bin/awk";
"path[bc]" string => "/usr/bin/bc";
"path[cat]" string => "/usr/bin/cat";
"path[cksum]" string => "/usr/bin/cksum";
"path[crontab]" string => "/usr/bin/crontab";
"path[crontabs]" string => "/var/spool/cron/crontabs";
"path[curl]" string => "/usr/bin/curl";
"path[cut]" string => "/usr/bin/cut";
"path[dc]" string => "/usr/bin/dc";
"path[df]" string => "/usr/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/sbin/dig";
"path[echo]" string => "/usr/bin/echo";
"path[egrep]" string => "/usr/bin/egrep";
"path[find]" string => "/usr/bin/find";
"path[grep]" string => "/usr/bin/grep";
"path[ls]" string => "/usr/bin/ls";
"path[netstat]" string => "/usr/bin/netstat";
"path[ping]" string => "/usr/bin/ping";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[tr]" string => "/usr/bin/tr";
"path[wget]" string => "/usr/bin/wget";
#
"path[svcs]" string => "/usr/bin/svcs";
"path[svcadm]" string => "/usr/sbin/svcadm";
"path[svccfg]" string => "/usr/sbin/svccfg";
"path[svcprop]" string => "/usr/bin/svcprop";
"path[netadm]" string => "/usr/sbin/netadm";
"path[dladm]" string => "/usr/sbin/dladm";
"path[ipadm]" string => "/usr/sbin/ipadm";
"path[pkg]" string => "/usr/bin/pkg";
"path[pkginfo]" string => "/usr/bin/pkginfo";
"path[pkgadd]" string => "/usr/sbin/pkgadd";
"path[pkgrm]" string => "/usr/sbin/pkgrm";
"path[zoneadm]" string => "/usr/sbin/zoneadm";
"path[zonecfg]" string => "/usr/sbin/zonecfg";
redhat::
"path[awk]" string => "/bin/awk";
"path[bc]" string => "/usr/bin/bc";
"path[cat]" string => "/bin/cat";
"path[cksum]" string => "/usr/bin/cksum";
"path[createrepo]" string => "/usr/bin/createrepo";
"path[crontab]" string => "/usr/bin/crontab";
"path[crontabs]" string => "/var/spool/cron";
"path[curl]" string => "/usr/bin/curl";
"path[cut]" string => "/bin/cut";
"path[dc]" string => "/usr/bin/dc";
"path[df]" string => "/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[domainname]" string => "/bin/domainname";
"path[echo]" string => "/bin/echo";
"path[egrep]" string => "/bin/egrep";
"path[ethtool]" string => "/usr/sbin/ethtool";
"path[find]" string => "/usr/bin/find";
"path[free]" string => "/usr/bin/free";
"path[grep]" string => "/bin/grep";
"path[hostname]" string => "/bin/hostname";
"path[init]" string => "/sbin/init";
"path[iptables]" string => "/sbin/iptables";
"path[iptables_save]" string => "/sbin/iptables-save";
"path[ls]" string => "/bin/ls";
"path[lsof]" string => "/usr/sbin/lsof";
"path[netstat]" string => "/bin/netstat";
"path[nologin]" string => "/sbin/nologin";
"path[ping]" string => "/usr/bin/ping";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/bin/sed";
"path[sort]" string => "/bin/sort";
"path[test]" string => "/usr/bin/test";
"path[tr]" string => "/usr/bin/tr";
"path[wc]" string => "/usr/bin/wc";
"path[wget]" string => "/usr/bin/wget";
"path[realpath]" string => "/usr/bin/realpath";
#
"path[chkconfig]" string => "/sbin/chkconfig";
"path[groupadd]" string => "/usr/sbin/groupadd";
"path[groupdel]" string => "/usr/sbin/groupdel";
"path[ifconfig]" string => "/sbin/ifconfig";
"path[ip]" string => "/sbin/ip";
"path[rpm]" string => "/bin/rpm";
"path[service]" string => "/sbin/service";
"path[svc]" string => "/sbin/service";
"path[useradd]" string => "/usr/sbin/useradd";
"path[userdel]" string => "/usr/sbin/userdel";
"path[yum]" string => "/usr/bin/yum";
darwin::
"path[awk]" string => "/usr/bin/awk";
"path[bc]" string => "/usr/bin/bc";
"path[cat]" string => "/bin/cat";
"path[cksum]" string => "/usr/bin/cksum";
"path[createrepo]" string => "/usr/bin/createrepo";
"path[crontab]" string => "/usr/bin/crontab";
"path[crontabs]" string => "/usr/lib/cron/tabs";
"path[cut]" string => "/usr/bin/cut";
"path[dc]" string => "/usr/bin/dc";
"path[df]" string => "/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[domainname]" string => "/bin/domainname";
"path[dscl]" string => "/usr/bin/dscl";
"path[echo]" string => "/bin/echo";
"path[egrep]" string => "/usr/bin/egrep";
"path[find]" string => "/usr/bin/find";
"path[grep]" string => "/usr/bin/grep";
"path[hostname]" string => "/bin/hostname";
"path[ls]" string => "/bin/ls";
"path[lsof]" string => "/usr/sbin/lsof";
"path[netstat]" string => "/usr/sbin/netstat";
"path[ping]" string => "/sbin/ping";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[test]" string => "/bin/test";
"path[tr]" string => "/usr/bin/tr";
#
"path[brew]" string => "/usr/local/bin/brew";
"path[sudo]" string => "/usr/bin/sudo";
debian::
"path[awk]" string => "/usr/bin/awk";
"path[bc]" string => "/usr/bin/bc";
"path[cat]" string => "/bin/cat";
"path[chkconfig]" string => "/sbin/chkconfig";
"path[cksum]" string => "/usr/bin/cksum";
"path[createrepo]" string => "/usr/bin/createrepo";
"path[crontab]" string => "/usr/bin/crontab";
"path[crontabs]" string => "/var/spool/cron/crontabs";
"path[curl]" string => "/usr/bin/curl";
"path[cut]" string => "/usr/bin/cut";
"path[dc]" string => "/usr/bin/dc";
"path[df]" string => "/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[dmidecode]" string => "/usr/sbin/dmidecode";
"path[domainname]" string => "/bin/domainname";
"path[echo]" string => "/bin/echo";
"path[egrep]" string => "/bin/egrep";
"path[ethtool]" string => "/sbin/ethtool";
"path[find]" string => "/usr/bin/find";
"path[free]" string => "/usr/bin/free";
"path[grep]" string => "/bin/grep";
"path[hostname]" string => "/bin/hostname";
"path[init]" string => "/sbin/init";
"path[iptables]" string => "/sbin/iptables";
"path[iptables_save]" string => "/sbin/iptables-save";
"path[ls]" string => "/bin/ls";
"path[lsof]" string => "/usr/bin/lsof";
"path[netstat]" string => "/bin/netstat";
"path[nologin]" string => "/usr/sbin/nologin";
"path[ping]" string => "/bin/ping";
"path[perl]" string => "/usr/bin/perl";
"path[printf]" string => "/usr/bin/printf";
"path[sed]" string => "/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[test]" string => "/usr/bin/test";
"path[tr]" string => "/usr/bin/tr";
"path[wc]" string => "/usr/bin/wc";
"path[wget]" string => "/usr/bin/wget";
"path[realpath]" string => "/usr/bin/realpath";
#
"path[apt_cache]" string => "/usr/bin/apt-cache";
"path[apt_config]" string => "/usr/bin/apt-config";
"path[apt_get]" string => "/usr/bin/apt-get";
"path[apt_key]" string => "/usr/bin/apt-key";
"path[aptitude]" string => "/usr/bin/aptitude";
"path[dpkg]" string => "/usr/bin/dpkg";
"path[groupadd]" string => "/usr/sbin/groupadd";
"path[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";
"path[mailx]" string => "/usr/bin/mailx";
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 is 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 }
See also: scope
Arguments:
scope
: The scope in which the class should be definedclass_prefix
: The unique part of the classes to be defined
This is a more concise version of scoped_classes_generic
.
Key difference is that only suffixed classes are defined, and only for
outcomes that we can know. For example _ok is not defined by this bundle, as
a promsie could be both kept and failed at the same time.
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 errored * _failed indicates the promise failed * _denided indicates the promsie was denied * _timeout indicates the promise timed out
Example: ```cf3 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:**
scoped_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";
scope => "namespace";
}
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";
scope => "namespace";
}
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)"),
if => fileexists( $(log_files) );
}
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");
}
prunetree
Prototype: prunetree(dir, depth, max_days)
Description: Delete files and directories inside "dir" up to "depth" older than "max_days".
Arguments:
dir
: directory to examine for filesdepth
: How many levels to descendmax_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:
"prunetree" usebundle => prunetree( @(dirs), inf, "1" );
}
Implementation:
bundle agent prunetree(dir, depth, max_days)
{
files:
"$(dir)"
comment => "Delete files and directories under $(dir) up to $(depth)
depth older than $(max_days)",
delete => tidy,
file_select => days_old( $(max_days) ),
depth_search => recurse_with_base( $(depth) );
}
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";
}
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";
}
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 => "Insert an ini section with values if not present";
"$(k)=$($(config)[$(k)])"
location => after("[$(name)]");
}
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)");
# if there's more than one comment, just add new (don't know who to use)
"multiple_comments_$(cindex[$(index)])" expression => isgreaterthan("$(index_comment_matches_$(cindex[$(index)]))","1");
replace_patterns:
# If the line is commented out, uncomment and replace with
# the correct value
"^\s*#\s*($(index)\s+.*|$(index))$"
comment => "Uncommented the value $(index)",
replace_with => value("$(index) $($(v)[$(index)])"),
ifvarclass => "!line_exists_$(cindex[$(index)]).!replace_attempted_$(cindex[$(index)])_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_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 intented 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";
redhat::
"platform_default" string => "yum";
}
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_no_locking_knowledge
Prototype: redhat_no_locking_knowledge
Description: common Red Hat knowledge bundle
This common bundle has useful information about Red Hat and its derivatives
Implementation:
bundle common redhat_no_locking_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 3/4, Yum doesn't know how to be --quiet
"yum_options" string => ifelse("centos_4|redhat_4|centos_3|redhat_3", "",
"--quiet ${redhat_no_locking_knowledge.no_locking_option}");
"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.'";
}
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 3/4, Yum doesn't know how to be --quiet
"yum_options" string => ifelse("centos_4|redhat_4|centos_3|redhat_3", "",
"--quiet ${redhat_no_locking_knowledge.no_locking_option}");
"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 => {};
}
yum
Prototype: yum
Implementation:
body package_module yum
{
query_installed_ifelapsed => "60";
query_updates_ifelapsed => "1440";
#default_options => {};
}
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_add -r";
package_delete_command => "/usr/sbin/pkg_delete";
}
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_add -r";
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_enabled" -> { "CFE-2923" }
expression => returnszero( "$(call_systemctl) is-enabled $(service) > /dev/null 2>&1", useshell);
"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",
# 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.(start|restart|reload).(inform_mode|verbose_mode)::
"$(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";
}
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";
classes:
"am_root" expression => strcmp($(this.promiser_uid), "0");
"need_to_drop" not => strcmp($(this.promiser_uid), $(repo_uid));
commands:
am_root.need_to_drop::
"$(oneliner)"
args => "$(subcmd) $(args)",
classes => kept_successful_command,
contain => setuidgid_dir( $(repo_uid), $(repo_gid), $(repo_path) );
!am_root|!need_to_drop::
"$(oneliner)"
args => "$(subcmd) $(args)",
classes => kept_successful_command,
contain => in_dir( $(repo_path) );
reports:
"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 '$(oneliner) $(subcmd) $(args)'";
"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
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
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" ],
},
}
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
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
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
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
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.
secureinput
: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
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_$(){}\[\].]+
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
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
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
meta
:slist
delete
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.*
meta
:slist
depth_search
depth
:int
in range0,99999999999
exclude_dirs
:slist
in range.*
include_basedir
:boolean
include_dirs
:slist
in range.*
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
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.*
meta
:slist
select_field
:int
in range0,99999999
start_fields_from_zero
:boolean
value_separator
:string
in range^.$
environment_interface
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.*
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
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
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
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
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
meta
:slist
select_line_matching
:string
in range.*
match_value
extraction_regex
:string
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
meta
:slist
mount_options
:slist
mount_server
:string
mount_source
:string
in range"?(/.*)
mount_type
: one ofnfs
,nfs2
,nfs3
,nfs4
unmount
:boolean
package_method
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
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_$.-]+
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"?(/.*)
meta
:slist
number_of_lines
:int
in range0,99999999999
process_count
in_range_define
:slist
match_range
:irange
in range0,99999999999
meta
:slist
out_of_range_define
:slist
process_select
command
:string
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,2147483647
tty
:string
vsize
:irange
in range0,99999999999
rename
disable
:boolean
disable_mode
:string
in range[0-7augorwxst,+-]+
disable_suffix
:string
meta
:slist
newname
:string
rotate
:int
in range0,99
replace_with
meta
:slist
occurrences
: one ofall
,first
replace_value
:string
in range.*
report_data_select
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
meta
:slist
select_end
:string
in range.*
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
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%]
meta
:slist
scan_arrivals
:boolean
sensible_count
:int
in range0,99999999999
sensible_size
:int
in range0,99999999999