The Complete CFEngine Reference
Table of Content
- Macros
- Components and Common Control
- Promise Types and Attributes
- Functions
- shuffle
- format
- ago
- "readstringarray"
- string_reverse
- parsejson
- translatepath
- hash
- canonifyuniquely
- string_mustache
- isipinsubnet
- remotescalar
- file_hash
- lsdir
- registryvalue
- reverse
- nth
- returnszero
- countclassesmatching
- readjson
- every
- sublist
- min
- peerleader
- dirname
- fileexists
- bundlesmatching
- readstringlist
- network_connections
- makerule
- regarray
- isvariable
- readyaml
- classmatch
- storejson
- product
- selectservers
- "parsestringarray"
- classify
- ldapvalue
- getfields
- ifelse
- readreallist
- execresult
- data_readstringarray
- usemodule
- hubknowledge
- hostsseen
- rrange
- data_sysctlvalues
- readdata
- data_regextract
- eval
- packagesmatching
- string_upcase
- "on"
- getclassmetatags
- sum
- accessedbefore
- filesexist
- intersection
- hash_to_int
- ip2host
- findprocesses
- regcmp
- length
- some
- getgid
- string_length
- expandrange
- escape
- not
- unique
- lastnode
- getuid
- and
- randomint
- isexecutable
- irange
- getuserinfo
- "readintarray"
- string_head
- variance
- hostinnetgroup
- getenv
- hostswithclass
- string_split
- regline
- countlinesmatching
- classesmatching
- readfile
- readenvfile
- filestat
- reglist
- url_get
- peers
- filter
- mergedata
- peerleaders
- concat
- "parserealarray"
- variablesmatching_as_data
- regldap
- isdir
- findfiles
- maplist
- isplain
- readtcp
- accumulated
- isnewerthan
- readstringarrayidx
- now
- or
- hashmatch
- splitstring
- iprange
- splayclass
- string_downcase
- bundlestate
- mean
- filesize
- callstack_callers
- diskfree
- none
- parseyaml
- "parseintarray"
- userexists
- max
- remoteclassesmatching
- laterthan
- mapdata
- islink
- regextract
- getvalues
- variablesmatching
- strftime
- grep
- canonify
- groupexists
- packageupdatesmatching
- maparray
- changedbefore
- callstack_promisers
- regex_replace
- readcsv
- ldaparray
- parsestringarrayidx
- ldaplist
- sysctlvalue
- string_tail
- data_expand
- processexists
- join
- strcmp
- isgreaterthan
- getusers
- sort
- "readrealarray"
- data_readstringarrayidx
- host2ip
- readintlist
- getvariablemetatags
- datastate
- difference
- islessthan
- getindices
- hostrange
- Special Variables
- Language Concepts
- Enterprise API Reference
- Package Modules
- Masterfiles Policy Framework
- update.cf
- promises.cf
- controls/
- controls/cf_monitord.cf
- controls/update_def_inputs.cf
- controls/cf_serverd.cf
- controls/cf_hub.cf
- controls/update_def.cf
- controls/def.cf
- controls/reports.cf
- controls/cf_agent.cf
- controls/cf_runagent.cf
- controls/cf_execd.cf
- controls/def_inputs.cf
- cfe_internal/
- cfe_internal/CFE_cfengine.cf
- cfe_internal/update/
- cfe_internal/update/update_processes.cf
- cfe_internal/update/systemd_units.cf
- cfe_internal/update/cfe_internal_update_from_repository.cf
- cfe_internal/update/cfe_internal_local_git_remote.cf
- cfe_internal/update/lib.cf
- cfe_internal/update/cfe_internal_dc_workflow.cf
- cfe_internal/update/update_policy.cf
- cfe_internal/update/update_bins.cf
- cfe_internal/core/watchdog/watchdog.cf
- cfe_internal/core/watchdog
- cfe_internal/core/
- cfe_internal/enterprise/
- inventory/
- inventory/suse.cf
- inventory/linux.cf
- inventory/any.cf
- inventory/macos.cf
- inventory/lsb.cf
- inventory/redhat.cf
- inventory/freebsd.cf
- inventory/os.cf
- inventory/windows.cf
- inventory/debian.cf
- inventory/generic.cf
- services/autorun/
- services/
- services/main.cf
- standalone_self_upgrade.cf
- lib/
- lib/databases.cf
- lib/cfengine_enterprise_hub_ha.cf
- lib/cfe_internal.cf
- lib/files.cf
- lib/packages.cf
- lib/vcs.cf
- lib/paths.cf
- lib/event.cf
- lib/guest_environments.cf
- lib/commands.cf
- lib/cfe_internal_hub.cf
- lib/processes.cf
- lib/stdlib.cf
- lib/common.cf
- lib/reports.cf
- lib/monitor.cf
- lib/storage.cf
- lib/examples.cf
- lib/services.cf
- lib/users.cf
- lib/autorun.cf
- lib/edit_xml.cf
- lib/bundles.cf
- lib/feature.cf
- lib/testing.cf
- 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
- Language Concepts
- Special Variables
- Enterprise API Reference
- Syntax, identifiers and names
- Masterfiles Policy Framework
- All Promise and Body Types
- Macros
See Also: All Promise Types, All Body Types
Macros
Macros allow you to target different versions of the CFEngine binaries / parser. They can be used to conditionally include or exclude parts of the policy file, depending on version number, or supported features. A typical use case is to use new functionality or syntax on newer binaries which support it, and provide a different implementation on older versions.
Macros are evaluated in the lexer, before syntax checking. This allows you to put syntax inside a macro which is not valid in all versions. Note that all your binaries must support the macro that you are using. Don't start using new macros until you know that the macro is supported on all versions you are running.
@if
calls have to match up: you can't nest them and each one requires a matching @endif
before the end of the file.
Versions with less specificity are considered equal to the more specific, so 3.15.4
is equal to 3.15
, which is also equal to 3
.
This applies to all version macros.
Minimum version
The contained policy is only included if the version is greater than or equal to the specified version.
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
}
History: This macro was introduced in CFEngine 3.7.0
Maximum version
The contained policy is only included if the version is lower than or equal to the specified version.
Example:
bundle agent extractor
{
@if maximum_version(3.15)
# This policy will only be parsed on versions 3.15 and earlier
vars:
"container" data => old_function_3_15(...);
@endif
}
Note: Don't start using new macros until all your hosts support them.
History: This macro was introduced in CFEngine 3.16.0, 3.15.1, 3.12.4.
At version
The contained policy is only included if the version is equal to the specified version.
Example:
bundle agent extractor
{
@if at_version(3.15)
# This policy will only be parsed on 3.15 clients
vars:
"container" data => old_function_3_15(...);
@endif
}
Note: Don't start using new macros until all your hosts support them.
History: This macro was introduced in CFEngine 3.16.0, 3.15.1, 3.12.4.
Between versions
The contained policy is only included if the version is between (inclusive) the two specified versions.
Example:
bundle agent extractor
{
@if between_versions(3.12, 3.15)
# Policy specific to 3.12, 3.13, 3.14, 3.15
vars:
"container" data => workaround_3_12_3_15(...);
@endif
}
Note: Don't start using new macros until all your hosts support them.
History: This macro was introduced in CFEngine 3.16.0, 3.15.1, 3.12.4.
Before version
The contained policy is only included if the version is below the specified version (Not inclusive).
Example:
bundle agent extractor
{
@if before_version(3.15)
# Policy to work around issue which was fixed in 3.15
vars:
"container"
data => workaround_pre_3_15(...);
@endif
}
Note: Don't start using new macros until all your hosts support them.
History: This macro was introduced in CFEngine 3.16.0, 3.15.1, 3.12.4.
After version
The contained policy is only included if the version is above the specified version (Not inclusive).
Example:
bundle agent extractor
{
@if after_version(3.15)
# This policy is only parsed on 3.16+
vars:
"container"
data => not_neded_on_3_15(...);
@endif
}
Note: Don't start using new macros until all your hosts support them.
History: This macro was introduced in CFEngine 3.16.0, 3.15.1, 3.12.4.
Else
Must come after an @if
macro, and before the matching @endif
.
Inverts the skipping state from the @if
macro.
If the policy before @else
was skipped due to the @if
macro, the policy after will not be skipped, and vice versa.
Example:
bundle agent extractor
{
@if minimum_version(3.16)
# Implementation for 3.16+
vars:
"container"
data => classfiltercsv(...);
@else
# Implementation for versions before 3.16
vars:
"container"
data => readcsv(...);
@endif
}
Note: Don't start using new macros until all your hosts support them.
History: This macro was introduced in CFEngine 3.16.0, 3.15.1, 3.12.4.
Features
You can conditionally include policy test using the @if
macro.
bundle agent extractor
{
@if feature(xml)
# the yaml library may not be compiled in
vars: "container" data => parseyaml(...);
@endif
}
The text will be inserted verbatim in the policy. This happens before syntax validation, so any CFEngine binary that is not compiled with the feature support macro will be able to exclude syntax from possibly incompatible versions.
Currently available features are :
* xml
* yaml
* curl
History: This macro was introduced in CFEngine 3.8.0
Components and Common Control
While promises to configure your system are entirely user-defined, the
details of the operational behavior of the CFEngine software is of
course hard-coded. You can still configure the details of this
behavior using the control
promise bodies. Control behavior is
defined in bodies because the actual promises are fixed and you only
change their details within sensible limits.
See the introduction for a high-level overview of the CFEngine components, and each component's reference documentation for the details about the specific control bodies.
Common Control
The common
control body refers to those promises that are
hard-coded into all the components of CFEngine, and therefore
affect the behavior of all the components.
body common control
{
inputs => {
"update.cf",
"library.cf"
};
bundlesequence => {
update("policy_host.domain.tld"),
"main",
"cfengine2"
};
goal_categories => { "goals", "targets", "milestones" };
goal_patterns => { "goal_.*", "target.*" };
output_prefix => "cfengine>";
version => "1.2.3";
}
bundlesequence
Description: The bundlesequence
contains promise bundles
to verify, in a specific order.
The bundlesequence
determines which of the compiled bundles will be executed
by cf-agent
and in what order they will be executed. The list refers to the
names of bundles (which might be parameterized, function-like objects).
The default value for bundlesequence
is { "main" }
.
A bundlesequence
may also be specified using the -b
or
--bundlesequence
command line option.
Type: slist
Allowed input range: .*
Example:
body common control
{
bundlesequence => {
update("policy_host.domain.tld"),
"main",
"cfengine2"
};
}
Note: Only common
and agent
bundles are allowed to be listed in the
bundlesequence.
The order in which you execute bundles can affect the outcome of your promises. In general you should always define variables before you use them.
The bundlesequence
is like a genetic makeup of a machine. The
bundles act like characteristics of the systems. If you want
different systems to have different bundlesequences
, distinguish
them with classes
webservers::
bundlesequence => { "main", "web" };
others::
bundlesequence => { "main", "otherstuff" };
If you want to add a basic common sequence to all sequences, then use global variable lists to do this:
body common control
{
webservers::
bundlesequence => { @(g.bs), "web" };
others::
bundlesequence => { @(g.bs), "otherstuff" };
}
bundle common g
{
vars:
"bs" slist => { "main", "basic_stuff" };
}
History: The default to { "main" }
was introduced in version 3.7.0, so if
you expect your policies to be run by older version, you'll need an explicit
bundlesequence
.
bwlimit
Description: Coarse control of bandwidth any cf-serverd or cf-agent process will send out. In Bytes/sec.
Bandwidth limit is meant to set an upper bound of traffic coming out of CFEngine agents or servers, as a countermeasure against network abuse from them. The limit is applied to all interfaces (in total), a single process at a time. It can prevent network being flooded by CFEngine traffic when large files or many agents hit a single cf-serverd.
For more fine-grained control, please use operating system (eg. iptables) facilities.
Note: Bandwidth limiting is currently not supported on Windows.
Type: [float
][float]
Default value: none (no limit)
Example:
body common control
{
bwlimit => "10M";
}
In this example, bwlimit is set to 10MBytes/sec = 80Mbit/s meaning that CFEngine would only consume up to ~80% of any 100Mbit ethernet interface.
cache_system_functions
Description: Controls the caching of the results of system
functions, e.g. execresult()
and returnszero()
for shell execution and
ldapvalue()
and friends for LDAP queries. Without this setting,
CFEngine's evaluation model will evaluate functions multiple times,
which is a performance concern. See Functions
.
Although you can override this to false
, in practice you should
almost never need to do so. The effect of having it true
(the
default) is that the expensive functions will be run just once and
then their result will be cached.
Note that caching is per-process so results will not be cached between
runs of e.g. cf-agent
and cf-promises
.
Type: boolean
Default value: true
Example:
cache_system_functions => "true";
History: Was introduced in version 3.6.0.
domain
Description: The domain
string specifies the domain name for this host.
There is no standard, universal or reliable way of determining the DNS domain name of a host, so it can be set explicitly to simplify discovery and name-lookup.
Type: string
Allowed input range: .*
Example:
body common control
{
domain => "example.org";
}
goal_patterns
Description: Contains regular expressions that match promisees/topics considered to be organizational goals
It is used as identifier to mark business and organizational goals in CFEngine Enterprise. CFEngine uses this to match promisees that represent business goals in promises.
Type: slist
Allowed input range: (arbitrary string)
Example:
body common control
{
goal_patterns => { "goal_.*", "target.*" };
}
History: Was introduced in version 3.1.5, Nova 2.1.0 (2011)
ignore_missing_bundles
Description: Determines whether to ignore missing bundles.
If ignore_missing_bundles
is set to true, if any bundles in the bundle
sequence do not exist, ignore and continue.
Type: boolean
Default value: false
Example:
ignore_missing_bundles => "true";
Notes:
This authorizes the bundlesequence to contain possibly "nonexistent" pluggable modules. It defaults to false, whereupon undefined bundles cause a fatal error in parsing, and a transition to failsafe mode.
ignore_missing_inputs
Description: If any input files do not exist, ignore and continue
The inputs lists determines which files are parsed by CFEngine. Normally stringent security checks are made on input files to prevent abuse of the system by unauthorized users.
Sometimes however, it is appropriate to consider the automatic plug-in of modules that might or might not exist. This option permits CFEngine to list possible files that might not exist and continue 'best effort' with those that do exist. The default of all Booleans is false, so the normal behavior is to signal an error if an input is not found.
Type: boolean
Default value: false
Example:
ignore_missing_inputs => "true";
inputs
Description: The inputs
slist contains additional filenames to parse for promises.
The filenames specified are all assumed to be in the same directory
as the file which references them (this is usually
$(sys.workdir)/inputs
, but may be overridden by the -f
or
--file
command line option.
Type: slist
Allowed input range: .*
Example:
body common control
{
inputs => {
"update.cf",
"library.cf"
};
}
See also: inputs
in body file control
Notes:
If no filenames are specified, no other filenames will be included in the compilation process.
Library contents are checked for duplication by path and by hash. For
example, if you put library.cf
twice in your inputs
, the duplicate
library.cf
is noticed because the same path is included twice. A
verbose-level message is emitted but otherwise there is no error.
In addition, if you include a file once with path /x/y/z.cf
and
again with path /x/./y/z.cf
, the duplicate file will be rejected
regardless of any path tricks or symbolic links. The contents are
hashed, so the same file can't be included twice.
lastseenexpireafter
Description: The value of lastseenexpireafter
is the number of minutes
after which last-seen entries are purged. It is an enterprise-only feature.
Type: int
Allowed input range: 0,99999999999
Default value: One week
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
--workdir , -w value - Override the default /var/cfengine work directory for testing (same as setting CFENGINE_TEST_OVERRIDE_WORKDIR)
--debug , -d - Enable debugging output
--define , -D value - Define a list of comma separated classes to be defined at the start of execution
--self-diagnostics, -x value - Run checks to diagnose a CFEngine agent installation
--dry-run , -n - All talk and no action mode - make no changes, only inform of promises not kept
--file , -f value - Specify an alternative input file than the default. 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
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--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
--log-modules , - value - Enable even more detailed debug logging for specific areas of the implementation. Use together with '-d'. Use --log-modules=help for a list of available modules
--show-evaluated-classes, - value - Show *final* evaluated classes, including those defined in common bundles in policy. Optionally can take a regular expression.
--show-evaluated-vars, - value - Show *final* evaluated variables, including those defined without dependency to user-defined classes in policy. Optionally can take a regular expression.
--skip-db-check, - value - Do not run database integrity checks and repairs at startup
Automatic Bootstrapping
Automatic bootstrapping allows the user to connect a CFEngine Host to a Policy
Server without specifying the IP address manually. It uses the Avahi service
discovery implementation of zeroconf
to locate the Policy Server, obtain its IP
address, and then connect to it. To use automatic bootstrap, install the
following Avahi libraries:
- libavahi-client
- libavahi-common
To make the CFEngine Server discoverable, it needs to register itself as an Avahi service. Run the following command:
$ /var/cfengine/bin/cf-serverd -A
This generates the configuration file for Avahi in /etc/avahi/services
and
restarts the Avahi daemon in order to register the new service.
From this point on, the Policy Server will be discovered with the Avahi service.
To verify that the server is visible, run the following command (requires
avahi-utils
):
$ avahi-browse -atr | grep cfenginehub
The sample output looks like this:
eth0 IPv4 CFEngine Community 3.5.0 Policy Server on policy_hub_debian7
_cfenginehub._tcp local
Once the Policy Server is configured with the Avahi service, you can auto-bootstrap Hosts to it.
$ /var/cfengine/bin/cf-agent -B :avahi
The Hosts require Avahi libraries to be installed in order to use this
functionality. By default cf-agent
looks for libraries in standard install
locations. Install locations vary from system to system. If Avahi is
installed in a non-standard location (i.e. compiled from source), set the
AVAHI_PATH
environmental variable to specify the path.
$ AVAHI_PATH=/lib/libavahi-client.so.3 /var/cfengine/bin/cf-agent -B
If more than one server is found, or if the server has more than one IP address, the list of all available servers is printed and the user is asked to manually specify the IP address of the correct server by running the standard bootstrap command of cf-agent:
$ /var/cfengine/bin/cf-agent --bootstrap <IP address>
If only one Policy Server is found in the network, cf-agent
performs the
bootstrap without further manual user intervention.
Note: Automatic bootstrapping support is ONLY for Linux, and it is limited only to one subnet.
Control Promises
Settings describing the details of the fixed behavioral promises
made by cf-agent
.
body agent control
{
# Agent email report settings based on their domain.
alpha_cfengine_com::
domain => "alpha.cfengine.com";
mailto => "admins@alpha.cfengine.com";
beta_domain_com::
domain => "beta.cfengine.com";
mailto => "admins@beta.cfengine.com";
any::
mailfrom => "root";
}
abortclasses
Description: The abortclasses
slist contains regular expressions that
match classes which if defined lead to termination of cf-agent.
Regular expressions are used for classes that cf-agent
will watch out
for. If any matching class becomes defined, it will cause the
current execution of cf-agent
to be aborted. This may be used for
validation, for example.
Type: slist
Allowed input range: .*
Example:
body agent control
{
abortclasses => { "danger.*", "should_not_continue" };
}
bundle agent main
{
methods:
"bundle_a";
"bundle_b";
"bundle_c";
}
bundle agent bundle_a
{
classes:
"abort_condition_a"
expression => "any",
scope => "namespace";
}
bundle common bundle_b
{
classes:
"abort_condition_b" expression => "any";
}
bundle agent bundle_c
{
classes:
# Here we define a class that will match the abortclasses under more complex
# conditions
"should_not_continue"
expression => "(abort_condition_a.abort_condition_b).!something_else",
scope => "namespace";
}
Output:
error: Fatal CFEngine error: cf-agent aborted on defined class 'should_not_continue'
Note: CFEngine class expressions are not supported. To handle class expressions, simply create an alias for the expression with a single name.
abortbundleclasses
Description: The abortbundleclasses
slist contains regular expressions
that match classes which if defined lead to termination of current bundle.
Regular expressions are used for classes, or class expressions
that cf-agent
will watch out for. If any of these classes becomes
defined, it will cause the current bundle to be aborted. This may
be used for validation, for example.
Type: slist
Allowed input range: .*
Example: This example shows how to use the feature to validate input to a method bundle.
body common control
{
bundlesequence => { "testbundle" };
version => "1.2.3";
}
#################################
body agent control
{
abortbundleclasses => { "invalid.*" };
}
#################################
bundle agent testbundle
{
vars:
"userlist" slist => { "xyz", "mark", "jeang", "jonhenrik", "thomas", "eben" };
methods:
"any" usebundle => subtest("$(userlist)");
}
#################################
bundle agent subtest(user)
{
classes:
"invalid" not => regcmp("[a-z]{4}","$(user)");
reports:
!invalid::
"User name $(user) is valid at exactly 4 letters";
# abortbundleclasses will prevent this from being evaluated
invalid::
"User name $(user) is invalid";
}
addclasses
Description: The addclasses
slist contains classes to be defined
always in the current context.
This adds global, literal classes. The only predicates available during the control section are hard-classes.
Type: slist
Allowed input range: .*
Example:
any::
addclasses => { "My_Organization" }
solaris::
addclasses => { "some_solaris_alive", "running_on_sunshine" };
Notes:
Another place to make global aliases for system hardclasses. Classes here are added unequivocally to the system. If classes are used to predicate definition, then they must be defined in terms of global hard classes.
agentaccess
Description: A agentaccess
slist contains user names that are
allowed to execute cf-agent.
This represents a list of user names that will be allowed to attempt execution of the current configuration. This is mainly a sanity check rather than a security measure.
Type: slist
Allowed input range: .*
Example:
agentaccess => { "mark", "root", "sudo" };
agentfacility
Type: (menu option)
Allowed input range:
LOG_USER
LOG_DAEMON
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
Default value: LOG_USER
Description: The agentfacility
menu option policy sets the agent's
syslog facility level.
Example:
agentfacility => "LOG_USER";
Notes:
This is ignored on Windows, as CFEngine Enterprise creates event logs.
See Also: Manual pages for syslog.
allclassesreport
Description: The allclassesreport
menu option policy determines
whether to generate the allclasses.txt
report.
If set to true, the state/allclasses.txt
file will be written to disk
during agent execution.
Type: boolean
Default value: false
Example:
body agent control
{
allclassesreport => "true";
}
Notes:
This functionality is retained only for CFEngine 2 compatibility. As of
CFEngine 3.5, the classesmatching()
function provides
a more convenient way to retrieve a list of set classes at execution time.
History: Was introduced in 3.2.4, Enterprise 2.1.4 (2011)
alwaysvalidate
Description: The alwaysvalidate
menu option policy is a true/false
flag to determine whether configurations will always be checked before
executing, or only after updates.
Type: boolean
Example:
body agent control
{
Min00_05::
# revalidate once per hour, regardless of change in configuration
alwaysvalidate => "true";
}
Notes:
The agents cf-agent
and cfserverd
can run cf-promises
to
validate inputs before attempting to execute a configuration. As of
version 3.1.2 core, this only happens if the configuration file has
changed to save CPU cycles. When this attribute is set, cf-agent
will force a revalidation of the input.
History: Was introduced in version 3.1.2,Enterprise 2.0.1 (2010)
auditing
Deprecated: This menu option policy is deprecated, does nothing and is kept for backward compatibility.
binarypaddingchar
Deprecated: This attribute was deprecated in 3.6.0.
bindtointerface
Description: The bindtointerface
string describes the interface
to be used for outgoing connections.
On multi-homed hosts, the server and client can bind to a specific interface for server traffic. The IP address of the interface must be given as the argument, not the device name.
Type: string
Allowed input range: .*
Example:
bindtointerface => "192.168.1.1";
hashupdates
Description: The hashupdates
determines whether stored hashes are
updated when change is detected in source.
If 'true' the stored reference value is updated as soon as a warning message has been given. As most changes are benign (package updates etc) this is a common setting.
Type: boolean
Default value: false
Example:
body agent control
{
hashupdates => "true";
}
childlibpath
Description: The childlibpath
string contains the LD_LIBRARY_PATH
for child processes.
This string may be used to set the internal LD_LIBRARY_PATH
environment
of the agent.
Type: string
Allowed input range: .*
Example:
body agent control
{
childlibpath => "/usr/local/lib:/usr/local/gnu/lib";
}
checksum_alert_time
Description: The value of checksum_alert_time represents the persistence time for the checksum_alert class.
When checksum changes trigger an alert, this is registered as a persistent class. This value determines the longevity of that class.
Type: int
Allowed input range: 0,60
Default value: 10 mins
Example:
body agent control
{
checksum_alert_time => "30";
}
defaultcopytype
Description: The defaultcopytype
menu option policy sets the global
default policy for comparing source and image in copy transactions.
Type: (menu option)
Allowed input range:
mtime
atime
ctime
digest
hash
binary
Example:
body agent control
{
#...
defaultcopytype => "digest";
}
default_repository
Description: The default_repository
string contains the path to the
default file repository.
If defined the default repository is the location where versions of files altered by CFEngine are stored. This should be understood in relation to the policy for 'backup' in copying, editing etc. If the backups are time-stamped, this becomes effective a version control repository.
Type: string
Allowed input range: "?(/.*)
Default value: 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";
}
select_end_match_eof
Description: When select_end_match_eof
is set to true select_end
will consider end of file as the end region if it is unable to match
the end pattern. For more details see edit_line
promise.
Note: bundle edit_line select_end_match_eof can override this setting at the individual promise level.
Type: boolean
Default value: false
Example:
body agent control
{
select_end_match_eof => "true";
}
sensiblecount
Description: The value of sensiblecount
represents the minimum
number of files a mounted filesystem is expected to have.
Type: int
Allowed input range: 0,99999999999
Default value: 2 files
Example:
body agent control
{
sensiblecount => "20";
}
sensiblesize
Description: The value of sensiblesize
represents the minimum
number of bytes a mounted filesystem is expected to have.
Type: int
Allowed input range: 0,99999999999
Default value: 1000 bytes
Example:
body agent control
{
sensiblesize => "20K";
}
skipidentify
Description: The skipidentify
menu option policy determines whether
to send an IP/name during server connection because address resolution is
broken.
Hosts that are not registered in DNS cannot supply reasonable credentials for a secondary confirmation of their identity to a CFEngine server. This causes the agent to ignore its missing DNS credentials.
Type: boolean
Default value: false
Example:
body agent control
{
skipidentify => "true";
}
suspiciousnames
Description: The suspiciousnames
slist contains names to skip and warn
about if found during any file search.
If CFEngine sees these names during recursive (depth) file searches, it will skip them and output a warning message.
Type: slist
Allowed input range: (arbitrary string)
Example:
body agent control
{
suspiciousnames => { ".mo", "lrk3", "rootkit" };
}
syslog
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";
}
report_class_log
Description: The report_class_log
option enables logging of classes set by
cf-agent. Each class set by cf-agent will be logged at the end of agent
execution (all classes defined during the same cf-agent execution will have the
same timestamp).
Time classes are ignored. Destination: '/var/cfengine/state/classes.jsonl'
Format(jsonl):
{"name":"class_123","timestamp":1456933993}\r\n
{"name":"pk_sha_123","timestamp":1456933993}\r\n
Type: boolean
Default value: false
Example:
body agent control
{
report_class_log => "true";
}
History:
- Added in 3.9.0
Notes:
- Available in CFEngine Enterprise.
- Persistent classes are logged with the timestamp of each agent run.
The following classes are excluded from logging:
- Time based classes (
Hr01
,Tuesday
,Morning
, etc ...) license_expired
any
from_cfexecd
- Life cycle (
Lcycle_0
,GMT_Lcycle_3
)
cf-serverd
cf-serverd
is a socket listening daemon providing two services: it acts as a
file server for remote file copying and it allows an authorized
cf-runagent
to start a cf-agent
run. cf-agent
typically connects to a cf-serverd
instance to request updated policy code,
but may also request additional files for download. cf-serverd
employs
role based access control (defined in policy code) to authorize
requests.
cf-serverd
keeps the promises made in common
and server
bundles, and is
affected by common
and server
control bodies.
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
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--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" };
allowallconnects => { "127.0.0.1" , "::1" };
# Uncomment me under controlled circumstances
#trustkeysfrom => { "127.0.0.1" , "::1" };
}
allowconnects
Description: List of IP addresses that may connect to the server port. They are denoted in either IP or subnet form. For compatibility reasons, regular expressions are also accepted.
This is the first line of defence; clients who are not in this list may not connect or send any data to the server.
See also the warning about regular expressions in
allowallconnects
.
Type: slist
Allowed input range: (arbitrary string)
Examples:
allowconnects => {
"127.0.0.1",
"::1",
"200.1.10.0/24",
"200\.1\.10\..*",
};
allowallconnects
Description: List of IP addresses that may have more than one connection to the server port. They are denoted in either IP or subnet form. For compatibility reasons, regular expressions are also accepted.
The clients that are not listed here may have only one open connection at the time with the server.
Note that 127.0.0.1
is a regular expression (i.e., "127 any
character 0 any character 0 any character 1"), but this will only
match the IP address 127.0.0.1
. Take care with IP addresses and
domain names, as the hostname regular expression www.domain.com
will potentially match more than one hostname (e.g.,
wwwxdomain.com
, in addition to the desired hostname
www.domain.com
).
Type: slist
Allowed input range: (arbitrary string)
Examples:
allowallconnects => {
"127.0.0.1",
"::1",
"200.1.10.0/24",
"200\.1\.10\..*",
};
allowlegacyconnects
Description: List of hosts from which the server accepts connections that are not using the latest protocol.
To define subnets or address ranges, use CIDR notation:
allowlegacyconnects => { "192.168.1.0/24", "192.168.2.123" }
In CFEngine <= 3.8, absence of this attribute means that connections from all hosts are accepted, for compatibility with pre-3.6 CFEngine versions.
Set this attribute to an empty list to not allow any incoming connections using legacy protocol versions:
allowlegacyconnects => { }
In CFEngine >= 3.9, legacy protocol is disallowed by default, and you have to specify a list of hosts allowed to use the legacy protocol.
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";
}
See also: cf-runagent, bundle resource_type in server access promises
call_collect_interval
CFEngine Enterprise only.
Description: The interval in minutes in between collect calls to the CFEngine Server offering a tunnel for report collection.
If option time is set, it causes the server daemon to peer with a policy hub by attempting a connection at regular intervals of the value of the parameter in minutes.
This feature is designed to allow Enterprise report collection from
hosts that are not directly addressable from a hub data-aggregation
process. For example, if some of the clients of a policy hub are
behind NAT or firewall then the hub possibly is not able to
open a connection to port 5308 of the client. The solution is to
enable call_collect_interval
on the client's cf-serverd.
Note: also remember to admit the client's IP on the hub's
collect_calls
ACL (see resource_type
in
bundle server access_rules
).
If this option is set, the client's cf-serverd
will "peer" with
the server daemon on a policy hub. This means that, cf-serverd
on
an unreachable (e.g. NATed) host will attempt to report in to the
cf-serverd
on its assigned policy hub and offer it a short time
window in which to download reports over the established
connection. The effect is to establish a temporary secure tunnel
between hosts, initiated from the satellite host end. The
connection is made in such a way that host autonomy is not
compromised. Either hub may refuse or decline to play their role at
any time, in the usual way (avoiding DOS attacks). Normal access
controls must be set for communication in both directions.
Collect calling cannot be as efficient as data collection by the
cf-hub
, as the hub is not able to load balance. Hosts that use this
approach should exclude themselves from the cf-hub data
collection.
The sequence of events is this:
- The host's
cf-serverd
connects to its registered CFEngine Server - The host identifies itself to authentication and access control and sends a collect-call pull-request to the server
- The server might honor this, if the access control grants access.
- If access is granted, the server has
collect_window
seconds to initiate a query to the host for its reports. - The server identifies itself to authentication and access control and sends a query request to the host to collect the reports.
- When finished, the host closes the tunnel.
Type: int
Allowed input range: 0,99999999999
Example:
call_collect_interval => "5";
The full configuration would look something like this
#########################################################
# Server config
#########################################################
body server control
{
allowconnects => { "10.10.10.0/24" , "::1" };
allowallconnects => { "10.10.10.0/24" , "::1" };
trustkeysfrom => { "10.10.10.0/24" , "::1" };
call_collect_interval => "5";
}
#########################################################
bundle server access_rules()
{
access:
policy_server::
"collect_calls"
resource_type => "query",
admit => { "10.10.10.10" };
satellite_hosts::
"delta"
comment => "Grant access to cfengine hub to collect report deltas",
resource_type => "query",
admit => { "policy_hub" };
"full"
comment => "Grant access to cfengine hub to collect full report dump",
resource_type => "query",
admit => { "policy_hub" };
}
History: Was introduced in Enterprise 3.0.0 (2012)
collect_window
CFEngine Enterprise only.
Description: A time in seconds that a collect-call tunnel remains open to a hub to attempt a report transfer before it is closed
Type: int
Allowed input range: 0,99999999999
collect_window => "15";
Default value: 30.
History: Was introduced in Enterprise 3.0.0 (2012)
denybadclocks
Description: true/false accept connections from hosts with clocks that are out of sync
A possible form of attack on the fileserver is to request files based on time by setting the clocks incorrectly. This option prevents connections from clients whose clocks are drifting too far from the server clock (where "too far" is currently defined as "more than an hour off"). This serves as a warning about clock asynchronization and also a protection against Denial of Service attempts based on clock corruption.
Type: boolean
Default value: true
Example:
body server control
{
denybadclocks => "true";
}
denyconnects
Description: List of IPs that may NOT connect to the server port
Hosts or IP addresses that are explicitly denied access. This should only be used in special circumstances. One should never grant generic access to everything and then deny special cases. Since the default server behavior is to grant no access to anything, this list is unnecessary unless you have already granted access to some set of hosts using a generic pattern, to which you intend to make an exception.
See also the warning about regular expressions in
allowallconnects
.
Type: slist
Allowed input range: (arbitrary string)
Example:
body server control
{
denyconnects => { "badhost\.domain\.evil", "host3\.domain\.com" };
}
logallconnections
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 concurrent connections the server will accept. Recommended value for a hub is two times the total number of hosts bootstrapped to this hub.
Type: int
Allowed input range: 0,99999999999
Default value: 30
Example:
# client side
body agent control
{
maxconnections => "1000";
}
# server side
body server control
{
maxconnections => "1000";
}
port
Description: Default port for the CFEngine server
Type: int
Allowed input range: 1,65535
Default value: 5308
Example:
body hub control
{
port => "5308";
}
body server control
{
specialhost::
port => "5308";
!specialhost::
port => "5308";
}
Notes:
The standard or registered port number is tcp/5308. CFEngine does not presently use its registered udp port with the same number, but this could change in the future.
Changing the standard port number is not recommended practice. You should not do it without a good reason.
serverfacility
Description: Menu option for syslog facility level
Type: (menu option)
Allowed input range:
LOG_USER
LOG_DAEMON
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
See syslog notes.
Default value: LOG_USER
Example:
body server control
{
serverfacility => "LOG_USER";
}
skipverify
Description: This option is obsolete, does nothing and is retained for backward compatibility.
Type: slist
Allowed input range: (arbitrary string)
Example:
body server control
{
skipverify => { "special_host.*", "192.168\..*" };
}
trustkeysfrom
Description: List of IPs from whom the server will accept and trust new (untrusted) public keys. They are denoted in either IP or subnet form. For compatibility reasons, regular expressions are also accepted.
The new accepted public keys are written to the ppkeys
directory, and a message is logged:
192.168.122.254> Trusting new key: MD5=0d5603d68dd62d35bab2150e35d055ae
NOTE: trustkeysfrom
should normally be an empty list except in
controlled circumstances, for example when the network is being set up
and keys are to be exchanged for the first time.
See also the warning about regular expressions in
allowallconnects
.
Type: slist
Allowed input range: (arbitrary string)
Example:
body server control
{
trustkeysfrom => { "10.0.1.1", "192.168.0.0/16"};
}
listen
Description: true/false enable server daemon to listen on defined port
This attribute allows to disable cf-serverd
from listening on any
port. Should be used in conjunction with call_collect_interval
.
This setting only applies to CFEngine clients, the policy hub will
not be affected. Changing this setting requires a restart of
cf-serverd
for the change to take effect.
Type: boolean
Default value: true
Example:
body server control
{
listening_host_context::
listen => "true";
!listening_host_context::
listen => "false";
}
History: Was introduced in 3.4.0, Enterprise 3.0 (2012)
Deprecated attributes in body server control
The following attributes were functional in previous versions of CFEngine, but today they are deprecated, either because their functionality is being handled trasparently or because it doesn't apply to current CFEngine version.
auditing
dynamicaddresses
hostnamekeys
keycacheTTL
cf-execd
cf-execd
is the scheduling daemon for cf-agent
. It runs
cf-agent
locally according to a schedule specified in policy code (executor
control body). After a cf-agent
run is completed, cf-execd
gathers output
from cf-agent
, and may be configured to email the output to a specified
address. It may also be configured to splay (randomize)
the execution schedule to prevent synchronized cf-agent
runs across a
network.
cf-execd
keeps the promises made in common
bundles, and is affected by
common
and executor
control bodies.
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)
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--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
--skip-db-check, - value - Do not run database integrity checks and repairs at startup
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: "?(/.*)
Note: If exec_command
is not defined cf-agent
will be executed with the failsafe.cf
policy.
Example:
exec_command => "$(sys.workdir)/bin/cf-agent -f update.cf && $(sys.workdir)/bin/cf-agent";
mailfilter_exclude
Description: List of anchored regular expressions that, if matched by a log entry, will cause that log entry to be excluded from agent execution emails.
If no filter is set, cf-execd
acts as if no log entry matches the exclude
pattern. If a log entry also matches a pattern in mailfilter_include
, the
exclude pattern takes precedence.
Type: slist
Allowed input range: .*
Note: Merely adding or removing a pattern that causes the number of matching log entries to change, does not guarantee that the next agent execution will generate an email from cf-execd. The actual output from cf-agent still has to be different from the previous run for an email to be generated.
Example:
body executor control
{
# Ignore agent execution emails about permission errors.
mailfilter_exclude => { ".*Permission denied.*" };
}
History: Introduced in CFEngine 3.9.
mailfilter_include
Description: List of anchored regular expressions that must match a log entry in order for it to be included in agent execution emails.
If no filter is set, cf-execd
acts as if every log entry matches the include
pattern. If a log entry also matches a pattern in mailfilter_exclude
, the
exclude pattern takes precedence.
Type: slist
Allowed input range: .*
Note: Merely adding or removing a pattern that causes the number of matching log entries to change, does not guarantee that the next agent execution will generate an email from cf-execd. The actual output from cf-agent still has to be different from the previous run for an email to be generated.
Example:
body executor control
{
# Only include reports in agent execution emails.
mailfilter_include => { "R:.*" };
}
History: Introduced in CFEngine 3.9.
mailfrom
Description: Email-address cfengine mail appears to come from
Type: string
Allowed input range: .*@.*
Example:
body executor control
{
mailfrom => "mrcfengine@example.org";
}
mailmaxlines
Description: Maximum number of lines of output to send by email
This limit prevents anomalously large outputs from clogging up a system
administrator's mailbox. The output is truncated in the email report, but the
complete original transcript is stored in WORKDIR/outputs/*
where it can be
viewed on demand. A reference to the appropriate file is given.
Type: int
Allowed input range: 0,1000
Default value: 30
Example:
body executor control
{
mailmaxlines => "100";
}
mailsubject
Description: The subject in the mail sent by CFEngine.
The subject can contain system variables, like for example IP address or architecture.
Type: string
Allowed input range: .*
Example:
body executor control
{
mailsubject => "CFEngine report ($(sys.fqhost))";
}
mailto
Description: Email-address cfengine mail is sent to
The address to whom email is sent if an smtp host is configured.
Type: string
Allowed input range: .*@.*
Example:
body executor control
{
mailto => "cfengine_alias@example.org";
}
schedule
Description: The class schedule used by cf-execd for activating cf-agent
The list should contain class expressions comprised of classes
which are visible to the cf-execd
daemon. In principle, any
defined class expression will cause the daemon to wake up and
schedule the execution of the cf-agent
. In practice, the classes
listed in the list are usually date- and time-based.
The actual execution of cf-agent
may be delayed by splaytime
,
and may be deferred by promise caching and the value of
ifelapsed
. Note also that the effectiveness of the splayclass
function may be affected by changing the schedule
.
Type: slist
Allowed input range: (arbitrary string)
Default value:
schedule => { "Min00", "Min05", "Min10", "Min15", "Min20", "Min25",
"Min30", "Min35", "Min40", "Min45", "Min50", "Min55" };
Example:
body executor control
{
schedule => { "Min00", "(Evening|Night).Min15_20", "Min30", "(Evening|Night).Min45_50" };
}
smtpserver
Description: Name or IP of a willing smtp server for sending email
This should point to a standard port 25 server without encryption. If you are running secured or encrypted email then you should run a mail relay on localhost and point this to localhost.
Type: string
Allowed input range: .*
Example:
body executor control
{
smtpserver => "smtp.example.org";
}
splaytime
Description: Time in minutes to splay this host based on its name hash
Whenever any class listed in the schedule
attribute is present,
cf-execd
can schedule an execution of cf-agent
. The actual
execution will be delayed an integer number of seconds between
0-splaytime
minutes. The specific amount of delay for "this" host
is based on a hash of the hostname. Thus a collection of hosts will
all execute at different times, and surges in network traffic can
be avoided.
A general rule for scaling of small updates is to set the splay time to
runinterval-1 minutes for up a few thousand hosts. For example, the default
schedule executes once every 5 minutes, so the splay time should be set to no
more than 4 minutes. The splaytime
should be set to a value less than the
cf-execd
scheduling interval, else multiple clients might contend for data.
In other words, splaytime
+ cf-agent
run time should be less than the
scheduling interval.
Type: int
Allowed input range: 0,99999999999
Default value: 0
The CFEngine default policy sets splaytime
to 1.
Example:
body executor control
{
splaytime => "2";
}
See also: The splayclass()
function for a task-specific
means for setting splay times.
cf-promises
cf-promises
is a tool for checking CFEngine policy code. It operates by
first parsing policy code checking for syntax errors. Second, it validates the
integrity of policy consisting of multiple files. Third, it checks for
semantic errors, e.g. specific attribute set rules. Finally, cf-promises
attempts to expose errors by partially evaluating the policy, resolving as
many variable and classes promise statements as possible. At no point does
cf-promises
make any changes to the system.
In 3.6.0 and later, cf-promises
will not evaluate function calls
either. This may affect customers who use execresult
for instance.
Use the new --eval-functions yes
command-line option (default is
no
) to retain the old behavior from 3.5.x and earlier.
cf-agent
calls cf-promises
to validate the policy before running
it. In that case --eval-functions
is not specified, so functions
are not evaluated prematurely (as you would expect).
Command reference
--workdir , -w value - Override the work directory for testing (same as setting CFENGINE_TEST_OVERRIDE_WORKDIR)
--eval-functions value - Evaluate functions during syntax checking (may catch more run-time errors). Possible values: 'yes', 'no'. Default is 'yes'
--show-classes value - Show discovered classes, including those defined in common bundles in policy. Optionally can take a regular expression.
--show-vars value - Show discovered variables, including those defined without dependency to user-defined classes in policy. Optionally can take a regular expression.
--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
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--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)
--policy-output-format, -p value - Output the parsed policy. Possible values: 'none', 'cf', 'json' (this file only), 'cf-full', 'json-full' (all parsed promises). Default is 'none'. (experimental)
--syntax-description, -s value - Output a document describing the available syntax elements of CFEngine. Possible values: 'none', 'json'. Default is 'none'.
--full-check , -c - Ensure full policy integrity checks
--warn , -W value - Pass comma-separated <warnings>|all to enable non-default warnings, or error=<warnings>|all
--color , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
--tag-release , -T value - Tag a directory with promises.cf with cf_promises_validated and cf_promises_release_id
--timestamp , -l - Log timestamps on each line of log output
--log-modules , - value - Enable even more detailed debug logging for specific areas of the implementation. Use together with '-d'. Use --log-modules=help for a list of available modules
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.
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--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
--inform , -I - Print basic information about key generation
--debug , -d - Enable debugging output
--verbose , -v - Output verbose information about the behaviour of the agent
--version , -V - Output the version of the software
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--output-file , -f value - Specify an alternative output file than the default.
--key-type , -T value - Specify a RSA key size in bits, the default value is 2048.
--show-hosts , -s - Show lastseen hostnames and IP addresses
--no-truncate , -N - Don't truncate -s / --show-hosts output
--remove-keys , -r value - Remove keys for specified hostname/IP/MD5/SHA (cf-key -r SHA=12345, cf-key -r MD5=12345, cf-key -r host001, cf-key -r 203.0.113.1)
--force-removal, -x - Force removal of keys
--install-license, -l value - Install license file on Enterprise server (CFEngine Enterprise Only)
--print-digest, -p value - Print digest of the specified public key
--trust-key , -t value - Make cf-serverd/cf-agent trust the specified public key. Argument value is of the form [[USER@]IPADDR:]FILENAME where FILENAME is the local path of the public key for client at IPADDR address.
--color , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
--timestamp - Log timestamps on each line of log output
--numeric , -n - Do not lookup host names
cf-runagent
cf-runagent
connects to a list of running instances of
cf-serverd
. It allows foregoing the usual cf-execd
schedule to activate cf-agent
.
Additionally, a user may send classes to be defined
on the remote host. Two kinds of classes may be sent: classes to decide on
which hosts cf-agent
will be started, and classes that the user requests
cf-agent
should define on execution. The latter type is regulated by
cf-serverd
's role based access control.
Command reference
--help , -h - Print the help message
--background , -b value - Parallelize connections (50 by default)
--debug , -d - Enable debugging output
--verbose , -v - Output verbose information about the behaviour of the agent
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--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 - (deprecated)
--hail , -H value - Hail the following comma-separated lists of hosts, overriding default list
--interactive , -i - Enable interactive mode for key trust
--timeout , -t value - Connection timeout, seconds
--color , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
--timestamp , -l - Log timestamps on each line of log output
--log-modules , - value - Enable even more detailed debug logging for specific areas of the implementation. Use together with '-d'. Use --log-modules=help for a list of available modules
--remote-bundles, - value - Bundles to execute on the remote agent
See also: bundle resource_type in server access promises, cfruncommand in body server control
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 value - Continuous update mode of operation. Number of report gathering iterations can be specified.
--color , -C value - Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'
--debug , -d value - Set debugging level 0,1,2,3
--file , -f value - Specify an alternative input file than the default
--no-fork , -F - Run as a foreground processes (do not fork)
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--help , -h - Print the help message
--query-host , -H value - Remote hosts to gather reports from (for -q)
--inject-hosts, -i value - Inject and trust hosts from a hosts file (mapping IPs to public key file paths, one entry per line)
--inform , -I - Print basic information about actions taken
--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 log directory
--manpage , -M - Print the man page
--dry-run , -n - All talk and no action mode - make no changes, only inform of promises not kept
--query , -q value - Collect reports from remote host. Value is 'rebase' or 'delta'. -H option is required.
--splay_updates, -s - Splay/load balance full-updates, overriding bootstrap times, assuming a default 5 minute update schedule.
--timestamp , -t - Log timestamps on each line of log output
--verbose , -v - Output verbose information about the behaviour of the agent
--version , -V - Output the version of the software
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)
cf-check
Utility for diagnosis and repair of local CFEngine databases.
Command reference
Commands:
dump - Print the contents of a database file
diagnose - Assess the health of one or more database files
backup - Copy database files to a timestamped folder
repair - Diagnose, then backup and delete any corrupt databases
version - Print version information
help - Print this help menu
Usage:
$ cf-check <command> [options] [file ...]
Examples:
$ cf-check dump /var/cfengine/state/cf_lastseen.lmdb
$ cf-check lmdump -a /var/cfengine/state/cf_lastseen.lmdb
$ cf-check diagnose
$ cf-check repair
cf-net
cf-net
can be used to send simple protocol commands to a policy server.
It is a Command-Line-Interface (CLI) to the CFEngine network protocol, and a standalone tool.
cf-net
is not needed or used by any of the other binaries.
The tool can be used to send commands like GET
and OPENDIR
without writing policy.
It is in some ways an extremely light-weight version of cf-agent
- policy evaluation is replaced with easy to use command line arguments.
Command reference
--help , -h - Print the help message
--manpage , -M - Print the man page
--host , -H value - Server hostnames or IPs, comma-separated (defaults to policy server)
--debug , -d - Enable debugging output
--verbose , -v - Enable verbose output
--log-level , -g value - Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'
--inform , -I - Enable basic information output
Bootstrapping and cf-key
cf-net
needs a key-pair generated by cf-key
to communicate with a server.
Thus, the easiest way to use cf-net is on a successfully bootstrapped client:
$ sudo /var/cfengine/bin/cf-key
$ sudo /var/cfengine/bin/cf-agent --bootstrap myhostname
$ sudo /var/cfengine/bin/cf-net connect
Connected & authenticated successfully to 'myhostname'
(myhostname
can also be an IP address)
All three commands above are run with sudo, so they access the same key file.
cf-net commands
cf-net
syntax follows the general structure:
$ cf-net [global options] command [command-specific options/arguments]
Note: cf-net
command names are case insensitive, so cf-net get
and cf-net GET
are equivalent.
All other options, arguments and file names are case sensitive.
Help
Description: cf-net help
is used to access help pages for cf-net
.
Example:
$ cf-net help
Usage: cf-net [OPTIONS] command
Options:
[...]
$ cf-net help connect
Command: connect
Usage: cf-net -H 192.168.50.50,192.168.50.51 connect
Description: Checks if host(s) is available by connecting
Note: cf-net --help
cannot be used with arguments like cf-net help
.
Connect
Description: cf-net connect
attempts to connect and authenticate to one or more hosts running cf-serverd
.
If no hostname is specified policy_server.dat
is used (this is true for all cf-net
commands).
Example:
$ sudo /var/cfengine/bin/cf-net -H 192.168.50.50,myhostname,myhostname:5308 connect
Connected & authenticated successfully to '192.168.50.50'
Connected & authenticated successfully to 'myhostname'
Connected & authenticated successfully to 'myhostname:5308'
$ sudo /var/cfengine/bin/cf-net connect
Connected & authenticated successfully to 'myhostname:5308'
Stat
Description: cf-net stat
is similar to UNIX stat, it gives information about a file/directory.
Example:
$ cf-net stat /var/cfengine/masterfiles/update.cf
myhostname:5308:'/var/cfengine/masterfiles/update.cf' is a regular file
$ cf-net stat masterfiles
myhostname:5308:'masterfiles' is a directory
$ cf-net -I stat masterfiles
info: Inform log level enabled
info: Detailed stat output:
mode = 40700, size = 4096,
uid = 0, gid = 0,
atime = 1495551229, mtime = 1495551172
myhostname:5308:'masterfiles' is a directory
Get
Description: Performs a stat
and then get
command, downloading the specified file to the current working directory.
Use the -o
option to specify output path.
Example:
$ cf-net get masterfiles/update.cf
$ ls
cfengine update.cf
$ cf-net get -o test.cf masterfiles/update.cf
$ ls
cfengine test.cf update.cf
Note: The -o
option must come before the remote filename:
Opendir
Description: Similar to UNIX ls
, prints everything inside a directory, in no particular order.
Example:
$ cf-net opendir masterfiles
services
cf_promises_validated
cfe_internal
..
controls
templates
cf_promises_release_id
lib
inventory
update.cf
promises.cf
.
file control
body file control
{
namespace => "name1";
}
bundle agent private
{
....
}
History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)
This directive can be given multiple times within any file, outside of body and bundle definitions.
Only soft classes from common bundles can
be used in class decisions inside file control bodies
.
inputs
Description: The inputs
slist contains additional filenames to parse for promises.
The filenames specified are all assumed to be relative to the directory
of the file which references them. Use an absolute file name if you need an absolute path.
Use sys.libdir
(absolute library path), sys.local_libdir
(library path relative to the
current masterfiles), and this.promise_dirname
(the directory of the currently processed
file) to avoid hard-coding paths.
See also: inputs
in
body common control
History: Was introduced in CFEngine 3.6.0
namespace
Description: The namespace string identifies a private namespace to switch to in order to protect the current file from duplicate definitions.
Type: string
Allowed input range: [a-zA-Z0-9_$(){}\[\].:]+
Example:
body file control
{
namespace => "name1";
}
Notes:
History: Was introduced in 3.4.0, Enterprise 3.0.0 (2012)
This directive can be given within any file, outside of body and bundle definitions, to change the namespace of subsequent bundles and bodies. A namespace applies until the next namespace declaration in a file, or until the end of a file. This is similar to how class expressions apply until the next class expression or end of bundle.
See Also: namespaces
Promise Types and Attributes
Within a bundle, the promise types are executed in a round-robin fashion in the following normal ordering. Which promise types are available depends on the bundle type:
Promise Type | common | agent | server | monitor |
---|---|---|---|---|
defaults - a default value for bundle parameters | x | x | x | x |
classes - a class, representing a state of the system | x | x | x | x |
meta - information about promise bundles | x | x | x | x |
reports - report a message | x | x | x | x |
vars - a variable, representing a value | x | x | x | x |
commands - execute a command | x | |||
databases - configure a database | x | |||
files - configure a file | x | |||
packages - install a package | x | |||
guest_environments | x | |||
methods - take on a whole bundle of other promises | x | |||
processes - start or terminate processes | x | |||
services - manage services or define new abstractions | x | |||
storage - verify attached storage | x | |||
users - add or remove users | x | |||
access - grant or deny access to file objects | x | |||
roles - allow certain users to activate certain classes | x | |||
measurements - measure or sample data from the system | x |
See each promise type's reference documentation for detailed lists of available attributes.
Common Body Attributes
The following attributes are available to all body types.
inherit_from
Description: Inherits all attributes from another body of the same type as a function call. For a detailed description, see Bodies.
Type: fncall
Allowed input range: (arbitrary body invocation)
Example:
A simple example first, which has no parameters:
body TYPE parent
{
atribute1 => 100;
atribute2 => { "a" };
atribute3 => 75;
}
body TYPE child
{
inherit_from => parent; # same as parent()
atribute3 => 300; # overwrites parent's attribute3
# has atribute1 => 100;
# has atribute2 => { "a" };
}
Now with parameters. The child calls the parent as a function call. Note that the child's parameters can be passed up to the parent.
body TYPE parent(a1, a2)
{
atribute1 => $(a1);
atribute2 => { $(a2) };
atribute3 => 75;
}
body TYPE child(aaa)
{
inherit_from => parent(5, $(aaa));
atribute3 => 300; # overwrites parent's attribute3
# has atribute1 => 5;
# has atribute2 => { $(aaa) };
}
History: Was introduced in 3.8.0.
meta
Description: A list of strings to be associated with the promise for knowledge management purposes. The strings are usually called "meta tags" or simply "tags."
Any promise (of any type) can be given a "meta" attribute. Since the right hand side for this attribute is an slist, multiple strings (tags) can be associated with the same promise.
A "meta" attribute can likewise be added into any body (of any type).
Type: slist
Allowed input range: (arbitrary string list)
Example:
body ANYTYPE mybody
{
meta => { "deprecated" };
}
Another example:
some_promise_type:
any::
"my_promiser"
meta => { "Team Foo", "workaround", "non-critical" };
The meta tags may be referred to programmatically in various ways, or may be solely for human consumption. Meta tags on vars promises and classes promises are particularly suited for programmatic interpretation; meta tags on other promise types (or in bodies) are more likely to be intended only for human consumption.
Relevant CFEngine functions are: classesmatching()
,
classmatch()
, countclassesmatching()
, getclassmetatags()
,
getvariablemetatags()
, variablesmatching()
, variablesmatching_as_data()
Also see meta promises: While "meta" attribute can be added to a promise of any type, there can also be promises of promise type "meta" added to any bundle. If mention is made of "tags" on a bundle, what is actually meant is meta promises in that bundle. (This is just a terminology point.)
Note: When a variable is re-defined the associated meta tags are also re-defined.
History: Was introduced in 3.7.0.
Common Attributes
The following attributes are available to all promise types.
action
Type: body action
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.
action_policy
Description: Determines whether to repair or report about non-kept promises
Type: (menu option)
Allowed input range:
fix
makes changes to move toward the desired statewarn
does not make changes, emits a warning level log message about non-compliance, raise repair_failed (not-kept)nop
alias for warn
Default value: fix
Example:
Policy:
body file control
{
# Include the stdlib for local_dcp, policy, delete_lines
inputs => { "$(sys.libdir)/stdlib.cf" };
}
bundle agent example_action_policy
{
files:
# We make sure there is some files to operate on, so we simply make a copy
# of ourselves
"$(this.promise_filename).nop"
copy_from => local_dcp( $(this.promise_filename) );
"$(this.promise_filename).warn"
copy_from => local_dcp( $(this.promise_filename) );
"$(this.promise_filename).fix"
copy_from => local_dcp( $(this.promise_filename) );
# We exercise each valid value of action_policy (nop, fix, warn) defining
# classes named for the action_policy
"$(this.promise_filename).nop"
handle => "delete_lines_action_nop",
edit_line => delete_lines_matching ( ".*" ),
action => policy( "nop" ),
classes => results( "namespace", "MY_files_promise_nop" );
"$(this.promise_filename).warn"
handle => "delete_lines_action_warn",
edit_line => delete_lines_matching ( ".*" ),
action => policy( "warn" ),
classes => results( "namespace", "MY_files_promise_warn" );
"$(this.promise_filename).fix"
handle => "delete_lines_action_fix",
edit_line => delete_lines_matching ( ".*" ),
action => policy( "fix" ),
classes => results( "namespace", "MY_files_promise_fix" );
commands:
"/bin/echo Running Command nop"
handle => "command_nop",
action => policy( "nop" ),
classes => results( "namespace", "MY_commands_promise_nop" );
"/bin/echo Running Command warn"
handle => "command_warn",
action => policy( "warn" ),
classes => results( "namespace", "MY_commands_promise_warn" );
"/bin/echo Running Command fix"
handle => "command_fix",
action => policy( "fix" ),
classes => results( "namespace", "MY_commands_promise_fix" );
reports:
"MY classes:$(const.n)$(const.t)$(with)"
with => join( "$(const.n)$(const.t)", sort( classesmatching( "MY_.*" ), "lex" ));
}
bundle agent __main__
{
methods:
"example_action_policy";
}
This policy can be found in
/var/cfengine/share/doc/examples/action_policy.cf
and downloaded directly from
github.
Output:
warning: Should edit file '/tmp/action_policy.cf.nop' but only a warning promised
warning: Should edit file '/tmp/action_policy.cf.warn' but only a warning promised
warning: Command '/bin/echo Running Command nop' needs to be executed, but only warning was promised
warning: Command '/bin/echo Running Command warn' needs to be executed, but only warning was promised
R: MY classes:
MY_commands_promise_fix_reached
MY_commands_promise_fix_repaired
MY_commands_promise_nop_error
MY_commands_promise_nop_failed
MY_commands_promise_nop_not_kept
MY_commands_promise_nop_reached
MY_commands_promise_warn_error
MY_commands_promise_warn_failed
MY_commands_promise_warn_not_kept
MY_commands_promise_warn_reached
MY_files_promise_fix_reached
MY_files_promise_fix_repaired
MY_files_promise_nop_error
MY_files_promise_nop_failed
MY_files_promise_nop_not_kept
MY_files_promise_nop_reached
MY_files_promise_warn_error
MY_files_promise_warn_failed
MY_files_promise_warn_not_kept
MY_files_promise_warn_reached
warning: Method 'example_action_policy' invoked repairs, but only warnings promised
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";
}
See also: persistance
classes attribute, persist_time
in classes body
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 verbose logs 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.
with
Description: Reusable promise attribute to avoid repetition.
Very often, it's handy to refer to a single value in many places in a promise,
especially in the promiser. Especially when iterating over a list, the with
attribute can save you a lot of work and code.
Another use of the with
attribute is when in reports
you want to use
format()
or other functions that produce lots of text, but don't want to
create an intermediate variable.
Another common use of with
is to avoid canonifying a value. In that case,
you'd use with => canonify("the value")
so you don't have to create a
"canonification" array.
Type: string
Allowed input range: (arbitrary string)
Example:
bundle agent main
{
vars:
"todo" slist => { "a 1", "b 2", "c 3" };
# Here, `with` is the canonified version of $(todo), letting us avoid an
# intermediate canonification array.
"$(with)" string => "$(todo)", with => canonify($(todo));
"complex" data => '
{
"x": 200,
"y": [ 1, 2, null, true, false ]
}
';
reports:
"For iterable '$(todo)' we created variable '$(with)' and its value is '$(todo)'"
with => canonify($(todo));
"We can print a data container compactly without creating a temporary variable: $(with)"
with => format("%S", complex);
"We can print a data container fully without creating a temporary variable: $(with)"
with => storejson(complex);
}
Output:
R: For iterable 'a 1' we created variable 'a_1' and its value is 'a 1'
R: For iterable 'b 2' we created variable 'b_2' and its value is 'b 2'
R: For iterable 'c 3' we created variable 'c_3' and its value is 'c 3'
R: We can print a data container compactly without creating a temporary variable: {"x":200,"y":[1,2,null,true,false]}
R: We can print a data container fully without creating a temporary variable: {
"x": 200,
"y": [
1,
2,
null,
true,
false
]
}
History: Was introduced in 3.11.0
bundle edit_line
Line based editing is a simple model for editing files. Before XML, and later JSON, most configuration files were line based. The line-based editing offers a powerful environment for model-based editing and templating.
File editing in CFEngine 3
File editing is not just a single kind of promise but a whole range of 'promises within files'. It is therefore not merely a body to a single kind of promise, but a bundle of sub-promises. After all, inside each file are new objects that can make promises, quite separate from files' external attributes.
A typical file editing stanza has the elements in the following example:
body common control
{
version => "1.2.3";
bundlesequence => { "outerbundle" };
}
bundle agent outerbundle
{
files:
"/home/mark/tmp/cf3_test"
create => "true", # Like autocreate in cf2
edit_line => inner_bundle;
}
bundle edit_line inner_bundle
{
vars:
"who" string => "SysAdmin John"; # private variable in bundle
insert_lines:
"/* This file is maintained by CFEngine (see $(who) for details) */",
location => first_line;
replace_patterns:
# replace shell comments with C comments
"#(.*)"
replace_with => C_comment,
select_region => MySection("New section");
reports:
"This is file $(edit.filename)";
}
body replace_with C_comment
{
replace_value => "/* $(match.1) */"; # backreference
occurrences => "all"; # first, last all
}
body select_region MySection(x)
{
select_start => "\[$(x)\]";
select_end => "\[.*\]";
}
body location first_line
{
before_after => "before";
first_last => "first";
select_line_matching => ".*";
}
There are several things to notice:
- The line-editing promises are all convergent promises about patterns within the file. They have bodies, just like other attributes do and these allow us to make simple templates about file editing while extending the power of the basic primitives.
All file edits specified in a single
edit_line
bundle are handled "atomically". CFEngine edits files like this:- CFEngine makes a copy of the file you you want to edit.
- CFEngine makes all the edits in the copy of the file. The filename is the same as your original file with the extension .cf-after-edit appended.
- After all edits are complete (the
delete_lines
,field_edits
,insert_lines
, and finallyreplace_patterns
promises), CFEngine checks to see if the new file is the same as the original one. If there are no differences, the promises have converged, so it deletes the copy, and the original is left completely unmodified. - If there are any differences, CFEngine makes a copy of your original file with the extension .cf-before-edit (so you always have the most recent backup available), and then renames the edited version to your original filename.
Because file rename is an atomic operation (guaranteed by the operating system), any application program will either see the old version of the file or the new one. There is no "window of opportunity" where a partially edited file can be seen (unless an application intentionally looks for the .cf-after-edit file). Problems during editing (such as disk-full or permission errors) are likewise detected, and CFEngine will not rename a partial file over your original.
All pattern matching is through Perl Compatible Regular Expressions
Editing takes place within a marked region (which defaults to the whole file if not otherwise specified).
Search/replace functions now allow back-references.
The line edit model now contains a field or column model for dealing with tabular files such as Unix passwd and group files. We can now apply powerful convergent editing operations to single fields inside a table, to append, order and delete items from lists inside fields.
The special variable
$(edit.filename)
contains the name of the file being edited within an edit bundle.On Windows, a text file may be stored stored either with CRLF line endings (Windows style), or LF line endings (Unix style). CFEngine will respect the existing line ending type and make modifications using the same type. New files will get CRLF line ending type.
In the example above, back references are used to allow conversion of comments from shell-style to C-style.
Another example of files promises is to look for changes in files. The
following example reports on all recent changes to files in a directory
by maintaining the most recent version of the md5
hash of the file
contents. Similar checks can be used to examine metadata or both the
contents and metadata, as well as using different difference checks. The
Community Edition only reports that changes were found, but Enterprise
versions of CFEngine can also report on what exactly the significant
changes were.
bundle agent example
{
files:
"/home/mark/tmp" -> "Security team"
changes => lay_a_tripwire,
depth_search => recurse("inf"),
action => background;
}
body changes lay_a_tripwire
{
hash => "md5";
report_changes => "content";
update => "yes";
}
Scope and lifetime of the select_region
The region selected with select_region
exists during the lifetime of the promise.
This means that once a promise has been started the selected region will be used regardless
of what the changes are.
There is a down side to this, promise lifetime is shorter than expected. For instance let's look at the following code example:
bundle agent init
{
vars:
"states" slist => { "actual", "expected" };
"actual" string =>
"header
header
BEGIN
One potato
Two potato
Three potatoe
Four
END
trailer
trailer";
"expected" string =>
"header
header
One potato
Two potato
Four
trailer
trailer";
files:
"testfile.$(states)"
create => "true",
edit_line => init_insert("$(init.$(states))"),
edit_defaults => init_empty;
}
bundle edit_line init_insert(str)
{
insert_lines:
"$(str)";
}
body edit_defaults init_empty
{
empty_file_before_editing => "true";
}
#######################################################
bundle agent test
{
vars:
"tstr" slist => { "BEGIN", " Three potatoe", "END" };
files:
"testfile.actual"
edit_line => test_delete("$(test.tstr)");
}
bundle edit_line test_delete(str)
{
delete_lines:
"$(str)"
select_region => test_select;
}
body select_region test_select
{
select_start => "BEGIN";
select_end => "END";
include_start_delimiter => "true";
include_end_delimiter => "true";
select_end_match_eof => "true";
}
The code generates two files, testfile.actual
and testfile.expected
. The idea is that both files will be
equal after the promise is run, since the transformations applied to testfile.actual
will convert it into
testfile.equal
.
However due to the lifetime of promises, this is not true. The attribute select_region
lives as long as the
promise that created it lives and it will be recreated on the next incarnation.
Notice that tstr
is a slist
that is used as a parameter for edit_line
, which uses it to select the strings that
will be removed. The select_region
body specifies that the select_start
attribute is "BEGIN", which holds true only
for the first invocation of the promise because during that iteration it will be removed. Once it is removed
the select_region
body will never be able to match select_start
again.
In the previous example, it is easy to think that the select_region
will be kept during the whole iteration of the
slist
. This is not true, each element in the slist
will trigger its own invocation of the promise, therefore
select_region
will only match the first iteration.
The solution to this problem is simple: if the marker for a region needs to be removed, then it cannot be used as a marker. In the example above it is enough to change the markers from "BEGIN" to "header" and from "END" to "trailer" to obtain the desired result.
Attributes
select_region
Type: body select_region
Description: Restrict edit_line promise to specific section
Restrict edits to a specific region of a file based on select_start
and select_end
regular expressions. If the beginning and ending regular
expressions match more than one region only the first region will be
selected for editing.
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.
The default behavior can be modified by using select_end_match_eof
or select_end_match_eof
.
select_end_match_eof
Description: When select_end_match_eof
is set to true select_end
will consider end of file as the end region if it is unable to match
the end pattern.
See also select_start
and select_end
. These delimiters
mark out the region of a file to be edited.
Type: boolean
Default value: false
Example:
body select_region example(x)
{
select_start => "\[$(x)\]";
select_end => "\[.*\]";
select_end_match_eof => "true";
}
If the select_end
attribute is omitted, the selected region will run to the end of the file no matter what the value of select_end_match_eof
is set to.
Note: The global body agent control `select_end_match_eof sets the default behaviour for the entire policy. The local edit_line
promise constraint takes precedence over the body agent control
configuration option.
History: This attribute was introduced in CFEngine version 3.9.0 (2016)
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 => ",";
}
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";
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)";
}
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.
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
with
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)
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.
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.
Noteable differences from package_method
based implementation:
The promiser must be the fully qualified path to a file or a package name.
package_modules
do not have the concept of a flexible naming convention.For example, here are valid ways to specify a specific package version when using the
package_module
based implementation.packages: debian:: "apache2" policy => "present", version => "2.2.22", package_module => apt_get, comment => "Install apache from repository"; redhat:: "/mnt/nfs/packages/apache2-2.2.22.x86_64.rpm" policy => "present", package_module => yum, comment => "Install apache from a specific RPM";
The following usage is NOT valid.
packages: debian:: "apache2-2.2.22" policy => "present", package_module => apt_get, comment => "INVALID specification of package version"; redhat:: "/mnt/nfs/packages/apache2-2.2.22.x86_64.rpm" policy => "present", package_module => yum, version => "2.2.22", comment => "INVALID specification of package version.";
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
with
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" };
debian::
"php7.0"
policy => "present",
package_module => apt_get,
options => { "-o", "APT::Install-Recommends=0" };
policy
Description: Whether the package should be present or absent on the system.
Default value: present
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
interpreter
Description: Absolute path to the interpreter to run the package module (script) with.
If the package module is implemented as a script, it has to be executed with some interpreter. Using a hashbang/shebang may not always be possible or easy (different versions/paths of python, etc.). This attribute tells CFEngine the path to the interpreter to use when running the package module.
Type: string
Allowed input range: (arbitrary string)
Example:
body package_module apt_get
{
# better use variable like $(def.python)
interpreter => "/usr/bin/python3.6";
}
See Also: Package Modules
History: Introduced in 3.13.0, 3.12.2
module_path
Description: Absolute path to the the package module.
By default, the package module implementation has to be in a file with the same
name as the package module itself, under the $(sys.workdir)/modules/packages
directory. In some cases, it might be useful to use a different name or path or
to have multiple package modules using the same implementation just with
different attributes (e.g. default_options
).
Type: string
Allowed input range: (arbitrary string)
Example:
body package_module yum_all_repos
{
module_path => $(sys.workdir)/modules/packages/yum;
default_options => { "--enablerepo=*" };
}
See Also: Package Modules
History: Introduced in 3.13.0, 3.12.2
Package modules out-of-the-box
yum
Manage packages using yum
. This is the default package module for Red Hat, CentOS and Amazon Linux.
Examples:
File based package source.
packages:
redhat|centos|amazon_linux::
"/mnt/nfs/packages/httpd-2.2.22.x86_64.rpm"
policy => "present";
Repository based package source with a specific version of the package.
packages:
redhat|centos|amazon_linux::
"httpd"
policy => "present",
version => "2.2.22";
Enable a specific repository for a specific promise.
bundle agent example
{
packages:
redhat|centos|amazon_linux::
# Enable the EPEL repo when making sure git is installed
# and up to date.
"git"
policy => "present",
version => "latest",
options => { "enablerepo=EPEL" };
# Only consider updates from the main repositories for
# httpd and disable all other repositories
"httpd"
policy => "present",
version => "latest",
options => { "disablerepo=* enablerepo=UPDATES" };
}
Notes:
Supports file path and repository sourced packages.
Requires Python version 2 or 3 to be installed on the host.
If
policy => "latest"
andversion
is set this package module will downgrade the promised package if necessary.[root ~]# yum --show-duplicates list screen Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: centos.mirror.constant.com * epel: epel.mirror.constant.com * extras: mirror.ette.biz * updates: mirror.trouble-free.net Installed Packages screen.x86_64 4.1.0-0.25.20120314git3c2946.el7 @base Available Packages screen.x86_64 4.1.0-0.19.20120314git3c2946.el7 local-repo screen.x86_64 4.1.0-0.25.20120314git3c2946.el7 base
Policy with promise that old version of screen is installed.
bundle agent example_yum_downgrades_if_necessary { packages: redhat_7|centos_7:: "screen" policy => "present", version => "4.1.0-0.19.20120314git3c2946.el7"; }
Executing policy and the version of screen installed after policy run.
[root ~]# cf-agent -Kb example_yum_downgrades_if_necessary; rpm -q screen screen-4.1.0-0.19.20120314git3c2946.el7.x86_64
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
slackpkg
Manage packages using Slackware's slackpkg.
Example
packages:
slackware::
"nmap"
policy => "absent",
package_module => slackpkg;
History:
- Added in CFEngine 3.12.0
msiexec
Manage MSI packages using MSI installer on Windows.
Due to lack of central software repository on supported versions of Windows, neither installation from repository nor checking for upgrades is supported. The full path to the MSI package file must be supplied in order to promise the package is installed. In order to promise a package is absent (not installed) the package name must be used.
Example: install Google Chrome but prevent it from self-upgrading (otherwise Google Chrome's self-upgrading will conflict with CFEngine ensuring that version from this particluar MSI is installed):
packages:
windows::
"C:\GoogleChromeStandaloneEnterprise.msi"
policy => "present",
package_module => msiexec;
"Google Update Helper"
policy => "absent",
package_module => msiexec;
History:
- Added in CFEngine 3.12.2 and 3.14.0
snap
Manage packages using snap).
bundle agent main
{
packages:
ubuntu::
"genpw"
policy => "present",
package_module => snap;
"genpw"
policy => "absent",
package_module => snap;
"genpw"
policy => "present",
package_module => snap,
version => "2.0.0";
"genpw"
policy => "absent",
package_module => snap,
version => "2.0.0";
"genpw"
policy => "present",
package_module => snap,
version => "latest";
}
History:
- Added in CFEngine 3.15.0, 3.12.3, 3.10.7
Notes:
- version
latest
is not supported when promising an absence list-updates
is not implemented, snaps are automatically updated by default
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
with
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";
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" };
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.
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 recommended 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
with
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.
access
Access promises are conditional promises made by resources living on the server.
The promiser is the name of the resource affected and is interpreted to be a path, unless a
different resource_type
is specified. Access is then granted to hosts listed in admit_ips
,
admit_keys
and admit_hostnames
, or denied using the counterparts deny_ips
, deny_keys
and deny_hostnames
.
You layer the access policy by denying all access and then allowing it only to selected clients, then denying to an even more restricted set.
bundle server access_rules()
{
access:
"/source/directory"
comment => "Access to file transfer",
admit_ips => { "192.168.0.1/24" };
}
For file copy requests, the file becomes transferable to the remote client according to the
conditions specified in the access promise. Use ifencrypted
to grant access only if the
transfer is encrypted in the "classic" CFEngine protocol (the TLS protocol is always encrypted).
When access is granted to a directory, the promise is automatically made about all of its contents and sub-directories.
Use the maproot
attribute (like its NFS counterpart) to control
which hosts can see file objects not owned by the server process
owner.
File resources are specified using an absolute filepath, but can set a shortcut
through
which clients can access the resource using a logical name, without having any detailed
knowledge of the filesystem layout on the server. Specifically in access promises about
files, a special variable context connection
is available with variables ip
, key
and hostname
, containing information about the connection through which access is attempted.
"/var/cfengine/cmdb/$(connection.key).json"
shortcut => "me.json",
admit_keys => { "$(connection.key)" };
In this example, requesting the file me.json
will transfer the file stored on the
server under the name /var/cfengine/cmdb/SHA=....json
to the requesting host,
where it will be received as me.json
.
Note that the usage of the $(connection.*)
variables is strictly
limited to literal strings within the promiser and admit/deny lists; they cannot be
passed to functions or stored in other variables.
With CFEngine Enteprise, access promises can be made about additional query data for reporting and orchestration.
# Grant orchestration communication
"did.*"
comment => "Access to class context (enterprise)",
resource_type => "context",
admit_ips => { "127.0.0.1" };
"value of my test_scalar, can expand variables here - $(sys.host)"
comment => "Grant access to the string in quotes, by name test_scalar",
handle => "test_scalar",
resource_type => "literal",
admit_ips => { "127.0.0.1" };
"XYZ"
comment => "Grant access to contents of persistent scalar variable XYZ",
resource_type => "variable",
admit_ips => { "127.0.0.1" };
# Client grants access to CFEngine hub access
"delta"
comment => "Grant access to cfengine hub to collect report deltas",
resource_type => "query",
report_data_select => default_data_select_host,
admit_ips => { "127.0.0.1" };
"full"
comment => "Grant access to cfengine hub to collect full report dump",
resource_type => "query",
report_data_select => default_data_select_host,
admit_ips => { "127.0.0.1" };
policy_server::
"collect_calls"
comment => "Grant access to cfengine client to request the collection of its reports",
resource_type => "query",
admit_ips => { "10.1.2.0/24" };
}
Using the built-in report_data_select
body default_data_select_host
:
report_data_select => default_data_select_host,
admit => { @(def.policy_servers) };
policy_server.enterprise::
"$(query_types)"
handle => "report_access_grant_$(query_types)_for_hub",
comment => "Grant $(query_types) reporting query for the hub on the policy server",
resource_type => "query",
report_data_select => default_data_select_policy_hub,
admit => { "127.0.0.1", "::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)
Notes: Failure to resolve a hostname or it's reverse results in a denial. Since this control is sensitive to temporary DNS failures, and cases, where reverse DNS is not present, it should be used with extreme scrutiny.
See also: admit_hostnames
, deny_ips
, deny_keys
History: Introduced in CFEngine 3.6.0
deny_ips
Description: A list of IP addresses that should be denied access to the object.
Subnets are specified using CIDR notation.
This overrides the grants in admit_hostnames
, admit_ips
and admit_keys
.
For example, here we'll deny one host, then a subnet, then everyone:
access:
"/path/file"
deny_ips => {"192.168.0.1", "192.168.0.0/24", "0.0.0.0/0"};
Type: slist
Allowed input range: (arbitrary string)
See also: admit_ips
, deny_hostnames
, deny_keys
History: Introduced in CFEngine 3.6.0
deny_keys
Description: A list of RSA keys of hosts that should be denied access to the object.
This overrides the grants in admit_hostnames
, admit_ips
and admit_keys
.
Type: slist
Allowed input range: (arbitrary string)
For example, here we'll deny the fictitious SHA key abcdef
:
access:
"/path/file"
deny_keys => {"SHA=abcdef"};
In Community, MD5 keys are used, so similarly we can deny the
fictitious MD5 key abcdef
:
access:
"/path/file"
deny_keys => {"MD5=abcdef"};
See also: admit_keys
, deny_hostnames
, deny_ips
History: Introduced in CFEngine 3.6.0
admit
Description: The admit
slist can contain a mix of entries in the
syntax of admit_ips
, admit_hostnames
and admit_keys
, and offers
the same functionality. It's a legacy attribute that was split in the
aforementioned attributes, and it's not recommended to use in new
policy.
deny
Description: The deny
slist can contain a mix of entries in the
syntax of deny_ips
, deny_hostnames
and deny_keys
, and offers the
same functionality. It's a legacy attribute that was split in the
aforementioned attributes, and it's not recommended to use in new
policy. Example:
bundle server access_rules()
{
access:
"/directory/"
admit => { "127.0.0.1", ".example.org" },
deny => { "badhost_1.example.org", "badhost_1.example.org" };
}
The best way to write the same policy would be the following:
bundle server access_rules()
{
access:
"/directory/"
admit_ips => { "127.0.0.1" },
admit_hostnames => { ".example.org" },
deny_hostnames => { "badhost_1.example.org", "badhost_1.example.org" };
}
Notes: Only regular expressions or exact matches are allowed in this list, as non-specific matches are too greedy for denial.
deny
will be deprecated in CFEngine 3.7 in favor of deny_ips
,
deny_hostnames
, and deny_keys
.
maproot
Description: The maproot
slist contains host names or IP addresses
to grant full read-privilege on the server.
Normally users authenticated by the server are granted access only to
files owned by them and no-one else. Even if the cf-serverd
process
runs with root privileges on the server side of a client-server
connection, the client is not automatically granted access to download
files owned by non-privileged users. If maproot
is true then remote
root
users are granted access to all files.
A typical case where mapping is important is in making backups of many user files.
Type: slist
Allowed input range: (arbitrary string)
Example:
access:
"/home"
admit_hostnames => { "backup_host.example.org" },
ifencrypted => "true",
# Backup needs to have access to all users
maproot => { "backup_host.example.org" };
Notes:
On Windows, cf-serverd
, maproot
is required to read files if the
connecting user does not own the file on the server.
ifencrypted
Description: The ifencrypted
menu option determines whether the
current file access promise is conditional on the connection from the
client being encrypted.
This option has no effect with the TLS CFEngine protocol, where encryption is always enabled.
If this flag is true a client cannot access the file object unless its connection is encrypted.
Type: boolean
Default value: false
Example:
access:
"/path/file"
admit_hostnames => { ".example.org" },
ifencrypted => "true";
Note: This attribute is a noop when used with
protocol_version
2 or
greater.
See also: protocol_version
, allowtlsversion
, allowciphers
, tls_min_version
, tls_ciphers
, encrypt
, logencryptedtransfers
, ifencrypted
report_data_select
This body is only available in CFEngine Enterprise.
Description: The report_data_select
body restricts which data is included
for query resources, and allows filtering of data reported to the
CFEngine Enterprise server.
Use this body template to control the content of reports collected by the CFEngine Enterprise server, and to strip unwanted data (e.g. temporary variables) from reporting.
By default, no filtering is applied. If include and exclude rules are combined, then the exclude statement is applied to the subset from the include statement.
If more than one report_data_select
body applies to the same host, all of them are applied.
Usage of this body is only allowed in conjunction with using
resource_type => "query"
, as this is the resource type that is being affected.
Type: body report_data_select
Example:
body report_data_select report_data
{
metatags_include => { "inventory", "compliance" };
promise_handle_exclude => { "_.*" };
monitoring_exclude => { "mem_.*swap" };
}
Example:
Here are the built-in report_data_select
bodies default_data_select_host()
and
default_data_select_policy_hub()
:
report_data_select => default_data_select_host,
admit => { @(def.policy_servers) };
policy_server.enterprise::
"$(query_types)"
handle => "report_access_grant_$(query_types)_for_hub",
comment => "Grant $(query_types) reporting query for the hub on the policy server",
resource_type => "query",
report_data_select => default_data_select_policy_hub,
admit => { "127.0.0.1", "::1", @(def.policy_servers) };
}
report_data_select => default_data_select_policy_hub,
admit => { "127.0.0.1", "::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
(resource_type => "path"
).
However, sometimes it is useful to cache literal
strings, hints and
data on the server for easy access (e.g. the contents of variables or
hashed passwords). In the case of literal data, the promise handle
serves as the reference identifier for queries. Queries are instigated
by function calls by any agent.
Type: (menu option)
Allowed input range:
path
literal
context
query
variable
bundle
If the resource type is literal
, CFEngine will grant access to a
literal data string. This string is defined either by the promiser
itself, but the name of the variable is the identifier given by the
promise handle of the access promise, since the promiser string might be
complex.
If the resource type is variable
then the promiser is the name of a
persistent scalar variable defined on the server-host. Currently
persistent scalars are only used internally by Enterprise CFEngine to
hold enumerated classes for orchestration purposes.
If you want to send the value of a policy defined variable in the server host (which for some reason is not available directly through policy on the client, e.g. because they have different policies), then you could use the following construction:
access:
"$(variable_name)"
handle => "variable_name",
resource_type => "literal";
If the resource type is context
, the promiser is treated as a regular
expression to match persistent classes defined on the server host. If
these are matched by the request from the client, they will be
transmitted (See remoteclassesmatching()
).
The term query
may also be used in CFEngine Enterprise to query the server
for data from embedded databases. This is currently for internal use only, and
is used to grant access to report 'menus'. If the promiser of a query request
is called collect_calls
, this grants access to server peering collect-call
tunneling (see also call_collect_interval
).
If the resource type is bundle
then the specific bundles are allowed
to be remotely executed with cf-runagent --remote-bundles
from the
specified hosts. The promiser is an anchored regular expression.
Example:
bundle server access_rules()
{
access:
"value of my test_scalar, can expand variables here - $(sys.host)"
handle => "test_scalar",
comment => "Grant access to contents of test_scalar VAR",
resource_type => "literal",
admit_ips => { "127.0.0.1" };
"XYZ"
resource_type => "variable",
handle => "XYZ",
admit_ips => { "$(sys.policy_hub)" };
"delta"
comment => "Grant access to cfengine hub to collect report deltas",
resource_type => "query",
admit_ips => { "$(sys.policy_hub)" };
"full"
comment => "Grant access to cfengine hub to collect full report dump",
resource_type => "query",
admit_ips => { "$(sys.policy_hub)" };
"magic_bundle"
comment => "Grant access to the hub to activate magic_bundle with cf-runagent",
resource_type => "bundle",
admit_ips => { "$(sys.policy_hub)" };
am_policy_hub::
"collect_calls"
comment => "Enable call-collect report collection for the specific client",
resource_type => "query",
admit_ips => { "1.2.3.4" };
}
See also: --remote-bundles option for cf-runagent, cfruncommand in body server control
History:
bundle
resource_type
added in 3.9.0
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
services
services
type promises in their simplest generic form are an abstraction on
bundles. services
type promises are implemented by mapping a bundle to
service_bundle
in a service_method
body. Reference the
services bodies and bundles in the standard library.
Most commonly services type promises are use to manage standard operating system
services using the platforms standard service management tools via the
standard_services
bundle in the standard library. However, services type
promises can be leveraged to build standard abstractions around custom services
as well.
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
with
service_policy
Description: Policy for service status.
The service_policy
is expected to be passed to the service bundle in order to
manage it's state. It is up to the mapped service_bundle
to determine which
promises should be actuated in order to converge to the specified
service_policy
.
Reference the policy for detailed information about the semantics of a given
service_method
or service_bundle
, for example the standard_services
in the
standard library.
Type: string
Allowed input range: (arbitrary string)|(menu_option) depending on service_type
When
service_type
iswindows
allowed values are limited tostart
,stop
,enable
, ordisable
.- start|enable :: Will start the service if it is not running.
Startup Type
will be set toManual
if it is notAutomatic
orAutomatic (Delayed Start)
. For a service to be configured to start automatically on boot aservice_method
must be declared andservice_autostart_policy
must be set toboot_time
. - stop :: Will stop the service if it is running.
Startup Type
will not be modified unless aservice_method
is declared andservice_autostart_policy
is set. - disable :: Will stop the service if it is running, and
Startup Type
will be set toDisabled
.
- start|enable :: Will start the service if it is not running.
When
service_type
isgeneric
any string is allowed andservice_bundle
is responsible for interpreting and implementing the desired state based on theservice_policy
value.Historically
service_type
generic
has supportedstart
,stop
,enable
,disable
,restart
andreload
.
Example:
bundle agent example
{
services:
redhat|centos::
# Manage a service using the standard_services implementation from the
# standard library.
"httpd"
service_policy => "disable";
any::
# Manage a custom service using custom service_method
"myservice"
service_policy => "my_custom_state",
service_method => "my_custom_service_method";
windows::
"AdobeARMservice"
service_policy => "stop",
comment => "Ensure the Adobe Acrobat Update Service is not running. It
may or may not be automatically started on the next boot
depending on the configuration.";
"CfengineNovaExec"
service_policy => "enable",
service_method => bootstart, # Ref stdlib
comment => "Ensure cf-execd is running and configured to start on
boot.";
"VBoxService"
service_policy => "start",
service_method => bootstart, # Ref stdlib
comment => "Ensure VirtualBox Guest Additions Service is running and
configured to start on boot.";
"Spooler"
service_policy => "disable",
comment => "Ensure the Print Spooler is not running and will not start
automatically on boot. We do not want to kill any trees.";
"tzautoupdate"
service_policy => "start",
comment => "Ensure that the Auto Time Zone Updated is running, and set
Startup Type to Manual.";
}
body service_method my_custom_service_method
{
windows::
service_bundle => my_custom_service_method_windows( $(this.promiser), $(this.service_policy) );
redhat|centos::
service_bundle => my_custom_service_method_EL( $(this.promiser), $(this.service_policy) );
debian|ubuntu::
service_bundle => my_custom_service_method_DEB( $(this.promiser), $(this.service_policy) );
}
bundle agent my_custom_service_method_windows( service_identifier, desired_service_state )
{
# Specific windows implementation
}
bundle agent my_custom_service_method_EL( service_identifier, desired_service_state )
{
# Specific Redhat|Centos implementation
}
bundle agent my_custom_service_method_DEB( service_identifier, desired_service_state )
{
# Specific Debian|Ubuntu implementation
}
See Also: [generic standard_services][Services Bodies and Bundles#standard_services]
History:
- Type changed from
menu_option
tostring
and allowed input range changed to arbitrary string from start|stop|enable|disable|restart|reload in CFEngine 3.10. Previously enable was mapped to start, disable was mapped to stop and reload was mapped to restart.
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
Description: The agent bundle to use when managing the service.
Default: The canonified promiser string prefixed with service_
. Note,
the service_bundle
must be in the same namespace.
Type: bundle agent
Example:
bundle agent main
{
services:
"my-custom-service"
service_method => my_custom_service,
service_policy => "stop";
}
body service_method my_custom_service
{
service_type => "generic";
}
bundle agent service_my_custom_service(service, state)
{
reports:
"$(service) should have state $(state)";
}
R: my-custom-service should have state stop
This policy can be found in
/var/cfengine/share/doc/examples/services_default_service_bundle.cf
and downloaded directly from
github.
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. service_bundle
is not used when service_type
is windows
.
processes
Process promises refer to items in the system process table, i.e., a command in some state of execution (with a Process Control Block). Promiser objects are patterns that are unanchored, meaning that they match parts of command lines in the system process table.
processes:
"regex contained in process line"
process_select = process_filter_body,
restart_class = "activation class for process",
..;
Note: CFEngine uses the output from the ps
command to inspect running
processes, and these formats differ between platforms. You can see how cfengine
views the process table for your platform by inspecting cf_otherprocs
,
cf_procs
, and cf_rootprocs
which can be found in $(sys.workdir)/state/
(typically /var/cfengine/state
).
This is an example showing how to restart a splunk process owned by root:
bundle agent example
{
processes:
"splunkd"
process_select => by_owner( "root" ),
handle => "example_splunk_stop_gracefully",
process_stop => "/opt/splunkforwarder/bin/splunk stop",
comment => "Find splunkd processes owned by root. Stop it gracefully
with the internal splunk binary.";
"splunkd"
restart_class => "splunk_not_running",
comment => "Set splunk_not_running class if we cant find any root owned
splunkd processes so that we can restart it using a
commands promise";
commands:
splunk_not_running::
"/opt/splunkforwarder/bin/splunk"
args => "--accept-license --answer-yes --no-prompt start";
}
This example shows using process_select
and process_count
to define a class
when a process has been running for longer than a day.
bundle agent main
{
processes:
"init"
process_count => any_count("booted_over_1_day_ago"),
process_select => days_older_than(1),
comment => "Define a class indicating we found an init process running
for more than 1 day.";
reports:
booted_over_1_day_ago::
"This system was booted over 1 days ago since there is an init process
that is older than 1 day.";
!booted_over_1_day_ago::
"This system has been rebooted recently as the init process has been
running for less than a day";
}
body process_count any_count(cl)
{
match_range => "0,0";
out_of_range_define => { "$(cl)" };
}
body process_select days_older_than(d)
{
stime_range => irange(ago(0,0,"$(d)",0,0,0),now);
process_result => "!stime";
}
This policy can be found in
/var/cfengine/share/doc/examples/processes_define_class_based_on_process_runtime.cf
and downloaded directly from
github.
Take care to not oversimplify your patterns as it may match
unexpected processes. For example, on many systems, the process pattern "^cp"
may not match any processes, even though "cp"
is running. This is because the
process table entry may list "/bin/cp"
. However, the process pattern "cp"
will also match a process containing "scp"
, (the PCRE pattern anchors "\b"
and "\B"
may prove very useful to you).
process_stop
should only be used for commands that stop processes. To start or
restart a process, you should set a class to activate and then use a commands
promise together with that class.
processes:
"/path/executable"
restart_class => "restart_me";
commands:
restart_me::
"/path/executable" ... ;
Notes:
CFEngine will not allow you to signal processes 1-4 or the agent process itself for fear of bringing down the system.
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 theDEFAULT_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
with
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
Note: For historical reasons, this attribute is identical to the match performed by using the promiser, except that the regular expression is anchored.
This expression should match the entire COMMAND
field of the process
table, not just a fragment. This field is usually the last field on the
line, so it thus starts with the first non-space character and ends with
the end of line.
Type: string
Allowed input range: (arbitrary string)
Example:
body process_select example
{
command => "cf-.*";
process_result => "command";
}
pid
Description: Range of integers matching the process id of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
pid => irange("1","10");
process_result => "pid";
}
pgid
Description: Range of integers matching the parent group id of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
pgid => irange("1","10");
process_result => "pgid";
}
ppid
Description: Range of integers matching the parent process id of a process
Type: irange[int,int]
Allowed input range: 0,99999999999
Example:
body process_select example
{
ppid => irange("407","511");
process_result => "ppid";
}
priority
Description: Range of integers matching the priority field (PRI/NI) of a process
Type: irange[int,int]
Allowed input range: -20,+20
Example:
body process_select example
{
priority => irange("-5","0");
}
process_owner
Description: List of regexes matching the user of a process
The regular expressions should match a legal user name on the system. The regex is anchored, meaning it must match the entire name.
Type: slist
Allowed input range: (arbitrary string)
Example:
bundle agent main
{
processes:
# Any /usr/local/web/tomcat-logviewer processes not
# running as buildsrv should be killed on sight.
"/usr/local/web/tomcat-logviewer" -> { "security" }
process_select => not_running_as("buildsrv"),
signals => { "kill" },
comment => "It is against the security policy for this
service to run under the wrong user id.";
}
body process_select not_running_as(owner)
{
process_owner => { $(owner) };
process_result => "!process_owner";
}
info: Signalled 'kill' (9) to process 7211 (root 7211 7199 7211 0.0 0.1 100908 0 596 1 15:26 00:06 00:00:00 /usr/local/web/tomcat-logviewer 500)
This policy can be found in
/var/cfengine/share/doc/examples/kill_process_running_wrong_user.cf
and downloaded directly from
github.
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" };
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
with
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";
}
classes
Classes promises may be made in any
bundle. Classes that are set in common
bundles are global in scope,
while classes in all other bundles are local.
Note: The term class and context are sometimes used interchangeably.
bundle common g
{
classes:
"one" expression => "any"; # always defined
"two"; # always defined
"client_network" expression => iprange("128.39.89.0/24");
}
Note: You can use the following attributes to make a complete promise.
- and
- expression
- dist
- or
- not
- xor
If you omit any of them, the class is always defined (as if you said expression
=> "any"
).
For example, the following promise defines the class web
when a file exists:
bundle agent example
{
classes:
"web"
if => fileexists("/etc/httpd/httpd.conf");
}
History: The context attributes expression
, and
, or
, not
, xor
,
dist
were made optional in CFEngine 3.9.0. Before that, one of them was
required. So the following examples were the valid equivalents of the example
above before 3.9.0:
bundle agent example
{
classes:
"web"
expression => fileexists("/etc/httpd/httpd.conf");
"webserver"
expression => "any",
if => fileexists("/etc/httpd/httpd.conf");
}
Attributes
and
Description: Combine class sources with AND
The class on the left-hand side is set if all of the class expressions listed on the right-hand side are true.
Type: clist
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
classes:
"compound_class" and => { classmatch("host[0-9].*"), "Monday", "Hr02" };
Notes:
If an expression contains a mixture of different object types that need to be ANDed together, this list form is more convenient than providing an expression.
dist
Description: Generate a probabilistic class distribution
Always set one generic class and one additional class, randomly weighted on a probability distribution.
Type: rlist
Allowed input range: -9.99999E100,9.99999E100
Example:
classes:
"my_dist"
dist => { "10", "20", "40", "50" };
Notes:
In the example above the values sum up to 10+20+40+50 = 120
. When generating
the distribution, CFEngine picks a number between 1-120
, and set the class
my_dist
as well as one of the following classes:
my_dist_10 (10/120 of the time)
my_dist_20 (20/120 of the time)
my_dist_40 (40/120 of the time)
my_dist_50 (50/120 of the time)
expression
Description: Evaluate string expression of classes in normal form
Set the class on the left-hand side if the expression on the right-hand side evaluates to true. With classes, the notion of "true" is not a boolean state, because classes can never be false. They are not booleans. They can be defined or undefined, but it's important to understand that a class may be defined during the execution of the agent, so the result of an expression may change during execution.
Expressions can be:
class names, with or without a namespace
the literals
true
(always defined) andfalse
(never defined) that allow JSON booleans to be used inside expressionsthe logical and operation, expressed as
a&b
ora.b
, which is true if botha
andb
are truethe logical or operation, expressed as
a|b
, which is true if eithera
orb
are truethe logical not operation, expressed as
!a
, which is true ifa
is not true. Note again here thata
could become true during the execution. So if you have"myclass" expression => "!x"
andx
starts undefined but is defined later, you could have bothx
andmyclass
defined!parenthesis
(whatever)
which operate as expected to prioritize expression evaluationthe return value of a function that returns a class, such as
fileexists()
and()
userexists()
etc.
Type: class
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
classes:
"class_name" expression => "solaris|(linux.specialclass)";
"has_toor" expression => userexists("toor");
# it's unlikely a machine will become Linux during execution
# so this is fairly safe
"not_linux" expression => "!linux";
"a_or_b" expression => "a|b";
# yes, it's OK to define a class twice, and this is the same outcome
# with different syntax
"a_and_b" expression => "a&b";
"a_and_b" expression => "a.b";
# yes, it's OK to define a class twice, and this is the same outcome
# with different syntax
"linux_and_has_toor" expression => and(userexists("toor"), "linux");
"linux_and_has_toor" and => { userexists("toor"), "linux" };
or
Description: Combine class sources with inclusive OR
The class on the left-hand side will be set if any one (or more) of the class expressions on the right-hand side are true.
Type: clist
Allowed input range: [a-zA-Z0-9_!@@$|.()\[\]{}:]+
Example:
classes:
"compound_test"
or => { classmatch("linux_x86_64_2_6_22.*"), "suse_10_3" };
Notes:
This is useful construction for writing expressions that contain functions.
persistence
Description: Make the class persistent to avoid re-evaluation
The value specifies time in minutes.
Type: int
Allowed input range: 0,99999999999
Example:
bundle common setclasses
{
classes:
"cached_classes"
or => { "any" },
persistence => "1";
"cached_class"
expression => "any",
persistence => "1";
}
Notes:
This feature can be used to avoid recomputing expensive classes calculations on each invocation. This is useful if a class discovered is essentially constant or only slowly varying, such as a hostname or alias from a non-standard naming facility. Persistent classes are always global and can not be set to local by scope directive.
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
See also: persistance
classes attribute, persist_time
in classes body
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.
This feature is useful for decentralized dynamic grouping. 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 given stable input a host will always end up in the same class every time while running a given version of CFEngine.
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.
Given stable input, the output of this function will not change between executions of the same version of CFEngine. Its output should not change between versions of CFEngine within the same minor release (3.12.0 -> 3.12.1). Its output may change between minor versions (3.12.0 -> 3.13.0).
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
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
with
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 and will only be enforced if the file object is stored on a file system supporting this ACL 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. Whenacl_type
is set tontfs
, the valid flags and their mappings is as follows:CFEngine nperm flag NTFS Special Permission x Execute File / Traverse Folder r Read Data / List Folder t Read Attributes T Read Extended Attributes w Write Data / Create Files a Append Data / Create Folders b Write Attributes B Write Extended Attributes D Delete Sub-folders and Files d Delete p Read Permissions c Change Permissions o Take Ownership 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 type of hash used 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";
}
missing_ok
Description: Treat a missing source file as a promise kept.
This allows you to override the promise outcome when a source file is missing.
When set to true
if the promise is a remote copy and there is a failure to
connect the promise will not be considered kept. If the agent is able to request
the file and the file is missing the promise will be kept.
Type: boolean
Default value: false
Example:
bundle agent main
{
files:
"/tmp/copied_from_missing_ok"
copy_from => missing_ok( "/var/cfengine/masterfiles/missing" ),
classes => results("bundle", "copy_from_missing_ok");
reports:
"$(with)"
with => string_mustache( "", sort( classesmatching( "copy_from_.*" ), lex));
}
body copy_from missing_ok( file_path )
{
source => "$(file_path)";
missing_ok => "true";
# Run with these classes to try remote copies
remote_copy_self::
servers => { "127.0.0.1" };
remote_copy_policy_hub::
servers => { $(sys.policy_hub) };
}
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" };
}
In the above example /tmp/copied_from_missing_ok
promises to be a copy of the local file
/var/cfengine/masterfiles/missing
. In the missing_ok
copy_from
body
missing_ok
is set to true. This causes the promise to be considered kept if
the source file is missing. The results
classes body is used to define bundle
scoped classes prefixed with copy_from_missing_ok
. The reports
promise
outputs a sorted list of the classes defined starting with copy_from_
.
R: [
"copy_from_missing_ok_kept",
"copy_from_missing_ok_reached"
]
We can see in the output that the class defined for copying a local file that does not exist is seen to be a promise kept.
This policy can be found in
/var/cfengine/share/doc/examples/missing_ok.cf
and downloaded directly from
github.
Notes:
This can be useful for opportunistically coping files that are not necessarily required or available at all times. For example if there is a host specific data that each host attempts to copy this will allow you to not have many promise failures when a host does not have any data prepared for it.
See also: seed_cp
in the MPF, compare
in body copy_from
History:
- Introduced in 3.12.0
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.
Note: This attribute will not preserve ownership (user/group).
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: When true files and directories on different devices from the promiser will be excluded from depth_search
results.
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: Path to Mustache or native-CFEngine template file to expand
Type: string
Allowed input range: "?(/.*)
Example:
bundle agent example
{
files:
!use_mustache::
"/etc/motd"
create => "true",
edit_template => "$(this.promise_dirname)/templates/motd.tpl",
template_method => "cfengine";
use_mustache::
"/etc/motd"
create => "true",
edit_template => "$(this.promise_dirname)/templates/motd.mustache",
template_method => "mustache";
}
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
, Customize Message of the Day
edit_template_string
Description: Mustache string to expand
Type: string
Allowed input range: (arbitrary string)
Example:
touch /tmp/example.txt printf "Welcome to host 'docs.cfengine.com'" > /tmp/example.txt ```
bundle agent example_using_template_method_inline_mustache
{
vars:
# Here we construct a data container that will be passed to the mustache
# templating engine
"d"
data => '{ "host": "docs.cfengine.com" }';
# Here we specify a string that will be used as an inline mustache template
"mustache_template_string"
string => "Welcome to host '{{{host}}}'";
files:
# Here we render the file using the data container and inline template specification
"/tmp/example.txt"
create => "true",
template_method => "inline_mustache",
edit_template_string => "$(mustache_template_string)",
template_data => @(d);
reports:
"/tmp/example.txt"
printfile => cat( $(this.promiser) );
}
body printfile cat(file)
{
file_to_print => "$(file)";
number_of_lines => "inf";
}
bundle agent __main__
{
methods: "example_using_template_method_inline_mustache";
}
R: /tmp/example.txt
R: Welcome to host 'docs.cfengine.com'
This policy can be found in
/var/cfengine/share/doc/examples/template_method-inline_mustache.cf
and downloaded directly from
github.
History: Was introduced in 3.12.0
See also: template_method
, template_data
, readjson()
, parsejson()
,
readyaml()
, parseyaml()
, mergedata()
,
data
, Customize Message of the Day
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
file_type
Description: By default, regular
files are created, when specifying
create => "true"
. You can create fifos through this mechanism as well, by
specifying fifo
in file_type
.
Type: string
Allowed input range:
regular
fifo
Type: (menu option)
Allowed input range:
regular
fifo
Default value: cfengine
link_from
Type: body link_from
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 implementation:
body link_from linkchildren(tofile)
{
source => "$(tofile)";
link_type => "symlink";
when_no_source => "force";
link_children => "true";
when_linking_children => "if_no_such_file"; # "override_file";
}
body link_from linkfrom(source, type)
{
source => $(source);
link_type => $(type);
}
Example usage:
body file control
{
inputs => { "$(sys.libdir)/stdlib.cf" };
}
bundle agent main
{
files:
# This will make symlinks to each file in /var/cfengine/bin
# for example:
# '/usr/local/bin/cf-agent' -> '/var/cfengine/bin/cf-agent'
# '/usr/local/bin/cf-serverd' -> '/var/cfengine/bin/cf-serverd'
"/usr/local/bin"
link_from => linkchildren("/var/cfengine/bin"),
comment => "We like for cfengine binaries to be available inside of the
common $PATH";
}
This policy can be found in
/var/cfengine/share/doc/examples/symlink_children.cf
and downloaded directly from
github.
link_type
Description: The type of link used to alias the file
This determines what kind of link should be used to link files. Users
are advised to be wary of 'hard links' (see Unix manual pages for the
ln
command). The behavior of non-symbolic links is often precarious and
unpredictable.
Note that symlink is synonymous with absolute links, which are different from relative links. Although all of these are symbolic links, the nomenclature here is defined such that symlink and absolute are equivalent . When verifying a link, choosing 'relative' means that the link must be relative to the source, so relative and absolute links are mutually exclusive.
Type: (menu option)
Allowed input range:
symlink
hardlink
relative
absolute
Default value: symlink
Example impelementation:
body link_from ln_s(x)
{
link_type => "symlink";
source => "$(x)";
when_no_source => "force";
}
body link_from example
{
link_type => "symlink";
source => "/tmp/source";
}
Example usage:
body file control
{
inputs => { "$(sys.libdir)/stdlib.cf" };
}
bundle agent main
{
files:
# We use move_obstructions because we want the symlink to replace a
# regular file if necessary.
"/etc/apache2/sites-enabled/www.cfengine.com" -> { "webmaster@cfengine.com" }
link_from => ln_s( "/etc/apache2/sites-available/www.cfengine.com" ),
move_obstructions => "true",
comment => "We always want our website to be enabled.";
}
This policy can be found in
/var/cfengine/share/doc/examples/symlink.cf
and downloaded directly from
github.
Notes: On Windows, hard links are the only supported type.
source
Description: The source file to which the link should point
For remote copies this refers to the file name on the remote server.
Type: string
Allowed input range: .+
Example:
body link_from example
{
source => "/path/to/source";
}
when_linking_children
Description: Policy for overriding existing files when linking directories of children
The options refer to what happens if the directory already exists, and is already partially populated with files. If the directory being copied from contains a file with the same name as that of a link to be created, it must be decided whether to override the existing destination object with a link, or simply omit the automatic linkage for files that already exist. The latter case can be used to make a copy of one directory with certain fields overridden.
Type: (menu option)
Allowed input range:
override_file
if_no_such_file
Example:
body link_from example
{
when_linking_children => "if_no_such_file";
}
when_no_source
Description: Behavior when the source file to link to does not exist
This describes how CFEngine should respond to an attempt to create a link to a file that does not exist. The options are to force the creation to a file that does not (yet) exist, delete any existing link, or do nothing.
Type: (menu option)
Allowed input range:
force
delete
nop
Default value: nop
Example:
body link_from example
{
when_no_source => "force";
}
move_obstructions
Description: true/false whether to move obstructions to file-object creation
If we have promised to make file X a link, but it already exists as a file, or vice-versa, or if a file is blocking the creation of a directory, then normally CFEngine will report an error. If this is set, existing objects will be moved aside to allow the system to heal without intervention. Files and directories are saved/renamed, but symbolic links are deleted.
Note that symbolic links for directories are treated as directories, not links. This behavior can be discussed, but the aim is to err on the side of caution.
Type: boolean
Default value: false
Example:
files:
"/tmp/testcopy"
copy_from => mycopy("/tmp/source"),
move_obstructions => "true",
depth_search => recurse("inf");
Notes: Some operating systems (Solaris) use symbolic links in path names. Copying to a directory could then result in renaming of the important link, if the behavior is different.
pathtype
Description: Menu option for interpreting promiser file object
By default, CFEngine makes an educated guess as to whether the promise pathname involves a regular expression or not. This guesswork is needed due to cross-platform differences in filename interpretation.
If CFEngine guesses (or is told) that the pathname uses a regular expression pattern, it will undertake a file search to find possible matches. This can consume significant resources, and so the guess option will always try to optimize this. Guesswork is, however, imperfect, so you have the option to declare your intention.
Type: (menu option)
Allowed input range:
literal
regex
guess
If the keyword literal
is invoked, a path will be treated as a literal
string regardless of what characters it contains. If it is declared
regex
, it will be treated as a pattern to match.
Note that CFEngine splits the promiser up into path links before
matching, so that each link in the path chain is matched separately.
Thus it it meaningless to have a /
in a regular expression, as the
comparison will never see this character.
Default value: guess
Example:
files:
"/var/lib\d"
pathtype => "guess", # best guess (default)
perms => system;
"/var/lib\d"
pathtype => "regex", # force regex interpretation
perms => system;
"/var/.*/lib"
pathtype => "literal", # force literal interpretation
perms => system;
In these examples, at least one case implies an iteration over all files/directories matching the regular expression, while the last case means a single literal object with a name composed of dots and stars.
Notes:
On Windows paths using regex
must use the forward slash (/
) as path
separator, since the backward slash has a special meaning in a regular
expression. Literal paths may also use backslash (\
) as a path
separator.
perms
Type: body perms
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 container to be passed to the template (Mustache or inline_mustache). It can come from a function call like mergedata()
or from a data container reference like @(mycontainer)
.
Type: data
Allowed input range: (arbitrary string)
Example:
files:
"/path/file"
...
edit_template => "mytemplate.mustache",
template_data => parsejson('{"message":"hello"}'),
template_method => "mustache";
Example:
vars:
"mycontainer" data => '[ 1, 2, 3 ]';
files:
"/path/file"
...
edit_template => "mytemplate.mustache",
template_data => @(mycontainer),
template_method => "mustache";
If this attribute is omitted, the result of the datastate()
function
call is used instead. See edit_template
for how you can use the data
state in Mustache.
See also: edit_template
, template_method
, datastate()
template_method
Description: The template type.
By default cfengine
requests the native CFEngine template
implementation, but you can use mustache
or inline_mustache
as well.
Type: (menu option)
Allowed input range:
cfengine
inline_mustache
mustache
Default value: cfengine
template_method cfengine
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.
Note: Because classic templates are built on top of edit_line
, identical
lines will not be rendered more than once unless they are included within a
block. This includes blank lines.
Example contrived cfengine
template:
#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)
Example cfengine
template for apache vhost directives:
[%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 %]
template_method inline_mustache
When template_method
is inline_mustache
the mustache input is not a file
but a string and you must set edit_template_string
. The same rules apply
for inline_mustache
and mustache
. For mustache explanation see
template_method mustache
Example:
touch /tmp/example.txt printf "Welcome to host 'docs.cfengine.com'" > /tmp/example.txt ```
bundle agent example_using_template_method_inline_mustache
{
vars:
# Here we construct a data container that will be passed to the mustache
# templating engine
"d"
data => '{ "host": "docs.cfengine.com" }';
# Here we specify a string that will be used as an inline mustache template
"mustache_template_string"
string => "Welcome to host '{{{host}}}'";
files:
# Here we render the file using the data container and inline template specification
"/tmp/example.txt"
create => "true",
template_method => "inline_mustache",
edit_template_string => "$(mustache_template_string)",
template_data => @(d);
reports:
"/tmp/example.txt"
printfile => cat( $(this.promiser) );
}
body printfile cat(file)
{
file_to_print => "$(file)";
number_of_lines => "inf";
}
bundle agent __main__
{
methods: "example_using_template_method_inline_mustache";
}
R: /tmp/example.txt
R: Welcome to host 'docs.cfengine.com'
This policy can be found in
/var/cfengine/share/doc/examples/template_method-inline_mustache.cf
and downloaded directly from
github.
History: Was introduced in 3.12.0
See also: edit_template_string
, template_data
, datastate()
template_method mustache
When template_method
is mustache
data must be provided to render the
template with. Data can be provided by functions that return data ( i.e.
mergedata()
, mapdata()
, readdata()
, findprocesses()
, etc ...), lists or
data variables by reference @(data)
, or by specifying data as inline json
(which can take advantage of data wrapping). For convenience if template_data
is not specified the output of datastate()
will be used. Thus the
datastate()
(all variables and classes defined) is the default.
From mustache Variables, Sections, Inverted Sections, Comments, and the Set Delimiter TAG TYPES are implemented.
NOTE: Partials and Lambdas are not currently supported.
template_method mustache Variables
The most basic tag type is the variable. A {{name}}
tag in a basic
template will try to find the name key in the current context. If there is no
name key, the parent contexts will be checked recursively. If the top context is
reached and the name key is still not found, nothing will be rendered.
All variables are HTML escaped by default. If you want to return unescaped
HTML, use the triple mustache: {{{name}}}
or an ampersand
({{& name}}
).
A variable "miss" returns an empty string.
bundle agent main
{
vars:
"data" data => '{ "key": "Hello World & 3>2!" }';
reports:
"$(with)"
with => string_mustache(
"{{key}}
{{{key}}}
{{&key}}
Missing '{{missing}}' varibles render empty strings.",
data);
}
R: Hello World & 3>2!
Hello World & 3>2!
Hello World & 3>2!
Missing '' varibles render empty strings.
This policy can be found in
/var/cfengine/share/doc/examples/mustache_variables.cf
and downloaded directly from
github.
template_method mustache Sections
Sections render blocks of text one or more times, depending on the value of the key in the current context.
A section begins with a pound and ends with a slash. That is, {{#key}}
begins a "person" section while {{/key}}
ends it.
The behavior of the section is determined by the value of the key.
Empty lists:
If the key exists and has a value of false or an empty list, the HTML between the pound and slash will not be displayed.
bundle agent main
{
vars:
"data" data => '{ "list": [ ] }';
reports:
"The list contains $(with)."
with => string_mustache("{{#list}} {{{.}}}{{/list}}",
data);
}
R: The list contains .
This policy can be found in
/var/cfengine/share/doc/examples/mustache_sections_empty_list.cf
and downloaded directly from
github.
Non-Empty Lists:
bundle agent main
{
vars:
"data" data => '{ "list": [ "1", "2", "3" ] }';
reports:
"$(with)"
with => string_mustache("{{#list}} {{{.}}}{{/list}}",
data);
}
R: 1 2 3
This policy can be found in
/var/cfengine/share/doc/examples/mustache_sections_non_empty_list.cf
and downloaded directly from
github.
Non-False Values:
When the value is non-false but not a list, it will be used as the context for a single rendering of the block.
bundle agent main
{
vars:
"data" data => parsejson('{ "classes": { "example": true } }');
reports:
"$(with)"
with => string_mustache("{{#classes.example}}This is an example.{{/classes.example}}",
data);
}
R: This is an example.
This policy can be found in
/var/cfengine/share/doc/examples/mustache_sections_non_false_value.cf
and downloaded directly from
github.
template_method mustache Inverted Sections
An inverted section begins with a caret (hat) and ends with a slash. That is
{{^key}}
begins a "key" inverted section while
{{/key}}
ends it.
While sections can be used to render text one or more times based on the value of the key, inverted sections may render text once based on the inverse value of the key. That is, they will be rendered if the key doesn't exist, is false, or is an empty list.
bundle agent main
{
reports:
"CFEngine $(with)"
with => string_mustache("{{^classes.enterprise_edition}}Community{{/classes.enterprise_edition}}{{#classes.enterprise_edition}}Enterprise{{/classes.example}}",
datastate());
}
R: CFEngine Community
This policy can be found in
/var/cfengine/share/doc/examples/mustache_sections_inverted.cf
and downloaded directly from
github.
template_method mustache Comments
Comments begin with a bang and are ignored. Comments may contain newlines.
bundle agent main
{
reports:
"$(with)"
with => string_mustache("Render this.{{! But not
this}}",
datastate());
}
R: Render this.
This policy can be found in
/var/cfengine/share/doc/examples/mustache_comments.cf
and downloaded directly from
github.
template_method mustache Set Delimiter
Set Delimiter tags start with an equal sign and change the tag delimiters from
{{
and }}
to custom strings.
bundle agent main
{
vars:
"data" data => '{ "key": "value of key in data" }';
reports:
"$(with)"
with => string_mustache("{{=<% %>=}}key has <% key %>",
data);
}
R: key has value of key in data
This policy can be found in
/var/cfengine/share/doc/examples/mustache_set_delimiters.cf
and downloaded directly from
github.
template_method mustache extensions
The following are CFEngine-specific extensions.
-top-
special key representing the complete data given. Useful for iterating
over the top level of a container {{#-top-}} ... {{/-top-}}
and rendering json representation of data given with $
and %
.
bundle agent main
{
vars:
"data" data => '{ "key": "value", "list": [ "1", "2" ] }';
reports:
"-top- contains$(const.n)$(with)"
with => string_mustache("{{%-top-}}", data );
}
R: -top- contains
{
"key": "value",
"list": [
"1",
"2"
]
}
This policy can be found in
/var/cfengine/share/doc/examples/mustache_extension_top.cf
and downloaded directly from
github.
%
variable prefix causing data to be rendered as multi-line json
representation. Like output from storejson()
.
bundle agent main
{
reports:
"$(with)"
with => string_mustache( "{{%mykey}}",
'{ "mykey": { "msg": "Hello World" } }' );
}
R: {
"msg": "Hello World"
}
This policy can be found in
/var/cfengine/share/doc/examples/mustache_extension_multiline_json.cf
and downloaded directly from
github.
$
variable prefix causing data to be rendered as compact json representation.
Like output from format()
with the %S
format string.
bundle agent main
{
vars:
"msg" string => "Hello World";
"report"
string => string_mustache("{{$-top-}}", bundlestate( $(this.bundle) ) ),
unless => isvariable( $(this.promiser) ); # Careful to not include
# ourselves so we only grab the
# state of the first pass.
reports:
"$(report)";
}
R: {"msg":"Hello World"}
This policy can be found in
/var/cfengine/share/doc/examples/mustache_extension_compact_json.cf
and downloaded directly from
github.
@
expands the current key being iterated to complement the value as accessed
with .
.
bundle agent main
{
reports:
"$(with)"
with => string_mustache("datastate() provides {{#-top-}} {{{@}}}{{/-top-}}", datastate() );
}
R: datastate() provides classes vars
This policy can be found in
/var/cfengine/share/doc/examples/mustache_extension_expand_key.cf
and downloaded directly from
github.
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.
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
with
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";
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
.
measurements
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. It also records a
three year trend summary based any 'shift'-averages.
Custom measurements
promises can 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;
}
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.
History:
- Custom measurements open sourced in 3.12.0
Attributes
stream_type
Description: The datatype being collected.
Default: pipe
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,99999999999
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.
Inline YAML data has to begin with the ---
preamble followed by a newline. This preamble
is normally optional but here (for inline YAML) it's required by
CFEngine. To generate that in CFEngine, use $(const.n)
or a literal
newline as shown in the example.
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.
For example:
Inline Yaml example
bundle agent example_inline_yaml
{
vars:
# YAML requires "---" header (followed by newline)
# NOTE \n is not interpreted as a newline, instead use $(const.n)
"yaml_multi_line" data => '---
- "CFEngine Champions":
- Name: "Aleksey Tsalolikhin"
Year: 2011
- Name: "Ted Zlatanov"
Year : 2013';
"yaml_single_line" data => '---$(const.n)- key1: value1$(const.n)- key2: value2';
reports:
"Data container defined from yaml_multi_line: $(with)"
with => storejson( @(yaml_multi_line) );
"Data container defined from yaml_single_line: $(with)"
with => storejson( @(yaml_single_line) );
}
bundle agent __main__
{
methods:
"example_inline_yaml";
}
R: Data container defined from yaml_multi_line: [
{
"CFEngine Champions": [
{
"Name": "Aleksey Tsalolikhin",
"Year": 2011
},
{
"Name": "Ted Zlatanov",
"Year": 2013
}
]
}
]
R: Data container defined from yaml_single_line: [
{
"key1": "value1"
},
{
"key2": "value2"
}
]
This policy can be found in
/var/cfengine/share/doc/examples/inline-yaml.cf
and downloaded directly from
github.
Inline Json example
bundle agent example_inline_json
{
vars:
"json_multi_line" data => '{
"CFEngine Champions": [
{
"Name": "Aleksey Tsalolikhin",
"Year": "2011"
},
{
"Name": "Ted Zlatanov",
"Year": "2013"
}
]
}';
"json_single_line" data => '[{"key1":"value1"},{"key2":"value2"}]';
reports:
"Data container defined from json_multi_line: $(with)"
with => storejson( @(json_multi_line) );
"Data container defined from json_single_line: $(with)"
with => storejson( @(json_single_line) );
}
bundle agent __main__
{
methods:
"example_inline_json";
}
R: Data container defined from json_multi_line: {
"CFEngine Champions": [
{
"Name": "Aleksey Tsalolikhin",
"Year": "2011"
},
{
"Name": "Ted Zlatanov",
"Year": "2013"
}
]
}
R: Data container defined from json_single_line: [
{
"key1": "value1"
},
{
"key2": "value2"
}
]
This policy can be found in
/var/cfengine/share/doc/examples/inline-json.cf
and downloaded directly from
github.
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 => '---$(const.n)- key2: value2'; # YAML requires "---$(const.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).
commands
Commands and processes are separated cleanly. Restarting of processes must be coded as a separate command. This stricter type separation allows for more careful conflict analysis to be carried out.
commands:
"/path/to/command args"
args => "more args",
contain => contain_body,
module => "true|false";
Output from commands executed here is quoted inline, but prefixed with
the letter Q to distinguish it from other output; for example, from
reports
, which is prefixed with the letter R
.
It is possible to set classes based on the return code of a
commands-promise in a very flexible way. See the kept_returncodes
,
repaired_returncodes
and failed_returncodes
attributes.
bundle agent example
{
commands:
"/bin/sleep 10"
action => background;
"/bin/sleep"
args => "20",
action => background;
}
When referring to executables the full path to the executable must be used. When reffereing to executables whose paths contain spaces, you should quote the entire program string separately so that CFEngine knows the name of the executable file. For example:
commands:
windows::
"\"c:\Program Files\my name with space\" arg1 arg2";
linux::
"\"/usr/bin/funny command name\" -a -b -c";
Note: Commands executed with CFEngine get the environment variables set in
environment
in body agent control. If you want to set
environment variables for an individual command you can prefix the command with
env
and set variables before executing the command.
bundle agent example
{
commands:
"/usr/bin/env MY_ENVIRONMENT_VARIABLE=something_special /tmp/cmd";
# Or equivlent
"/usr/bin/env"
args => "ME=something_special /tmp/cmd";
}
Note: Some unices leave a hanging pipe on restart (they never manage to
detect the end of file condition). This occurs on POSIX.1 and SVR4 popen calls
which use wait4. For some reason they fail to find and end-of-file for an
exiting child process and go into a deadlock trying to read from an already
dead process. This leaves a zombie behind (the parent daemon process which
forked and was supposed to exit) though the child continues. A way around this
is to use a wrapper script which prints the line cfengine-die
to STDOUT after
restarting the process. This causes cfengine to close the pipe forcibly and
continue.
Attributes
Common Attributes
Common attributes are available to all promise types. Full details for common attributes can be found in the Common Attributes section of the Promise Types and Attributes page. The common attributes are as follows:
action
classes
comment
depends_on
handle
ifvarclass
meta
with
args
Description: Allows to separate the arguments to the command from the command itself.
Sometimes it is convenient to separate command and arguments. The final arguments are the concatenation with one space.
Type: string
Allowed input range: (arbitrary string)
commands:
"/bin/echo one"
args => "two three";
So in the example above the command would be:
/bin/echo one two three
See also: arglist
, join()
, concat()
, format()
arglist
Description: Allows to separate the arguments to the command from the command itself, using an slist.
As with args
, it is convenient to separate command and arguments.
With arglist
you can use a slist directly instead of having to
provide a single string as with args
. That's particularly useful
when there are embedded spaces and quotes in your arguments, but also
when you want to get them directly from a slist without going through
join()
or other functions.
The arglist
is appended to args
if that's defined, to preserve
backwards compatibility.
Type: slist
Allowed input range: (arbitrary string)
commands:
"/bin/echo one"
args => "two three",
arglist => { "four", "five" };
So in the example above the command would be:
/bin/echo one two three four five
History: Was introduced in CFEngine 3.9.0.
See also: args
, join()
, concat()
, format()
contain
Description: Allows running the command in a 'sandbox'.
Command containment allows you to make a `sandbox' around a command, to run it as a non-privileged user inside an isolated directory tree.
Type: body contain
Example:
body contain example
{
useshell => "noshell";
umask => "077";
exec_owner => "mysql_user";
exec_group => "nogroup";
exec_timeout => "60";
chdir => "/working/path";
chroot => "/private/path";
}
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
^persistence=10
sets any following classes to persist for 10 minutes (use 0 to reset)^persistence=0
sets any following classes to have no persistence (this is the default)
- lines which begin with a
+
are treated as classes to be defined (like -D). 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
/
.
Type: boolean
Default value: false
Example:
Here is an example module written in shell:
#!/bin/sh
/bin/echo "@mylist= { \"one\", \"two\", \"three\" }"
/bin/echo "=myscalar= scalar val"
/bin/echo "=myarray[key]= array key val"
/bin/echo "%mydata=[1,2,3]"
/bin/echo "+module_class"
/bin/echo "^persistence=10"
/bin/echo "+persistent_10_minute_class"
And here is an example using it:
body common control
{
bundlesequence => { def, modtest };
}
bundle agent def
{
commands:
"$(sys.workdir)/modules/module_name"
module => "true";
reports:
# Each module forms a private context with its name as id
module_class::
"Module set variable $(module_name.myscalar)";
}
bundle agent modtest
{
vars:
"mylist" slist => { @(module_name.mylist) };
reports:
module_class::
"Module set variable $(mylist)";
}
Here is an example module written in Perl:
#!/usr/bin/perl
#
# module:myplugin
#
# lots of computation....
if (special-condition)
{
print "+specialclass";
}
If your module is simple and is best expressed as a shell command, then we
suggest that you expose the class being defined in the command being
executed (making it easier to see what classes are used when reading the
promises file). For example, the promises could read as follows (the two
echo
commands are to ensure that the shell always exits with a successful
execution of a command):
bundle agent sendmail
{
commands:
# This next module checks a specific failure mode of dcc, namely
# more than 3 error states since the last time we ran cf-agent
is_mailhost::
"/bin/test `/usr/bin/tail -100 /var/log/maillog | /usr/bin/grep 'Milter (dcc): to error state' | /usr/bin/wc -l` -gt 3 echo '+start_dccm' || echo
''"
contain => shell_command,
module => "true";
start_dccm::
"/var/dcc/libexec/start-dccm"
contain => not_paranoid;
}
body contain shell_command
{
useshell => "useshell";
}
body contain not_paranoid
{
useshell => "no";
exec_owner => "root";
umask => "22";
}
Modules inherit the environment variables from cf-agent
and accept
arguments, just as a regular command does.
See Also: usemodule()
History:
@
allowed in variables (intended for keys in classic array) 3.15.0, 3.12.3, 3.10.7 (2019)/
allowed in variables (intended for keys in classic array) 3.14.0, 3.12.2, 3.10.6 (2019)
guest_environments
WARNING: Due to lack of use this promise type has been removed from the default binaries beginning with version 3.11.0. We have seen that in most cases users want to interact with hypervisors as peripheral instead of managing the underlying guest virtual machines. If you want to use this functionality you must build the agent with libvirt support.
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
with
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 environment_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";
}
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.
WARNING: Neither MySQL/MariaDB or PostgreSQL support is built into the default binaries. If you wish to use this functionality you must compile the agent with support.
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
with
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";
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 (string|int|real|bool), list (slist, ilist, rlist) and data values:
printf "one\ntwo\nthree\n" > /tmp/list.txt
printf "1\n2\n3\n" >> /tmp/list.txt
printf "1.0\n2.0\n3.0" >> /tmp/list.txt
bundle agent example_function_return_types
{
classes:
"this_file_exists" expression => fileexists( $(this.promise_filename) );
vars:
"my_string" string => concat( "Promises you cannot keep",
" are no better than lies");
"my_list_of_strings"
slist => readstringlist( "/tmp/list.txt", # File to read
"", # Don't ignore any lines
"\n", # Split on newlines
inf, # Extract as many entries as possible
inf); # Read in as much data as possible
"my_list_of_integers"
ilist => readintlist( "/tmp/list.txt", # File to read
"^(\D+)|(\d+[^\n]+)", # Ignore any lines that are not integers
"\n", # Split on newlines
inf, # Maximum number of entries
inf); # Maximum number of bytes to read
"my_list_of_reals"
rlist => readreallist( "/tmp/list.txt", # File to read
"^(\D+)", # Ignore any lines that are not digits
"\n", # Split on newlines
inf, # Maximum number of entries
inf); # Maximum number of bytes to read
"my_integer" int => string_length( $(my_string) );
"my_real" real => sum( my_list_of_integers );
"my_data" data => mergedata( '{ "Hello": "world!" }' );
reports:
"my_string: '$(my_string)'";
"my_list_of_strings includes '$(my_list_of_strings)'";
"my_list_of_integers includes '$(my_list_of_integers)'";
"my_list_of_reals includes '$(my_list_of_reals)'";
"my_integer: '$(my_integer)'";
"my_real: '$(my_real)'";
"my_data: '$(with)'"
with => string_mustache( "", my_data );
this_file_exists::
"This file exists.";
}
bundle agent __main__
{
methods: "example_function_return_types";
}
R: my_string: 'Promises you cannot keep are no better than lies'
R: my_list_of_strings includes 'one'
R: my_list_of_strings includes 'two'
R: my_list_of_strings includes 'three'
R: my_list_of_strings includes '1'
R: my_list_of_strings includes '2'
R: my_list_of_strings includes '3'
R: my_list_of_strings includes '1.0'
R: my_list_of_strings includes '2.0'
R: my_list_of_strings includes '3.0'
R: my_list_of_integers includes '1'
R: my_list_of_integers includes '2'
R: my_list_of_integers includes '3'
R: my_list_of_reals includes '1'
R: my_list_of_reals includes '2'
R: my_list_of_reals includes '3'
R: my_list_of_reals includes '1.0'
R: my_list_of_reals includes '2.0'
R: my_list_of_reals includes '3.0'
R: my_integer: '48'
R: my_real: '6.000000'
R: my_data: '{
"Hello": "world!"
}'
R: This file exists.
This policy can be found in
/var/cfengine/share/doc/examples/function-return-types.cf
and downloaded directly from
github.
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 queriesfindprocesses()
, andprocessexists()
for querying processes.host2ip()
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: Cached functions are executed multiple times during
policy validation and pre-evaluation.
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.
Collecting Functions
Some function arguments are marked as collecting which means they can "collect" an argument from various sources. The data is normalized into the JSON format internally, so all of the following data types have consistent behavior.
If a key inside a data container is specified (
mycontainer[key]
), the value under that key is collected. The key can be a string for JSON objects or a number for JSON arrays.If a single data container, CFEngine array, or slist is specified (
mycontainer
ormyarray
ormyslist
), the contents of it are collected.If a single data container, CFEngine array, or slist is specified with
@()
around it (@(mycontainer)
or@(myarray)
or@(myslist)
), the contents of it are collected.If a function call that returns a data container or slist is specified, that function call is evaluated and the results are inserted, so you can say for instance
sort(data_expand(...), "lex")
to expand a data container then sort it.If a list (slist, ilist, or rlist) is named, its entries are collected.
If any CFEngine "classic" array (
array[key]
) is named, it's first converted to a JSON key-value map, then collected.If a literal JSON string like
[ 1,2,3 ]
or{ "x": 500 }
is provided, it will be parsed and used.If any of the above-mentioned ways to reference variables are used inside a literal JSON string they will be expanded (or the function call will fail). This is similar to the behavior of Javascript, for instance. For example,
mergedata('[ thing, { "mykey": otherthing[123] } ]')
will wrap thething
in a JSON array; then the contents ofotherthing[123]
will be wrapped in a JSON map which will also go in the array.
Delayed Evaluation Functions
Since CFEngine 3.10, some functions are marked as delayed evaluation which
means they can evaluate a function call across every element of a collection.
This makes intuitive sense for the collection traversing functions maparray()
,
maplist()
, and mapdata()
.
The practical use is for instance maplist(format("%03d", $(this)), mylist)
which will evaluate that format()
call once for every element of mylist
.
Before 3.10, the same call would have resulted in running the format()
function before the list is traversed, which is almost never what the user
wants.
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
shuffle
Prototype: shuffle(list, seed)
Return type: slist
Description: Return list
shuffled with seed
.
This function can accept many types of data parameters.
The same seed will produce the same shuffle every time. For a random shuffle,
provide a random seed with the randomint
function.
Arguments:
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"mylist" slist => { "b", "c", "a" };
"seeds" slist => { "xx", "yy", "zz" };
"shuffled_$(seeds)" slist => shuffle(mylist, $(seeds));
"joined_$(seeds)" string => join(",", "shuffled_$(seeds)");
reports:
"shuffled RANDOMLY by $(seeds) = '$(joined_$(seeds))'";
}
Output:
R: shuffled RANDOMLY by xx = 'b,a,c'
R: shuffled RANDOMLY by yy = 'a,c,b'
R: shuffled RANDOMLY by zz = 'c,b,a'
History: The collecting function behavior was added in 3.9.
See also: sort()
, about collecting functions, and data
documentation.
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.
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
"readstringarray"
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
...
Prepare:
echo "1: 5.0:7:21:13" > /tmp/readrealarray.txt
echo "2:19:8.1:14:14" >> /tmp/readrealarray.txt
echo "3:45:1:78.2:22" >> /tmp/readrealarray.txt
echo "4:64:2:98:99.3" >> /tmp/readrealarray.txt
Run:
bundle agent main
{
vars:
"lines" int => readrealarray("array_name",
"/tmp/readrealarray.txt",
"#[^\n]*",
":",
10,
4000);
reports:
"array_name contains $(lines) keys$(const.n)$(with)"
with => string_mustache("", "array_name");
}
Output:
R: array_name contains 4 keys
{
"1": {
"0": "1",
"1": " 5.0",
"2": "7",
"3": "21",
"4": "13"
},
"2": {
"0": "2",
"1": "19",
"2": "8.1",
"3": "14",
"4": "14"
},
"3": {
"0": "3",
"1": "45",
"2": "1",
"3": "78.2",
"4": "22"
},
"4": {
"0": "4",
"1": "64",
"2": "2",
"3": "98",
"4": "99.3"
}
}
See Also: readrealarray()
, readintarray()
, parserealarray()
, parserealarray()
, parsestringarray()
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()
.
parsejson
Prototype: parsejson(json_data)
Return type: data
Description: Parses JSON data directly from an inlined string and
returns the result as a data
variable
Arguments:
json_data
:string
, in the range:.*
Please note that because JSON uses double quotes, it's usually most convenient to use single quotes for the string (CFEngine allows both types of quotes around a string).
This function can accept many types of data parameters.
Example:
vars:
"loadthis"
data => parsejson('{ "key": "value" }');
# inline syntax since 3.7
"loadthis_inline"
data => '{ "key": "value" }';
History: The collecting function behavior was added in 3.9.
See also: readjson()
, parseyaml()
, readyaml()
, mergedata()
, Inline YAML and JSON data
, about collecting functions, and data
documentation.
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.
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()
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()).
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
.
isipinsubnet
Prototype: isipinsubnet(range, ip_address1, ip_address2, ...)
Return type: boolean
Description: Returns whether the given range
contains any of the following IP addresses.
Arguments:
range
:string
, in the range:.*
Example:
Run:
bundle agent main
{
classes:
"in_192" expression => isipinsubnet("192.0.0.0/8", "192.1.2.3");
"in_192_2_2_2" expression => isipinsubnet("192.2.2.0/24", "192.1.2.3");
reports:
in_192::
"The address 192.1.2.3 is in the 192/8 subnet";
!in_192_2_2_2::
"The address 192.1.2.3 is not in the 192.2.2/24 subnet";
}
Output:
R: The address 192.1.2.3 is in the 192/8 subnet
R: The address 192.1.2.3 is not in the 192.2.2/24 subnet
See Also: iprange()
, host2ip()
, ip2host()
History: Was introduced in CFEngine 3.10.
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()
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()
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)
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.
reverse
Prototype: reverse(list)
Return type: slist
Description: Reverses a list.
This is a simple function to reverse a list.
This function can accept many types of data parameters.
Arguments:
- list : The name of the list variable to check, in the range
[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"one", "two", "three",
};
"reversed" slist => reverse("test");
reports:
"Original list is $(test)";
"The reversed list is $(reversed)";
}
Output:
R: Original list is 1
R: Original list is 2
R: Original list is 3
R: Original list is one
R: Original list is two
R: Original list is three
R: Original list is long string
R: The reversed list is three
R: The reversed list is two
R: The reversed list is one
R: The reversed list is long string
R: The reversed list is 3
R: The reversed list is 2
R: The reversed list is 1
History: The collecting function behavior was added in 3.9.
See also: filter()
, grep()
, every()
, some()
, none()
, about collecting functions, and data
documentation.
nth
Prototype: nth(list_or_container, position_or_key)
Return type: string
Description: Returns the element of list_or_container
at zero-based position_or_key
.
If an invalid position (below 0 or above the size of the list minus 1) or missing key is requested, this function does not return a valid value.
This function can accept many types of data parameters.
list_or_container
can be an slist or a data container. If it's a
slist, the offset is simply the position in the list. If it's a data
container, the meaning of the position_or_key
depends on its
top-level contents: for a list like [1,2,3,4]
you will get the list
element at position_or_key
. For a key-value map like
{ a: 100, b: 200 }
, a position_or_key
of a
returns 100
.
Arguments:
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test_str" string => format("%S", test);
"test2" data => parsejson("[1, 2, 3, null]");
"test2_str" string => format("%S", test2);
"test3" data => parsejson('{ "x": true, "y": "z" }');
"test3_str" string => format("%S", test3);
"nth" slist => { 1, 2, 6, 10, 11, 1000 };
"nth2" slist => getindices(test2);
"nth3" slist => getindices(test3);
"access[$(nth)]" string => nth(test, $(nth));
"access[0]" string => nth(test, 0);
"access2[$(nth2)]" string => nth(test2, $(nth2));
"access3[$(nth3)]" string => nth(test3, $(nth3));
reports:
"The test list is $(test_str)";
"element #$(nth) of the test list: $(access[$(nth)])";
"element #0 of the test list: $(access[0])";
"The test2 data container is $(test2_str)";
"element #$(nth2) of the test2 data container: $(access2[$(nth2)])";
"The test3 data container is $(test3_str)";
"element #$(nth3) of the test3 data container: $(access3[$(nth3)])";
}
Output:
R: The test list is { "1", "2", "3", "one", "two", "three", "long string", "four", "fix", "six", "one", "two", "three" }
R: element #1 of the test list: 2
R: element #2 of the test list: 3
R: element #6 of the test list: long string
R: element #10 of the test list: one
R: element #11 of the test list: two
R: element #0 of the test list: 1
R: The test2 data container is [1,2,3,null]
R: element #0 of the test2 data container: 1
R: element #1 of the test2 data container: 2
R: element #2 of the test2 data container: 3
R: element #3 of the test2 data container: null
R: The test3 data container is {"x":true,"y":"z"}
R: element #x of the test3 data container: true
R: element #y of the test3 data container: z
History: The collecting function behavior was added in 3.9.
See also: length()
, about collecting functions, and data
documentation.
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
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()
.
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
See also: classes defined via augments, classmatch(), classesmatching()
readjson
Prototype: readjson(filename, optional_maxbytes)
Return type: data
Description: Parses JSON data from the file filename
and returns the
result as a data
variable. maxbytes
is optional, if specified, only the
first maxbytes
bytes are read from filename
.
Arguments:
Example:
vars:
"loadthis"
data => readjson("/tmp/data.json", 4000);
See also: readdata()
, parsejson()
, storejson()
, parseyaml()
, readyaml()
, mergedata()
, and data
documentation.
every
Prototype: every(regex, list)
Return type: boolean
Description: Returns whether every element in the variable list
matches
the unanchored regex
.
This function can accept many types of data parameters.
Arguments:
regex
: Regular expression to find, in the range.*
list
: The name of the list variable to check, in the range[a-zA-Z0-9_$(){}\[\].:]+
. It can be a data container or a regular list.
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
classes:
"every_dot_star" expression => every(".*", test);
"every_dot" expression => every(".", test);
"every_number" expression => every("[0-9]", test);
"every2_dot_star" expression => every(".*", test2);
"every2_dot" expression => every(".", test2);
"every2_number" expression => every("[0-9]", test2);
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
reports:
"The test list is $(test)";
every_dot_star::
"every() test passed: every element matches '.*'";
!every_dot_star::
"every() test failed: not every element matches '.*'";
every_number::
"every() test failed: every element matches '[0-9]'";
!every_number::
"every() test passed: not every element matches '[0-9]'";
every_dot::
"every() test failed: every element matches '.'";
!every_dot::
"every() test passed: not every element matches '.'";
"The test2 list is $(test2)";
every2_dot_star::
"every() test2 passed: every element matches '.*'";
!every2_dot_star::
"every() test2 failed: not every element matches '.*'";
every2_number::
"every() test2 failed: every element matches '[0-9]'";
!every2_number::
"every() test2 passed: not every element matches '[0-9]'";
every2_dot::
"every() test2 failed: every element matches '.'";
!every2_dot::
"every() test2 passed: not every element matches '.'";
}
Output:
R: The test list is 1
R: The test list is 2
R: The test list is 3
R: The test list is one
R: The test list is two
R: The test list is three
R: The test list is long string
R: The test list is four
R: The test list is fix
R: The test list is six
R: every() test passed: every element matches '.*'
R: every() test passed: not every element matches '[0-9]'
R: every() test passed: not every element matches '.'
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: every() test2 passed: every element matches '.*'
R: every() test2 passed: not every element matches '[0-9]'
R: every() test2 passed: not every element matches '.'
History: The collecting function behavior was added in 3.9.
See also: About collecting functions, filter()
, some()
, and none()
.
sublist
Prototype: sublist(list, head_or_tail, max_elements)
Return type: slist
Description: Returns list of up to max_elements
of list
, obtained from head or tail depending on head_or_tail
.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
head_or_tail
: one ofhead
tail
max_elements
:int
, in the range:0,99999999999
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
};
"test_head9999" slist => sublist("test", "head", 9999);
"test_head1" slist => sublist("test", "head", 1);
"test_head0" slist => sublist("test", "head", 0);
"test_tail9999" slist => sublist("test", "tail", 9999);
"test_tail10" slist => sublist("test", "tail", 10);
"test_tail2" slist => sublist("test", "tail", 2);
"test_tail1" slist => sublist("test", "tail", 1);
"test_tail0" slist => sublist("test", "tail", 0);
reports:
"The test list is $(test)";
"This line should not appear: $(test_head0)";
"The head(1) of the test list is $(test_head1)";
"The head(9999) of the test list is $(test_head9999)";
"This line should not appear: $(test_tail0)";
"The tail(1) of the test list is $(test_tail1)";
"The tail(10) of the test list is $(test_tail10)";
"The tail(2) of the test list is $(test_tail2)";
"The tail(9999) of the test list is $(test_tail9999)";
}
Output:
R: The test list is 1
R: The test list is 2
R: The test list is 3
R: The test list is one
R: The test list is two
R: The test list is three
R: The test list is long string
R: The test list is four
R: The test list is fix
R: The test list is six
R: The head(1) of the test list is 1
R: The head(9999) of the test list is 1
R: The head(9999) of the test list is 2
R: The head(9999) of the test list is 3
R: The head(9999) of the test list is one
R: The head(9999) of the test list is two
R: The head(9999) of the test list is three
R: The head(9999) of the test list is long string
R: The head(9999) of the test list is four
R: The head(9999) of the test list is fix
R: The head(9999) of the test list is six
R: The tail(1) of the test list is six
R: The tail(10) of the test list is 1
R: The tail(10) of the test list is 2
R: The tail(10) of the test list is 3
R: The tail(10) of the test list is one
R: The tail(10) of the test list is two
R: The tail(10) of the test list is three
R: The tail(10) of the test list is long string
R: The tail(10) of the test list is four
R: The tail(10) of the test list is fix
R: The tail(10) of the test list is six
R: The tail(2) of the test list is fix
R: The tail(2) of the test list is six
R: The tail(9999) of the test list is 1
R: The tail(9999) of the test list is 2
R: The tail(9999) of the test list is 3
R: The tail(9999) of the test list is one
R: The tail(9999) of the test list is two
R: The tail(9999) of the test list is three
R: The tail(9999) of the test list is long string
R: The tail(9999) of the test list is four
R: The tail(9999) of the test list is fix
R: The tail(9999) of the test list is six
History: The collecting function behavior was added in 3.9.
See also: nth()
, filter()
, about collecting functions, and data
documentation.
min
Prototype: min(list, sortmode)
Return type: string
Description: Return the minimum of the items in list
according to sortmode
(same sort modes as in sort()
).
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
sortmode
: one oflex
int
real
IP
ip
MAC
mac
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
# the behavior will be the same whether you use a data container or a list
# "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
"mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
"mylist_str" string => format("%S", mylist);
"max_int" string => max(mylist, "int");
"max_lex" string => max(mylist, "lex");
"max_ip" string => max(mylist, "ip");
"min_int" string => min(mylist, "int");
"min_lex" string => min(mylist, "lex");
"min_ip" string => min(mylist, "ip");
"mean" real => mean(mylist);
"variance" real => variance(mylist);
reports:
"my list is $(mylist_str)";
"mean is $(mean)";
"variance is $(variance) (use eval() to get the standard deviation)";
"max int is $(max_int)";
"max IP is $(max_ip)";
"max lexicographically is $(max_lex)";
"min int is $(min_int)";
"min IP is $(min_ip)";
"min lexicographically is $(min_lex)";
}
Output:
R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1
History: Was introduced in version 3.6.0 (2014). The collecting function behavior was added in 3.9.
See also: sort()
, variance()
, sum()
, max()
, mean()
, about collecting functions, and data
documentation.
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 -f)" | tr 'A-Z' 'a-z' >> /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)";
"Unable to find my fully qualified hostname $(sys.fqhost) in /tmp/cfe_hostlist. Can't determine peers."
if => not( regline( $(sys.fqhost), "/tmp/cfe_hostlist" ) );
}
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
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()
.
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()
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:
# Here we find all bundles in the default namespace whos name starts with
# run.
"todo" slist => bundlesmatching("default:run.*");
}
bundle agent mefirst
{
methods:
# Here, we actuate each of the bundles that were found using
# bundlesmatching in bundle common g.
"" usebundle => $(g.todo);
}
bundle agent run_deprecated
{
meta:
# This bundle is tagged with deprecated
"tags" slist => { "deprecated" };
}
bundle agent run_123_456
{
vars:
# Here we find all bundles in our policy.
"bundles" slist => bundlesmatching(".*");
# Here we find all the bundles that are tagged as deprecated.
"deprecated_bundles" slist => bundlesmatching(".*", "deprecated");
# Here we find all bundles that match 891 (none will).
"no_bundles" slist => bundlesmatching("891");
reports:
# Here we report on our findings:
"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()
.
readstringlist
Prototype: readstringlist(filename, comment, split, maxentries, maxbytes)
Return type: 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:
printf "one\ntwo\nthree\n" > /tmp/list.txt
printf " # commented line\n" >> /tmp/list.txt
printf "1\n2\n3\n" >> /tmp/list.txt
printf "# another commented line\n" >> /tmp/list.txt
printf "Not a commented # line\n" >> /tmp/list.txt
printf "1.0\n2.0\n3.0" >> /tmp/list.txt
Run:
bundle agent example_readstringlist
{
vars:
"my_list_of_strings"
slist => readstringlist( "/tmp/list.txt", # File to read
"^\s*#[^\n]*", # Exclude hash comment lines lines
"\n", # Split on newlines
inf, # Maximum number of entries
inf); # Maximum number of bytes to read
reports:
"my_list_of_strings includes '$(my_list_of_strings)'";
}
bundle agent __main__
{
methods: "example_readstringlist";
}
Output:
R: my_list_of_strings includes 'one'
R: my_list_of_strings includes 'two'
R: my_list_of_strings includes 'three'
R: my_list_of_strings includes '1'
R: my_list_of_strings includes '2'
R: my_list_of_strings includes '3'
R: my_list_of_strings includes 'Not a commented # line'
R: my_list_of_strings includes '1.0'
R: my_list_of_strings includes '2.0'
R: my_list_of_strings includes '3.0'
See Also: readintlist()
, readreallist()
network_connections
Prototype: network_connections()
Return type: data
Description: Return the list of current network connections.
The returned data container has four keys:
tcp
has all the TCP connections over IPv4tcp6
has all the TCP connections over IPv6udp
has all the UDP connections over IPv4udp6
has all the UDP connections over IPv6
Under each key, there's an array of connection objects that all look like this:
{
"local": {
"address": "...source address...",
"port": "...source port..."
},
"remote": {
"address": "...remote address...",
"port": "...remote port..."
},
"state": "...connection state..."
}
The address will be either IPv4 or IPv6 as appropriate. The port will
be an integer stored as a string. The state will be a string like
UNKNOWN
.
Note: This function is supported on Linux.
On Linux, usually a state of UNKNOWN
and a remote address 0.0.0.0
or 0:0:0:0:0:0:0:0
with port 0
mean this is a listening IPv4 and
IPv6 server. In addition, usually a local address of 0.0.0.0
or
0:0:0:0:0:0:0:0
means the server is listening on every IPv4 or IPv6
interface, while 127.0.0.1
(the IPv4 localhost address) or
0:100:0:0:0:0:0:0
means the server is only listening to connections
coming from the same machine.
A state of ESTABLISHED
usually means you're looking at a live
connection.
On Linux, all the data is collected from the files /proc/net/tcp
,
/proc/net/tcp6
, /proc/net/udp
, and /proc/net/udp6
.
Example:
vars:
"connections" data => network_connections();
Output:
The SSH daemon:
{
"tcp": [
{
"local": {
"address": "0.0.0.0",
"port": "22"
},
"remote": {
"address": "0.0.0.0",
"port": "0"
},
"state": "UNKNOWN"
}
]
}
The printer daemon listening only to local IPv6 connections on port 631
:
"tcp6": [
{
"local": {
"address": "0:100:0:0:0:0:0:0",
"port": "631"
},
"remote": {
"address": "0:0:0:0:0:0:0:0",
"port": "0"
},
"state": "UNKNOWN"
}
]
An established connection on port 2200:
"tcp": [
{
"local": {
"address": "192.168.1.33",
"port": "2200"
},
"remote": {
"address": "1.2.3.4",
"port": "8533"
},
"state": "ESTABLISHED"
}
]
History: Introduced in CFEngine 3.9
See also: sys.inet
, sys.inet6
.
makerule
Prototype: makerule(target, sources)
Return type: string
Description: Evaluates whether a target
file needs to be built or
rebuilt from one or more sources
files.
This function can accept many types of data parameters.
The function is provided to emulate the semantics of the Unix make
program.
In a traditional Makefile, rules take the form
target: source1 source2 ..
(tab) commands
The top line evaluates to a predicate for executing a number of commands, which is true
if the target
file does not exist, or if any of the sources
dependencies
in the list has been changed since the target was last built.
The makerule function emulates the same semantics and sets a class if the target needs to be built or rebuit, i.e. if the top line of an equivalent makefile is true.
Arguments:
The sources
argument may be either a scalar (indicating a single
source) or a list reference or a data container. If the sources
argument specifies a list, then the entire list of sources is used to
determine whether the target needs rebuilding.
Example:
classes:
"build_me" expression => makerule("/tmp/target", "/tmp/source.c");
"build_me_ab" expression => makerule("/tmp/target", '["/tmp/source.a","/tmp/source.b"]' );
commands:
build_me::
"/usr/bin/gcc -o /tmp/target /tmp/source.c";
History: The collecting function behavior was added in 3.9.
See also: About collecting functions.
regarray
Prototype: regarray(array, regex)
Return type: boolean
Description: Returns whether array
contains elements matching the
anchoredregular expression regex
.
Arguments:
array
:string
, in the range:.*
regex
: regular expression, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"myarray[0]" string => "bla1";
"myarray[1]" string => "bla2";
"myarray[3]" string => "bla";
classes:
"ok" expression => regarray("myarray","b.*2");
reports:
ok::
"Found in list";
!ok::
"Not found in list";
}
Output:
R: Found in list
isvariable
Prototype: isvariable(var)
Return type: boolean
Description: Returns whether a variable named var
is defined.
The variable need only exist. This says nothing about its value. Use
regcmp
to check variable values. Variable references like foo[bar]
are also checked, so this is a way to check if a classic CFEngine
array or a data container has a specific key or element.
Arguments:
var
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"bla" string => "xyz..";
classes:
"exists" expression => isvariable("bla");
reports:
exists::
"Variable exists: \"$(bla)\"..";
}
Output:
R: Variable exists: "xyz.."..
readyaml
Prototype: readyaml(filename, optional_maxbytes)
Return type: data
Description: Parses YAML data from the file filename
and returns the
result as a data
variable. maxbytes
is optional, if specified, only the
first maxbytes
bytes are read from filename
.
Arguments:
Example:
vars:
"loadthis"
data => readyaml("/tmp/data.yaml", 4000);
See also: readdata()
, parsejson()
, parseyaml()
, storejson()
, mergedata()
, and data
documentation.
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
See also: canonify(), classify(), classesmatching(), classes defined via augments, countclassesmatching()
storejson
Prototype: storejson(data_container)
Return type: string
Description: Converts a data container to a JSON string.
This function can accept many types of data parameters.
Arguments:
data_container
:string
, in the range:.*
Example:
vars:
"loadthis"
data => readjson("/tmp/data.json", 4000);
"andback"
string => storejson(loadthis);
reports:
"Converted /tmp/data.json to '$(andback)'";
History: The collecting function behavior was added in 3.9.
See also: readjson()
, readyaml()
, parsejson()
, parseyaml()
, about collecting functions, and data
documentation.
product
Prototype: product(list)
Return type: real
Description: Returns the product of the reals in list
.
This function might be used for simple ring computation. Of course, you could
easily combine product
with readstringarray
or readreallist
etc., to
collect summary information from a source external to CFEngine.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"series" rlist => { "1.1", "2.2", "3.3", "5.5", "7.7" };
"prod" real => product("series");
"sum" real => sum("series");
reports:
"Product result: $(prod) > $(sum)";
}
Output:
R: Product result: 338.207100 > 19.800000
History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010). The collecting function behavior was added in 3.9.
See also: sort()
, variance()
, sum()
, max()
, min()
, about collecting functions, and data
documentation.
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.
"parsestringarray"
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 mirrors the exact behavior of
readstringarray()
, 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)
See Also: parserealarray()
, parseintarray()
, readstringarray()
, readintarray()
, readrealarray()
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(), classmatch(), classesmatching()
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"
);
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.
ifelse
Prototype: ifelse(...)
Return type: string
Description: Evaluate each pair of arguments up to the last one as a (class
, value
) tuple, returning value
if class
is set.
If none are set, returns the last argument.
Arguments:
The ifelse
function is like a multi-level if-else statement. It was
inspired by Oracle's DECODE
function. It must have an odd number of
arguments (from 1 to N). The last argument is the default value, like
the else
clause in standard programming languages. Every pair of
arguments before the last one are evaluated as a pair. If the first
one evaluates true then the second one is returned, as if you had used
the first one in a class expression
. So the first item in the pair
can be more than just a class name, it's a whole context like
Tuesday.linux.!verbose
)
Generally, if ifelse
were called with arguments (a1, a2, b1,
b2, c)
, the behavior expressed as pseudo-code is:
if a1 then return a2
else-if b1 then return b2
else return c
(But again, note that any odd number of arguments is supported.)
The ifelse
function is extremely useful when you want to avoid
explicitly stating the negative of all the expected cases; this
problem is commonly seen like so:
class1.class2::
"myvar" string => "x";
class3.!class2::
"myvar" string => "y";
!((class1.class2)||class3.!class2)::
"myvar" string => "z";
That's hard to read and error-prone (do you know how class2
will
affect the default case?). Here's the alternative with ifelse
:
"myvar" string => ifelse("class1.class2", "x",
"class3.!class2", "y",
"z");
Example:
bundle agent example
{
classes:
"myclass" expression => "any";
"myclass2" expression => "any";
"secondpass" expression => "any";
vars:
# we need to use the secondpass class because on the first pass,
# myclass and myclass2 are not defined yet
secondpass::
# result: { "1", "single string parameter", "hardclass OK", "bundle class OK", "5 parameters OK" }
"mylist" slist => {
ifelse(1),
ifelse("single string parameter"),
ifelse("cfengine", "hardclass OK", "hardclass broken"),
ifelse("myclass.myclass2", "bundle class OK", "bundle class broken"),
ifelse("this is not true", "5 parameters broken",
"this is also not true", "5 parameters broken 2",
"5 parameters OK"),
};
reports:
"ifelse result list: $(mylist)";
}
Note: As a general rule function evaluation is skipped when undefined variables are used. However this function has special behavior when exactly three arguments are used, allowing it to be evaluated even if it contains undefined variables. For example:
bundle agent example
{
vars:
"passwd_path"
string => ifelse( isvariable("def.passwd_path"), "$(def.passwd_path)",
"/etc/passwd"),
comment => "Use the user provided path for the passwd file if its defined
in the def scope, else use a sane default. This can allow for
easier policy testing and default overrides.";
}
History:
- Special behavior actuating function with undefined variable references when 3
parameters are in use added in
3.7.4
and3.9.1
.
readreallist
Prototype: readreallist(filename, comment, split, maxentries, maxbytes)
Return type: rlist
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:
printf "one\ntwo\nthree\n" > /tmp/list.txt
printf "1\n2\n3\n" >> /tmp/list.txt
printf "1.0\n2.0\n3.0" >> /tmp/list.txt
Run:
bundle agent example_readreallist
{
vars:
"my_list_of_reals"
rlist => readreallist( "/tmp/list.txt", # File to read
"^(\D+)", # Ignore any non-digits
"\n", # Split on newlines
inf, # Maximum number of entries
inf ); # Maximum number of bytes to read
reports:
"my_list_of_reals includes '$(my_list_of_reals)'";
}
bundle agent __main__
{
methods: "example_readreallist";
}
Output:
R: my_list_of_reals includes '1'
R: my_list_of_reals includes '2'
R: my_list_of_reals includes '3'
R: my_list_of_reals includes '1.0'
R: my_list_of_reals includes '2.0'
R: my_list_of_reals includes '3.0'
See Also: readstringlist()
, readintlist()
execresult
Prototype: execresult(command, shell)
Return type: string
The return value is cached.
Description: Execute command
and return output (both stdout
and stderr
) 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
echo "#!/usr/bin/env sh" >/tmp/testhere/echo-stdout-and-stderr
echo "echo stderr >&2" >>/tmp/testhere/echo-stdout-and-stderr
echo "echo stdout" >>/tmp/testhere/echo-stdout-and-stderr
chmod +x /tmp/testhere/echo-stdout-and-stderr
Run:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"my_result"
string => execresult("/bin/ls /tmp/testhere", noshell);
"my_result_with_stdout_and_stderr"
string => execresult("/tmp/testhere/echo-stdout-and-stderr", noshell);
"my_result_with_stdout"
string => execresult("/tmp/testhere/echo-stdout-and-stderr 2>/dev/null", useshell);
"my_result_with_stderr"
string => execresult("/tmp/testhere/echo-stdout-and-stderr 1>/dev/null", useshell);
reports:
"/bin/ls /tmp/testhere returned '$(my_result)'";
"my_result_with_stdout_and_stderr == '$(my_result_with_stdout_and_stderr)'";
"my_result_with_stdout == '$(my_result_with_stdout)'";
"my_result_with_stderr == '$(my_result_with_stderr)'";
}
Output:
R: /bin/ls /tmp/testhere returned 'a
b
c
d
e
echo-stdout-and-stderr'
R: my_result_with_stdout_and_stderr == 'stderr
stdout'
R: my_result_with_stdout == 'stdout'
R: my_result_with_stderr == 'stderr'
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
. If capturing stderr is undesirable, consider useshell and
redirecting it to /dev/null
.
See also: returnszero()
.
Change: policy change in CFEngine 3.0.5. Previously newlines were changed for spaces, now newlines are preserved.
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
usemodule
Prototype: usemodule(module, args)
Return type: boolean
Description: Execute CFEngine module script module
with args
, and
return whether successful.
The module script is expected to be located in the registered modules
directory, WORKDIR/modules
.
Arguments:
Example:
bundle agent test
{
classes:
# returns $(user)
"done" expression => usemodule("getusers","");
commands:
"/bin/echo" args => "test $(user)";
}
See Also: Module Protocol
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()
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 up to lastseenexpireafter
.
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
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.
data_sysctlvalues
Prototype: data_sysctlvalues()
Return type: data
Description: Returns all sysctl values using /proc/sys
.
Example:
vars:
"sysctl" data => data_sysctlvalues(); # get everything!
Output:
{
"sysctl": {
...
"net.unix.max_dgram_qlen": "512",
...
}
Notes:
History: Was introduced in version 3.11.0 (2017)
See also: sysctlvalue()
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
or .yml
means
YAML. If the file doesn't match any of those names, JSON is used.
When filetype
is CSV
,JSON
,YAML
or ENV
,
this function behaves like readcsv()
, readjson()
, readyaml()
or readenvfile()
respectively.
These functions have an optional parameter maxbytes
(default: inf
).
maxbytes
can not be set using readdata()
, if needed use one of the mentioned functions instead.
Arguments:
filename
:string
, in the range:"?(/.*)
filetype
: one ofCSV
YAML
JSON
ENV
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()
, readenvfile()
, data
documentation.
History: Was introduced in 3.7.0.
data_regextract
Prototype: data_regextract(regex, string)
Return type: data
Description: Returns a data container filled with backreferences
and named captures if the multiline 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)
Since the regular expression is run with /dotall/ and /multiline/ modes, to match the end of a line, use [^\n]*
instead of $
.
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);
# Illustrating multiline regular expression
"instance_guid_until_end_of_string"
data => data_regextract( "^guid\s?+=\s?+(?<value>.*)$",
readfile( "/tmp/instance.cfg", 200));
"instance_guid"
data => data_regextract( "^guid\s+=\s+(?<value>[^\n]*)",
readfile( "/tmp/instance.cfg", 200));
"instance_port"
data => data_regextract( "^port\s?+=\s?+(?<value>[^\n]*)",
readfile( "/tmp/instance.cfg", 200));
reports:
"$(this.bundle): parsed[0] '$(parsed[0])' parses into: $(parsed_str)";
"$(this.bundle): instance_guid_until_end_of_string[value] '$(instance_guid_until_end_of_string[value])'";
"$(this.bundle): instance_guid[value] '$(instance_guid[value])'";
"$(this.bundle): instance_port[value] '$(instance_port[value])'";
}
Output:
R: main: parsed[0] 'abcdef12-345-67andsoon' parses into: {"0":"abcdef12-345-67andsoon","2":"def","3":"12","5":"67","name1":"abc","name2":"345"}
R: main: instance_guid_until_end_of_string[value] '9CB197F0-4569-446A-A987-1DDEC1205F6B
port=5308'
R: main: instance_guid[value] '9CB197F0-4569-446A-A987-1DDEC1205F6B'
R: main: instance_port[value] '5308'
Notes:
History: Was introduced in version 3.7.0 (2015)
See also: regextract()
, regex_replace()
, pcre regular expression syntax summary
eval
Prototype: eval(expression, mode, options)
Return type: string
Description: Returns expression
evaluated according to mode
and options
. Currently only the math
and class
modes with
infix
option are supported for evaluating traditional math
expressions.
All the math is done with the C double
type internally. The results are returned as a string. When the mode
is math
the returned value is a floating-point value formatted to 6 decimal places as a string.
mode
and options
are optional and default to math
and infix
,
respectively.
Example:
vars:
# returns 20.000000
"result" string => eval("200/10", "math", "infix");
When the mode
is class
, the returned string is either false for 0 (!any
) or true for anything else (any
) so it can be used in a class expression under classes
. The ==
operator (see below) is very convenient for this purpose. The actual accepted values for false allow a tiny margin around 0, just like ==
.
Example:
classes:
# the class will be set
"they_are_equal" expression => eval("20 == (200/10)", "class", "infix");
The supported infix mathematical syntax, in order of precedence, is:
(
and)
parentheses for grouping expressions^
operator for exponentiation*
and/
operators for multiplication and division%
operators for modulo operation+
and-
operators for addition and subtraction==
"close enough" operator to tell if two expressions evaluate to the same number, with a tiny margin to tolerate floating point errors. It returns 1 or 0.>=
"greater or close enough" operator with a tiny margin to tolerate floating point errors. It returns 1 or 0.>
"greater than" operator. It returns 1 or 0.<=
"less than or close enough" operator with a tiny margin to tolerate floating point errors. It returns 1 or 0.<
"less than" operator. 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 };
}
body agent control
{
inform => "true";
}
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' or '-nan' (on some platforms)
"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 => sort( getindices("values"), int);
"eval[$(indices)]" string => eval("$(values[$(indices)])", "math", "infix");
reports:
"math/infix eval('$(values[$(indices)])') = '$(eval[$(indices)])'";
}
info: eval error: expression could not be parsed (input 'x')
info: eval error: expression could not be parsed (input '+ 200')
info: eval error: expression could not be parsed (input '- - -')
R: math/infix eval('x') = ''
R: math/infix eval('+ 200') = ''
R: math/infix eval('200 + 100') = '300.000000'
R: math/infix eval('200 - 100') = '100.000000'
R: math/infix eval('- - -') = ''
R: math/infix eval('2 + 3 - 1') = '4.000000'
R: math/infix eval('') = '0.000000'
R: math/infix eval('3 / 0') = 'inf'
R: math/infix eval('3^3') = '27.000000'
R: math/infix eval('sin(20)') = '0.912945'
R: math/infix eval('cos(20)') = '0.408082'
R: math/infix eval('20 % 3') = '2.000000'
R: math/infix eval('sqrt(0.2)') = '0.447214'
R: math/infix eval('ceil(3.5)') = '4.000000'
R: math/infix eval('floor(3.4)') = '3.000000'
R: math/infix eval('abs(-3.4)') = '3.400000'
R: math/infix eval('-3.4 == -3.4') = '1.000000'
R: math/infix eval('-3.400000 == -3.400001') = '0.000000'
R: math/infix eval('e') = '2.718282'
R: math/infix eval('pi') = '3.141593'
R: math/infix eval('100m') = '100000000.000000'
R: math/infix eval('100k') = '100000.000000'
info: eval error: expression could not be parsed (input 'x')
info: eval error: expression could not be parsed (input '+ 200')
info: eval error: expression could not be parsed (input '- - -')
info: eval error: expression could not be parsed (input 'x')
info: eval error: expression could not be parsed (input '+ 200')
info: eval error: expression could not be parsed (input '- - -')
This policy can be found in
/var/cfengine/share/doc/examples/eval.cf
and downloaded directly from
github.
History:
- Function added in 3.6.0.
mode
andoptions
optional and default tomath
andinfix
, respectively in 3.9.0.- comparison
<
,<=
,>
,>=
operators added in 3.10.0
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()
.
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()
.
"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.
getclassmetatags
Prototype: getclassmetatags(classname, optional_tag)
Return type: slist
Description: Returns the list of meta
tags for class classname
.
Arguments:
classname
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
The optional_tag
can be used to look up a specific tag's value. If you format
your tags like meta => { "mykey=myvalue1", "mykey=myvalue2"}
then the
optional_tag
of mykey
will fetch you a list with two entries, { "myvalue1",
"myvalue2" }
.
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
classes:
"c" expression => "any", meta => { "mytag", "other=once", "other=twice" };
vars:
"ctags" slist => getclassmetatags("c");
"othertag_values" slist => getclassmetatags("c", "other");
reports:
"Found tags: $(ctags)";
"Found tags for key 'other': $(othertag_values)";
}
Output:
R: Found tags: source=promise
R: Found tags: mytag
R: Found tags: other=once
R: Found tags: other=twice
R: Found tags for key 'other': once
R: Found tags for key 'other': twice
Notes:
See also: getvariablemetatags()
History: Function added in 3.6.0. optional_tag
added in 3.10.0
sum
Prototype: sum(list)
Return type: real
Description: Return the sum of the reals in list
.
This function can accept many types of data parameters.
This function might be used for simple ring computation. Of course, you could
easily combine sum
with readstringarray
or readreallist
etc., to collect
summary information from a source external to CFEngine.
Arguments:
list
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"adds_to_six" ilist => { "1", "2", "3" };
"six" real => sum("adds_to_six");
"adds_to_zero" rlist => { "1.0", "2", "-3e0" };
"zero" real => sum("adds_to_zero");
reports:
"six is $(six), zero is $(zero)";
}
Output:
R: six is 6.000000, zero is 0.000000
Because $(six)
and $(zero)
are both real numbers, the report that is
generated will be:
six is 6.000000, zero is 0.000000
Notes:
History: Was introduced in version 3.1.0b1,Nova 2.0.0b1 (2010). The collecting function behavior was added in 3.9.
See also: product()
, about collecting functions, and data
documentation.
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
See Also: changedbefore()
, isnewerthan()
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.
This function can accept many types of data parameters.
Arguments:
- list : The name of the list variable or data container to check, 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
History: The collecting function behavior was added in 3.12.
See also: About collecting functions, grep()
, every()
, some()
, and none()
.
intersection
Prototype: intersection(list1, list2)
Return type: slist
Description: Returns the unique elements in list1 that are also in list2.
This function can accept many types of data parameters.
Arguments:
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"a" slist => { 1,2,3,"x" };
"b" slist => { "x" };
"mylist1" slist => { "a", "b" };
"mylist2" slist => { "a", "b" };
"$(mylist1)_str" string => join(",", $(mylist1));
"int_$(mylist1)_$(mylist2)" slist => intersection($(mylist1), $(mylist2));
"int_$(mylist1)_$(mylist2)_str" string => join(",", "int_$(mylist1)_$(mylist2)");
reports:
"The intersection of list '$($(mylist1)_str)' with '$($(mylist2)_str)' is '$(int_$(mylist1)_$(mylist2)_str)'";
}
Output:
R: The intersection of list '1,2,3,x' with '1,2,3,x' is '1,2,3,x'
R: The intersection of list '1,2,3,x' with 'x' is 'x'
R: The intersection of list 'x' with '1,2,3,x' is 'x'
R: The intersection of list 'x' with 'x' is 'x'
See also: About collecting functions, difference()
.
hash_to_int
Prototype: hash_to_int(lower, upper, string)
Return type: int
Description: Generates an integer between lower
and upper
range based on hash of string
.
Notes:
This function is similar to splayclass()
but more widely usable. Anything that
involves orchestration of many hosts could use this function, either for evenly
spreading out the scheduling, or even for static load balancing. The result
would may be coupled with an ifelse()
clause of some sort, or just used
directly.
Arguments:
lower (inclusive)
:int
, in the range:-99999999999,99999999999
upper (exclusive)
:int
, in the range:-99999999999,99999999999
string
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"hello" int => hash_to_int(0, 1000, "hello");
"world" int => hash_to_int(0, 1000, "world");
# Hash can vary on hostkey or policy hub:
"hour" int => hash_to_int(0, 24, "$(sys.key_digest)");
"minute" int => hash_to_int(0, 60, "$(sys.policy_hub)");
reports:
"'hello' hashed to: $(hello)";
"'world' hashed to: $(world)";
}
Output:
R: 'hello' hashed to: 172
R: 'world' hashed to: 760
History:
- Introduced in 3.12.0.
See Also: splayclass()
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()
, isipinsubnet()
History: Was introduced in version 3.1.3, Nova 2.0.2 (2010)
findprocesses
Prototype: findprocesses(regex)
Return type: data
The return value is cached.
Description: Return the list of processes that match the given regular
expression regex
.
This function searches for the given regular expression in the process
table. Use .*sherlock.*
to find all the processes that match
sherlock
. Use .*\bsherlock\b.*
to exclude partial matches like
sherlock123
(\b
matches a word boundary).
Arguments:
regex
: regular expression, in the range:.*
The returned data container is a list of key-value maps. Each one is
guaranteed to have the key pid
with the process ID. The key line
will also be available with the raw process table contents.
The process table is usually obtained with something like ps -eo
user,pid,ppid,pgid,%cpu,%mem,vsize,ni,rss,stat,nlwp,stime,time,args
, and the
CMD
or COMMAND
field (args
) is used to match against. However the exact
data used may change per platform and per CFEngine release.
Example:
vars:
"holmes" data => findprocesses(".*sherlock.*");
Output:
[ { "pid": "2378", "line": "...the ps output here" }, ... ]
History: Introduced in CFEngine 3.9
See also: processes
processexists()
.
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.
length
Prototype: length(list)
Return type: int
Description: Returns the length of list
.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"length" int => length("test");
"test_str" string => join(",", "test");
reports:
"The test list is $(test_str)";
"The test list has $(length) elements";
}
Output:
R: The test list is 1,2,3,one,two,three,long string,four,fix,six,one,two,three
R: The test list has 13 elements
History: The collecting function behavior was added in 3.9.
See also: nth()
, mergedata()
, about collecting functions, and data
documentation.
some
Prototype: some(regex, list)
Return type: boolean
Description: Return whether any element of list
matches the
Unanchored regular expression regex
.
This function can accept many types of data parameters.
Arguments:
regex
: regular expression, in the range:.*
list
:string
, in the range:.*
some()
will return as soon as any element matches.
It's convenient to set a class to not => some(".*", mylist)
in order
to check if mylist
is empty. Since some()
returns as soon as
possible, that is better than using length()
or every()
or
none()
which must traverse the entire list.
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
classes:
# This is an easy way to check if a list is empty, better than
# expression => strcmp(length(x), "0")
# Note that if you use length() or none() or every() they will
# go through all the elements!!! some() returns as soon as any
# element matches.
"empty_x" not => some(".*", x);
"empty_y" not => some(".*", y);
"some11" expression => some("long string", test1);
"some12" expression => some("none", test1);
"some21" expression => some("long string", test2);
"some22" expression => some("none", test2);
vars:
"x" slist => { "a", "b" };
"y" slist => { };
"test1" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
reports:
empty_x::
"x has no elements";
empty_y::
"y has no elements";
any::
"The test1 list is $(test1)";
some11::
"some() test1 1 passed";
!some11::
"some() test1 1 failed";
some12::
"some() test1 2 failed";
!some12::
"some() test1 2 passed";
"The test2 list is $(test2)";
some21::
"some() test2 1 passed";
!some21::
"some() test2 1 failed";
some22::
"some() test2 2 failed";
!some22::
"some() test2 2 passed";
}
Output:
R: y has no elements
R: The test1 list is 1
R: The test1 list is 2
R: The test1 list is 3
R: The test1 list is one
R: The test1 list is two
R: The test1 list is three
R: The test1 list is long string
R: The test1 list is four
R: The test1 list is fix
R: The test1 list is six
R: some() test1 1 passed
R: some() test1 2 passed
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: some() test2 1 passed
R: some() test2 2 passed
History: The collecting function behavior was added in 3.9.
See also: About collecting functions, filter()
, every()
, and none()
.
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.
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()
.
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
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)
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)
unique
Prototype: unique(list)
Return type: slist
Description: Returns list of unique elements from list
.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test_str" string => join(",", "test");
"test_unique" slist => unique("test");
"unique_str" string => join(",", "test_unique");
reports:
"The test list is $(test_str)";
"The unique elements of the test list: $(unique_str)";
}
Output:
R: The test list is 1,2,3,one,two,three,long string,four,fix,six,one,two,three
R: The unique elements of the test list: 1,2,3,one,two,three,long string,four,fix,six
History: The collecting function behavior was added in 3.9.
See also: filter()
, about collecting functions, and data
documentation.
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()
.
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.
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)
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'
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)
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");
getuserinfo
Prototype: getuserinfo(optional_uidorname)
Return type: data
Description: Return information about the current user or any other, looked up by user ID (UID) or user name.
This function searches for a user known to the system. If the
optional_uidorname
parameter is omitted, the current user (that is currently
running the agent) is retrieved. If optional_uidorname
is specified, the user
entry is looked up by name or ID, using the standard getpwuid()
and
getpwnam()
POSIX functions (but note that these functions may in turn talk to
LDAP, for instance).
On platforms that don't support these POSIX functions, the function simply fails.
Arguments:
optional_uidorname
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
# note the results here will vary depending on your platform
"me" data => getuserinfo(); # the current user's info
"root" data => getuserinfo("root"); # the "root" user's info (usually UID 0)
"uid0" data => getuserinfo(0); # lookup user info for UID 0 (usually "root")
# sys.user_data has the information for the user that started the agent
"out" string => format("I am '%s', root shell is '%s', and the agent was started by %S", "$(me[description])", "$(root[shell])", "sys.user_data");
reports:
"$(out)";
}
Typical Results:
R: I am 'Mr. Current User', root shell is '/bin/bash', and the agent was started by {"description":"Mr. Current User","gid":1000,"home_dir":"/home/theuser","shell":"/bin/sh","uid":1000,"username":"theuser"}
And variable contents:
"me": {
"description": "Mr. Current User",
"gid": 1000,
"home_dir": "/home/theuser",
"shell": "/bin/sh",
"uid": 1000,
"username": "theuser"
}
"root": {
"description": "root",
"gid": 0,
"home_dir": "/root",
"shell": "/bin/bash",
"uid": 0,
"username": "root"
}
"uid0": {
"description": "root",
"gid": 0,
"home_dir": "/root",
"shell": "/bin/bash",
"uid": 0,
"username": "root"
}
History: Introduced in CFEngine 3.10
See also: getusers()
, users
.
"readintarray"
Prototype: readintarray(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:
Prepare:
echo 1: 5:7:21:13 > /tmp/readintarray.txt
echo 2:19:8:14:14 >> /tmp/readintarray.txt
echo 3:45:1:78:22 >> /tmp/readintarray.txt
echo 4:64:2:98:99 >> /tmp/readintarray.txt
Run:
bundle agent main
{
vars:
"lines" int => readintarray("array_name",
"/tmp/readintarray.txt",
"#[^\n]*",
":",
10,
4000);
reports:
"array_name contains $(lines) keys$(const.n)$(with)"
with => string_mustache("", "array_name");
}
Output:
R: array_name contains 4 keys
{
"1": {
"0": "1",
"1": "5",
"2": "7",
"3": "21",
"4": "13"
},
"2": {
"0": "2",
"1": "19",
"2": "8",
"3": "14",
"4": "14"
},
"3": {
"0": "3",
"1": "45",
"2": "1",
"3": "78",
"4": "22"
},
"4": {
"0": "4",
"1": "64",
"2": "2",
"3": "98",
"4": "99"
}
}
See Also: readstringarray()
, readrealarray()
, parseintarray()
, parserealarray()
, parsestringarray()
string_head
Prototype: string_head(data, max)
Return type: string
Description: Returns the first max
bytes of data
.
If max
is negative, then everything but the last max
bytes is returned.
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()
.
variance
Prototype: variance(list)
Return type: real
Description: Return the variance of the numbers in list
.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
Use the eval()
function to easily get the standard deviation (square root of the variance).
This is not part of a full statistical package but a convenience function.
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
# the behavior will be the same whether you use a data container or a list
# "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
"mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
"mylist_str" string => format("%S", mylist);
"max_int" string => max(mylist, "int");
"max_lex" string => max(mylist, "lex");
"max_ip" string => max(mylist, "ip");
"min_int" string => min(mylist, "int");
"min_lex" string => min(mylist, "lex");
"min_ip" string => min(mylist, "ip");
"mean" real => mean(mylist);
"variance" real => variance(mylist);
reports:
"my list is $(mylist_str)";
"mean is $(mean)";
"variance is $(variance) (use eval() to get the standard deviation)";
"max int is $(max_int)";
"max IP is $(max_ip)";
"max lexicographically is $(max_lex)";
"min int is $(min_int)";
"min IP is $(min_ip)";
"min lexicographically is $(min_lex)";
}
Output:
R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1
History: Was introduced in version 3.6.0 (2014). The collecting function behavior was added in 3.9.
See also: sort()
, mean()
, sum()
, max()
, min()
, about collecting functions, and data
documentation.
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");
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)
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()
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()
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";
}
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
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:
bundle agent main
{
classes:
"example_one";
"example_two"
meta => { "plus", "defined_from=$(this.bundle)" };
"example_three"
meta => { "plus", "defined_from=$(this.bundle)" };
vars:
"cfengine_classes"
slist => sort( classesmatching("cfengine"), lex);
"example_with_plus"
slist => sort( classesmatching("example.*", "plus"), 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)'";
"Classes matching 'cfengine' = '$(cfengine_classes)'";
# this should produce no output
"Classes matching 'example.*' with the 'plus' tag = $(example_with_plus)";
}
Output:
R: Classes matching 'cfengine' = 'cfengine'
R: Classes matching 'example.*' with the 'plus' tag = example_three
R: Classes matching 'example.*' with the 'plus' tag = example_two
See also: variablesmatching(), bundlesmatching(), classes defined via augments, classmatch(), countclassesmatching()
Note: This function replaces the allclasses.txt
static file available
in older versions of CFEngine.
History: Introduced in CFEngine 3.6
readfile
Prototype: readfile(filename, optional_maxbytes)
Return type: string
Description:
Returns the first maxbytes
bytes from file filename
.
maxbytes
is optional, if specified, only the first maxbytes
bytes are read from filename
.
When maxbytes
is 0
or not specified, the whole file will be read (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
readenvfile
Prototype: readenvfile(filename, optional_maxbytes)
Return type: data
Description:
Parses key-value pairs from the file filename
in env file format (man os-release
).
Returns the result as a data
variable.
Keys and values are interpreted as strings.
maxbytes
is optional, if specified, only the first maxbytes
bytes are read from filename
.
Details of the os-release/env file format on freedesktop.org
Arguments:
Syntax example:
vars:
"loadthis"
data => readenvfile("/etc/os-release");
Complete example:
Prepare:
echo 'PRETTY_NAME="Ubuntu 14.04.5 LTS"' > /tmp/os-release
Run:
body edit_defaults empty
{
empty_file_before_editing => "true";
edit_backup => "false";
}
bundle edit_line insert_lines(lines)
{
insert_lines:
"$(lines)";
}
body printfile cat(file)
{
file_to_print => "$(file)";
number_of_lines => "inf";
}
bundle agent main
{
classes:
"file_found" expression => fileexists("/tmp/os-release");
# Use readenvfile() to load /tmp/os-release, then convert to json:
vars:
file_found::
"envdata"
data => readenvfile("/tmp/os-release");
"jsonstring"
string => storejson(envdata);
# Print input(os-release) and output(json) files:
reports:
file_found::
"/tmp/os-release :"
printfile => cat("/tmp/os-release");
"/tmp/os-release converted to json:";
"$(jsonstring)";
"(The data for this system is available in sys.os_release)";
!file_found::
"/tmp/os-release doesn't exist, run this command:";
"echo 'PRETTY_NAME=\"Ubuntu 14.04.5 LTS\"' > /tmp/os-release";
}
Output:
R: /tmp/os-release :
R: PRETTY_NAME="Ubuntu 14.04.5 LTS"
R: /tmp/os-release converted to json:
R: {
"PRETTY_NAME": "Ubuntu 14.04.5 LTS"
}
R: (The data for this system is available in sys.os_release)
Notes:
This function is used internally to load /etc/os-release
into sys.os_release
.
See also: readdata()
, parsejson()
, parseyaml()
, storejson()
, mergedata()
, and data
documentation.
History:
- Introduced in 3.11.0
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
: time of last change in Unix epoch formatatime
: last access time in Unix epoch formatmtime
: last modification time in Unix epoch formatmode
: file mode as a decimal numbermodeoct
: file mode as an octal number, e.g.10777
permstr
: permission string, e.g.-rwx---rwx
(not available on Windows)permoct
: permissions as an octal number, e.g.644
(not available on Windows)type
: file type (not available on Windows):block device
,character device
,directory
,FIFO/pipe
,symlink
,regular file
,socket
, orunknown
devno
: device number (drive letter on Windows, e.g.C:
)dev_minor
: minor device number (not available on Windows)dev_major
: major device number (not available on Windows)basename
: the file name minus the directorydirname
: the directory portion of the file namelinktarget
: if the file is asymlink
, its final target. The target is chased up to 32 levels of recursion. On Windows, this returns the file name itself.linktarget_shallow
: if the file is asymlink
, its first target. On Windows, this returns the file name itself.xattr
: a string with newline-separated extended attributes and SELinux contexts inkey=value<NEWLINE>key2=value2<NEWLINE>tag1<NEWLINE>tag2
format.
On Mac OS X, you can list and set extended attributes with the xattr
utility.
On SELinux, the contexts are the same as what you see with ls -Z
.
Example:
Prepare:
echo 1234567890 > FILE.txt
chmod 0755 FILE.txt
chown 0 FILE.txt
chgrp 0 FILE.txt
Run:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"file" string => "$(this.promise_filename).txt";
methods:
"fileinfo" usebundle => fileinfo("$(file)");
}
bundle agent fileinfo(f)
{
vars:
# use the full list if you want to see all the attributes!
# "fields" slist => splitstring("size,gid,uid,ino,nlink,ctime,atime,mtime,mode,modeoct,permstr,permoct,type,devno,dev_minor,dev_major,basename,dirname,linktarget,linktarget_shallow", ",", 999);
# ino (inode number), ctime (creation time),
# devno/dev_minor/dev_major (device numbers) were omitted but
# they are all integers
"fields" slist => splitstring("size,gid,uid,nlink,mode,modeoct,permstr,permoct,type,basename", ",", 999);
"stat[$(f)][$(fields)]" string => filestat($(f), $(fields));
reports:
"$(this.bundle): file $(stat[$(f)][basename]) has $(fields) = $(stat[$(f)][$(fields)])";
}
Output:
R: fileinfo: file filestat.cf.txt has size = 11
R: fileinfo: file filestat.cf.txt has gid = 0
R: fileinfo: file filestat.cf.txt has uid = 0
R: fileinfo: file filestat.cf.txt has nlink = 1
R: fileinfo: file filestat.cf.txt has mode = 33261
R: fileinfo: file filestat.cf.txt has modeoct = 100755
R: fileinfo: file filestat.cf.txt has permstr = -rwxr-xr-x
R: fileinfo: file filestat.cf.txt has permoct = 755
R: fileinfo: file filestat.cf.txt has type = regular file
R: fileinfo: file filestat.cf.txt has basename = filestat.cf.txt
Notes:
linktarget
will prepend the directory name to relative symlink targets, in order to be able to resolve them. Uselinktarget_shallow
to get the exact link as-is in case it is a relative link.- The list of fields may be extended as needed by CFEngine.
History:
- 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()
.
reglist
Prototype: reglist(list, regex)
Return type: boolean
Description: Returns whether the anchored regular expression
regex
matches any item in list
.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
regex
: regular expression, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"nameservers" slist => {
"128.39.89.10",
"128.39.74.16",
"192.168.1.103",
"10.132.51.66"
};
classes:
"am_name_server" expression => reglist(@(nameservers), "127\.0\.0\.1");
reports:
am_name_server::
"127.0.0.1 is currently set as a nameserver";
!am_name_server::
"127.0.0.1 is NOT currently set as a nameserver";
}
Output:
R: 127.0.0.1 is NOT currently set as a nameserver
In the example above, the IP address in $(sys.ipv4[eth0])
must be escape
d,
so that the (.) characters in the IP address are not interpreted as the
regular expression "match any" characters.
History: The collecting function behavior was added in 3.9.
See also: getindices()
, getvalues()
, about collecting functions, and data
documentation.
url_get
Prototype: url_get(url, options_container)
Return type: data
Description: Retrieves the contents of a url
using options from
a data container. The data is returned in a
data container.
NOTE that the options_container
can be specified as inline JSON
This function can accept many types of data parameters.
Currently only file
, http
, and ftp
URLs are supported.
Internally, libcurl
is used.
url_get()
caches its results. To invalidate the cache, use a
different set of options, e.g. by modifying an unused key with the
system time.
If the libcurl
integration is not available, the function will exit
with an error and the variable will remain undefined. If the libcurl
initialization fails, the function will also exit with an error. In
every other normal case, the function will return a valid data
container. In official CFEngine packages, libcurl
integration is
always provided.
The available options currently are:
url.max_content
: if present, specifies the maximum number of content bytes to retrieve ( default 4096 ).url.max_headers
: if present, specifies the maximum number of response headers to retrieve ( default 4096 ).url.verbose
: if 1,libcurl
will be more verbose while retrieving the content ( default 0 ).url.timeout
: if present,libcurl
will time out the request after that many seconds ( default 3 ).url.referer
: if present,libcurl
will set the Referer to this ( default unset ).url.user-agent
: if present,libcurl
will set the User-Agent to this ( default unset ).url.headers
: an array of strings in the formatFoo: bar
specifying headers for the request ( default[Host: host , Accept: \*/\*]
).
The returned data container will have the following keys:
returncode
: the HTTP response code, e.g.200
.rc
: thelibcurl
integer result code, either0
for success or something else for failureerror_message
: when present, indicates the request was unsuccessful and explains whysuccess
: a boolean. Whensuccess
isfalse
, the result code was not0
and the request was unsuccessful.content
: the response content as a stringheaders
: the response headers as a string
Arguments:
Example:
This example retrieves two URLs using one set of options. The options
are specified in JSON and parsed into a data container options
. That
data container is then passed to each invocation of url_get
.
bundle agent main
{
vars:
"options_str" string => '
{
"url.max_content": 512,
"url.verbose": 0,
"url.headers": [ "Foo: bar" ]
}';
"options" data => parsejson($(options_str));
"url" string => "http://cfengine.com";
"res" data => url_get($(url), options);
"out" string => format("%S", res);
"url2" string => "http://nosuchcfenginehost.com";
"res2" data => url_get($(url2), options);
"out2" string => format("%S", res2);
reports:
"$(this.bundle): from $(url) with options $(options_str) we got $(out)";
"$(this.bundle): from $(url2) with options $(options_str) we got $(out2)";
}
Output:
R: main: from http://cfengine.com with options
{
"cfengine.max_content": 512,
"curl.verbose": 0,
"curl.headers": [ "Foo: bar" ]
} we got {"returncode":200,"rc":0,"success":true,"content":"\n<!DOCTYPE html>\n<!--[if lt IE 7]>\n<html class=\"no-js lt-ie9 lt-ie8 lt-ie7\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <![endif]-->\n<!--[if IE 7]>\n<html class=\"no-js lt-ie9 lt-ie8\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <![endif]-->\n<!--[if IE 8]>\n<html class=\"no-js lt-ie9\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <![endif]-->\n<!--[if gt IE 8]><!-->\n<html class=\"no-js\" lang=\"en-US\" prefix=\"og: http://ogp.me/ns#\"> <!--<![endif]-->\n<head>\n\n \n <meta charset=\"utf-8\">\n\n <title>\n CFEng","headers":"HTTP/1.1 200 OK\r\nDate: Fri, 27 Mar 2015 18:13:01 GMT\r\nServer: Apache\r\nX-Powered-By: PHP/5.3.3\r\nX-Pingback: http://cfengine.com/xmlrpc.php\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n"}
R: main: from http://nosuchcfenginehost.com with options
{
"cfengine.max_content": 512,
"curl.verbose": 0,
"curl.headers": [ "Foo: bar" ]
} we got {"returncode":0,"rc":6,"success":false,"content":"","headers":""}
History: Introduced in CFEngine 3.8. The collecting function behavior was added in 3.9.
See also: readtcp()
, mergedata()
, parsejson()
, about collecting functions, and data
documentation.
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 -f)" | tr 'A-Z' 'a-z' >> /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)";
"Unable to find my fully qualified hostname $(sys.fqhost) in /tmp/cfe_hostlist. Can't determine peers."
if => not( regline( $(sys.fqhost), "/tmp/cfe_hostlist" ) );
}
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
.
This function can accept many types of data parameters.
Arguments:
- filter : Anchored regular expression or static string to find, in the range
.*
- list : The name of the list variable or data container to check, in the range
[a-zA-Z0-9_$(){}\[\].:]+
- is_regex_ : Boolean
Treat filter
as a regular expression or as a static string.
invert
: Boolean
Invert filter.
max_return
: Maximum number of elements to return in the range0,999999999
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"test" slist => {
1,2,3,
"one", "two", "three",
"long string",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3, "ab", "c"]');
"test_filtergrep" slist => filter("[0-9]", test, "true", "false", 999);
"test_exact1" slist => filter("one", test, "false", "false", 999);
"test_exact2" slist => filter(".", test, "false", "false", 999);
"test_invert" slist => filter("[0-9]", test, "true", "true", 999);
"test_max2" slist => filter(".*", test, "true", "false", 2);
"test_max0" slist => filter(".*", test, "true", "false", 0);
"test_grep" slist => grep("[0-9]", test);
"test2_filtergrep" slist => filter("[0-9]", test2, "true", "false", 999);
"test2_exact1" slist => filter("one", test2, "false", "false", 999);
"test2_exact2" slist => filter(".", test2, "false", "false", 999);
"test2_invert" slist => filter("[0-9]", test2, "true", "true", 999);
"test2_max2" slist => filter(".*", test2, "true", "false", 2);
"test2_max0" slist => filter(".*", test2, "true", "false", 0);
"test2_grep" slist => grep("[0-9]", test2);
"todo" slist => { "test", "test2", "test_filtergrep", "test_exact1",
"test_exact2", "test_invert", "test_max2",
"test_max0", "test_grep", "test2_filtergrep",
"test2_exact1", "test2_exact2",
"test2_invert", "test2_max2", "test2_max0",
"test2_grep"};
"$(todo)_str" string => format("%S", $(todo));
"tests" slist => { "test", "test2" };
reports:
"The $(tests) list is $($(tests)_str)";
"The grepped list (only single digits from $(tests)) is $($(tests)_grep_str)";
"The filter-grepped list (only single digits from $(tests)) is $($(tests)_grep_str)";
"The filter-exact list, looking for only 'one' in $(tests), is $($(tests)_exact1_str)";
"This list should be empty, the '.' is not literally in the list $(tests): $($(tests)_exact2_str)";
"The filter-invert list, looking for non-digits in $(tests), is $($(tests)_invert_str)";
"The filter-bound list, matching at most 2 items from the whole list $(tests), is $($(tests)_max2_str)";
"This list should be empty because 0 elements of $(tests) were requested: $($(tests)_max0_str)";
}
Output:
R: The test list is { "1", "2", "3", "one", "two", "three", "long string", "one", "two", "three" }
R: The test2 list is [1,2,3,"ab","c"]
R: The grepped list (only single digits from test) is { "1", "2", "3" }
R: The grepped list (only single digits from test2) is { "1", "2", "3" }
R: The filter-grepped list (only single digits from test) is { "1", "2", "3" }
R: The filter-grepped list (only single digits from test2) is { "1", "2", "3" }
R: The filter-exact list, looking for only 'one' in test, is { "one", "one" }
R: The filter-exact list, looking for only 'one' in test2, is { }
R: This list should be empty, the '.' is not literally in the list test: { }
R: This list should be empty, the '.' is not literally in the list test2: { }
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: { }
R: This list should be empty because 0 elements of test2 were requested: { }
History: The collecting function behavior was added in 3.9.
See also: About collecting functions, grep()
, every()
, some()
, and none()
.
mergedata
Prototype: mergedata(one, two, etc)
Return type: data
Description: Returns the merger of any named data containers or lists. Can also wrap and unwrap data containers.
The returned data container will have the keys from each of the named data containers, arrays, or lists.
If all the data containers are JSON arrays, they are merged into a single array, as you'd expect from merging two arrays.
If any of the data containers are JSON objects, all the containers are treated as JSON objects (for arrays, the key is the element's offset).
This function can accept many types of data parameters.
mergedata()
is thus a convenient way, together with getindices()
and
getvalues()
, to bridge the gap between data container and the
traditional list and array data types in CFEngine.
Notes:
- Bare values try to expand a named cfengine data container
- It is only possible to wrap data containers in the current namespace.
- true and false are reserved bare values
Example:
body common control
{
bundlesequence => { "test", "test2", "test3" };
}
bundle agent test
{
vars:
"d1" data => parsejson('{ "a": [1,2,3], "b": [] }');
"d2" data => parsejson('{ "b": [4,5,6] }');
"d3" data => parsejson('[4,5,6]');
"list1" slist => { "element1", "element2" };
"array1[mykey]" slist => { "array_element1", "array_element2" };
"array2[otherkey]" string => "hello";
"merged_d1_d2" data => mergedata("d1", "d2");
"merged_d1_d3" data => mergedata("d1", "d3");
"merged_d3_list1" data => mergedata("d3", "list1");
"merged_d1_array1" data => mergedata("d1", "array1");
"merged_d2_array2" data => mergedata("d2", "array2");
"merged_d1_wrap_array_d2" data => mergedata("d1", "[ d2 ]");
"merged_d1_wrap_map_d2" data => mergedata("d1", '{ "newkey": d2 }');
"merged_d1_d2_str" string => format("merging %S with %S produced %S", d1, d2, merged_d1_d2);
"merged_d1_wrap_array_d2_str" string => format("merging %S with wrapped [ %S ] produced %S", d1, d2, merged_d1_wrap_array_d2);
"merged_d1_wrap_map_d2_str" string => format('merging %S with wrapped { "newkey": %S produced %S', d1, d2, merged_d1_wrap_map_d2);
"merged_d1_d3_str" string => format("merging %S with %S produced %S", d1, d3, merged_d1_d3);
"merged_d3_list1_str" string => format("merging %S with %S produced %S", d3, list1, merged_d3_list1);
"merged_d1_array1_str" string => format("merging %S with %s produced %S", d1, array1, merged_d1_array1);
"merged_d2_array2_str" string => format("merging %S with %s produced %S", d2, array2, merged_d2_array2);
reports:
"$(merged_d1_d2_str)";
"$(merged_d1_wrap_array_d2_str)";
"$(merged_d1_wrap_map_d2_str)";
"$(merged_d1_d3_str)";
"$(merged_d3_list1_str)";
"$(merged_d1_array1_str)";
"$(merged_d2_array2_str)";
}
bundle agent test2
{
vars:
"a" data => parsejson('{ "a": "1" }'), meta => { "mymerge" };
"b" data => parsejson('{ "b": "2" }'), meta => { "mymerge" };
"c" data => parsejson('{ "c": "3" }'), meta => { "mymerge" };
"d" data => parsejson('{ "d": "4" }'), meta => { "mymerge" };
"todo" slist => variablesmatching(".*", "mymerge");
methods:
"go" usebundle => cmerge(@(todo)); # a, b, c, d
reports:
"$(this.bundle): merged containers with cmerge = $(cmerge.all_str)";
}
bundle agent cmerge(varlist)
{
vars:
"all" data => parsejson('[]'), policy => "free";
"all" data => mergedata(all, $(varlist)), policy => "free";
"all_str" string => format("%S", all), policy => "free";
}
bundle agent test3
{
vars:
"dest_files" slist => { "/tmp/default.json", "/tmp/epel.json" };
"template_file" string => "repository.mustache";
"process_templates" data => mergedata('{ "$(template_file)" : dest_files }');
"process__templates_str" string => format("%S", "process_templates");
reports:
"$(this.bundle) $(process__templates_str)";
"$(this.bundle) $(process_templates[$(template_file)])";
}
Output:
R: merging {"a":[1,2,3],"b":[]} with {"b":[4,5,6]} produced {"a":[1,2,3],"b":[4,5,6]}
R: merging {"a":[1,2,3],"b":[]} with wrapped [ {"b":[4,5,6]} ] produced {"0":{"b":[4,5,6]},"a":[1,2,3],"b":[]}
R: merging {"a":[1,2,3],"b":[]} with wrapped { "newkey": {"b":[4,5,6]} produced {"a":[1,2,3],"b":[],"newkey":{"b":[4,5,6]}}
R: merging {"a":[1,2,3],"b":[]} with [4,5,6] produced {"0":4,"1":5,"2":6,"a":[1,2,3],"b":[]}
R: merging [4,5,6] with { "element1", "element2" } produced [4,5,6,"element1","element2"]
R: merging {"a":[1,2,3],"b":[]} with array1 produced {"a":[1,2,3],"b":[],"mykey":["array_element1","array_element2"]}
R: merging {"b":[4,5,6]} with array2 produced {"b":[4,5,6],"otherkey":"hello"}
R: test2: merged containers with cmerge = {"a":"1","b":"2","c":"3","d":"4"}
R: test3 {"repository.mustache":["/tmp/default.json","/tmp/epel.json"]}
R: test3 /tmp/default.json
R: test3 /tmp/epel.json
History: Was introduced in CFEngine 3.6.0 (2014). The collecting function behavior was added in 3.9.
See also: getindices()
, getvalues()
, readjson()
, parsejson()
, readyaml()
, parseyaml()
, about collecting functions, and data
documentation.
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 -f)" | tr 'A-Z' 'a-z' >> /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)";
"Unable to find my fully qualified hostname $(sys.fqhost) in /tmp/cfe_hostlist. Can't determine peers."
if => not( regline( $(sys.fqhost), "/tmp/cfe_hostlist" ) );
}
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
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)
"parserealarray"
Prototype: parserealarray(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.
This function mirrors the exact behavior of
readrealarray()
, 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)
See Also: parsestringarray()
, parseintarray()
, readstringarray()
, readintarray()
, readrealarray()
variablesmatching_as_data
Prototype: variablesmatching_as_data(name, tag1, tag2, ...)
Return type: data
Description: Return a data container with the map of variables matching
name
and any tags given to the variable contents. 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_as_data(".*", "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.
This function behaves exactly like variablesmatching()
but returns a data
container with the full contents of all the variables instead of just their
names.
Example:
body common control
{
bundlesequence => { run };
}
bundle agent run
{
vars:
"all" data => variablesmatching_as_data(".*"); # this is huge
"v" data => variablesmatching_as_data("default:sys.cf_version_major.*");
"v_dump" string => format("%S", v);
reports:
"Variables matching 'default:sys.cf_version.*' = $(v_dump)";
}
Output:
R: Variables matching 'default:sys.cf_version.*' = {"default:sys.cf_version_major":"3"}
See also: classesmatching(), bundlesmatching(), variablesmatching()
History: Introduced in CFEngine 3.10
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"
);
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()
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
This function, used together with the bundlesmatching
function,
allows you to do dynamic inputs and a dynamic bundle call chain.
Notes:
- Brace expansion is not currently supported,
{x,y,anything}
will not matchx
ory
oranything
.
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()
.
maplist
Prototype: maplist(pattern, list)
Return type: slist
Description: Return a list with each element in list
modified by a
pattern.
This function can accept many types of data parameters.
The $(this)
variable expands to the currently processed entry from list
.
This is essentially like the map() function in Perl, and applies to
lists.
Arguments:
Example:
body common control
{
bundlesequence => { "example" };
}
bundle common g
{
vars:
"otherlist" slist => { "x", "y", "z" };
}
bundle agent example
{
vars:
"oldlist" slist => { "a", "b", "c" };
"newlist1" slist => maplist("Element ($(this))","@(g.otherlist)");
"newlist2" slist => maplist("Element ($(this))",@(oldlist));
reports:
"Transform: $(newlist1)";
"Transform: $(newlist2)";
}
Output:
R: Transform: Element (x)
R: Transform: Element (y)
R: Transform: Element (z)
R: Transform: Element (a)
R: Transform: Element (b)
R: Transform: Element (c)
History: Was introduced in 3.3.0, Nova 2.2.0 (2011). The collecting function behavior was added in 3.9. The delayed evaluation behavior was introduced in 3.10.
See also: maplist()
, maparray()
, about collecting functions, and data
documentation.
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()
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.
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.
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
See Also: accessedbefore()
, changedbefore()
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
...
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.
Examples:
Reporting the system time of agent start and calculating what yesterday was.
bundle agent example_now
{
vars:
"epoch" int => now();
"24_hours_ago"
string => format( "%d",
eval( "$(epoch)-86400", math, infix ));
reports:
"Today is $(with) or in unix format '$(epoch)'"
with => strftime( gmtime, "%Y-%m-%d %T", $(epoch) );
"24 hours ago was $(with) or in unix format '$(epoch)'"
with => strftime( gmtime, "%Y-%m-%d %T", $(24_hours_ago) );
}
bundle agent __main__
{
methods:
"example_now";
}
Output:
R: Today is 2019-06-12 20:40:00 or in unix format '1560372000'
R: 24 hours ago was 2019-06-11 20:40:00 or in unix format '1560372000'
files
type promises using file_select
to limit recursive file selection
based on a time relative to the agent start can make use of this function.
bundle agent gzip_recent_pdfs
{
files:
# Ensure that any file ending in .pdf that has been
# modified in the last year is compressed
"/tmp/"
file_select => pdf_modified_within_last_year,
transformer => '/bin/gzip $(this.promiser)';
}
body file_select pdf_modified_within_last_year
# @brief Sllect files that have been modified in the last year AND end in .pdf
{
mtime => irange(ago(1,0,0,0,0,0),now);
leaf_name => { ".*\.pdf" };
file_result => "mtime.leaf_name";
}
processes
type promises using process_select
can use this function to
select processes based on relative execution time.
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.
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)
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";
}
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()
iprange
Prototype: iprange(range, optional_interface)
Return type: boolean
Description: Returns whether the current host lies in the range of
IP addresses specified, optionally checking only interface
.
Pattern matching based on IP addresses.
Arguments:
range
:string
, in the range:.*
Example:
bundle agent example
{
classes:
"dmz_1" expression => iprange("128.39.89.10-15");
"lab_1" expression => iprange("128.39.74.1/23");
"dmz_1_eth0" expression => iprange("128.39.89.10-15", "eth0");
"lab_1_eth0" expression => iprange("128.39.74.1/23", "eth0");
reports:
dmz_1::
"DMZ 1 subnet";
lab_1::
"Lab 1 subnet";
}
See Also: isipinsubnet()
, host2ip()
, ip2host()
History:
- Optional
interface
parameter introduced in CFEngine 3.9.
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";
}
See Also: hash_to_int()
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()
.
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
, inline_mustache
, datastate()
mean
Prototype: mean(list)
Return type: real
Description: Return the mean of the numbers in list
.
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
# the behavior will be the same whether you use a data container or a list
# "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
"mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
"mylist_str" string => format("%S", mylist);
"max_int" string => max(mylist, "int");
"max_lex" string => max(mylist, "lex");
"max_ip" string => max(mylist, "ip");
"min_int" string => min(mylist, "int");
"min_lex" string => min(mylist, "lex");
"min_ip" string => min(mylist, "ip");
"mean" real => mean(mylist);
"variance" real => variance(mylist);
reports:
"my list is $(mylist_str)";
"mean is $(mean)";
"variance is $(variance) (use eval() to get the standard deviation)";
"max int is $(max_int)";
"max IP is $(max_ip)";
"max lexicographically is $(max_lex)";
"min int is $(min_int)";
"min IP is $(min_ip)";
"min lexicographically is $(min_lex)";
}
Output:
R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1
History: Was introduced in version 3.6.0 (2014). The collecting function behavior was added in 3.9.
See also: sort()
, variance()
, sum()
, max()
, min()
, about collecting functions, and data
documentation.
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).
callstack_callers
Prototype: callstack_callers()
Return type: data
Description: Return the call stack for the current promise.
This is a call stack inspection function and the specific content may be tied to a specific CFEngine version. Using it requires writing code that takes the specific CFEngine version into account.
The returned data container is a list of key-value maps.
The maps all have a type
key and a frame
key with a counter. For different
frames along the stack frame path, the maps have additional keys:
- whenever possible,
- bodies: under key
body
the entry has a full dump of the body policy as JSON, same as whatcf-promises -p json
would produce, using the internal C functionBodyToJson()
. This may include theline
andsourcePath
to locate the exact code line. - bundles: under key
bundle
the entry has a full dump of the bundle policy as JSON, same as whatcf-promises -p json
would produce, using the internal C functionBundleToJson()
. This may include theline
andsourcePath
to locate the exact code line. - promise iteration: the
iteration_index
is recorded - promises: the
promise_type
,promiser
,promise_classes
, andpromise_comment
are recorded - promise sections (types): the
promise_type
is recorded
Example:
vars:
"stack" data => callstack_callers();
Output:
[ ... call stack information ... ,
{
"depth": 2,
"frame": 9,
"promise_classes": "any",
"promise_comment": "",
"promise_type": "methods",
"promiser": "",
"type": "promise"
}, ... more call stack information ... ]
History: Introduced in CFEngine 3.9
See also: callstack_promisers()
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
none
Prototype: none(regex, list)
Return type: boolean
Description: Returns whether no element in list
matches the regular
expression regex
.
This function can accept many types of data parameters.
Arguments:
regex
: regular expression, in the range:.*
list
:string
, in the range:.*
The regular expression is unanchored.
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
classes:
"none11" expression => none("jebadiah", test1);
"none12" expression => none("2", test1);
"none21" expression => none("jebadiah", test2);
"none22" expression => none("2", test2);
vars:
"test1" slist => {
1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",
};
"test2" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
reports:
"The test1 list is $(test1)";
none11::
"none() test1 1 passed";
!none11::
"none() test1 1 failed";
none12::
"none() test1 2 failed";
!none12::
"none() test1 2 passed";
"The test2 list is $(test2)";
none21::
"none() test2 1 passed";
!none21::
"none() test2 1 failed";
none22::
"none() test2 2 failed";
!none22::
"none() test2 2 passed";
}
Output:
R: The test1 list is 1
R: The test1 list is 2
R: The test1 list is 3
R: The test1 list is one
R: The test1 list is two
R: The test1 list is three
R: The test1 list is long string
R: The test1 list is four
R: The test1 list is fix
R: The test1 list is six
R: none() test1 1 passed
R: none() test1 2 passed
R: The test2 list is 1
R: The test2 list is 2
R: The test2 list is 3
R: The test2 list is one
R: The test2 list is two
R: The test2 list is three
R: The test2 list is long string
R: The test2 list is four
R: The test2 list is fix
R: The test2 list is six
R: none() test2 1 passed
R: none() test2 2 passed
History: The collecting function behavior was added in 3.9.
See also: About collecting functions, filter()
, every()
, and some()
.
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.
"parseintarray"
Prototype: parseintarray(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.
This function mirrors the exact behavior of
readintarray()
, 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
: 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**
See Also: parsestringarray()
, parserealarray()
, readstringarray()
, readintarray()
, readrealarray()
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
max
Prototype: max(list, sortmode)
Return type: string
Description: Return the maximum of the items in list
according to sortmode
(same sort modes as in sort()
).
This function can accept many types of data parameters.
Arguments:
list
:string
, in the range:.*
sortmode
: one oflex
int
real
IP
ip
MAC
mac
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
# the behavior will be the same whether you use a data container or a list
# "mylist" slist => { "foo", "1", "2", "3000", "bar", "10.20.30.40" };
"mylist" data => parsejson('["foo", "1", "2", "3000", "bar", "10.20.30.40"]');
"mylist_str" string => format("%S", mylist);
"max_int" string => max(mylist, "int");
"max_lex" string => max(mylist, "lex");
"max_ip" string => max(mylist, "ip");
"min_int" string => min(mylist, "int");
"min_lex" string => min(mylist, "lex");
"min_ip" string => min(mylist, "ip");
"mean" real => mean(mylist);
"variance" real => variance(mylist);
reports:
"my list is $(mylist_str)";
"mean is $(mean)";
"variance is $(variance) (use eval() to get the standard deviation)";
"max int is $(max_int)";
"max IP is $(max_ip)";
"max lexicographically is $(max_lex)";
"min int is $(min_int)";
"min IP is $(min_ip)";
"min lexicographically is $(min_lex)";
}
Output:
R: my list is ["foo","1","2","3000","bar","10.20.30.40"]
R: mean is 502.200000
R: variance is 1497376.000000 (use eval() to get the standard deviation)
R: max int is 3000
R: max IP is 10.20.30.40
R: max lexicographically is foo
R: min int is bar
R: min IP is 1
R: min lexicographically is 1
History: Was introduced in version 3.6.0 (2014). canonify
mode was introduced in 3.9.0. The collecting function behavior was added in 3.9.
See also: sort()
, variance()
, sum()
, mean()
, min()
, about collecting functions, and data
documentation.
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()
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()
mapdata
Prototype: mapdata(interpretation, pattern, array_or_container)
Return type: data
Description: Returns a data container holding a JSON array. The
array is a map across each element of array_or_container
, modified by
a pattern
. The map is either collected literally when interpretation
is none
, or canonified when interpretation
is canonify
,
or parsed as JSON when interpretation
is json
, or collected from pattern
,
invoked as a program, when interpretation
is json_pipe
.
This function can accept many types of data parameters.
The $(this.k)
and $(this.v)
variables expand to the key and value
of the current element, similar to the way this
is available for
maplist
.
If the array or data container has two levels, you'll also be able to
use the $(this.k[1])
variable for the key at the second level. See
the example below for an illustration.
The order of the keys is not guaranteed. Use the sort()
function if
you need order in the resulting output.
Arguments:
interpretation
: one ofnone
canonify
json
json_pipe
pattern
:string
, in the range:.*
array_or_container
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "run" };
}
bundle agent run
{
vars:
"myarray[lookup][big]" string => "lookup big";
"myarray[lookup][small]" string => "lookup small";
# every item must parse as valid JSON when the interpretation is `json`
"mapa_json" data => mapdata("json", '{ "key": "$(this.k)", "key2": "$(this.k[1])", "value": "$(this.v)" }', myarray);
"mapa_json_str" string => format("%S", mapa_json);
# every item is just a string when the interpretation is `none`
"mapa_none" data => mapdata("none", 'key=$(this.k), level 2 key = $(this.k[1]), value=$(this.v)', myarray);
"mapa_none_str" string => format("%S", mapa_none);
"mycontainer" data => parsejson('
{
"top":
{
"x": 100,
"y": 200
}
}');
# every item must parse as valid JSON when the interpretation is `json`
"mapc_json" data => mapdata("json", '{ "key": "$(this.k)", "key2": "$(this.k[1])", "value": "$(this.v)" }', mycontainer);
"mapc_json_str" string => format("%S", mapc_json);
# every item is just a string when the interpretation is `none`
"mapc_none" data => mapdata("none", 'key=$(this.k), level 2 key = $(this.k[1]), value=$(this.v)', mycontainer);
"mapc_none_str" string => format("%S", mapc_none);
reports:
show_example::
"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)";
any::
"Note that the output of the above reports is not deterministic,"
"because the order of the keys returned by mapdata() is not guaranteed."
}
Output: (when show_example
is defined)
R: mapdata/json on classic CFEngine array result: [{"key":"lookup","key2":"big","value":"lookup big"},{"key":"lookup","key2":"small","value":"lookup small"}]
R: mapdata/none on classic CFEngine array result: ["key=lookup, level 2 key = big, value=lookup big","key=lookup, level 2 key = small, value=lookup small"]
R: mapdata/json on data container result: [{"key":"top","key2":"x","value":"100"},{"key":"top","key2":"y","value":"200"}]
R: mapdata/none on data container result: ["key=top, level 2 key = x, value=100","key=top, level 2 key = y, value=200"]
json_pipe
The json_pipe
interpretation is intended to work with programs that take JSON
as input and produce JSON as output. This is a standard tool convention in the
Unix world. See the example below for the typical usage.
jq has a powerful programming language that
fits the json_pipe
interpretation well. It will take JSON input and product
JSON output. Please read the jq manual and
cookbook to get a feel for the power of this tool. When available,
jq will offer tremendous data manipulation
power for advanced cases where the built-in CFEngine functions are not enough.
Example with json_pipe:
body common control
{
bundlesequence => { "run" };
}
bundle agent run
{
vars:
"tester" data => '{ "x": 100, "y": [ true, "a", "b" ] }';
# "jq ." returns the same thing that was passed in
"pipe_passthrough" data => mapdata("json_pipe", '$(def.jq) .', tester);
"pipe_passthrough_str" string => format("%S", pipe_passthrough);
# "jq .x" returns what was under x wrapped in an array: [100]
"pipe_justx" data => mapdata("json_pipe", '$(def.jq) .x', tester);
"pipe_justx_str" string => format("%S", pipe_justx);
# "jq .y" returns what was under y wrapped in an array: [[true,"a","b"]]
"pipe_justy" data => mapdata("json_pipe", '$(def.jq) .y', tester);
"pipe_justy_str" string => format("%S", pipe_justy);
# "jq .y[]" returns each entry under y *separately*: [true,"a","b"]
"pipe_yarray" data => mapdata("json_pipe", '$(def.jq) .y[]', tester);
"pipe_yarray_str" string => format("%S", pipe_yarray);
# "jq .z" returns null because the key "z" is missing: [null]
"pipe_justz" data => mapdata("json_pipe", '$(def.jq) .z', tester);
"pipe_justz_str" string => format("%S", pipe_justz);
# "jq" can do math too! and much more!
"pipe_jqmath" data => mapdata("json_pipe", '$(def.jq) 1+2+3', tester);
"pipe_jqmath_str" string => format("%S", pipe_jqmath);
reports:
"mapdata/json_pipe passthrough result: $(pipe_passthrough_str)";
"mapdata/json_pipe just x result: $(pipe_justx_str)";
"mapdata/json_pipe just y result: $(pipe_justy_str)";
"mapdata/json_pipe array under y result: $(pipe_yarray_str)";
"mapdata/json_pipe just z result: $(pipe_justz_str)";
"mapdata/json_pipe math expression result: $(pipe_jqmath_str)";
}
Output:
R: mapdata/json_pipe passthrough result: [{"x":100,"y":[true,"a","b"]}]
R: mapdata/json_pipe just x result: [100]
R: mapdata/json_pipe just y result: [[true,"a","b"]]
R: mapdata/json_pipe array under y result: [true,"a","b"]
R: mapdata/json_pipe just z result: [null]
R: mapdata/json_pipe math expression result: [6]
History: Was introduced in 3.7.0. canonify
mode was introduced in 3.9.0. The collecting function behavior was added in 3.9. The json_pipe
mode was added in 3.9. The delayed evaluation behavior was introduced in 3.10.
See also: maplist()
, maparray()
, canonify()
, about collecting functions, and data
documentation.
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()
regextract
Prototype: regextract(regex, string, backref)
Return type: boolean
Description: Returns whether the anchored regex
matches the
string
, and fills the array backref
with back-references.
This function should be avoided in favor of data_regextract()
because it creates classic CFEngine array variables and does not
support named captures.
If there are any back reference matches from the regular expression, then the array will be populated with the values, in the manner:
$(backref[0]) = entire string
$(backref[1]) = back reference 1, etc
Arguments:
regex
: regular expression, in the range:.*
string
:string
, in the range:.*
backref
:string
, in the range:[a-zA-Z0-9_$(){}\[\].:]+
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
classes:
# Extract regex backreferences and put them in an array
"ok" expression => regextract(
"xx ([^\s]+) ([^\s]+).* xx",
"xx one two three four xx",
"myarray"
);
reports:
ok::
"ok - \"$(myarray[0])\" = xx + \"$(myarray[1])\" + \"$(myarray[2])\" + .. + xx";
}
Output:
R: ok - "xx one two three four xx" = xx + "one" + "two" + .. + xx
See also: data_regextract()
regex_replace()
getvalues
Prototype: getvalues(varref)
Return type: slist
Description: Returns the list of values in varref
which can be
the name of an array or container.
This function can accept many types of data parameters.
If the array contains list values, then all of the list elements are flattened into a single list to make the return value a list.
If the data container contains non-scalar values (e.g. nested
containers) they are skipped. The special values true
, false
, and
null
are serialized to their string representations. Numerical
values are serialized to their string representations.
You can specify a path inside the container. For example, below you'll
look at the values of d[k]
, not at the top level of d
.
Make sure you specify the correct scope when supplying the name of the variable.
Arguments:
varref
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"v[index_1]" string => "value_1";
"v[index_2]" string => "value_2";
"values" slist => getvalues("v");
"values_sorted" slist => sort(values, lex);
# works with data containers too
"d" data => parsejson('{ "k": [ 1, 2, 3, "a", "b", "c" ] }');
"cvalues" slist => getvalues("d[k]");
"cvalues_sorted" slist => sort(cvalues, lex);
reports:
"Found values: $(values_sorted)";
"Found container values: $(cvalues_sorted)";
}
Output:
R: Found values: value_1
R: Found values: value_2
R: Found container values: 1
R: Found container values: 2
R: Found container values: 3
R: Found container values: a
R: Found container values: b
R: Found container values: c
History: The collecting function behavior was added in 3.9.
See also: getindices()
, about collecting functions, and data
documentation.
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.
This function behaves exactly like variablesmatching_as_data()
but returns
just the list of all the variables. If you want their contents as well, see that
function.
Example:
body common control
{
bundlesequence => { run };
}
bundle agent run
{
vars:
"all" slist => variablesmatching(".*");
"v" slist => variablesmatching("default:sys.cf_version.*");
"v_sorted" slist => sort(v, lex);
reports:
"Variables matching 'default:sys.cf_version.*' = $(v_sorted)";
}
Output:
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_major
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_minor
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_patch
R: Variables matching 'default:sys.cf_version.*' = default:sys.cf_version_release
See also: classesmatching(), bundlesmatching(), variablesmatching_as_data()
History: Introduced in CFEngine 3.6
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.
grep
Prototype: grep(regex, list)
Return type: slist
Description: Returns the sub-list if items in list
matching the
anchored regular expression regex
.
This function can accept many types of data parameters.
Arguments:
regex
: regular expression, in the range:.*
list
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"mylist" slist => { "One", "Two", "Three", "Four", "Five" };
"Tlist" slist => grep("T.*","mylist");
"empty_list" slist => grep("ive","mylist");
"datalist" data => parsejson('[1,2,3, "Tab", "chive"]');
"data_Tlist" slist => grep("T.*","datalist");
"data_empty_list" slist => grep("ive","datalist");
"todo" slist => { "mylist", "Tlist", "empty_list", "datalist", "data_Tlist", "data_empty_list" };
"$(todo)_str" string => format("%S", $(todo));
reports:
"$(todo): $($(todo)_str)";
}
Output:
R: mylist: { "One", "Two", "Three", "Four", "Five" }
R: Tlist: { "Two", "Three" }
R: empty_list: { }
R: datalist: [1,2,3,"Tab","chive"]
R: data_Tlist: { "Tab" }
R: data_empty_list: { }
History: The collecting function behavior was added in 3.9.
See also: About collecting functions, filter()
, every()
, some()
, and none()
.
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()
.
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
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()
.
maparray
Prototype: maparray(pattern, array_or_container)
Return type: slist
Description: Returns a list with each array_or_container
element
modified by a pattern
.
This function can accept many types of data parameters.
The $(this.k)
and $(this.v)
variables expand to the key and value
of the current element, similar to the way this
is available for
maplist
.
If the array has two levels, you'll also be able to use the
$(this.k[1])
variable for the key at the second level. See the
example below for an illustration.
If a value in the array is an slist
, you'll get one result for each
value (implicit looping).
The order of the array keys is not guaranteed. Use the sort
function if you need order in the resulting output.
Arguments:
Example:
body common control
{
bundlesequence => { "run" };
}
bundle agent run
{
vars:
"static[2]" string => "lookup 2";
"static[two]" string => "lookup two";
"static[big]" string => "lookup big";
"static[small]" string => "lookup small";
"todo[1]" string => "2";
"todo[one]" string => "two";
"todo[3999]" slist => { "big", "small" };
"map" slist =>
maparray("key='$(this.k)', static lookup = '$(static[$(this.v)])', value='$(this.v)'",
todo);
"map_sorted" slist => sort(map, lex);
"mycontainer" data => parsejson('
{
"top":
{
"x": 2,
"y": "big"
}
}');
"mapc" slist =>
maparray("key='$(this.k)', key2='$(this.k[1])', static lookup = '$(static[$(this.v)])', value='$(this.v)'",
mycontainer);
"mapc_str" string => format("%S", mapc);
reports:
"mapped array: $(map_sorted)";
"mapped container: $(mapc_str)";
}
Output:
R: mapped array: key='1', static lookup = 'lookup 2', value='2'
R: mapped array: key='3999', static lookup = 'lookup big', value='big'
R: mapped array: key='3999', static lookup = 'lookup small', value='small'
R: mapped array: key='one', static lookup = 'lookup two', value='two'
R: mapped container: { "key='top', key2='x', static lookup = 'lookup 2', value='2'", "key='top', key2='y', static lookup = 'lookup big', value='big'" }
History: The collecting function behavior was added in 3.9. The delayed evaluation behavior was introduced in 3.10.
See also: maplist()
, mapdata()
, about collecting functions, and data
documentation.
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";
}
See Also: accessedbefore()
, isnewerthan()
callstack_promisers
Prototype: callstack_promisers()
Return type: slist
Description: Return the promisers along the call stack for the current promise.
This is a call stack inspection function and the specific content may be tied to a specific CFEngine version. Using it requires writing code that takes the specific CFEngine version into account.
The returned data container is a slist
of promiser names. It's a much simpler
version of callstack_callers()
intended for quick debugging.
Example:
vars:
"my_promisers" slist => callstack_promisers();
Output:
{ "my_promisers" }
History: Introduced in CFEngine 3.9
See also: callstack_callers()
regex_replace
Prototype: regex_replace(string, regex, replacement, options)
Return type: string
Description: In a given string, replaces a regular expression with something else.
Arguments:
string
:string
, in the range:.*
regex
: regular expression, in the range:.*
replacement
:string
, in the range:.*
options
:string
, in the range:.*
The supported options are single letters you place in the options
string in any order. Consult http://pcre.org/pcre.txt for the exact
meaning of the uppercase options, and note that some can be turned on
inside the regular expression, e.g. (?s)
.
i
: case-insensitivem
: multiline (PCRE_MULTILINE
)s
: dot matches newlines too (PCRE_DOTALL
)x
: extended regular expressions (PCRE_EXTENDED
, very nice for readability)U
: ungreedy (PCRE_UNGREEDY
)T
: disables special characters and backreferences in the replacement string
In the replacement, $1
and \1
refer to the first capture group.
$2
and \2
refer to the second, and so on, except there is no \10
or higher, you have to use $10
etc.
In addition, $+
is replaced with the capture count. $'
(dollar
sign + single quote) is the part of the string after the regex match.
$` (dollar sign + backtick) is the part of the string before the
regex match. $&
holds the entire regex match.
Example:
bundle agent main
{
vars:
# global regex replace A with B
"AB" string => regex_replace("This has AAA rating", "A", "B", "g");
# global regex replace [Aa] with B (case insensitive)
"AaB" string => regex_replace("This has AAA rating", "A", "B", "gi");
# global replace every three characters with [cap=thecharacters] using $1
"cap123" string => regex_replace("abcdefghijklmn", "(...)", "[cap=$1]", "g");
# multiple captures using \1 \2 (just like $1 $2 but can only go up to \9)
"path_breakdown" string => regex_replace("/a/b/c/example.txt", "(.+)/(.+)", "dirname = \1 file basename = \2", "");
reports:
# in order, the above...
"AB replacement = '$(AB)'";
"AaB replacement = '$(AaB)'";
"cap123 replacement = '$(cap123)'";
"path_breakdown replacement = '$(path_breakdown)'";
}
Output:
R: AB replacement = 'This has BBB rating'
R: AaB replacement = 'This hBs BBB rBting'
R: cap123 replacement = '[cap=abc][cap=def][cap=ghi][cap=jkl]mn'
R: path_breakdown replacement = 'dirname = /a/b/c file basename = example.txt'
History: Was introduced in version 3.8.0 (2015)
See also: data_regextract()
regextract()
readcsv
Prototype: readcsv(filename, optional_maxbytes)
Return type: data
Description:
Parses CSV data from the file filename
and returns the result as a data
variable.
maxbytes
is optional, if specified, only the first maxbytes
bytes are read from filename
.
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.
Arguments:
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.
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");
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)
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"
);
sysctlvalue
Prototype: sysctlvalue(key)
Return type: string
Description: Returns the sysctl value of key
using /proc/sys
.
Arguments:
key
:string
, in the range:.*
Example:
vars:
"tested" slist => { "net.ipv4.tcp_mem", "net.unix.max_dgram_qlen" };
"values[$(tested)]" string => sysctlvalue($(tested));
Output:
"values[net.ipv4.tcp_mem]" = "383133\t510845\t766266"
"values[net.unix.max_dgram_qlen]" = "512"
Notes:
History: Was introduced in version 3.11.0 (2017)
See also: data_sysctlvalues()
string_tail
Prototype: string_tail(data, max)
Return type: string
Description: Returns the last max
bytes of data
.
If max
is negative, then everything but the first max
bytes is returned.
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()
.
data_expand
Prototype: data_expand(data_container)
Return type: data
Description: Transforms a data container to expand all variable references.
This function will take a data container and expand variable references once in all keys and values.
This function can accept many types of data parameters.
Any compound (arrays or maps) data structures will be expanded recursively, so for instance data in a map inside another map will be expanded.
This function is chiefly useful if you want to read data from an external source and it can contain variable references.
Arguments:
data_container
:string
, in the range:.*
Example:
Prepare to run the example policy:
echo '{ "$(main.x)": "$(main.y)" }' > /tmp/expand.json
Policy:
bundle agent main
{
vars:
"x" string => "the expanded x";
"y" string => "the expanded y";
"read" data => readjson("/tmp/expand.json", inf);
"expanded" data => data_expand(read);
"expanded_str" string => format("%S", expanded);
reports:
"$(this.bundle): the x and y references expanded to $(expanded_str)";
}
Output:
R: main: the x and y references expanded to {"the expanded x":"the expanded y"}
Notes:
History: Was introduced in version 3.7.0 (2015). The collecting functions behavior was added in 3.9.
See also: readcsv()
, readjson()
, readyaml()
, mergedata()
, about collecting functions, and data
documentation.
processexists
Prototype: processexists(regex)
Return type: boolean
The return value is cached.
Description: Return whether a process matches the given anchored regular expression
regex
.
This function searches for the given regular expression in the process
table. Use .*sherlock.*
to find all the processes that match
sherlock
. Use .*\bsherlock\b.*
to exclude partial matches like
sherlock123
(\b
matches a word boundary).
Arguments:
regex
: regular expression, in the range:.*
The process table is usually obtained with something like ps -eo
user,pid,ppid,pgid,%cpu,%mem,vsize,ni,rss,stat,nlwp,stime,time,args
, and the
CMD
or COMMAND
field (args
) is used to match against. However the exact
data used may change per platform and per CFEngine release.
Example:
classes:
# the class "holmes" will be set if a process line contains the word "sherlock"
"holmes" expression => processexists(".*sherlock.*");
History: Introduced in CFEngine 3.9
See also: processes
findprocesses()
.
join
Prototype: join(glue, list)
Return type: string
Description: Join the items of list
into a string, using the conjunction in glue
.
Converts a list or data container into a scalar variable using the join string in first argument.
This function can accept many types of data parameters.
Arguments:
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"mylist" slist => { "one", "two", "three", "four", "five" };
"datalist" data => parsejson('[1,2,3,
"one", "two", "three",
"long string",
"four", "fix", "six",
"one", "two", "three",]');
"mylist_str" string => format("%S", mylist);
"datalist_str" string => format("%S", datalist);
"myscalar" string => join("->", mylist);
"datascalar" string => join("->", datalist);
reports:
"Concatenated $(mylist_str): $(myscalar)";
"Concatenated $(datalist_str): $(datascalar)";
}
Output:
R: Concatenated { "one", "two", "three", "four", "five" }: one->two->three->four->five
R: Concatenated [1,2,3,"one","two","three","long string","four","fix","six","one","two","three"]: 1->2->3->one->two->three->long string->four->fix->six->one->two->three
History: The collecting function behavior was added in 3.9.
See also: string_split()
, about collecting functions.
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
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
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.
- This function will return both local and remote (for example, users defined in an external directory like LDAP) users on a system.
History:
- Introduced in CFEngine 3.1.0b1, CFEngine Nova/Enterprise 2.0.0b1 (2010).
See also: getuserinfo()
, users
.
sort
Prototype: sort(list, mode)
Return type: slist
Description: Returns list
sorted according to mode
.
This function can accept many types of data parameters.
Lexicographical, integer, real, IP, and MAC address sorting is
supported currently. The example below will show each sorting mode in
action. mode
is optional, and defaults to lex
.
Note IPv6 addresses can not use uppercase hexadecimal characters
(A-Z
) but must use lowercase (a-z
) instead.
Arguments:
list
:string
, in the range:.*
mode
: one oflex
int
real
IP
ip
MAC
mac
Example:
body common control
{
bundlesequence => { test };
}
bundle agent test
{
vars:
"a" slist => { "b", "c", "a" };
"b" slist => { "100", "9", "10", "8.23" };
"c" slist => { };
"d" slist => { "", "a", "", "b" };
"e" slist => { "a", "1", "b" };
"ips" slist => { "100.200.100.0", "1.2.3.4", "9.7.5.1", "9", "9.7", "9.7.5", "", "-1", "where are the IP addresses?" };
"ipv6" slist => { "FE80:0000:0000:0000:0202:B3FF:FE1E:8329",
"FE80::0202:B3FF:FE1E:8329",
"::1",
# the following should all be parsed as the same address and sorted together
"2001:db8:0:0:1:0:0:1",
"2001:0db8:0:0:1:0:0:1",
"2001:db8::1:0:0:1",
"2001:db8::0:1:0:0:1",
"2001:0db8::1:0:0:1",
"2001:db8:0:0:1::1",
"2001:db8:0000:0:1::1",
"2001:DB8:0:0:1::1", # note uppercase IPv6 addresses are invalid
# examples from https://www.ripe.net/lir-services/new-lir/ipv6_reference_card.pdf
"8000:63bf:3fff:fdd2",
"::ffff:192.0.2.47",
"fdf8:f53b:82e4::53",
"fe80::200:5aee:feaa:20a2",
"2001:0000:4136:e378:",
"8000:63bf:3fff:fdd2",
"2001:0002:6c::430",
"2001:10:240:ab::a",
"2002:cb0a:3cdd:1::1",
"2001:db8:8:4::2",
"ff01:0:0:0:0:0:0:2",
"-1", "where are the IP addresses?" };
"macs" slist => { "00:14:BF:F7:23:1D", "0:14:BF:F7:23:1D", ":14:BF:F7:23:1D", "00:014:BF:0F7:23:01D",
"00:14:BF:F7:23:1D", "0:14:BF:F7:23:1D", ":14:BF:F7:23:1D", "00:014:BF:0F7:23:01D",
"01:14:BF:F7:23:1D", "1:14:BF:F7:23:1D",
"01:14:BF:F7:23:2D", "1:14:BF:F7:23:2D",
"-1", "where are the MAC addresses?" };
"ja" string => join(",", "a");
"jb" string => join(",", "b");
"jc" string => join(",", "c");
"jd" string => join(",", "d");
"je" string => join(",", "e");
"jips" string => join(",", "ips");
"jipv6" string => join(",", "ipv6");
"jmacs" string => join(",", "macs");
"sa" slist => sort("a", "lex");
"sb" slist => sort("b", "lex");
"sc" slist => sort("c", "lex");
"sd" slist => sort("d", "lex");
"se" slist => sort("e", "lex");
"sb_int" slist => sort("b", "int");
"sb_real" slist => sort("b", "real");
"sips" slist => sort("ips", "ip");
"sipv6" slist => sort("ipv6", "ip");
"smacs" slist => sort("macs", "mac");
"jsa" string => join(",", "sa");
"jsb" string => join(",", "sb");
"jsc" string => join(",", "sc");
"jsd" string => join(",", "sd");
"jse" string => join(",", "se");
"jsb_int" string => join(",", "sb_int");
"jsb_real" string => join(",", "sb_real");
"jsips" string => join(",", "sips");
"jsipv6" string => join(",", "sipv6");
"jsmacs" string => join(",", "smacs");
reports:
"sorted lexicographically '$(ja)' => '$(jsa)'";
"sorted lexicographically '$(jb)' => '$(jsb)'";
"sorted lexicographically '$(jc)' => '$(jsc)'";
"sorted lexicographically '$(jd)' => '$(jsd)'";
"sorted lexicographically '$(je)' => '$(jse)'";
"sorted integers '$(jb)' => '$(jsb_int)'";
"sorted reals '$(jb)' => '$(jsb_real)'";
"sorted IPs '$(jips)' => '$(jsips)'";
"sorted IPv6s '$(jipv6)' => '$(jsipv6)'";
"sorted MACs '$(jmacs)' => '$(jsmacs)'";
}
Output:
2013-09-05T14:05:04-0400 notice: R: sorted lexicographically 'b,c,a' => 'a,b,c'
2013-09-05T14:05:04-0400 notice: R: sorted lexicographically '100,9,10,8.23' => '10,100,8.23,9'
2013-09-05T14:05:04-0400 notice: R: sorted lexicographically '' => ''
2013-09-05T14:05:04-0400 notice: R: sorted lexicographically ',a,,b' => ',,a,b'
2013-09-05T14:05:04-0400 notice: R: sorted lexicographically 'a,1,b' => '1,a,b'
2013-09-05T14:05:04-0400 notice: R: sorted integers '100,9,10,8.23' => '8.23,9,10,100'
2013-09-05T14:05:04-0400 notice: R: sorted reals '100,9,10,8.23' => '8.23,9,10,100'
2013-09-05T14:05:04-0400 notice: R: sorted IPs '100.200.100.0,1.2.3.4,9.7.5.1,9,9.7,9.7.5,,-1,where are the IP addresses?' => ',-1,9,9.7,9.7.5,where are the IP addresses?,1.2.3.4,9.7.5.1,100.200.100.0'
2013-09-05T14:05:04-0400 notice: R: sorted IPv6s 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329,FE80::0202:B3FF:FE1E:8329,::1,2001:db8:0:0:1:0:0:1,2001:0db8:0:0:1:0:0:1,2001:db8::1:0:0:1,2001:db8::0:1:0:0:1,2001:0db8::1:0:0:1,2001:db8:0:0:1::1,2001:db8:0000:0:1::1,2001:DB8:0:0:1::1,8000:63bf:3fff:fdd2,::ffff:192.0.2.47,fdf8:f53b:82e4::53,fe80::200:5aee:feaa:20a2,2001:0000:4136:e378:,8000:63bf:3fff:fdd2,2001:0002:6c::430,2001:10:240:ab::a,2002:cb0a:3cdd:1::1,2001:db8:8:4::2,ff01:0:0:0:0:0:0:2,-1,where are the IP addresses?' => '-1,2001:0000:4136:e378:,2001:DB8:0:0:1::1,8000:63bf:3fff:fdd2,8000:63bf:3fff:fdd2,::ffff:192.0.2.47,FE80:0000:0000:0000:0202:B3FF:FE1E:8329,FE80::0202:B3FF:FE1E:8329,where are the IP addresses?,::1,2001:0002:6c::430,2001:10:240:ab::a,2001:db8:0000:0:1::1,2001:db8:0:0:1::1,2001:0db8::1:0:0:1,2001:db8::0:1:0:0:1,2001:db8::1:0:0:1,2001:0db8:0:0:1:0:0:1,2001:db8:0:0:1:0:0:1,2001:db8:8:4::2,2002:cb0a:3cdd:1::1,fdf8:f53b:82e4::53,fe80::200:5aee:feaa:20a2,ff01:0:0:0:0:0:0:2'
2013-09-05T14:05:04-0400 notice: R: sorted MACs '00:14:BF:F7:23:1D,0:14:BF:F7:23:1D,:14:BF:F7:23:1D,00:014:BF:0F7:23:01D,00:14:BF:F7:23:1D,0:14:BF:F7:23:1D,:14:BF:F7:23:1D,00:014:BF:0F7:23:01D,01:14:BF:F7:23:1D,1:14:BF:F7:23:1D,01:14:BF:F7:23:2D,1:14:BF:F7:23:2D,-1,where are the MAC addresses?' => '-1,:14:BF:F7:23:1D,:14:BF:F7:23:1D,where are the MAC addresses?,00:014:BF:0F7:23:01D,0:14:BF:F7:23:1D,00:14:BF:F7:23:1D,00:014:BF:0F7:23:01D,0:14:BF:F7:23:1D,00:14:BF:F7:23:1D,1:14:BF:F7:23:1D,01:14:BF:F7:23:1D,1:14:BF:F7:23:2D,01:14:BF:F7:23:2D'
History:
- Function added in 3.6.0.
- Collecting function behavior added in 3.9.0.
- Optional mode
defaulting to lex
behavior added in 3.9.0.
See also: shuffle()
, about collecting functions, and data
documentation.
"readrealarray"
Prototype: readrealarray(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.0:7:21:13
2:19:8.1:14:14
3:45:1:78.2:22
4:64:2:98:99.3
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
...
Prepare:
echo "1: 5.0:7:21:13" > /tmp/readrealarray.txt
echo "2:19:8.1:14:14" >> /tmp/readrealarray.txt
echo "3:45:1:78.2:22" >> /tmp/readrealarray.txt
echo "4:64:2:98:99.3" >> /tmp/readrealarray.txt
Run:
bundle agent main
{
vars:
"lines" int => readrealarray("array_name",
"/tmp/readrealarray.txt",
"#[^\n]*",
":",
10,
4000);
reports:
"array_name contains $(lines) keys$(const.n)$(with)"
with => string_mustache("", "array_name");
}
Output:
R: array_name contains 4 keys
{
"1": {
"0": "1",
"1": " 5.0",
"2": "7",
"3": "21",
"4": "13"
},
"2": {
"0": "2",
"1": "19",
"2": "8.1",
"3": "14",
"4": "14"
},
"3": {
"0": "3",
"1": "45",
"2": "1",
"3": "78.2",
"4": "22"
},
"4": {
"0": "4",
"1": "64",
"2": "2",
"3": "98",
"4": "99.3"
}
}
See Also: readstringarray()
, readintarray()
, parserealarray()
, parserealarray()
, parsestringarray()
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
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
.
If resolution fails, the input hostname
is returned (for compatibility reasons).
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()
, isipinsubnet()
, iprange()
History: This function was introduced in CFEngine version 3.0.4 (2010)
readintlist
Prototype: readintlist(filename, comment, split, maxentries, maxbytes)
Return type: ilist
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:
printf "one\ntwo\nthree\n" > /tmp/list.txt
printf "1\n2\n3\n" >> /tmp/list.txt
printf "1.0\n2.0\n3.0" >> /tmp/list.txt
Run:
bundle agent example_readintlist
{
vars:
"my_list_of_integers"
ilist => readintlist( "/tmp/list.txt", # File to read
"^(\D+)|(\d+[^\n]+)", # Ignore any lines that are not integers
"\n", # Split on newlines
inf, # Maximum number of entries
inf); # Maximum number of bytes to read
reports:
"my_list_of_integers includes '$(my_list_of_integers)'";
}
bundle agent __main__
{
methods: "example_readintlist";
}
Output:
R: my_list_of_integers includes '1'
R: my_list_of_integers includes '2'
R: my_list_of_integers includes '3'
See Also: readstringlist()
, readreallist()
getvariablemetatags
Prototype: getvariablemetatags(varname, optional_tag)
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_$(){}\[\].:]+
The optional_tag
can be used to look up a specific tag's value. If you format
your tags like meta => { "mykey=myvalue1", "mykey=myvalue2"}
then the
optional_tag
of mykey
will fetch you a list with two entries, { "myvalue1",
"myvalue2" }
.
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"v" string => "myvalue", meta => { "mytag", "other=once", "other=twice" };
"vtags" slist => getvariablemetatags("example.v");
"othertag_values" slist => getvariablemetatags("example.v", "other");
reports:
"Found tags: $(vtags)";
"Found tags for key 'other': $(othertag_values)";
}
Output:
R: Found tags: source=promise
R: Found tags: mytag
R: Found tags: other=once
R: Found tags: other=twice
R: Found tags for key 'other': once
R: Found tags for key 'other': twice
Notes:
See also: getclassmetatags()
History: Function in 3.6.0. optional_tag
added in 3.10.0
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
, inline_mustache
, bundlestate()
Notes:
- Beware, when assigning
datastate()
to a variable, multiple passes will result in recursive growth of the data structure. Consider guarding against re-definition of a variable populated bydatastate()
.
Example illustrating how to prevent recursive growth of variable populated by datastate()
.
bundle agent main
{
vars:
"_state"
data => datastate(),
unless => isvariable( $(this.promiser) );
}
History:
- Introduced in CFEngine 3.6.0
difference
Prototype: difference(list1, list2)
Return type: slist
Description: Returns the unique elements in list1
that are not in
list2
.
This function can accept many types of data parameters.
Arguments:
Example:
body common control
{
bundlesequence => { "test" };
}
bundle agent test
{
vars:
"a" slist => { 1,2,3,"x" };
"b" slist => { "x" };
# normal usage
"diff_between_a_and_b" slist => difference(a, b);
"diff_between_a_and_b_str" string => join(",", diff_between_a_and_b);
# NOTE: advanced usage!
"mylist1" slist => { "a", "b" };
"mylist2" slist => { "a", "b" };
"$(mylist1)_str" string => join(",", $(mylist1));
# Here we're going to really call difference(a,a) then difference(a,b) then difference(b,a) then difference(b,b)
# We create a new variable for each difference!!!
"diff_$(mylist1)_$(mylist2)" slist => difference($(mylist1), $(mylist2));
"diff_$(mylist1)_$(mylist2)_str" string => join(",", "diff_$(mylist1)_$(mylist2)");
reports:
# normal usage
"The difference between lists a and b is '$(diff_between_a_and_b_str)'";
# NOTE: advanced usage results!
"The difference of list '$($(mylist1)_str)' with '$($(mylist2)_str)' is '$(diff_$(mylist1)_$(mylist2)_str)'";
}
Output:
R: The difference between lists a and b is '1,2,3'
R: The difference of list '1,2,3,x' with '1,2,3,x' is ''
R: The difference of list '1,2,3,x' with 'x' is '1,2,3'
R: The difference of list 'x' with '1,2,3,x' is ''
R: The difference of list 'x' with 'x' is ''
History: The collecting function behavior was added in 3.9.
See also: About collecting functions, intersection()
.
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()
.
getindices
Prototype: getindices(varref)
Return type: slist
Description: Returns the list of keys in varref
which can be
the name of an array or container.
This function can accept many types of data parameters.
Make sure you specify the correct scope when supplying the name of the variable.
Note:
The function always returns a list. If called on something that has no index (for example, an undefined variable) an empty list is returned.
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. ifvarref[i][j]
exist for variousi
,j
and you ask for the keys ofvarref
, you get thei
values. For each suchi
you can then ask forgetindices("varref[i]")
to get a list of thej
values (and so on, for higher levels of indexing).
Arguments:
varref
:string
, in the range:.*
Example:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"ps[relayhost]" string => "[mymailrelay]:587";
"ps[mydomain]" string => "iu.hio.no";
"ps[smtp_sasl_auth_enable]" string => "yes";
"ps[smtp_sasl_password_maps]" string => "hash:/etc/postfix/sasl-passwd";
"ps[smtp_sasl_security_options]" string => "";
"ps[smtp_use_tls]" string => "yes";
"ps[default_privs]" string => "mailman";
"ps[inet_protocols]" string => "all";
"ps[inet_interfaces]" string => "127.0.0.1";
"parameter_name" slist => getindices("ps");
"parameter_name_sorted" slist => sort(parameter_name, lex);
reports:
"Found key $(parameter_name_sorted)";
}
Output:
R: Found key default_privs
R: Found key inet_interfaces
R: Found key inet_protocols
R: Found key mydomain
R: Found key relayhost
R: Found key smtp_sasl_auth_enable
R: Found key smtp_sasl_password_maps
R: Found key smtp_sasl_security_options
R: Found key smtp_use_tls
History:
- Collecting function behavior as of 3.9.0
- Always returns list as of 3.9.0
See also: getvalues()
, about collecting functions, and data
documentation.
hostrange
Prototype: hostrange(prefix, range)
Return type: boolean
Description: Returns whether the unqualified name of the current host lies
in the range
of enumerated hostnames specified with prefix
.
This is a pattern matching function for non-regular (enumerated)
expressions. The range
specification is in the format A-B
(using a minus
character -
) where A
and B
are decimal integers, optionally prefixed with
zeroes (e.g. 01
). The unqualified name of the current host used in this
function is the same as the contents of the sys.uqhost
variable. The function is using integer comparison on range
and the last
numeric part of the unqualified host name and string comparison of prefix
(lowercase) with the part of the unqualified host name until the last numeric
part.
Arguments:
Example:
bundle agent main
{
vars:
"range" string => "1-32";
"hostname_f" string => execresult( "hostname -f", useshell);
"hostname_s" string => execresult( "hostname -s", useshell);
"hostname" string => execresult( "hostname", useshell);
classes:
"hostgroup_alpha_no_leading_zeros" expression => hostrange("host", $(range) );
"hostgroup_alpha_leading_zeros" expression => hostrange("host", "00$(range)" );
"hostgroup_alpha_UPPERCASE_prefix" expression => hostrange("HOST", "0$(range)" );
reports:
"sys.fqhost = '$(sys.fqhost)'";
"sys.uqhost = '$(sys.uqhost)'";
"hostname -f = '$(hostname_f)'";
"hostname -s = '$(hostname_s)'";
"hostname = '$(hostname)'";
hostgroup_alpha_no_leading_zeros::
"This host is within the alpha host group range (host$(range))";
hostgroup_alpha_leading_zeros::
"This host is within the alpha host group range (host00$(range)) (NOTE: Leading zeros and prefix capitalization is insignificant)";
hostgroup_alpha_UPPERCASE_prefix::
"This host is within the alpha host group range (HOST0$(range)) (NOTE: Leading zeros and prefix capitalization is insignificant)";
}
This policy can be found in
/var/cfengine/share/doc/examples/hostrange.cf
and downloaded directly from
github.
Example Output:
R: sys.fqhost = 'host001.example.com'
R: sys.uqhost = 'host001'
R: hostname -f = 'HOST001.example.com'
R: hostname -s = 'HOST001'
R: hostname = 'HOST001'
R: This host is within the alpha host group range (host1-32)
R: This host is within the alpha host group range (host001-32) (NOTE: Leading zeros and prefix capitalization is insignificant)
R: This host is within the alpha host group range (HOST01-32) (NOTE: Leading zeros and prefix capitalization is insignificant)
This policy can be found in
/var/cfengine/share/doc/examples/hostrange.cf
and downloaded directly from
github.
Special Variables
Variables are promises that can be defined in any promise bundle. Users can create their own variables.
To see variables defined on a particular host during pre-evaluation run
cf-promises --show-vars
as a privileged user. To see all variables defined
over the course of an agent execution run cf-agent --show-evaluated-vars
. Note
cf-promises shows variables resolved during pre-evaluation while cf-agent can
show variables resolved during actual execution where the system may be
modified.
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.
def Variables with some default value that can be defined by augments file or in policy.
this Variables used to access information about promises during their execution.
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.
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])";
Note: The keys in this array are canonified. For example, the entry for wlan0.1
would be found under the wlan0_1
key. Ref: CFE-3224.
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.host
The name of the current host, according to the kernel. It is undefined whether this is qualified or unqualified with a domain name.
# host = myhost
sys.inet
The available information about the IPv4 network stack, from when the agent initialized. This is currently available only on Linux systems with the special /proc/net/*
information files.
From the route table, the default_gateway
is extracted. From the list of routes in routes
, the default_route
is copied to the top level for convenience.
Each route's flags are extracted in a convenient list format.
The stats
key contains all the TCP and IP counters provided by the system in /proc/net/netstat
.
History: Was introduced in 3.9.0.
See also: sys.inet6
, sys.interfaces_data
% cat /proc/net/route
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
enp4s0 00000000 0102A8C0 0003 0 0 100 00000000 0 0 0
enp4s0 0000FEA9 00000000 0001 0 0 1000 0000FFFF 0 0 0
enp4s0 0002A8C0 00000000 0001 0 0 100 00FFFFFF 0 0 0
% cat /proc/net/netstat
TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSPassive PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPPrequeued TCPDirectCopyFromBacklog TCPDirectCopyFromPrequeue TCPPrequeueDropped TCPHPHits TCPHPHitsToUser TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPForwardRetrans TCPSlowStartRetrans TCPTimeouts TCPLossProbes TCPLossProbeRecovery TCPRenoRecoveryFail TCPSackRecoveryFail TCPSchedulerFailed TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPRetransFail TCPRcvCoalesce TCPOFOQueue TCPOFODrop TCPOFOMerge TCPChallengeACK TCPSYNChallenge TCPFastOpenActive TCPFastOpenActiveFail TCPFastOpenPassive TCPFastOpenPassiveFail TCPFastOpenListenOverflow TCPFastOpenCookieReqd TCPSpuriousRtxHostQueues BusyPollRxPackets TCPAutoCorking TCPFromZeroWindowAdv TCPToZeroWindowAdv TCPWantZeroWindowAdv TCPSynRetrans TCPOrigDataSent TCPHystartTrainDetect TCPHystartTrainCwnd TCPHystartDelayDetect TCPHystartDelayCwnd TCPACKSkippedSynRecv TCPACKSkippedPAWS TCPACKSkippedSeq TCPACKSkippedFinWait2 TCPACKSkippedTimeWait TCPACKSkippedChallenge TCPWinProbe TCPKeepAlive
TcpExt: 0 0 0 19896 7 0 0 9 0 0 560727 0 0 0 0 3575 2049614 302 313016 0 0 17283401 130554 186252521 0 126381259 21978 34307113 42386136 481 386568 7 175 316 11 822 2028 483 20959 148926 16709 267 271328 38869 512579 72057 281202 375133 561590 150370 23 59420 0 106 391776 9062 174837 4389 211213 13931 0 14556 0 0 0 585 594 65103 100117 0 0 0 0 2199955 0 0 0 0 0 0 0 15 36402752 5236349 0 7020 7132 4057 0 0 0 0 0 0 70 0 17925237 24 30 71 693624 275201738 33 992 2253 49843 136 484 21848 0 25 218 10478 503111
IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets InCsumErrors InNoECTPkts InECT1Pkts InECT0Pkts InCEPkts
IpExt: 0 0 1304886 130589 3784495 6 437612883789 422416538003 334973818 8189234 817859007 284 1 487495405 18258 4804476 543340
# sys.inet = {
"default_gateway": "192.168.2.1",
"default_route": {
"active_default_gateway": true,
"dest": "0.0.0.0",
"flags": [
"up",
"net",
"default",
"gateway"
],
"gateway": "192.168.2.1",
"interface": "enp4s0",
"irtt": 0,
"mask": "0.0.0.0",
"metric": 100,
"mtu": 0,
"refcnt": 0,
"use": 0,
"window": 0
},
"routes": [
{
"active_default_gateway": true,
"dest": "0.0.0.0",
"flags": [
"up",
"net",
"default",
"gateway"
],
"gateway": "192.168.2.1",
"interface": "enp4s0",
"irtt": 0,
"mask": "0.0.0.0",
"metric": 100,
"mtu": 0,
"refcnt": 0,
"use": 0,
"window": 0
},
{
"active_default_gateway": false,
"dest": "169.254.0.0",
"flags": [
"up",
"net",
"not_default",
"local"
],
"gateway": "0.0.0.0",
"interface": "enp4s0",
"irtt": 0,
"mask": "255.255.0.0",
"metric": 1000,
"mtu": 0,
"refcnt": 0,
"use": 0,
"window": 0
},
{
"active_default_gateway": false,
"dest": "192.168.2.0",
"flags": [
"up",
"net",
"not_default",
"local"
],
"gateway": "0.0.0.0",
"interface": "enp4s0",
"irtt": 0,
"mask": "255.255.255.0",
"metric": 100,
"mtu": 0,
"refcnt": 0,
"use": 0,
"window": 0
}
],
"stats": {
"IpExt": {
"InBcastOctets": "817859007",
"InBcastPkts": "3784495",
"InCEPkts": "543340",
"InCsumErrors": "1",
"InECT0Pkts": "4804476",
"InECT1Pkts": "18258",
"InMcastOctets": "334973818",
"InMcastPkts": "1304886",
"InNoECTPkts": "487495405",
"InNoRoutes": "0",
"InOctets": "437612883789",
"InTruncatedPkts": "0",
"OutBcastOctets": "284",
"OutBcastPkts": "6",
"OutMcastOctets": "8189234",
"OutMcastPkts": "130589",
"OutOctets": "422416538003"
},
"TcpExt": {
"ArpFilter": "0",
"BusyPollRxPackets": "0",
"DelayedACKLocked": "302",
"DelayedACKLost": "313016",
"DelayedACKs": "2049614",
"EmbryonicRsts": "19896",
"IPReversePathFilter": "0",
"ListenDrops": "0",
"ListenOverflows": "0",
"LockDroppedIcmps": "0",
"OfoPruned": "0",
"OutOfWindowIcmps": "9",
"PAWSActive": "0",
"PAWSEstab": "3575",
"PAWSPassive": "0",
"PruneCalled": "7",
"RcvPruned": "0",
"SyncookiesFailed": "0",
"SyncookiesRecv": "0",
"SyncookiesSent": "0",
"TCPACKSkippedChallenge": "218",
"TCPACKSkippedFinWait2": "0",
"TCPACKSkippedPAWS": "484",
"TCPACKSkippedSeq": "21848",
"TCPACKSkippedSynRecv": "136",
"TCPACKSkippedTimeWait": "25",
"TCPAbortFailed": "0",
"TCPAbortOnClose": "13931",
"TCPAbortOnData": "211213",
"TCPAbortOnLinger": "0",
"TCPAbortOnMemory": "0",
"TCPAbortOnTimeout": "14556",
"TCPAutoCorking": "17925237",
"TCPBacklogDrop": "0",
"TCPChallengeACK": "7132",
"TCPDSACKIgnoredNoUndo": "65103",
"TCPDSACKIgnoredOld": "594",
"TCPDSACKOfoRecv": "4389",
"TCPDSACKOfoSent": "9062",
"TCPDSACKOldSent": "391776",
"TCPDSACKRecv": "174837",
"TCPDSACKUndo": "20959",
"TCPDeferAcceptDrop": "0",
"TCPDirectCopyFromBacklog": "130554",
"TCPDirectCopyFromPrequeue": "186252521",
"TCPFACKReorder": "175",
"TCPFastOpenActive": "0",
"TCPFastOpenActiveFail": "0",
"TCPFastOpenCookieReqd": "0",
"TCPFastOpenListenOverflow": "0",
"TCPFastOpenPassive": "0",
"TCPFastOpenPassiveFail": "0",
"TCPFastRetrans": "512579",
"TCPForwardRetrans": "72057",
"TCPFromZeroWindowAdv": "24",
"TCPFullUndo": "2028",
"TCPHPAcks": "42386136",
"TCPHPHits": "126381259",
"TCPHPHitsToUser": "21978",
"TCPHystartDelayCwnd": "49843",
"TCPHystartDelayDetect": "2253",
"TCPHystartTrainCwnd": "992",
"TCPHystartTrainDetect": "33",
"TCPKeepAlive": "503111",
"TCPLossFailures": "38869",
"TCPLossProbeRecovery": "150370",
"TCPLossProbes": "561590",
"TCPLossUndo": "148926",
"TCPLostRetransmit": "16709",
"TCPMD5NotFound": "0",
"TCPMD5Unexpected": "0",
"TCPMemoryPressures": "0",
"TCPMinTTLDrop": "0",
"TCPOFODrop": "0",
"TCPOFOMerge": "7020",
"TCPOFOQueue": "5236349",
"TCPOrigDataSent": "275201738",
"TCPPartialUndo": "483",
"TCPPrequeueDropped": "0",
"TCPPrequeued": "17283401",
"TCPPureAcks": "34307113",
"TCPRcvCoalesce": "36402752",
"TCPRcvCollapsed": "106",
"TCPRenoFailures": "267",
"TCPRenoRecovery": "481",
"TCPRenoRecoveryFail": "23",
"TCPRenoReorder": "11",
"TCPReqQFullDoCookies": "0",
"TCPReqQFullDrop": "0",
"TCPRetransFail": "15",
"TCPSACKDiscard": "585",
"TCPSACKReneging": "7",
"TCPSACKReorder": "316",
"TCPSYNChallenge": "4057",
"TCPSackFailures": "271328",
"TCPSackMerged": "0",
"TCPSackRecovery": "386568",
"TCPSackRecoveryFail": "59420",
"TCPSackShiftFallback": "2199955",
"TCPSackShifted": "0",
"TCPSchedulerFailed": "0",
"TCPSlowStartRetrans": "281202",
"TCPSpuriousRTOs": "100117",
"TCPSpuriousRtxHostQueues": "70",
"TCPSynRetrans": "693624",
"TCPTSReorder": "822",
"TCPTimeWaitOverflow": "0",
"TCPTimeouts": "375133",
"TCPToZeroWindowAdv": "30",
"TCPWantZeroWindowAdv": "71",
"TCPWinProbe": "10478",
"TW": "560727",
"TWKilled": "0",
"TWRecycled": "0"
}
}
}
sys.inet6
The available information about the IPv6 network stack, from when the agent initialized. This is currently available only on Linux systems with the special /proc/net/*
information files.
The configured devices with IPv6 addresses from /proc/net/if_inet6
are collected under addresses
.
The routes from /proc/net/ipv6_route
are collected but not analyzed for default route etc. as with IPv4 routes in sys.inet
.
The network statistics from /proc/net/snmp6
are converted to a convenient key-value format under stats
.
History: Was introduced in 3.9.0.
See also: sys.inet
, sys.interfaces_data
% cat /proc/net/if_inet6
00000000000000000000000000000001 01 80 10 80 lo
fe80000000000000004249fffebdd7b4 04 40 20 80 docker0
fe80000000000000c27cd1fffe3eada6 02 40 20 80 enp4s0
% cat /proc/net/ipv6_route
fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000001 00000004 00000001 enp4s0
00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 0007e26c 00200200 lo
00000000000000000000000000000001 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000009 0000020b 80200001 lo
fe80000000000000c27cd1fffe3eada6 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000002 00000004 80200001 lo
ff000000000000000000000000000000 08 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000008 0003ffc5 00000001 enp4s0
00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 0007e26c 00200200 lo
% cat /proc/net/snmp6
Ip6InReceives 492189
Ip6InHdrErrors 0
Ip6InTooBigErrors 0
Ip6InNoRoutes 0
Ip6InAddrErrors 0
Ip6InUnknownProtos 0
Ip6InTruncatedPkts 0
Ip6InDiscards 0
Ip6InDelivers 490145
Ip6OutForwDatagrams 0
Ip6OutRequests 12145
Ip6OutDiscards 6
Ip6OutNoRoutes 249070
Ip6ReasmTimeout 0
Ip6ReasmReqds 0
Ip6ReasmOKs 0
Ip6ReasmFails 0
Ip6FragOKs 0
Ip6FragFails 0
Ip6FragCreates 0
Ip6InMcastPkts 488766
Ip6OutMcastPkts 10304
Ip6InOctets 132343220
Ip6OutOctets 1522724
Ip6InMcastOctets 131896014
Ip6OutMcastOctets 1076616
Ip6InBcastOctets 0
Ip6OutBcastOctets 0
Ip6InNoECTPkts 492196
Ip6InECT1Pkts 0
Ip6InECT0Pkts 0
Ip6InCEPkts 0
Icmp6InMsgs 275
Icmp6InErrors 0
Icmp6OutMsgs 1815
Icmp6OutErrors 0
Icmp6InCsumErrors 0
Icmp6InDestUnreachs 0
Icmp6InPktTooBigs 0
Icmp6InTimeExcds 0
Icmp6InParmProblems 0
Icmp6InEchos 0
Icmp6InEchoReplies 0
Icmp6InGroupMembQueries 0
Icmp6InGroupMembResponses 1
Icmp6InGroupMembReductions 1
Icmp6InRouterSolicits 0
Icmp6InRouterAdvertisements 0
Icmp6InNeighborSolicits 5
Icmp6InNeighborAdvertisements 268
Icmp6InRedirects 0
Icmp6InMLDv2Reports 0
Icmp6OutDestUnreachs 0
Icmp6OutPktTooBigs 0
Icmp6OutTimeExcds 0
Icmp6OutParmProblems 0
Icmp6OutEchos 0
Icmp6OutEchoReplies 0
Icmp6OutGroupMembQueries 0
Icmp6OutGroupMembResponses 0
Icmp6OutGroupMembReductions 0
Icmp6OutRouterSolicits 396
Icmp6OutRouterAdvertisements 0
Icmp6OutNeighborSolicits 206
Icmp6OutNeighborAdvertisements 5
Icmp6OutRedirects 0
Icmp6OutMLDv2Reports 1208
Icmp6InType131 1
Icmp6InType132 1
Icmp6InType135 5
Icmp6InType136 268
Icmp6OutType133 396
Icmp6OutType135 206
Icmp6OutType136 5
Icmp6OutType143 1208
Udp6InDatagrams 486201
Udp6NoPorts 0
Udp6InErrors 0
Udp6OutDatagrams 7273
Udp6RcvbufErrors 0
Udp6SndbufErrors 0
Udp6InCsumErrors 0
Udp6IgnoredMulti 0
UdpLite6InDatagrams 0
UdpLite6NoPorts 0
UdpLite6InErrors 0
UdpLite6OutDatagrams 0
UdpLite6RcvbufErrors 0
UdpLite6SndbufErrors 0
UdpLite6InCsumErrors 0
# sys.inet6 = {
"addresses": {
"docker0": {
"address": "d7b4:febd:49ff:42:0:0:0:fe80",
"device_number": 4,
"interface": "docker0",
"prefix_length": 64,
"raw_flags": "80",
"scope": 32
},
"enp4s0": {
"address": "ada6:fe3e:d1ff:c27c:0:0:0:fe80",
"device_number": 2,
"interface": "enp4s0",
"prefix_length": 64,
"raw_flags": "80",
"scope": 32
},
"lo": {
"address": "1:0:0:0:0:0:0:0",
"device_number": 1,
"interface": "lo",
"prefix_length": 128,
"raw_flags": "80",
"scope": 16
}
},
"routes": [
{
"dest": "0:0:0:0:0:0:0:0",
"dest_prefix": "40",
"flags": [
"up",
"net",
"local"
],
"interface": "enp4s0",
"metric": 256,
"next_hop": "0:0:0:0:0:0:0:0",
"refcnt": 1,
"source_prefix": "00",
"use": 4
},
{
"dest": "0:0:0:0:0:0:0:0",
"dest_prefix": "80",
"flags": [
"up",
"net",
"local"
],
"interface": "lo",
"metric": 0,
"next_hop": "0:0:0:0:0:0:0:0",
"refcnt": 2,
"source_prefix": "00",
"use": 4
}
],
"stats": {
"Icmp6InCsumErrors": 0,
"Icmp6InDestUnreachs": 0,
"Icmp6InEchoReplies": 0,
"Icmp6InEchos": 0,
"Icmp6InErrors": 0,
"Icmp6InGroupMembQueries": 0,
"Icmp6InGroupMembReductions": 1,
"Icmp6InGroupMembResponses": 1,
"Icmp6InMLDv2Reports": 0,
"Icmp6InMsgs": 275,
"Icmp6InNeighborAdvertisements": 268,
"Icmp6InNeighborSolicits": 5,
"Icmp6InParmProblems": 0,
"Icmp6InPktTooBigs": 0,
"Icmp6InRedirects": 0,
"Icmp6InRouterAdvertisements": 0,
"Icmp6InRouterSolicits": 0,
"Icmp6InTimeExcds": 0,
"Icmp6InType131": 1,
"Icmp6InType132": 1,
"Icmp6InType135": 5,
"Icmp6InType136": 268,
"Icmp6OutDestUnreachs": 0,
"Icmp6OutEchoReplies": 0,
"Icmp6OutEchos": 0,
"Icmp6OutErrors": 0,
"Icmp6OutGroupMembQueries": 0,
"Icmp6OutGroupMembReductions": 0,
"Icmp6OutGroupMembResponses": 0,
"Icmp6OutMLDv2Reports": 1208,
"Icmp6OutMsgs": 1815,
"Icmp6OutNeighborAdvertisements": 5,
"Icmp6OutNeighborSolicits": 206,
"Icmp6OutParmProblems": 0,
"Icmp6OutPktTooBigs": 0,
"Icmp6OutRedirects": 0,
"Icmp6OutRouterAdvertisements": 0,
"Icmp6OutRouterSolicits": 396,
"Icmp6OutTimeExcds": 0,
"Icmp6OutType133": 396,
"Icmp6OutType135": 206,
"Icmp6OutType136": 5,
"Icmp6OutType143": 1208,
"Ip6FragCreates": 0,
"Ip6FragFails": 0,
"Ip6FragOKs": 0,
"Ip6InAddrErrors": 0,
"Ip6InBcastOctets": 0,
"Ip6InCEPkts": 0,
"Ip6InDelivers": 490145,
"Ip6InDiscards": 0,
"Ip6InECT0Pkts": 0,
"Ip6InECT1Pkts": 0,
"Ip6InHdrErrors": 0,
"Ip6InMcastOctets": 131896014,
"Ip6InMcastPkts": 488766,
"Ip6InNoECTPkts": 492196,
"Ip6InNoRoutes": 0,
"Ip6InOctets": 132343220,
"Ip6InReceives": 492189,
"Ip6InTooBigErrors": 0,
"Ip6InTruncatedPkts": 0,
"Ip6InUnknownProtos": 0,
"Ip6OutBcastOctets": 0,
"Ip6OutDiscards": 6,
"Ip6OutForwDatagrams": 0,
"Ip6OutMcastOctets": 1076616,
"Ip6OutMcastPkts": 10304,
"Ip6OutNoRoutes": 249070,
"Ip6OutOctets": 1522724,
"Ip6OutRequests": 12145,
"Ip6ReasmFails": 0,
"Ip6ReasmOKs": 0,
"Ip6ReasmReqds": 0,
"Ip6ReasmTimeout": 0,
"Udp6IgnoredMulti": 0,
"Udp6InCsumErrors": 0,
"Udp6InDatagrams": 486201,
"Udp6InErrors": 0,
"Udp6NoPorts": 0,
"Udp6OutDatagrams": 7273,
"Udp6RcvbufErrors": 0,
"Udp6SndbufErrors": 0,
"UdpLite6InCsumErrors": 0,
"UdpLite6InDatagrams": 0,
"UdpLite6InErrors": 0,
"UdpLite6NoPorts": 0,
"UdpLite6OutDatagrams": 0,
"UdpLite6RcvbufErrors": 0,
"UdpLite6SndbufErrors": 0
}
}
sys.inputdir
The name of the inputs directory where CFEngine looks for its policy files.
# inputdir = /var/cfengine/inputs
History: Introduced in CFEngine 3.6
sys.interface
The assumed (default) name of the main system interface on this host.
# interface = eth0
sys.interfaces
Displays a system list of configured interfaces currently active in use by the system. This list is detected at runtime and it passed in the variables report to the CFEngine Enterprise Database.
Example:
bundle agent example_sys_interfaces
{
reports:
"Address: $(sys.ip_addresses)";
"Interface: $(sys.interfaces)";
"Address of '$(sys.interfaces)' is '$(sys.ipv4[$(sys.interfaces)])'";
}
bundle agent __main__
{
methods:
"example_sys_interfaces";
}
Example Output:
R: Address: 127.0.0.1
R: Address: 192.168.42.189
R: Address: 192.168.122.1
R: Address: 172.17.0.1
R: Address: 192.168.33.1
R: Address: 172.27.224.211
R: Address: 192.168.69.1
R: Interface: wlan0
R: Interface: virbr0
R: Interface: docker0
R: Interface: vboxnet3
R: Interface: tun0
R: Interface: vboxnet13
R: Address of 'wlan0' is '192.168.42.189'
R: Address of 'virbr0' is '192.168.122.1'
R: Address of 'docker0' is '172.17.0.1'
R: Address of 'vboxnet3' is '192.168.33.1'
R: Address of 'tun0' is '172.27.224.211'
R: Address of 'vboxnet13' is '192.168.69.1'
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.interfaces_data
The network statistics of the system interfaces, from when the agent initialized. This is currently available only on Linux systems with the special /proc/net/dev
file.
History: Was introduced in 3.9.0.
% cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
enp4s0: 446377831179 492136556 0 0 0 0 0 0 428200856331 499195545 0 0 0 0 0 0
lo: 1210580426 1049790 0 0 0 0 0 0 1210580426 1049790 0 0 0 0 0 0
wlp3s0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# sys.interfaces_data = {
"enp4s0": {
"device": "enp4s0",
"receive_bytes": "446377831179",
"receive_compressed": "0",
"receive_drop": "0",
"receive_errors": "0",
"receive_fifo": "0",
"receive_frame": "0",
"receive_multicast": "0",
"receive_packets": "492136556",
"transmit_bytes": "428200856331",
"transmit_compressed": "0",
"transmit_drop": "0",
"transmit_errors": "0",
"transmit_fifo": "0",
"transmit_frame": "0",
"transmit_multicast": "0",
"transmit_packets": "499195545"
},
"lo": {
"device": "lo",
"receive_bytes": "1210580426",
"receive_compressed": "0",
"receive_drop": "0",
"receive_errors": "0",
"receive_fifo": "0",
"receive_frame": "0",
"receive_multicast": "0",
"receive_packets": "1049790",
"transmit_bytes": "1210580426",
"transmit_compressed": "0",
"transmit_drop": "0",
"transmit_errors": "0",
"transmit_fifo": "0",
"transmit_frame": "0",
"transmit_multicast": "0",
"transmit_packets": "1049790"
},
"wlp3s0": {
"device": "wlp3s0",
"receive_bytes": "0",
"receive_compressed": "0",
"receive_drop": "0",
"receive_errors": "0",
"receive_fifo": "0",
"receive_frame": "0",
"receive_multicast": "0",
"receive_packets": "0",
"transmit_bytes": "0",
"transmit_compressed": "0",
"transmit_drop": "0",
"transmit_errors": "0",
"transmit_fifo": "0",
"transmit_frame": "0",
"transmit_multicast": "0",
"transmit_packets": "0"
}
}
sys.interface_flags
Contains a space separated list of the flags of the named interface. e.g.
reports:
"eth0 flags: $(sys.interface_flags[eth0])";
Outputs:
R: eth0 flags: up broadcast running multicast
The following device flags are supported:
- up
- broadcast
- debug
- loopback
- pointopoint
- notrailers
- running
- noarp
- promisc
- allmulti
- multicast
History: Was introduced in 3.5.0 (2013)
sys.ip_addresses
Displays a system list of IP addresses currently in use by the system. This list is detected at runtime and passed in the variables report to the CFEngine Enterprise Database.
To use this list in a policy, you will need a local copy since only local variables can be iterated.
bundle agent test
{
vars:
# To iterate, we need a local copy
"i1" slist => { @(sys.ip_addresses)} ;
"i2" slist => { @(sys.interfaces)} ;
reports:
"Addresses: $(i1)";
"Interfaces: $(i2)";
"Addresses of the interfaces: $(sys.ipv4[$(i2)])";
}
History: Was introduced in 3.3.0, Enterprise 2.2.0 (2011)
sys.ip2iface
A map of full IPv4 addresses (key) to the system interface (value),
e.g. $(sys.ip2iface[1.2.3.4])
.
# If the IPv4 address on the interfaces are
# le0 = 192.168.1.101
# xr1 = 10.12.7.254
#
# Then you will have
# sys.ip2iface[192.168.1.101] = le0
# sys.ip2iface[10.12.7.254] = xr1
Notes:
The list of addresses may be acquired with
getindices("sys.ip2iface")
(or from any of the other associative arrays). Only those interfaces which are marked as "up" and have an IP address will have entries.The values in this array are canonified. For example, the entry for
wlan0.1
would bewlan0_1
. Ref: CFE-3224.
History: Was introduced in 3.9.
sys.ipv4
All four octets of the IPv4 address of the first system interface.
Note:
If your system has a single ethernet interface, $(sys.ipv4)
will contain
your IPv4 address. However, if your system has multiple interfaces, then
$(sys.ipv4)
will simply be the IPv4 address of the first interface in the
list that has an assigned address, Use $(sys.ipv4[interface_name])
for
details on obtaining the IPv4 addresses of all interfaces on a system.
sys.ipv4[interface_name]
The full IPv4 address of the system interface named as the associative
array index, e.g. $(sys.ipv4[le0])
or $(sys.ipv4[xr1])
.
# If the IPv4 address on the interfaces are
# le0 = 192.168.1.101
# xr1 = 10.12.7.254
#
# Then the octets of all interfaces are accessible as an associative array
# sys.ipv4_1[le0] = 192
# sys.ipv4_2[le0] = 192.168
# sys.ipv4_3[le0] = 192.168.1
# sys.ipv4[le0] = 192.168.1.101
# sys.ipv4_1[xr1] = 10
# sys.ipv4_2[xr1] = 10.12
# sys.ipv4_3[xr1] = 10.12.7
# sys.ipv4[xr1] = 10.12.7.254
Note:
The list of interfaces may be acquired with getindices("sys.ipv4")
(or
from any of the other associative arrays). Only those interfaces which
are marked as "up" and have an IP address will be listed.
sys.ipv4_1[interface_name]
The first octet of the IPv4 address of the system interface named as the
associative array index, e.g. $(ipv4_1[le0])
or $(ipv4_1[xr1])
.
Note: The keys in this array are canonified. For example, the entry for wlan0.1
would be found under the wlan0_1
key. Ref: CFE-3224.
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])
.
Note: The keys in this array are canonified. For example, the entry for wlan0.1
would be found under the wlan0_1
key. Ref: CFE-3224.
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])
.
Note: The keys in this array are canonified. For example, the entry for wlan0.1
would be found under the wlan0_1
key. Ref: CFE-3224.
sys.key_digest
The digest of the host's cryptographic public key.
# sys.key_digest = MD5=bc230448c9bec14b9123443e1608ac07
sys.last_policy_update
Timestamp when last policy change was seen by host
sys.libdir
The name of the directory where CFEngine looks for its libraries.
# libdir = /var/cfengine/inputs/lib
History: Introduced in CFEngine 3.6, version based sub directory removed in CFEngine 3.8.
sys.local_libdir
The name of the directory where CFEngine looks for its libraries, without any prefixes.
# local_libdir = lib
History: Introduced in CFEngine 3.6, version based sub directory removed in CFEngine 3.8.
sys.logdir
The name of the directory where CFEngine log files are saved
# logdir = /var/cfengine/
History: Introduced in CFEngine 3.6
sys.license_owner
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.nova_version
The variable gives the version of the running CFEngine Enterprise Edition.
# nova_version = 1.1.3
sys.os
The name of the operating system according to the kernel.
# os = linux
See also: sys.ostype
sys.os_release
Information parsed from /etc/os-release
if present.
bundle agent main
{
reports:
"$(with)"
with => string_mustache("", @(sys.os_release) );
}
Policy Output:
R: {
"BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/",
"HOME_URL": "https://www.ubuntu.com/",
"ID": "ubuntu",
"ID_LIKE": "debian",
"NAME": "Ubuntu",
"PRETTY_NAME": "Ubuntu 17.10",
"PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy",
"SUPPORT_URL": "https://help.ubuntu.com/",
"UBUNTU_CODENAME": "artful",
"VERSION": "17.10 (Artful Aardvark)",
"VERSION_CODENAME": "artful",
"VERSION_ID": "17.10"
}
History:
- Added in 3.11.0
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_entry_basename
The basename of the first policy file read by the agent. For example
promises.cf
or update.cf
.
See Also: sys.policy_entry_dirname
sys.policy_entry_filename
History:
- Introduced 3.12.0
sys.policy_entry_dirname
The full path to the directory containing the first policy file read by the agent. For example
/var/cfengine/inputs
or ~/.cfagent/inputs
.
See Also: sys.policy_entry_basename
sys.policy_entry_filename
History:
- Introduced 3.12.0
sys.policy_entry_filename
The full path to the first policy file read by the agent. For example
/var/cfengine/inputs/promises.cf
or ~/.cfagent/inputs/promises.cf
.
See Also: sys.policy_entry_basename
sys.policy_entry_dirname
History:
- Introduced 3.12.0
sys.policy_hub
IP of the machine acting as the policy server.
$(sys.workdir)/policy_server.dat
stores bootstrap information. If bootstrapped to a hostname, the value is the current IP the hostname resolves to. If bootstrapped to an IP, the value is the stored IP. The variable is undefined if $(sys.workdir)/policy_server.dat
does not exist or is empty.
reports:
"Policy hub is $(sys.policy_hub)";
History:
- Introduced in version 3.1.0b1,Enterprise 2.0.0b1 (2010).
- Available in Community since 3.2.0
sys.policy_hub_port
The default port which cf-agent
will use by default when making outbound
connections to cf-serverd
. This defaults to 5308
but can be
overridden based on the data provided during bootstrap stored in
$(sys.workdir)/policy_server.dat
.
History:
- Introduced in version 3.10.0 (2016).
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.user_data
A data container with the user information of the user that started the agent.
# user_data = {
"description": "root",
"gid": 0,
"home_dir": "/root",
"shell": "/bin/bash",
"uid": 0,
"username": "root"
}
History: Introduced in CFEngine 3.10
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
connection
The context connection
is used by the shortcut
attribute in access
promises to access information about the remote agent requesting access.
access:
"/var/cfengine/cmdb/$(connection.key).json"
shortcut => "me.json",
admit_keys => { "$(connection.key)" };
Note: The usage of the connection
variables is strictly limited to
literal strings within the promiser
and admit/deny lists of access
promise
types; they cannot be passed into functions or stored in other variables. These
variables can only be used with incoming connections that use
protocol_version
>=2 ( or "latest" ).
connection.key
This variable contains the public key sha of the connecting client in the form 'SHA=...'.
access:
"/var/cfengine/cmdb/$(connection.key).json"
shortcut => "me.json",
admit_keys => { "$(connection.key)" };
connection.ip
This variable contains the IP address of the connecting remote agent.
access:
"/var/cfengine/cmdb/$(connection.ip).json"
shortcut => "myip.json",
admit_keys => { "$(connection.key)" };
connection.hostname
This variable contains the hostname of the connecting client as determined by a
reverse DNS lookup from cf-serverd
.
access:
"/var/cfengine/cmdb/$(connection.hostname).json"
shortcut => "myhostname.json",
admit_keys => { "$(connection.key)" };
Note: Reverse lookups are only performed when necessary. To avoid the
performance impact of reverse dns lookups for each connection avoid using
admit_hostnames
, using hostnames in your admit
rules, and these
connection
variables.
const
CFEngine defines a number of variables for embedding unprintable values or values with special meanings in strings.
const.dollar
reports:
# This will report: The value of $(const.dollar) is $
"The value of $(const.dollar)(const.dollar) is $(const.dollar)";
# This will report: But the value of $(dollar) is $(dollar)
"But the value of $(dollar) is $(dollar)";
const.dirsep
reports:
# On Unix hosts this will report: The value of $(const.dirsep) is /
# On Windows hosts this will report: The value of $(const.dirsep) is \\
"The value of $(const.dollar)(const.dirsep) is $(const.dirsep)";
const.endl
reports:
"A newline with either $(const.n) or with $(const.endl) is ok";
"But a string with \n in it does not have a newline!";
const.n
reports:
"A newline with either $(const.n) or with $(const.endl) is ok";
"But a string with \n in it does not have a newline!";
const.r
reports:
"A carriage return character is $(const.r)";
const.t
reports:
"A report with a$(const.t)tab in it";
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.
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).
def
The context def
is populated by the
Masterfiles Policy Framework and can also be populated
by the augments file.
Note: Variables defined from policy in a bundle named def
will
override the variables defined by the augments file unless the policy
explicitly guards against it.
For example mailto
is only defined from policy if it is not yet defined by the
augments file.:
bundle common def
{
vars:
# ...
"mailto"
string => "root@$(def.domain)",
ifvarclass => not(isvariable("mailto"));
# ...
}
def.jq
This variable gives a convenient way to invoke
jq for the mapdata()
function in json_pipe
mode and elsewhere. Note the below is the default value defined in the C
code that you can override in the vars
section of the
augments file or in policy as described above.
# def.jq = jq --compact-output --monochrome-output --ascii-output --unbuffered --sort-keys
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.
Language Concepts
There is only one grammatical form for statements in the language:
bundle bundle_type name
{
promise_type:
classes::
"promiser" -> { "promisee1", "promisee2", ... }
attribute_1 => value_1,
attribute_2 => value_2,
...
attribute_n => value_n;
}
In addition, CFEngine bodies can be defined and used as attribute values. Here's a real-life example of a body and its usage.
body edit_defaults no_backup
{
edit_backup => "false";
}
... and elsewhere, noting the attribute name matches the body type ...
files:
"myfile" edit_defaults => no_backup;
You can recognize everything in CFEngine from just those few concepts.
A declaration about the state we desire to maintain (e.g., the permissions or contents of a file, the availability or absence of a service, the (de)installation of a package).
A collection of promises.
A part of a promise which details and constrains its nature, possibly in separate and re-usable parts. Effectively a body is like a promise attribute that has several parameters.
CFEngine's boolean classifiers that describe context.
An association of the form "LVALUE represents RVALUE", where RVALUE may be a scalar value or a list of scalar values: a string, integer or real number.
This documentation about the language concepts introduces in addition
Syntax, identifiers and names
The CFEngine 3 language has a few simple rules:
- CFEngine built-in words, names of variables, bundles, body templates and classes may only contain the usual alphanumeric and underscore characters (
a-zA-Z0-9_
) - All other 'literal' data must be quoted.
Declarations of promise bundles in the form:
bundle agent-type identifier { ... }
where
agent-type
is the CFEngine component responsible for maintaining the promise.Declarations of promise body-parts in the form:
body constraint_type template_identifier { ... }
matching and expanding on a reference inside a promise of the form
constraint_type => template_identifier
attribute expressions in the body of a promise take the form
left-hand-side (CFEngine_word) => right-hand-side (user defined data).
This can take several forms:
cfengine_word => user_defined_template(parameters) user_defined_template builtin_function() "quoted literal scalar" { list }
In each of these cases, the right hand side is a user choice.
CFEngine uses many `constraint expressions' as part of the body of a promise. These take the form: left-hand-side (cfengine word) ‘=>’ right-hand-side (user defined data). This can take several forms:
cfengine_word => user_defined_template(parameters) user_defined_template builtin_function() "quoted literal scalar" { list }
In each of these cases, the right hand side is a user choice.
Filenames and Paths
Filenames in Unix-like operating systems use the forward slash '/' character for their directory separator. All references to file locations must be absolute pathnames in CFEngine, i.e. they must begin with a complete specification of which directory they are in or with a variable reference that resolves to that. For example:
/etc/passwd
/var/cfengine/masterfiles/distfile
$(sys.masterdir)/distfile # usually the same thing in 3.6
The only place where it makes sense to refer to a file without a complete directory specification is when searching through directories for different kinds of file, e.g. in pattern matching
leaf_name => { "tmp_.*", "output_file", "core" };
Here, one can write core without a path, because one is looking for any file of that name in a number of directories.
The Windows operating systems traditionally use a different filename convention. The following are all valid absolute file names under Windows:
c:\winnt
"c:\spaced name"
c:/winnt
/var/cfengine/inputs
//fileserver/share2/dir
The 'drive' name "C:" in Windows refers to a partition or device. Unlike Unix, Windows does not integrate these seamlessly into a single file-tree. This is not a valid absolute filename:
\var\cfengine\inputs
Paths beginning with a backslash are assumed to be win32 paths. They must begin with a drive letter or double-slash server name.
Note that in many cases, you have sys.inputdir
and other
Special Variables that work equally well on Windows and non-Windows
system.
Note in recent versions of Cygwin you can decide to use the
/cygdrive
to specify a path to windows file E.g
/cygdrive/c/myfile means c:\myfile or you can do it straight away
in CFEngine as c:\myfile
.
Bundles
A bundle is a collection of promises. They allow to group related promises together into named building blocks that can be thought of as "subroutines" in the CFEngine promise language. A bundle that groups a number of promises related to configuring a web server or a file system would be named "webserver" or "filesystem," respectively.
NOTE: Bundles are not functions. They maintain state across actuations within the same agent run.
- Classic arrays are cleared at the beginning of a bundle actuation.
- Lists, strings, ints, reals, and data-containers are preserved but can be
re-defined if not guarded with
if => isvariable()
. bundle
scoped classes are cleared at the end of the bundles executionnamespace
scoped classes are not cleared automatically, though they can be explicitly undefined.
Most promise types are specific to a particular kind of interpretation that
requires a typed interpreter - the bundle type. Bundles belong to the agent
that is used to keep the promises in the bundle. So cf-agent
has bundles
declared as:
bundle agent my_name
{
}
while cf-serverd
has bundles declared as:
bundle server my_name
{
}
and cf-monitord
has bundles declared as
bundle monitor my_name
{
}
A number of promises can be made in any kind of bundle since they are of a
generic input/output nature. These are vars
, classes
, defaults
,
meta
and reports
promises.
Common Bundles
Bundles of type common
may only contain the promise types that are common to
all bodies. Their main function is to define cross-component global
definitions.
bundle common globals
{
vars:
"global_var" string => "value";
classes:
"global_class" expression => "value";
}
Common bundles are observed by every agent, whereas the agent specific bundle types are ignored by components other than the intended recipient.
Rules for evaluation of common bundles
These are the specific evaluation differences between common and agent bundles:
- common bundles are automatically evaluated even if they are not in the bundlesequence, as long as they have no parameters
- auto-evaluated common bundles (not in the bundlesequence explicitly) don't evaluate their
reports
promises, so their reports won't be printed. - when common bundles define a class, it's global (
scope
isnamespace
) by default; the classes in agent bundles are local (scope
isbundle
) by default. - common bundles can only contain
meta
,default
,vars
,classes
, andreports
promises
Bundle Parameters
Bundles can be parameterized, allowing for code re-use. If you need to do the same thing over and over again with slight variations, using a promise bundle is an easy way to avoid unnecessary duplication in your promises.
bundle agent hello_world
{
vars:
"myfiles" => "/tmp/world.txt";
"desired_content" string => "hello";
"userinfo" data => parsejson('{ "mark": 10, "jeang":20, "jonhenrik":30, "thomas":40, "eben":-1 }');
methods:
"Hello World"
usebundle => ensure_file_has_content("$(myfiles)", "$(desired_content)");
"report" usebundle => subtest_c(@(userinfo));
}
bundle agent ensure_file_has_content(file, content)
{
files:
"$(file)"
handle => "$(this.bundle)_file_content",
create => "true",
edit_defaults => empty,
edit_line => append_if_no_line("$(content)"),
comment => "Ensure that the given parameter for file '$(file)' has only
the contents of the given parameter for content '$(content)'";
}
bundle agent subtest_c(info)
{
reports:
"user ID of mark is $(info[mark])";
}
You can pass slist
and data
variables to other bundles with
the @(var)
notation. You do NOT need to qualify the variable name
with the current bundle name.
Scope
All variables in CFEngine are globally accessible. If you refer to a variable by ‘$(unqualified)’, then it is assumed to belong to the current bundle. To access any other (scalar) variable, you must qualify the name, using the name of the bundle in which it is defined:
$(bundle_name.qualified)
The value of the variable depends on evaluation order, which is not controllable by the user. Thus you should not assume that you can evaluate a bundle twice with different variables and get variables from it that correspond to the second evaluation. In other words, if you have:
bundle agent mybundle(x)
{
vars:
"y" string => $(x);
}
and call mybundle(1)
and mybundle(2)
, the variable y
could be 1
or 2
.
By default classes defined by classes
type promises
inside agent
bundles are not visible outside those bundles, they are bundle
scoped. Classes defined by classes
type promises in
common
bundles have a namespace
scope, so they are visible everywhere.
Note that namespaced bundles work exactly the same way as
non-namespaced bundles (which are actually in the default
namespace). You just say namespace:bundle_name
instead of
bundle_name
. See Namespaces for more details.
Main bundles and bundlesequence
When running cf-agent
, the order of bundles to evaluate is determined by the bundlesequence
.
The default bundlesequence
contains main
for convenience, so this example works:
bundle agent main
{
reports:
"Hello, $(this.bundle) bundle.";
}
The policy promises to report the name of the current bundle, and produces this output:
R: Hello, main bundle.
This policy can be found in
/var/cfengine/share/doc/examples/main.cf
and downloaded directly from
github.
Custom bundle sequences
You can specify a custom bundlesequence
from the command line using --bundlesequence
, or in policy:
body common control
{
bundlesequence => { "hello" };
}
bundle agent hello
{
reports:
"Hello, $(this.bundle) bundle.";
}
The policy promises to report the name of the current bundle, and produces this output:
R: Hello, hello bundle.
This policy can be found in
/var/cfengine/share/doc/examples/bundlesequence.cf
and downloaded directly from
github.
In this example, cf-agent will not look for a main
bundle, since it is not in the bundlesequence
.
Library main bundles
Bundles must be unique within a namespace, so you cannot have main
bundles in different files which include each other.
Because of this, bundles which are called __main__
have special behavior.
If the entry point (the file specified at command line) has a __main__
bundle, it will be the main
bundle.
__main__
bundles from all other files will be ignored (removed before evaluation).
This is especially useful when writing libraries.
Each file can include its own testsuite or some default behavior in a __main__
bundle.
All parts of the library will then be runnable directly, but also easy to include from other policy:
main_library.cf
:
bundle agent libprint(message)
{
reports:
"Library: $(message).";
}
bundle agent __main__
{
methods:
"test" usebundle => libprint( "ok 1 - libprint works" );
}
The policy promises to call libprint to report that it works when the policy file is the main entry.
R: Library: ok 1 - libprint works
This policy can be found in
/var/cfengine/share/doc/examples/main_library.cf
and downloaded directly from
github.
main_entry_point.cf
:
body file control
{
inputs => { "$(sys.policy_entry_dirname)/main_library.cf" };
}
bundle agent __main__
{
methods:
"a" usebundle => libprint("Hello from $(sys.policy_entry_basename)");
}
R: Hello from policy.cf
This policy can be found in
/var/cfengine/share/doc/examples/main_entry_point.cf
and downloaded directly from
github.
Both of these policy files can be run directly, the included library policy will not have a main
bundle.
Bodies
While the idea of a promise is very simple, the definition of a promise can
grow complicated. Complex promises are best understood by breaking them down
into independent, re-usable components. The CFEngine reserved word body
is
used to encapsulate the details of complex promise attribute values. Bodies
can optionally have parameters.
bundle agent example
{
files:
!windows::
"/etc/passwd"
handle => "example_files_not_windows_passwd",
perms => system;
"/home/bill/id_rsa.pub"
handle => "example_files_not_windows_bills_priv_ssh_key",
perms => mog("600", "bill", "sysop"),
create => "true";
}
The promisers in this example are the files /etc/passwd
and
/home/bill/id_rsa.pub
. The promise is that the perms
attribute type is
associated with a named, user-defined promise body system
and mog
respectively.
body perms system
{
mode => "644";
owners => { "root" };
groups => { "root" };
}
body perms mog(mode,user,group)
{
owners => { "$(user)" };
groups => { "$(group)" };
mode => "$(mode)";
}
Like bundles, bodies have a type. The type of the body has to match the left-hand side of the promise attribute in which it is used. In this case, files
promises have an attribute perms
that can be associated with any body of type perms
.
The attributes within the body are then type specific. Bodies of type perms
consist of the file permissions, the file owner, and the file group, which the instance system
sets to 644
, root
and root
, respectively.
Such bodies can be reused in multiple promises. Like bundles, bodies can have parameters. The body mog
also consists of the file permissions, file owner, and file group, but the values of those attributes are passed in as parameters.
Body Inheritance
CFEngine 3.8 introduced body inheritance via the inherit_from
attribute. It's a parameterized single-inheritance system, so a body
can only inherit from one other body, and it can apply parameters. The
two bodies must have the same type.
Let's see it with the system
and mog
bodies from earlier:
body perms system
{
mode => "644";
owners => { "root" };
groups => { "root" };
}
body perms system_inherited
{
inherit_from => mog("644", "root", "root");
}
The earlier system
body and this system_inherited
body have the
same effect, eventually applying mode 644
, owner root
, and group
root
. But they are created differently: the first by explicitly
listing the parameters; the other by applying parameters to the
inherit_from
chain of inheritance.
Which one is better? Usually, inheriting from a more generic
specification is considered a better design pattern because it reduces
horizontal complexity. But it's less explicit and some users and sites
will prefer a more explicit listing of body attributes and their
values, as in the system
body. CFEngine will accomodate either.
Body parameters can be used in the inheritance chain. Here's another
body that inherits from mog
but takes a mode. All its other
parameters are specified inside the body. So
system_inherited_mode("234")
is exactly like mog("234", "root",
"root")
.
body perms system_inherited_mode(mode)
{
inherit_from => mog($(mode), "root", "root");
}
Again, whether you prefer this or directly calling the mog
body is
your choice. Keep in mind that if you want to maintain compatibility
with 3.7 and earlier, inherit_from
is not available.
Body inheritance simply copies attributes down the chain to the newest
body. The latest wins. Let's see an example with a chain of
inheritance from the system
body from earlier:
body perms system
{
mode => "644";
owners => { "root" };
groups => { "root" };
}
body perms system_once(x)
{
inherit_from => system;
owners => { $(x) };
mode => "645";
}
body perms system_twice
{
inherit_from => system_once("mark");
mode => "646";
}
The inheritance chain goes from system
to system_once
to
system_twice
. The owners
attribute in system_once
will be
whatever $(x)
is, overwriting the value from system
. Then
system_twice
will inherit that same owners
value which is now "mark"
.
The mode
attribute will be overwritten to 645
in system_once
and then overwritten to 646
in system_twice
.
If this gets complicated, just think "latest wins".
Implicit, Control Bodies
A special case for bodies are the implicit promises that configure the basic
operation of CFEngine. These are hard-coded to CFEngine and control the basic
operation of the agents, such as cf-agent
and cf-serverd
. Each agent has a
special body whose name is control
.
body agent control
{
bundlesequence => { "test" };
}
This promise bodies configures the bundlesequence
to execute on a cf-agent.
body server control
{
allowconnects => { "127.0.0.1" , "::1", @(def.acl) };
}
This promise body defines the clients allowed to connect to a cf-serverd. For more information, see the reference documentation about the CFEngine Agents
Default bodies
CFEngine 3.9 introduced a way to create default bodies. It allows defining, for given
promise and body types, a body that will be used each time no body is defined.
To use a body as default, name it <promise_type>_<body_type>
and put it
in the bodydefault
namespace. For example, a default action
body for files
promises will be named files_action
, and in each files
promise, if no
action
attribute is set, the files_action
action will be used.
Note: The default bodies only apply to promises in the default
namespace.
In the following example, we define a default action
body for files
promises, that specifies an action_policy => "warn"
to prevent actually modifying files
and to only warn about considered modifications. We define it once,
and don't have to explicitly put this body in all our files
promises.
body file control
{
namespace => "bodydefault";
}
body action files_action
{
action_policy => "warn";
}
body file control
{
namespace => "default";
}
Promises
One concept in CFEngine should stand out from the rest as being the most important: promises. Everything else is just an abstraction that allows us to declare promises and model the various actors in the system.
Everything is a Promise
Everything in CFEngine 3 can be interpreted as a promise. Promises can be made about all kinds of different subjects, from file attributes, to the execution of commands, to access control decisions and knowledge relationships. If you are managing a system that serves web pages you may define a promise that port 80 needs to be open on a web server. This same web server may also define a promise that a particular directory has a particular set of permissions and the proper owner to serve web pages via Apache.
This simple but powerful idea allows a very practical uniformity in CFEngine syntax.
Promise Types
The promise_type
defines what kind of object is making the promise. The type
dictates how CFEngine interprets the promise body. These promise types are
straightforward: The files
promise type deals with file permissions and file
content, and the packages
promise type allows you to work with packaging
systems such as rpm and apt.
Some promise types are common to all CFEngine components, while others can
only be executed by one of them. cf-serverd
cannot keep packages
promises,
and cf-agent
cannot keep access
promises. See the
Promise Type reference for a comprehensive
list of promise types.
The Promiser
The promiser is an object affected by a promise, and this can be anything: a file, a port on a network. It is the entity that is making a promise that a certain fact will be true. These facts are listed in the form of attributes and values. A file could promise that a permission attribute has a particular value (i.e. 775 permission value) and that an owner attribute has another value (i.e. "root").
When a promise is made in CFEngine it is made to another entity - a promisee. A promisee is an optional part of a promise declaration. The promisee can help provide insight into the system's configuration, and may become relevant as your system grows in complexity.
The classes in a promise control the conditions that make the promise valid. Examples are the operating system on which the policy is executed, or the day of the week. More about that in the classes and decision making section.
Not all of these elements are necessary every time, but when you combine them they enable a wide range of behavior.
Promise Example
# Promise type
files:
"/home/mark/tmp/test_plain" -> "system blue team",
create => "true",
perms => owner("@(usernames)"),
comment => "Hello World";
In this example, the promise is about a file named test_plain
in the
directory /home/mark/tmp
, and the promise is made to some entity named
system blue team
. The create
attribute instructs CFEngine to create the
file if it doesn't exist. It has a list of owners that is defined by a
variable named "usernames" (see the documentation about
Bodies for more details on this last
expression).
The comment attribute in this example can be added to any promise. It has no actual function other than to provide more information to the user in error tracing and auditing.
This is a promise that will affect the state of a file on the filesystem. In
CFEngine you can do this without having to execute the touch
, chmod
, and
chown
commands. CFEngine is declarative: you declare a contract (or a
promise) that you want CFEngine to keep and you leave the details up to the
tool.
Promise Locking
When a promise is validated (has an outcome of kept or repaired) it is locked for body agent control ifelapsed minutes (1 by default). Locks are based on a hash of the promise (promiser, associated attributes, and context).
Promise locks can be useful for controlling frequency.
access
, classes
, defaults
, meta
, roles
and vars
type promises do not
participate in locking.
See Also: ifelapsed in body agent control, ifelapsed action body attribute
Promise Attributes
Promise attributes have a type and a value. The type can be any of the datatypes that are allowed for variables, and in addition
Boolean - allowed input values are
"true"
/"false"
"on"
/"off"
"yes"
/"no"
irange[min, max]
andrrange[min, max]
- a range of integer or real values, created via theirange()
andrrange()
functionsclist
- a list of classes or class expressions. Note that these attributes can take both strings (which are evaluated as class expressions) and functions that return typeclass
Menu option - one value from a list of values
body
type - a complex set of attributes expressed in a separate, reusable blockbundle
type - a separate bundle that is used as a sub-routine or a sub-set of promises
Note: The language does not specifically disallow the use of the same attribute multiple times within a given promise. As a general rule the last definition wins but the behavior is not clearly defined and this should be avoided.
For example, the following promises use the same attribute multiple times.
bundle agent bad_example
{
classes:
"myclass"
expression => "cfengine",
expression => "my_other_class";
files:
"/tmp/example"
perms => m( 600 ),
perms => owner( "root" ),
perms => group( "root" );
}
Implicit Promises
Some promise types can have implicit behavior. For example, the following promise simply prints out a log message "hello world".
reports:
"hello world";
The same promise could be implemented using the commands
type, invoking the
echo command:
commands:
"/bin/echo hello world";
These two promises have default attributes for everything except the `promiser'. Both promises simply cause CFEngine to print a message.
Normal Ordering
CFEngine takes a pragmatic point of view to ordering. When promising scalar
attributes and properties, ordering is irrelevant and should not be considered.
More complex patterned data structures require ordering to be preserved, e.g.
editing in files. CFEngine solves this in a two-part strategy:
CFEngine maintains a default order of promise-types. This is based on a simple
logic of what needs to come first, e.g. it makes no sense to create something
and then delete it, but it could make sense to delete and then create (an
equilibrium). This is called normal ordering and is described below. You can
override normal ordering in exceptional circumstances by making a promise in a
class context and defining that class based on the outcome of another promise,
or using the depends_on
promise attribute.
Agent normal ordering
CFEngine tries to keep variable and class promises before starting to consider any other kind of promise. In this way, global variables and classes can be set.
If you set variables based on classes that are determined by other variables, then you introduce an order dependence to the resolution that might be non-unique. Since CFEngine starts trying to converge values as soon as possible, it is best to define variables in bundles before using them, i.e. as early as possible in your configuration. In order to make sure all global variables and classes are available early enough policy pre-evaluation step was introduced.
Policy evaluation overview
CFEngine policy evaluation is done in several steps:
- Classes provided as a command line argument (-D option) are read and set.
- Environment detection and hard classes discovery is done.
- Persistent classes are loaded.
- Policy sanity check using cf-promises -c (full-check) is performed.
- Pre-evaluation step is taking place.
- Exact policy evaluation is done.
For more information regarding each step please see the detailed description below.
Policy evaluation details
Before exact evaluation of promises takes place first command line parameters
are read and all classes defined using -D
parameter are set. Next,
environment detection takes place and hard classes are discovered. When
environment detection is complete all the persistent classes are loaded and a
policy sanity check is performed using cf-promises.
cf-promises policy validation step
In this step policy is validated and classes
and vars
promises are
evaluated. Note that cached functions are executed here, and then again during
the normal agent execution. Variables and classes resolved in this step do not
persist into the following evaluation step, so all functions will run again
during Agent pre-evaluation.
Agent pre-evaluation step
In order to support expansion of variables in body common control inputs and make sure all needed classes and variables are determined before they are needed in normal evaluation, pre-evaluation takes place immediately before policy evaluation.
During pre-evaluation files are loaded based on ordering in body common control (first) and body file control (after body common control). This means that files included in body common control are loaded and parsed before files placed in body file control. This is important from a common bundles evaluation perspective as bundles placed in files included in body common control inputs will be evaluated before bundles from file control inputs.
The following steps are executed per-bundle for each file parsed, in this order:
1. if it's a common bundle, evaluate vars promises
2. if it's a common bundle, evaluate classes promises
3. evaluate vars promises
(for details see PolicyResolve()
in the C code)
This is done because classes placed in common bundles are global whereas classes placed in agent bundles are local (by default) to the bundle where those are defined. This means that common bundles classes need these extra steps in order to be resolved for the next steps.
After all policy files are parsed and pre-evaluated, the above pre-evaluation sequence runs once again in order to help resolve dependencies between classes and vars placed in different files.
Agent evaluation step
After pre-evaluation is complete normal evaluation begins.
In this step CFEngine executes agent promise bundles in the strict order
defined by the bundlesequence (possibly overridden by the -b
or
--bundlesequence
command line option). If the bundlesequence is not provided
via command line argument or is not present in body common control agent will
attempt to execute a bundle named main
. If bundle main
is not defined, the
agent will error and exit.
Within a bundle, the promise types are executed in a round-robin fashion
according to so-called normal ordering
(essentially deletion first, followed
by creation). The actual sequence continues for up to three iterations of the
following, converging towards a final state:
meta
vars
defaults
classes
users
files
packages
guest_environments
methods
processes
services
commands
storage
databases
reports
Within edit_line
bundles in files promises,
the normal ordering is:
meta
vars
defaults
classes
delete_lines
field_edits
insert_lines
replace_patterns
reports
The order of promises within one of the above types follows their top-down
ordering within the bundle itself. In vars this can be used to override the
value of a variable, if you have two vars promises with the same name, the
last one will override the first one. The order may be overridden by making a
promise depend on a class that is set by another promise, or by using the
depends_on
attribute in the promise.
Note: The evaluation order of common bundles are classes
, then
variables
and finally reports
. All common bundles are evaluated regardless
if they are placed in bundlesequence
or not. Placing common bundles in
bundlesequence
will cause classes and variables to be evaluated again, and is
generally good practice to make sure evaluation works properly.
Server normal ordering
As with the agent, common bundles are executed before any server bundles;
following this all server bundles are executed (the bundlesequence
is only
used for cf-agent). Within a server bundle, the promise types are unambiguous.
Variables and classes are resolved in the same way as the agent. On
connection, access control must be handled first, then a role request might be
made once access has been granted. Thus ordering is fully constrained by
process with no additional freedoms.
Within a server bundle, the normal ordering is:
vars
classes
roles
access
Monitor normal ordering
As with the agent, common bundles are executed before any monitor bundles;
following this all monitor bundles are executed (the bundlesequence
is only
used for cf-agent). Variables and classes are resolved in the same way as the
agent.
Within a monitor bundle, the normal ordering is:
vars
classes
measurements
reports
Classes and Decisions
Classes are used to apply promises only to particular environments, depending on context. A promise might only apply to Linux systems, or should only be applied on Sundays, or only when a variable has a certain value.
Classes are simply facts that represent the current state or context of a system. The list of set classes classifies the environment at time of execution.
Classes are either set
or not set
, depending on context. Classes fall into
hard classes that are discovered by CFEngine, and soft classes that are
user-defined.
In CFEngine Enterprise, classes that are defined can be reported to the CFEngine Database Server and can be used there for reporting, grouping of hosts and inventory management. For more information about how this is configured please read the documentation on Enterprise Reporting.
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 to show
namespace scoped classes that can be resolved during pre-evaluation.
Alternatively run cf-agent --show-evaluated-classes
to get the listing of
classes at the end of the agent execution. This will show additional namespace
scoped classes that were defined over the course of the agent execution. This
output can be convenient for policy testing.
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 for cf-promises
and the
--show-evaluated-vars
option for cf-agent
.
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
Hard classes are discovered by CFEngine. Each time it wakes up, it discovers and reads properties of the environment or context in which it runs.It turns these properties of the environment into classes. This information is effectively cached and may be used to make decisions about configuration.
You can see all of the hard classes defined on a particular host by running the following command as a privileged user.
$ cf-promises --show-classes|grep hardclass
These are classes that describe your operating system, the time of
day, the week of the year, etc. Time-varying classes (tagged with
time_based
) will change if you do this a few times over the course
of a week.
- 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 are user-defined classes which you can use to implement your own classifications. These classes are defined in bundles and are evaluated when the bundle is evaluated. They can be based on functions or on other 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.
This example defines a few soft classes local to the myclasses
bundle.
bundle agent myclasses
{
classes:
"always";
"always2" expression => "any";
"solinux" expression => "linux||solaris";
"alt_class" or => { "linux", "solaris", fileexists("/etc/fstab") };
"oth_class" and => { fileexists("/etc/shadow"), fileexists("/etc/passwd") };
reports:
alt_class::
# This will only report "Boo!" on linux, solaris, or any system
# on which the file /etc/fstab exists
"Boo!";
}
The
always
andalways2
soft classes are always defined.The
solinux
soft class is defined as a combination of thelinux
or thesolaris
hard classes. This class will be set if the operating system family is either of these values.The
alt_class
soft class is defined as a combination oflinux
,solaris
, or the presence of a file named/etc/fstab
. If one of the two hard classes evaluate to true, or if there is a file named/etc/fstab
, thealt_class
class will also be set.The
oth_class
soft class is defined as the combination of twofileexists
functions -/etc/shadow
and/etc/passwd
. If both of these files are present theoth_class
class will also be set.
Negative Knowledge
If a class is set, then it is certain that the corresponding fact is true. However, that a class is not set could mean that something is not the case, or that something is simply not known. This is only a problem with soft classes, where the state of a class can change during the execution of a policy, depending on the order in which bundles and promises are evaluated.
Making Decisions based on classes
The easiest way to limit the application of a promise to certain conditions is to use the following notation:
bundle agent greetings
{
reports:
Morning::
"Good morning!";
Evening::
"Good evening!";
"! any"::
"This report won't ever be seen.";
# whitespace allowed only in 3.8 and later
Friday . Evening::
"It's Friday evening, TGIF!";
"Monday . Evening"::
"It's Monday evening.";
}
In this example, the report "Good morning!" is only printed if the class
Morning
is set, while the report "Good evening!" is only printed when the
class Evening
is set.
The "! any"
context will never be evaluated. Note that since
CFEngine 3.8 context expressions can contain spaces for legibility.
The "Monday . Evening"
context will only be true on Monday evenings.
The Friday . Evening
context will only be true on Friday evenings.
See below for more on context operators.
Sometimes it's convenient to put class names in variables. This example shows two ways to execute code conditionally based on such variables:
bundle agent greetings
{
vars:
"myclassname" string => "Evening";
reports:
"$(myclassname)"::
"Good evening!";
"any"::
"Good evening too!" ifvarclass => "$(myclassname)";
}
As you saw above, the class predicate ifvarclass
(aliased to if
;
unless
is also available) can be used if variable class expressions
are required. It is AND
ed with the normal class expression, and is
evaluated together with the promise. Both may contain variables as long
as the resulting expansion is a legal class expression.
bundle agent example
{
vars:
"french_cities" slist => { "toulouse", "paris" };
"german_cities" slist => { "berlin" };
"italian_cities" slist => { "milan" };
"usa_cities" slist => { "lawrence" };
"all_cities" slist => { @(french_cities), @(german_cities), @(italian_cities), @(usa_cities) };
classes:
"italy" or => { @(italian_cities) };
"germany" or => { @(german_cities) };
"france" or => { @(french_cities) };
reports:
"It's $(sys.date) here";
Morning.italy::
"Good morning from Italy",
ifvarclass => "$(all_cities)";
Afternoon.germany::
"Good afternoon from Germany",
ifvarclass => "$(all_cities)";
france::
"Hello from France",
ifvarclass => "$(all_cities)";
france::
"IMPOSSSIBLE! THIS WILL NOT PRINT!!!",
unless => "france";
"$(all_cities)"::
"Hello from $(all_cities)";
"Hello from $(all_cities), ifvarclass edition",
ifvarclass => "$(all_cities)";
}
Example Output:
cf-agent -Kf example.cf -D lawrence -b example
R: It's Tue May 28 16:47:33 2013 here
R: Hello from lawrence
R: Hello from lawrence, ifvarclass edition
cf-agent -Kf example.cf -D paris -b example
R: It's Tue May 28 16:48:18 2013 here
R: Hello from France
R: Hello from paris
R: Hello from paris, ifvarclass edition
cf-agent -Kf example.cf -D milan -b example
R: It's Tue May 28 16:48:40 2013 here
R: Hello from milan
R: Hello from milan, ifvarclass edition
cf-agent -Kf example.cf -D germany -b example
R: It's Tue May 28 16:49:01 2013 here
cf-agent -Kf example.cf -D berlin -b example
R: It's Tue May 28 16:51:53 2013 here
R: Good afternoon from Germany
R: Hello from berlin
R: Hello from berlin, ifvarclass edition
In this example, lists of cities are defined in the vars
section and these
lists are combined into a list of all cities. These variable lists are used to
qualify the greetings and to make the policy more concise. In the classes
section a country class is defined if a class described on the right hand side
evaluates to true. In the reports section the current time is always reported
but only agents found to have the Morning
and italy
classes defined will
report "Good morning from Italy", this is further qualified by ensuring that
the report is only generated if one of the known cities also has a class
defined.
Operators and Precedence
Classes promises define new classes based on combinations of old ones. This is how to make complex decisions in CFEngine, with readable results. It is like defining aliases for class combinations. Such class 'aliases' may be specified in any kind of bundle.
Since CFEngine 3.8, whitespace is allowed between operators. It was not allowed up to 3.7.
For example a . b
is equivalent to a.b
and perhaps more readable.
Classes may be combined with the operators listed here in order from highest to lowest precedence:
‘()':: ~ The parenthesis group operator.
‘!’:: ~ The NOT operator.
‘.’:: ~ The AND operator.
‘&’:: ~ The AND operator (alternative).
‘|’:: ~ The OR operator.
‘||’:: ~ The OR operator (alternative).
These operators can be combined to form complex expressions. For example, the following expression would be only true on Mondays or Wednesdays from 2:00pm to 2:59pm on Windows XP systems:
(Monday|Wednesday).Hr14.WinXP::
Operands that are functions
If an operand is another function and the return value of the function is undefined, the result of the logical operation will also be undefined. For this reason, when using functions as operators, it is safer to collapse the functions down to scalar values and to test if the values are either true or false before using them as operands in a logical expression.
e.g.
...
classes:
"variable_1"
expression => fileexists("/etc/aliases.db");
...
"result"
or => { isnewerthan("/etc/aliases", "/etc/aliases.db"),
"!variable_1" };
The function, isnewerthan
can return "undefined" if one or other of the files
does not exist. In that case, result would also be undefined. By checking the
validity of the return value before using it as an operand in a logical expression,
unpredictable results are avoided. i.e negative knowledge does not necessarily
imply that something is not the case, it could simply be unknown. Checking if
each file exists before calling isnewerthan
would avoid this problem.
Operands that are JSON booleans
If an operand is true
it will succeed, even though there doesn't have to be
a class named true
. If an operand is false
it will fail, even though
there may be a class named false
. This allows JSON booleans from data
containers to be used in context expressions:
bundle agent main
{
vars:
"checks" data => '[true, false]';
# find all classes named
"classes_named_true" slist => classesmatching('true');
classes:
# always defined
"first_check" expression => "$(checks[0])";
# never defined
"second_check" expression => "$(checks[1])";
reports:
# prints nothing, there are no classes named 'true'
"Classes named 'true': $(classes_named_true)";
first_check::
"The class was defined from '$(checks[0])'";
!first_check::
"The class was NOT defined from '$(checks[0])'";
second_check::
"The class was defined from '$(checks[1])'";
!second_check::
"The class was NOT defined from '$(checks[1])'";
}
Output:
R: The class was defined from 'true'
R: The class was NOT defined from 'false'
Class scope
Classes defined in bundles of type common
are global aka namespace
in scope
(globally available, can be seen from any bundle), whereas classes defined in
all other bundle types are local aka bundle
scoped (they can not be seen from
other bundles). Classes are evaluated when the bundle is evaluated (and the
bundles are evaluated in the order specified in the bundlesequence
).
Note that any class promise must have one - and only one - value constraint.
That is, you may not leave expression
in the example
above and add and
or xor
constraints to the
single promise.
Additionally classes can be defined or undefined as the result of a promise by using a classes body. To set a class if a promise is repaired, one might write:
"promiser..."
...
classes => if_repaired("signal_class");
These classes are namespace
scoped by default. The
scope
attribute can be used to make them
local to the bundle.
It is recommended to use bundle scoped classes whenever possible. This example
will define signal_class
prefixed classes with a suffix matching the
promise outcome (_kept
, _repaired
, _notkept
).
"promiser..."
...
classes => results("bundle", "signal_class");
reports:
signal_class_repaired::
"Some aspect of the promise was repaired.";
"The agent made a change to take us closer to the desired state";
signal_class_kept::
"Some aspect of the promise was kept";
signal_class_notkept::
"Some aspect of the promsie was unable to be repaired";
signal_class_kept.signal_class_notkept::
"All promise aspects were as desired";
Classes defined by the module protocol are namespace
scoped.
Finally, restart_class
classes in processes
are global.
Class Scopes: A More Complex Example
body common control
{
bundlesequence => { "global","local_one", "local_two" };
}
#################################
bundle common global
{
classes:
# The soft class "zero" is always satisfied,
# and is global in scope
"zero" expression => "any";
}
#################################
bundle agent local_one
{
classes:
# The soft class "one" is always satisfied,
# and is local in scope to local_one
"one" expression => "any";
}
#################################
bundle agent local_two
{
classes:
# The soft class "two" is always satisfied,
# and is local in scope to ls_2
"two" expression => "any";
reports:
zero.!one.two::
# This report will be generated
"Success";
}
In this example, there are three bundles. One common bundle named global
with a global scope. Two agent bundles define classes one
and two
which
are local to those bundles.
The local_two
bundle promises a report "Success" which applies only if
zero.!one.two
evaluates to true. Within the local_two
scope this evaluates
to true
because the one
class is not set.
Persistence
By default classes are re-computed on each agent execution. Once a class has been defined, it persists until the end of that agent execution. Persistent classes are always global and can not be set to local by scope directive. Classes can persist for a period of time. This can be useful to avoid the expense of re-evaluation, communicate states across multiple agent runs on the same host.
The standard library in the Masterfiles Policy Framework contains
the feature
bundle which implements a useful model for
defining classes for a period of time as well as canceling them on demand.
See also: persistance
classes attribute, persist_time
in classes body, lib/event.cf
in the MPF, lib/feature.cf
in the MPF
Canceling classes
You can cancel a class with a classes
body.
See the cancel_kept
, cancel_notkept
, and cancel_repaired
attributes.
Variables
Just like classes are defined as promises, variables (or "variable definitions") are also promises. Variables can be defined in any promise bundle. This bundle name can be used as a context when using variables outside of the bundle they are defined in.
CFEngine variables have three high-level types: scalars, lists, and data containers.
- A scalar is a single value,
- a list is a collection of scalars.
- a data container is a lot like a JSON document, it can be a key-value map or an array or anything else allowed by the JSON standard with unlimited nesting.
Scalar Variables
Each scalar may have one of three types: string, int or real. String scalars are sequences of characters, integers are whole numbers, and reals are float pointing numbers.
vars:
"my_scalar" string => "String contents...";
"my_int" int => "1234";
"my_real" real => "567.89";
Integer constants may use suffixes to represent large numbers. The following suffixes can be used to create integer values for common powers of 1000.
- 'k' = value times 1000
- 'm' = value times 10002
- 'g' = value times 10003
Since computing systems such as storage and memory are based on binary values, CFEngine also provide the following uppercase suffixes to create integer values for common powers of 1024.
- 'K' = value times 1024.
- 'M' = value times 10242
- 'G' = value times 10243
However, the values must have an integer numeric part (e.g. 1.5M is not allowed).
In some contexts, %
can be used a special suffix to denote percentages.
Lastly, there is a reserved value which can be used to specify a parameter as having no limit at all.
'inf' = a constant representing an unlimited value.
inf
is a special value that in the code corresponds to the magic number of999999999
(nine nines). Thus any function that accepts a number, can accept inf without a problem. Keep in mind though that you can get a higher number if you set the upper limit manually, but that's almost never a problem.For a few functions
inf
is being treated specially and truly means "there is no limit" instead of "nine nines limit". This is the case for themaxbytes
parameter and applies to most read* functions.
CFEngine typing is mostly dynamic, and CFEngine will try to coerce string values into int and real types, and if it cannot it will report an error. However, arguments to built-in functions check the defined argument type for consistency.
Scalar Referencing and Expansion
Scalar variables are referenced by $(my_scalar)
(or ${my_scalar}
) and
expand to the single value they hold at that time. If you refer to a variable
by $(unqualified)
, then it is assumed to belong to the current bundle. To
access any other (scalar) variable, you must qualify the name, using the name
of the bundle in which it is defined:
$(bundle_name.qualified)
Quoting
When quoting strings cfengine allows the use of '
, "
, and or `
. This
allows flexibilty when defining strings that contain quotes. Single or double
quotes can be escaped with \
however, please note that backticks (`
) can not
be escaped.
bundle agent main
{
vars:
'single' string => 'single quotes';
`backtick` string => `backtick quotes`;
"double" string => "double";
'single_escape' string => 'You can \'escape\' single quotes';
"double_escape" string => "You can \"escape\" double quotes";
`backtick_escape` string => `Note: You can't escape backtick quotes`;
reports:
"$(single)";
`$(backtick)`;
`$(double)`;
'$(single_escape)';
"$(double_escape)";
`$(backtick_escape)`;
}
R: single quotes
R: backtick quotes
R: double
R: You can 'escape' single quotes
R: You can "escape" double quotes
R: Note: You can't escape backtick quotes
This policy can be found in
/var/cfengine/share/doc/examples/quoting.cf
and downloaded directly from
github.
Scalar Size Limitations
At the moment, up to 4095 bytes can fit into a scalar variable. This limitation may be removed in the future.
If you try to expand strings in a variable or string context that add
up to more that 4095 bytes, you will notice this limitation as well.
The functions eval()
to do math, string_head()
and string_tail()
to extract a certain number of characters from either end of a string,
and string_length()
to find a string's length may be helpful.
See readfile()
for more detail on reading values from a file.
See data_readstringarray()
and data_readstringarrayidx()
for a way
to read large files' contents into a data container without going
through scalar variables or arrays.
Lists
List variables can be of type slist
, ilist
or rlist
to hold lists of
strings, integers or reals, respectively.
Every element of a list is subject to the same size limitations as a regular scalar.
They are declared as follows:
vars:
"my_slist" slist => { "list", "of", "strings" };
"my_ilist" ilist => { "1234", "5678" };
"my_rlist" rlist => { "567.89" };
List Substitution and Expansion
An entire list is referenced with the symbol ‘@’ and can be passed in their
entirety in any context where a list is expected as @(list)
. For example,
the following variable definition references a list named "shortlist":
vars:
"shortlist" slist => { "you", "me" };
"longlist" slist => { @(shortlist), "plus", "plus" };
The declaration order does not matter – CFEngine will understand the
dependency, and execute the promise to assign the variable @(shortlist)
before the promise to assign the variable @(longlist)
.
Using the @ symbol in a string scalar will not result in list substitution. For example, the string value "My list is @(mylist)" will not expand this reference.
Using the scalar reference to a local list variable, will cause CFEngine to
iterate over the values in the list. E.g. suppose we have local list variable
@(list)
, then the scalar $(list)
implies an iteration over every value of
the list.
In some function calls, listname
instead of @(listname)
is
expected. See the specific function's documentation to be sure.
Data Container Variables
The data
containers can contain several levels of data structures,
e.g. list of lists of key-value arrays. They are used to store
structured data, such as data read from JSON or YAML files. The
variable type is data
.
Data containers are obtained from functions that return data
types,
such as readjson()
or parsejson()
, readyaml()
or parseyaml()
,
or from merging existing containers.
They can NOT be modified, once created, but they can be re-defined.
Data containers do not have the size limitations of regular scalar variables.
bundle agent example_reference_values_inside_data
{
vars:
"data" data => '{
"Key1": "Value1",
"Key2": "Value2",
"Key3": [
"Value3",
"Value4"
]
}';
reports:
"Key1 contains '$(data[Key1])'";
"Key2 contains '$(data[Key2])'";
"Key3 iterates and contains '$(data[Key3])'";
}
bundle agent __main__
{
methods: "example_reference_values_inside_data";
}
R: Key1 contains 'Value1'
R: Key2 contains 'Value2'
R: Key3 iterates and contains 'Value3'
R: Key3 iterates and contains 'Value4'
This policy can be found in
/var/cfengine/share/doc/examples/reference_values_inside_data.cf
and downloaded directly from
github.
Associative Arrays
Note that associative arrays are being deprecated in favor of the data
variable type. It is recommended to use the data
variable type instead
whenever possible to ensure future compatibility of your CFEngine policy.
Every value in an associative array is subject to the same size limitations as a regular scalar.
Associative array variables are written with [
and ]
brackets that enclose
an arbitrary key. These keys are associated with values
bundle agent example
{
vars:
"component" slist => { "cf-monitord", "cf-serverd", "cf-execd" };
"array[cf-monitord]" string => "The monitor";
"array[cf-serverd]" string => "The server";
"array[cf-execd]" string => "The executor, not executioner";
commands:
"/bin/echo $(component) is"
args => "$(array[$(component)])";
}
This example defines three values in an associative array under the keys
cf-monitord
, cf-serverd
, and cf-execd
. They are sequentially printed
with the echo command.
Arrays are associative and may be of type scalar or list. Enumerated arrays are simply treated as a special case of associative arrays, since there are no numerical loops in CFEngine. Special functions exist to extract lists of keys from array variables for iteration purposes.
Here is an example of using the function getindices()
which
extracts all of the keys from an associative array. If this series of promises
were executed it would print out two messages, one for each key.
bundle agent array
{
vars:
"v[index_1]" string => "value_1";
"v[index_2]" string => "value_2";
"parameter_name" slist => getindices("v");
reports:
"Found index: $(parameter_name)";
}
Loops
There are no explicit loops in CFEngine, instead there are lists. To make a loop, you simply refer to a list as a scalar and CFEngine will assume a loop over all items in the list.
It's as if you said "I know three colors: red green blue. Let's talk about color."
body common control
{
bundlesequence => { "color_example" };
}
bundle agent color_example
{
vars:
"color" slist => { "red", "green", "blue" };
reports:
"Let's talk about $(color)";
}
CFEngine will implicitly loop over each $(color)
:
% cf-agent -K -f ./test_colors.cf
R: Let's talk about red
R: Let's talk about green
R: Let's talk about blue
Here's a more complex example.
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"component" slist => { "cf-monitord", "cf-serverd", "cf-execd" };
"array[cf-monitord]" string => "The monitor";
"array[cf-serverd]" string => "The server";
"array[cf-execd]" string => "The executor, not executionist";
reports:
"$(component) is $(array[$(component)])";
}
In this example, the list component
has three elements. The list as a whole
may be referred to as @(component)
, in order to pass the whole list to a
promise where a list is expected. However, if we write $(component)
,
i.e. the scalar variable, then CFEngine will substitute each scalar from the
list in turn, and thus iterate over the list elements using a loop.
The output looks something like this:
$ cf-agent unit_loops.cf
2013-06-12T18:56:01+0200 notice: R: cf-monitord is The monitor
2013-06-12T18:56:01+0200 notice: R: cf-serverd is The server
2013-06-12T18:56:01+0200 notice: R: cf-execd is The executor, not executionist
You see from this that, if we refer to a list variable using the scalar
reference operator $()
, CFEngine interprets this to mean “please iterate
over all values of the list”. Thus, we have effectively a `foreach' loop,
without the attendant syntax.
If a variable is repeated, its value is tied throughout the expression; so the output of:
body common control
{
bundlesequence => { "example" };
}
bundle agent example
{
vars:
"component" slist => { "cf-monitord", "cf-serverd", "cf-execd" };
"array[cf-monitord]" string => "The monitor";
"array[cf-serverd]" string => "The server";
"array[cf-execd]" string => "The executor, not executioner";
commands:
"/bin/echo $(component) is"
args => "$(array[$(component)])";
}
is as follows:
2013-06-12T18:57:34+0200 notice: Q: ".../bin/echo cf-mo": cf-monitord is The monitor
2013-06-12T18:57:34+0200 notice: Q: ".../bin/echo cf-se": cf-serverd is The server
2013-06-12T18:57:34+0200 notice: Q: ".../bin/echo cf-ex": cf-execd is The executor, not executioner
Iterating Across Multiple Lists
CFEngine can iterate across multiple lists simultaneously.
bundle agent iteration
{
vars:
"stats" slist => { "value", "av", "dev" };
"monvars" slist => {
"rootprocs",
"otherprocs",
"diskfree",
"loadavg"
};
reports:
"mon.$(stats)_$(monvars) is $(mon.$(stats)_$(monvars))";
}
This example uses two lists, stats
and monvars
. We can now iterate over both lists in the same promise. The reports that we thus generate will report on value_rootprocs
, av_rootprocs
, and dev_rootprocs
, followed next by value_otherprocs
, av_otherprocs
, etc, ending finally with dev_loadavg
.
The order of iteration is an implementation detail and should not be expected to be consistent. Use the sort()
function if you need to sort a list in a predictable way.
Augments
An augments file can be used to define variables and classes to the execution of all CFEngine components before any parsing or evaluation happen. It's a JSON data file, so you should view and edit it with a JSON-aware editor if possible.This is a convenient way to override defaults defined in the Masterfiles Policy Framework without modifying the shipped policy itself.
The file def.json
is found like the policy file to be run:
- with no arguments, it's in
$(sys.inputdir)/def.json
because$(sys.inputdir)/promises.cf
is used - with
-f /dirname/myfile.cf
, it's in/dirname/def.json
- with
-f ./myfile.cf
, it's in./def.json
Values will be expanded, so you can use the variables from Special Variables.
An augments file can contain the following keys:
inputs
Any filenames you put here will appear in the def.augments_inputs
variable.
The standard set of masterfiles refers to this variable and will autoload those
files.
vars
Any variables you put here will be put in the def
bundle scope. Thus:
"vars":
{
"phone": "22-333-4444",
"myplatform": "$(sys.os)",
}
results in the variable def.phone
with value 22-333-4444
being defined, and
def.myplatform
with the value of your current OS. Again, note that this
happens before policy is parsed or evaluated.
You can see the list of variables thus defined in the output of cf-promises
--show-vars
(see Components and Common Control). They will be tagged with
the tag source=augments_file
. For instance, the above two variables (assuming
you placed the data in $(sys.inputdir)/def.json
) result in
cf-promises --show-vars=default:def
...
default:def.myplatform linux source=augments_file
default:def.phone 22-333-4444 source=augments_file
Variables of other types than string can be defined too, like in this example
"vars" : {
"str1" : "string 1",
"num1" : 5,
"num2" : 3.5
"slist1" : ["sliststr1", "sliststr2"]
"array1" : {
"idx1" : "val1",
"idx2" : "val2"
}
}
classes
Any class defined via augments will be evaluated and installed as
hard classes. Each element
of the array is tested against currently defined classes
as an anchored regular expression unless the string ends with ::
indicating it should be interpreted as a
class expression.
Note that augments is processed at the very beginning of agent evaluation. You can use any hard classes, persistent classes, or classes defined earlier in the augments list. Test carefully, custom soft classes may not be defined early enough for use. Thus:
"classes":
{
"augments_class_from_regex_my_always": [ "any" ],
"augments_class_from_regex_my_other_apache": [ "server[34]", "debian.*" ],
"augments_class_from_regex_my_other_always": [ "augments_class_from_regex_my_always" ],
"augments_class_from_regex_when_MISSING_not_defined": [ "^(?!MISSING).*" ]
"augments_class_from_regex": [ "cfengine_\\d+" ],
"augments_class_from_single_class_as_regex": [ "cfengine" ],
"augments_class_from_single_class_as_expression": [ "cfengine::" ],
"augments_class_from_classexpression_and": [ "cfengine.cfengine_3::" ],
"augments_class_from_classexpression_not": [ "!MISSING::" ],
"augments_class_from_classexpression_or": [ "cfengine|cfengine_3::" ],
"augments_class_from_classexpression_complex": [ "(cfengine|cfengine_3).!MISSING::" ]
}
results in
* augments_class_from_rgex_my_always
being always defined.
augments_class_from_regex_my_other_apache
will be defined if the classesserver3
orserver4
are defined, or if any class starting withdebian
is defined.augments_class_from_regex_my_other_always
will be defined becauseaugments_class_from_regex_my_always
is listed first and always defined.augments_class_from_regex_when_MISSING_not_defined
will be defined if the classMISSING
is not defined.augments_class_from_single_class_as_regex
will be defined because the classcfengine
is always defined.augments_class_from_single_class_as_expression
will be defined becausecfengine
is defined when interpreted as a class expression.augments_class_from_classexpression_and
will be defined because the classcfengine
and the classcfengine_3
are defined and the class expressioncfengine.cfengine_3::
evaluates to true.augments_class_from_classexpression_not
will be defined because the class expression!MISSING::
evaluates to false since the classMISSING
is not defined.augments_class_from_classexpression_or
will be defined because the class expressioncfengine|cfengine_3::
evaluates to true since at least one ofcfengine
orcfengine_3
will always be defined by cfengine 3 agents.augments_class_from_classexpression_complex
will be defined because the class expression(cfengine|cfengine_3).!MISSING::
evaluates to true since at least one ofcfengine
orcfengine_3
will always be defined by cfengine 3 agents andMISSING
is not defined.
You can see the list of classes thus defined through def.json
in the output
of cf-promises --show-classes
(see Components and Common Control). They
will be tagged with the tags source=augments_file,hardclass
. For instance:
% cf-promises --show-classes=my_
...
augments_class_from_regex_my_always source=augments_file,hardclass
augments_class_from_regex_my_other_always source=augments_file,hardclass
augments_class_from_regex_my_other_apache source=augments_file,hardclass
See also:
- Functions that use regular expressions with classes:
classesmatching()
,classmatch()
,countclassesmatching()
augments
A list of file names that should be merged using mergedata()
semantic
Example:
Here we merge a platform specific augments on to the def.json
loaded next to
the policy entry and see what the resulting variable values will
be.
The def.json
next to the policy entry:
{
"vars":{
"my_var": "defined in def.json",
"my_other_var": "Defined ONLY in def.json"
},
"augments": [
"/var/cfengine/augments/$(sys.flavor).json"
]
}
The platform specific augments on a CentOS 6 host:
/var/cfengine/augments/centos_6.json
:
{
"vars": {
"my_var": "Overridden in centos_6.json",
"centos_6_var": "Defined ONLY in centos_6.json"
}
}
The expected values of the variables defined in the def bundle scope:
R: def.my_var == Overridden in centos_6.json
R: def.my_other_var == Defined ONLY in def.json
R: def.centos_6_var == Defined ONLY in centos_6.json
History
- 3.12.2, 3.14.0 introduced class expression interpretation (
::
suffix) to classes key - 3.12.0 introduced the
augments
key - 3.7.3 back port
def.json
parsing in core agent and loaddef.json
if present next to policy entry - 3.8.2 removed core support for
inputs
key, loaddef.json
if present next to policy entry - 3.8.1
def.json
parsing moved from policy to core agent for resolution of classes and variables to be able to affect control bodies - 3.7.0 introduced augments concept into the Masterfiles Policy Framework
Pattern Matching and Referencing
One of the strengths of CFEngine 3 is the ability to recognize and exploit patterns. All string patterns in CFEngine 3 are matched using PCRE regular expressions.
CFEngine has the ability to extract back-references from pattern matches. This makes sense in two cases. Back references are fragments of a string that match parenthetic expressions. For instance, suppose we have the string:
Mary had a little lamb ...
and apply the regular expression
"Mary ([^l]+)little (.*)"
The pattern matches the entire string, and it contains two parenthesized
subexpressions, which respectively match the fragments had a
and lamb
...
. The regular expression libraries assign three matches to this result,
labelled 0, 1 and 2.
The zeroth value is the entire string matched by the total expression. The first value is the fragment matched by the first parenthesis, and so on.
Each time CFEngine matches a string, these values are assigned to a special
variable context $(match.n)
. The fragments can be referred to in the remainder
of the promise. There are two places where this makes sense. One is in pattern
replacement during file editing, and the other is in searching for files.
Consider the examples below:
bundle agent testbundle
{
files:
# This might be a dangerous pattern - see explanation in the next section
# on "Runaway change warning"
"/home/mark/tmp/cf([23])?_(.*)"
edit_line => myedit("second backref: $(match.2)");
}
There are other filenames that could match this pattern, but if, for example,
there were to exist a file /home/mark/tmp/cf3_test
, then we would have:
‘$(match.0)’
equal to `/home/mark/tmp/cf3_test'
‘$(match.1)’
equal to `3'
‘$(match.2)’
equal to `test'
Note that because the pattern allows for an optional '2' or '3' to follow the
letters cf
, it is possible that $(match.1)
would contain the empty string.
For example, if there was a file named /home/mark/tmp/cf_widgets
, then we
would have
‘$(match.0)’
equal to `/home/mark/tmp/cf_widgets'
‘$(match.1)’
equal to `'
‘$(match.2)’
equal to `widgets'
Now look at the edit bundle. This takes a parameter (which is the
back-reference from the filename match), but it also uses back references to
replace shell comment lines with C comment lines (the same approach is used to
hash-comment lines in files). The back-reference variables $(match.n)
refer
to the most recent pattern match, and so in the C_comment
body, they do not
refer to the filename components, but instead to the hash-commented line in
the replace_patterns
promise.
bundle edit_line myedit(parameter)
{
vars:
"edit_variable" string => "private edit variable is $(parameter)";
insert_lines:
"$(edit_variable)";
replace_patterns:
# replace shell comments with C comments
"#(.*)"
replace_with => C_comment,
select_region => MySection("New section");
}
########################################
# Bodies
########################################
body replace_with C_comment
{
replace_value => "/* $(match.1) */"; # backreference from replace_patterns
occurrences => "all"; # first, last, or all
}
########################################################
body select_region MySection(x)
{
select_start => "\[$(x)\]";
select_end => "\[.*\]";
}
Try this example on the file
[First section]
one
two
three
[New section]
four
#five
six
[final]
seven
eleven
The resulting file is edited like this:
[First section]
one
two
three
[New section]
four
/* five */
six
[final]
seven
eleven
private edit variable is second backref: test
Runaway change warning
Be careful when using patterns to search for files that are altered by
CFEngine if you are not using a file repository. Each time CFEngine makes a
change it saves an old file into a copy like cf3_test.cf-before-edit
. These
new files then get matched by the same expression above – because it ends in
the generic.*
), or does not specify a tail for the expression. Thus CFEngine
will happily edit backups of the edit file too, and generate a recursive
process, resulting in something like the following:
cf3_test cf3_test.cf-before-edit
cf3_test~ cf3_test~.cf-before-edit.cf-before-edit
cf3_test~.cf-before-edit cf3_test~.cf-before-edit.cf-before-edit.cf-before-edit
Always try to be as specific as possible when specifying patterns. A lazy approach will often come back to haunt you.
Commenting lines
The following example shows how you would hash-comment lines in a file using CFEngine.
######################################################################
#
# HashCommentLines implemented in CFEngine 3
#
######################################################################
body common control
{
version => "1.2.3";
bundlesequence => { "testbundle" };
}
########################################################
bundle agent testbundle
{
files:
"/home/mark/tmp/comment_test"
create => "true",
edit_line => comment_lines_matching;
}
########################################################
bundle edit_line comment_lines_matching
{
vars:
"regexes" slist => { "one.*", "two.*", "four.*" };
replace_patterns:
"^($(regexes))$"
replace_with => comment("# ");
}
########################################
# Bodies
########################################
body replace_with comment(c)
{
replace_value => "$(c) $(match.1)";
occurrences => "all";
}
Regular expressions in paths
When applying regular expressions in paths, the path will first be split at
the path separators, and each element matched independently. For example, this
makes it possible to write expressions like /home/.*/file
to match a single
file inside a lot of directories — the .*
does not eat the whole string.
Note that whenever regular expressions are used in paths, the /
is always
used as the path separator, even on Windows. However, on Windows, if the
pathname is interpreted literally (no regular expressions), then the backslash
is also recognized as the path separator. This is because the backslash has a
special (and potentially ambiguous) meaning in regular expressions (a \d
means the same as [0-9]
, but on Windows it could also be a path separator
and a directory named d
).
The pathtype
attribute allows you to force a specific behavior when
interpreting pathnames. By default, CFEngine looks at your pathname and makes
an educated guess as to whether your pathname contains a regular expression.
The values literal
and regex
explicitly force CFEngine to interpret the
pathname either one way or another. (see the pathtype
attribute).
body common control
{
bundlesequence => { "wintest" };
}
########################################
bundle agent wintest
{
files:
"c:/tmp/file/f.*" # "best guess" interpretation
delete => nodir;
"c:\tmp\file"
delete => nodir,
pathtype => "literal"; # force literal string interpretation
"C:/windows/tmp/f\d"
delete => nodir,
pathtype => "regex"; # force regular expression interpretation
}
########################################
body delete nodir
{
rmdirs => "false";
}
Note that the path /tmp/gar.*
will only match filenames like /tmp/gar
,
/tmp/garbage
and /tmp/garden
. It will not match filename like
/tmp/gar/baz
(because even though the .*
in a regular expression means
"zero or more of any character", CFEngine restricts that to mean "zero or more
of any character in a path component"). Correspondingly, CFEngine also
restricts where you can use the /
character (you can't use it in a character
class like [^/]
or in a parenthesized or repeated regular expression
component.
This means that regular expressions which include "optional directory
components" won't work. You can't have a files promise to tidy the directory
(/usr)?/tmp
. Instead, you need to be more verbose and specify
/usr/tmp|/tmp
, or even better, think declaratively and create an slist
that contains both the strings /tmp
and /usr/tmp
, and then allow CFEngine
to iterate over the list!
This also means that the path /tmp/.*/something
will match files like
/tmp/abc/something
or /tmp/xyzzy/something
. However, even though the
pattern .*
means "zero or more of any character (except /
)", CFEngine
matches files bounded by directory separators. So even though the pathname
/tmp//something
is technically the same as the pathname /tmp/something
,
the regular expression /tmp/.*/something
will not match on the degenerate
case of /tmp//something
(or /tmp/something
).
Anchored vs. unanchored regular expressions
CFEngine uses the full power of regular expressions, but there are two “flavors” of regex. Because they behave somewhat differently (while still utilizing the same syntax), it is important to know which one is used for a particular component of CFEngine:
An “anchored” regular expression will only successfully match an entire
string, from start to end. An anchored regular expression behaves as if it
starts with ^
and ends with $
, whether you specify them yourself or not.
Furthermore, an anchored regular expression cannot have these automatic
anchors removed.
An “unanchored” regular expression may successfully match anywhere in a
string. An unanchored regex may use anchors (such as ^
, $
, \A
, \Z
,
\b
, etc.) to restrict where in the string it may match. That is, an
unanchored regular expression may be easily converted into a partially- or
fully-anchored regex.
For example, the comment parameter in readstringarray()
is an unanchored regex. If you specify the regular expression as #.*
, then
on any line which contains a pound sign, everything from there until the end
of the line will be removed as a comment. However, if you specify the regular
expression as ^#.*
(note the ^
anchor at the start of the regex), then
only lines which start with a #
will be removed as a comment! If you want to
ignore C-style comment in a multi-line string, then you have to a bit more
clever, and use this regex: (?s)/\*.*?\*/
Conversely, delete_lines
promises use anchored regular expressions to delete
lines. If our promise uses bob:\d*
as a line-matching regex, then only the
second line of this file will be deleted (because only the second line starts
with bob:
and is then followed exclusively by digits, all the way to the end
of the string).
bobs:your:uncle
bob:111770
thingamabob:1234
robert:bob:xyz
i:am:not:bob
If CFEngine expects an unanchored regular expression, then finding every line
that contains the letters bob
is easy. You just use the regex bob
. But if
CFEngine expects an anchored regular expression, then you must use .*bob.*
.
If you want to find every line that has a field which is exactly bob
with no
characters before or after, then it is only a little more complicated if
CFEngine expects an unanchored regex: (^|:)bob(:|$)
. But if CFEngine expects
an anchored regular expression, then it starts getting ugly, and you'd need to
use bob:.*|.*:bob:.*|.*:bob
.
Special topics on Regular Expressions
Regular expressions are a complicated subject, and really are beyond the scope of this document. However, it is worth mentioning a couple of special topics that you might want to know of when using regular expressions.
The first is how to not get a back reference. If you want to have a
parenthesized expression that does not generate a back reference, there is a
special PCRE syntax to use. Instead of using ()
to bracket the piece of a
regular expression, use (?:)
instead. For example, this will match the
filenames foolish, foolishly, bearish, bearishly, garish, and garishly in the
/tmp
directory. The variable $match.0
will contain the full filename, and
$match.1
will either contain the string ly
or the empty string. But the
(?:expression)
which matches foo, bear, or gar does not create a
back-reference:
files:
"/tmp/(?:foo|bear|gar)ish(ly)?"
Note that sometimes multi-line strings are subject to be matched by regular
expressions. CFEngine internally matches all regular expressions using
PCRE_DOTALL option, so .
matches newlines. If you want to match any
character except newline you could use \N
escape sequence.
Another thing you might want to do is ignore capitalization. CFEngine is
case-sensitive (in all things), so the files promise /tmp/foolish
will not
match the files /tmp/Foolish
or /tmp/fOoLish
, etc. There are two ways to
achieve case-insensitivity. The first is to use character classes:
files:
"/tmp/[Ff][Oo][Oo][Ll][Ii][Ss][Hh]"
While this is certainly correct, it can also lead to unreadability. The PCRE patterns in CFEngine have another way of introducing case-insensitivity into a pattern:
files:
"/tmp/(?i:foolish)"
The (?i:)
brackets impose case-insensitive matching on the text that it
surrounds, without creating a sub-expression. You could also write the regular
expression like this (but be aware that the two expressions are different, and
work slightly differently, so check the documentation for the specifics):
files:
"/tmp/(?i)foolish"
The /s
, /m
, and /x
switches from PCRE are also available, but use them
with great care!
Namespaces
Namespaces are private bundle and body "playgrounds", allowing multiple files to define the bundles and bodies with the same name in different namespaces without conflict. They are key to writing reusable policies.
Everything in CFEngine lives in a namespace (it's the default
namespace if not set).
Specifying a namespace
To isolate a file into its own namespace, you add a
file control promise to the file before the relevant
bundles or bodies. All bundles and bodies start off in the default
namespace
if you don't explicitly set this. Once set, this applies until the end of the
file or the next namespace change.
body file control
{
namespace => "myspace";
}
Accessing syntax elements between namespaces and the default namespace
To distinguish the bundle mymethod
in the default namespace from one in
another namespace, you prefix the bundle name with the namespace, separated by
a colon.
methods:
"namespace demo" usebundle => myspace:mymethod("arg1");
"namespace demo" usebundle => mymethod("arg1","arg2");
To distinguish a body from one in another namespace, you can prefix the body name with the namespace, separated by a colon.
files:
"/file"
create => "true",
perms => name1:settings;
If you don't make any namespace declarations, you'll be in the
default
namespace. Bundles, bodies, classes, and variables from the
default
namespace can be accessed like any other:
files:
"/file"
create => "true",
perms => default:settings;
If you use the standard library from your own namespace, remember to
specify this default:
prefix.
To access classes, variables, or meta-data in bundles in a different namespace, use the colon as a namespace prefix:
$(namespace:bundle.variable)
$(namespace:bundle_meta.variable)
Note that this means that if you are in a namespace that's not default
, you must qualify classes from default
fully:
default:myclass::
"do something" ifvarclass => "default:myotherclass";
Namespacing of classes and variables created in policy
In policy, you can't create classes outside your own namespace. So
the following, for example, will create the class mynamespace:done
if it runs in the namespace mynamespace
.
files:
"/file"
create => "true",
action => if_repaired("done");
Similarly, variables you create in a namespaced bundle have to be
prefixed like mynamespace:mybundle.myvar
from outside your
namespace, but can use mybundle.myvar
inside the namespace and
myvar
inside mybundle
.
As a workaround, you could have a helper bundle in another namespace to create classes and variables as needed.
Exceptions to namespacing rules
Exceptions to the rules above:
All hard classes can be used as-is from any namespace, without a namespace prefix. These are classes like
linux
. They will have the taghardclass
.All special variable contexts, as documented in Special Variables, are always accessible without a namespace prefix. For example,
this
,mon
,sys
, andconst
fall in this category.
Enterprise API Reference
The Enterprise API is a conventional REST API in the sense that it has a number of URI resources that support one or more GET, PUT, POST, or DELETE operations. While reporting is done using SQL, this query is always wrapped in a JSON request.
See Also: Enterprise API Examples
Requests
GET requests are one of listing or getting. Listing resources means that a number of results will be returned, but each entry may contain limited information. An example of a listing query is /api/user to list users. Notice that URI components are always non-plural. An exception to this is /api/settings, which returns the singleton resource for settings. Getting a resource specifies an individual resource to return, e.g. /api/user/homer.
PUT request typically create a new resource, e.g. a user.
POST requests typically updates an existing resource. DELETE requests are also supported in some cases.
Note: When updating objects via the REST API the behavior is to overwrite
existing objects. Any missing keys are reset to default values. For example if
you have custom LDAP settings and want to update the blueHostHorizon
you
should first query to get the current settings, and then post the complete
settings that you desire else the customized LDAP settings will be reset to
defaults.
This example shows using JQ to preserve existing setting when updating an individual key value.
[root@hub]# curl -s -u admin:admin http://localhost:80/api/settings \
| jq '.data[0] + {"blueHostHorizon": 2222, "logLevel": "warning"}' \
| curl -s -u admin:admin http://localhost:80/api/settings -X POST -d @-
[root@hub]# curl -s -u admin:admin http://localhost:80/api/settings | jq '.data[0]'
{
"blueHostHorizon": 2222,
"hostIdentifier": "default.sys.fqhost",
"ldapEnabled": true,
"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.
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_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
Parameters:
- username (string) User name
- password (string) User password
- email (string) User email
- roles (array)
User roles, emp:
["admin", "test"]
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.
Parameters:
- username (string) User name
- password (string) User password
- email (string) User email
- roles (array)
User roles, emp:
["admin", "test"]
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.
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
Inventory API
Inventory API allows to access inventory reports and attributes dictionary.
Inventory Reports
URI: https://hub.cfengine.com/api/inventory
Method: POST
Parameters:
select (array) Fields for selecting. Required parameter.
List of fields name you can obtain through List of inventory attributes call described below. Extra attributes are
hostkey
for selecting host key andresultCount
for selecting rows count.filter (json object) Optionally filter data. You can use array values for multiple filter, the logic will be AND. Format is
{ "Attribute name":{ "operator":["value","value1"], "operator2":"value2", "operator4":"value2" } }
Operators:
For filtering you can use operators bellow:
Operator < > = != <= >= matches not_match contains not_contain sort (string) Field name for sorting with "-" for DESC order. Optional parameter.
start (integer) Number of results to start from. Optional parameter.
limit (integer) Limit the number of results in the query. Default value is 1000, max value is 10000.
hostContextExclude (array) Includes only results that concern hosts which have all specified CFEngine contexts (class) set. Optional parameter.
hostContextInclude (array) Excludes results that concern hosts which have specified CFEngine context (class) set. Hosts that have at least one of the specified contexts set will be excluded from the results. Optional parameter.
curl -k --user <username>:<password> \
-X POST \
https://hub.cfengine.com/api/inventory \
-H 'content-type: application/json' \
-d '{
"sort":"Host name",
"filter":{
"Host name":{
"matches":["value1","value"],
"not_contain":"value2"
}
},
"select":[
"Host name",
"OS type",
"IPv4 addresses",
"CFEngine version",
"Ports listening"
],
"hostContextExclude":[
"policy_server"
]
}
'
Example Request Body:
{
"sort":"Host name",
"filter":{
"Host name":{
"matches":"value1",
"=":"value2"
}
},
"select":[
"Host name",
"OS type",
"IPv4 addresses",
"CFEngine version",
"Ports listening"
],
"hostContextExclude":[
"policy_server"
]
}
Example response:
{
"data": [
{
"header": [
{
"columnName": "Host name",
"columnType": "STRING"
},
{
"columnName": "OS type",
"columnType": "STRING"
},
{
"columnName": "IPv4 addresses",
"columnType": "STRING"
},
{
"columnName": "CFEngine version",
"columnType": "STRING"
},
{
"columnName": "Ports listening",
"columnType": "STRING"
}
],
"queryTimeMs": 30,
"rowCount": 2,
"rows": [
[
"host1.cfengine.com",
"linux",
"128.30.23.33",
"3.10.0",
"22, 25, 80, 443, 5308, 9000"
],
[
"host2.cfengine.com",
"linux",
"184.45.1.75",
"3.10.0",
null
]
]
}
],
"meta": {
"count": 1,
"page": 1,
"timestamp": 1496222472,
"total": 1
}
}
List of inventory attributes
URI: https://hub.cfengine.com/api/inventory/attributes-dictionary
Method: GET
Shows list of all inventory attributes available in the system.
See more details: * Custom Inventory
CURL request example
curl -k --user admin:admin -X GET https://hub.cfengine.com/api/inventory/attributes-dictionary
Example response:
[
{
"id": 2,
"attribute_name": "BIOS vendor",
"category": "Hardware",
"readonly": 1,
"type": "string",
"convert_function": null,
"keyname": "default.cfe_autorun_inventory_dmidecode.dmi[bios-vendor]"
},
{
"id": 3,
"attribute_name": "BIOS version",
"category": "Hardware",
"readonly": 1,
"type": "string",
"convert_function": null,
"keyname": "default.cfe_autorun_inventory_dmidecode.dmi[bios-version]"
}
]
Edit inventory attribute
URI: https://hub.cfengine.com/api/inventory/attributes-dictionary/:id
Method: PATCH
Set inventory attribute type (int/string..). This is needed for applying filtering in correct format.
Only readonly - 0
attribute can be edited
Parameters:
- id (integer) Attribute Id
- category (string) Category of attribute
- type (string) Attribute's type. Allowed values: int, real, slist, string
- convert_function (string)
Convert Function.
Emp.:
cf_clearSlist
- to transform string like{"1", "2"}
to1, 2
CURL request example
curl -k --user admin:admin -X PATCH https://hub.cfengine.com/api/inventory/attributes-dictionary/260 -H 'content-type: application/json' -d '{
"category":"Hardware",
"type": "int"
}'
Example Request Body:
{
"category":"Hardware",
"type": "int"
}
Example response:
{
"id": 1,
"attribute_name": "Architecture",
"category": "Hardware",
"readonly": 0,
"type": "slist",
"convert_function": "cf_clearSlist"
}
LDAP authentication API
LDAP authentication API allows to check ldap user credentials and change LDAP settings.
Login
URI: https://hub.cfengine.com/ldap/login
Method: POST
Parameters:
- username (string) Username from LDAP
- password (string) User password
Example response:
HTTP 200 Ok
{
"success": true,
"message": "You are successfully authenticated"
}
Get settings
URI: https://hub.cfengine.com/ldap/settings
Method: GET
Headers:
- Authorization: api_token (string)
Set token to access api. To get the token please look at -
/var/cfengine/httpd/htdocs/ldap/config/settings.php
Example response:
HTTP 200 Ok
{
"success": true,
"data": {
"domain_controller": "local.loc",
"base_dn": "DC=local,DC=loc",
"login_attribute": "cn",
"port": 365,
"use_ssl": false,
"use_tls": false,
"timeout": 5,
"admin_username": "cn=admin,DC=local,DC=loc",
"admin_password": "Password is set"
}
}
Output:
- domain_controller The domain controllers option is server name located on your network that serve Active Directory.
- base_dn The base distinguished name is the base distinguished name you'd like to perform operations on. An example base DN would be DC=corp,DC=acme,DC=org.
- login_attribute Login attribute like cn or uid
- port The port option is used for authenticating and binding to your AD server. The default ports are already used for non SSL and SSL connections (389 and 636).
- use_ssl Use ssl for connection
- use_tls Use tls for connection
- timeout The timeout option allows you to configure the amount of seconds to wait until your application receives a response from your LDAP server.
- admin_username LDAP admin distinguished name. Emp.: cn=admin,dc=jumpcloud,dc=com
- admin_password LDAP admin password.
Update settings
URI: https://hub.cfengine.com/ldap/settings
Method: PATCH
Note that the PATCH HTTP method only requires partial JSON for an update. Such as {"port":3269} instead of the entire set of parameters.
Headers:
Authorization: api_token (string) Set token to access api. To get the token please look at -
/var/cfengine/httpd/htdocs/ldap/config/settings.php
Content-Type: application/json (string) Content-Type must be application/json for the API to parse JSON provided.
Parameters:
- domain_controller (string) The domain controllers option is server name located on your network that serve Active Directory.
- base_dn (string) The base distinguished name is the base distinguished name you'd like to perform operations on. An example base DN would be DC=corp,DC=acme,DC=org.
- login_attribute (string) Login attribute like cn or uid
- port (integer) The port option is used for authenticating and binding to your AD server. The default ports are already used for non SSL and SSL connections (389 and 636). Optional parameter.
- use_ssl (boolean) Use ssl for connection. Optional parameter.
- use_tls (boolean) Use tls for connection. Optional parameter.
- timeout (integer) The timeout option allows you to configure the amount of seconds to wait until your application receives a response from your LDAP server. Optional parameter.
- admin_username LDAP admin distinguished name. Emp.: cn=admin,dc=jumpcloud,dc=com
- admin_password LDAP admin password.
Example response:
HTTP 200 Ok
{
"success": true,
"message": "Settings successfully saved."
}
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.
- include (array) Include only nodes that have set specified context (cfengine class). Defaults to include all nodes.
- exclude (array) Exclude only nodes that have set specified context (cfengine class). Defaults to exclude no 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.
- include (array) Include only nodes that have set specified context (cfengine class). Defaults to include all nodes.
- exclude (array) Exclude only nodes that have set specified context (cfengine class). Defaults to exclude no nodes.
- hostkey (string) Search results for nodes matching specified unique hostkey.
- stackpath (string) Search results matching specified stack path which is execution stack of the promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- promisetype (string) Search results matching specified promise type - such as commands, processes etc. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- promisehandle (string) Search results matching specified promise handle. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- bundlename (string) Search results matching specified bundle name. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- policyfile (string) Search results matching specified path for policy file where promise is defined. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- logmessages (string) Search results matching any of the messages logged for the promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- promisees (string) Search results matching any of the promisees specified for promise. Search is key insensitive. Additionally filter supports ending wildcard which can be enabled with placing '%' sign at the end.
- sort (string) Sort results by specified direction and attribute. By default sort direction is ascending, to sort as descending add '-' before attribute name. Result can be sorted by all returned fields. If not specified results are not sorted. Examples: sort=bundlename - sort ascending by bundlename, sort=-promisehandle - sort descending by promise handle.
- count (integer) Page size. Default 50 items.
- page (integer) Page number. Default 1st page.
Example response:
{
"data": [
{
"bundlename": "maintain_cfe_hub_process",
"changetime": 1430127161,
"hostkey": "SHA=de6ba9f406a2358e9169fb27e5459687d7107a001bb0abd4dd06485a63c2e50b",
"hostname": "hub",
"logmessages": [
"Unable to make file belong to an unknown user",
"Owner of '/var/log/postgresql.log' was 0, setting to 4294967295",
"Unknown user 'cfpostgres' in promise",
"Unable to make file belong to an unknown user",
"Owner of '/var/log/postgresql.log' was 0, setting to 4294967295"
],
"policyfile": "/var/cfengine/inputs/update/update_processes.cf",
"promisees": [],
"promisehandle": "cfe_internal_maintain_cfe_hub_process_files_create_postgresql_log",
"promiser": "/var/log/postgresql.log",
"promisetype": "files",
"stackpath": "/default/cfe_internal_update_processes/methods/'TAKING CARE CFE HUB PROCESSES'/default/maintain_cfe_hub_process/files/'/var/log/postgresql.log'[0]"
},
{
"bundlename": "generate_repairs",
"changetime": 1437566606,
"hostkey": "SHA=a5c09762c561f78ee16097c0524e9efc1a2181c910cefae533f9013acd888b9f",
"hostname": "e63dc85f0e3e",
"logmessages": [
"Executing 'no timeout' ... '/bin/echo 123'",
"Completed execution of '/bin/echo 123'"
],
"policyfile": "/var/cfengine/inputs/promises.cf",
"promisees": [],
"promisehandle": "",
"promiser": "/bin/echo 123",
"promisetype": "commands",
"stackpath": "/default/generate_repairs/commands/'/bin/echo 123'[0]"
}
],
"total": 382723,
"next": "https://hub.cfengine.com/api/v2/changes/policy/?page=2&count=2",
"previous": null
}
Output:
- total Total number of results.
- next Link for fetching next page. Set to NULL if current page is last.
- previous Link for previous page. Set to NULL if the current page if the first.
- data.bundlename Bundle name where the promise is executed.
- data.changetime Time of performing change by cf-agent to the system. Expressed as UNIT TIMESTAMP.
- data.hostkey Unique host identifier.
- data.hostname
Host name locally detected on the host, configurable as
hostIdentifier
option in Settings API and Mission Portal settings UI. - data.logmessages List of 5 last messages generated during promise execution. Log messages can be used for tracking specific changes made by CFEngine while repairing or failing promise execution.
- data.policyfile Path to the file where the promise is located in.
- data.promisees List of promisees defined for the promise.
- data.promisehandle A unique id-tag string for referring promise.
- data.promiser Object affected by a promise.
- data.promisetype Type of the promise.
- data.stackpath Call stack of the promise.
Example usage: Example: Show vacuum command executions
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: 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: 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: Contexts
CFEngine contexts present on hosts at their last reported cf-agent execution.
Columns:
HostKey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.ContextName (text) CFEngine context set by cf-agent.
MetaTags (text[]) List of meta tags set for the context.
ChangeTimeStamp (timestamp) Timestamp since when context is set in its current form. Note: If any of context attributes change, the timestamp will be updated.
Example query:
SELECT hostkey,
contextname,
metatags,
changetimestamp
FROM contexts;
Output:
-[ RECORD 1 ]---|-------------------------------------------------------
hostkey | SHA=a4dd5...
contextname | enterprise_3_6_5
metatags | {inventory,attribute_name=none,source=agent,hardclass}
changetimestamp | 2015-03-11 09:50:11+00
-[ RECORD 2 ]---|-------------------------------------------------------
hostkey | SHA=a4dd5...
contextname | production
metatags | {report,"Production environment"}
changetimestamp | 2015-03-11 09:50:11+00
-[ RECORD 3 ]---|-------------------------------------------------------
hostkey | SHA=a4dd5...
contextname | enterprise_edition
metatags | {inventory,attribute_name=none,source=agent,hardclass}
changetimestamp | 2015-03-11 09:50:11+00
Table: 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: 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: 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: Hosts_not_reported
Hosts_not_reported table contains information about not reported hosts.
Columns:
HostKey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.iscallcollected (boolean) Is host call collected
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,
iscallcollected,
lastreporttimestamp,
firstreporttimestamp
FROM hosts;
Output:
-[ RECORD 1 ]--------|-----------------------
hostkey | SHA=a4dd...
iscallcollected | t
lastreporttimestamp | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:40:20+00
-[ RECORD 2 ]--------|-----------------------
hostkey | SHA=3b94...
iscallcollected | f
lastreporttimestamp | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:34:20+00
-[ RECORD 3 ]--------|-----------------------
hostkey | SHA=2aab...
iscallcollected | f
lastreporttimestamp | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:40:20+00
Table: HubConnectionErrors
Networking errors encountered by cf-hub during its operation.
Columns:
HostKey (text) Unique identifier of the host that cf-hub was connecting to.
CheckTimeStamp (timestamp) Timestamp when the error occurred.
Message (text) Error type / message.
QueryType (text) Type of query that was intended to be sent by hub during failed connection attempt.
Example query:
SELECT hostkey,
checktimestamp,
message,
querytype,
FROM hubconnectionErrors;
Output:
-[ RECORD 1 ]--|--------------------------
hostkey | SHA=3b94d...
checktimestamp | 2015-03-13 13:16:10+00
message | ServerNoReply
querytype | delta
-[ RECORD 2 ]--|--------------------------
hostkey | SHA=3b94d...
checktimestamp | 2015-03-13 14:16:10+00
message | InvalidData
querytype | rebase
-[ RECORD 3 ]--|--------------------------
hostkey | SHA=3b94d...
checktimestamp | 2015-03-13 15:16:10+00
message | ServerAuthenticationError
querytype | delta
Table: Inventory
Inventory data
Columns:
HostKey (text) Unique identifier of the host.
keyname (text) Name of the key.
type (text) Type of the variable. List of supported variable types.
metatags (text[]) List of meta tags set for the variable.
value (text) Variable value serialized to string. * List types such as:
slist
,ilist
,rlist
are serialized with CFEngine list format: {'value','value'}. *Data
type is serialized as JSON string.
Example query:
SELECT hostkey,
keyname,
type,
metatags,
value
FROM Inventory;
Output:
-[ RECORD 1 ]--|--------------------------
hostkey | SHA=3b94d...
keyname | default.sys.fqhost
type | string
metatags | {inventory,source=agent,"attribute_name=Host name"}
value | host name
-[ RECORD 2 ]--|--------------------------
hostkey | SHA=3b94d...
keyname | default.sys.uptime
type | int
metatags | {inventory,source=agent,"attribute_name=Uptime minutes"}
value | 4543
Table: Inventory_new
Inventory data grouped by host
Columns:
HostKey (text) Unique identifier of the host.
values (jsonb) Inventory values presented in JSON format
Example query:
SELECT hostkey,
values
FROM Inventory_new;
Output:
-[ RECORD 1 ]--|--------------------------
hostkey | SHA=3b94d...
values | {"OS": "ubuntu", "OS type": "linux", "CPU model": "CPU model A10", "Host name": "SHA=aa11bb1", "OS kernel": "14.4.0-53-generic", "Interfaces": "pop, imap", "BIOS vendor": "BIOS vendor", "CFEngine ID": "SHA=aa11bb1", "CPU sockets": "229", "New OS type": "linux", "Architecture": "x86_64"}
-[ RECORD 2 ]--|--------------------------
hostkey | SHA=5rt43...
values | {"OS": "ubuntu", "OS type": "linux", "CPU model": "CPU model A10", "Host name": "SHA=aa11bb1", "OS kernel": "14.4.0-53-generic", "Interfaces": "pop, imap", "BIOS vendor": "BIOS vendor", "CFEngine ID": "SHA=aa11bb1", "CPU sockets": "229", "New OS type": "linux", "Architecture": "x86_64"}
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 received incoming connection.OUTGOING
- host opened connection to remote host.
RemoteHostKey (text)
HostKey
of the remote host.RemoteHostIP (text) IP address of the remote host.
LastSeenTimeStamp (timestamp) Time when the connection was established.
LastSeenInterval (real) 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: LastSeenHostsLogs
History of LastSeenHosts table
Columns:
HostKey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.LastSeenDirection (
INCOMING
/OUTGOING
) Direction within which the connection was established.INCOMING
- host received incoming connection.OUTGOING
- host opened connection to remote host.
RemoteHostKey (text)
HostKey
of the remote host.RemoteHostIP (text) IP address of the remote host.
LastSeenTimeStamp (timestamp) Time when the connection was established.
LastSeenInterval (real) Average time period (seconds) between connections for the given
LastSeenDirection
with the host.
Example query:
SELECT hostkey,
lastseendirection,
remotehostkey,
remotehostip,
lastseentimestamp,
lastseeninterval
FROM LastSeenHostsLogs;
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: MonitoringHg
Stores 1 record for each observable per host.
Columns:
host (text) Unique host identifier. Referred to in other tables as
HostKey
to connect data concerning same hosts.id (text) Name of monitored metric. The handle of the measurement promise.
ar1 (real) Average across 66 observations.
Table: MonitoringMgMeta
Stores 1 record for each observable per host.
Columns:
id (integer) Unique identifier for host observable.
hostkey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.observable (text) Name of monitored metric. The handle of the measurement promise.
global (boolean)
expected_min (real) Minimum expected value.
expected_max (real) Maximum expected value.
unit (text) Unit of measurement.
description (text) Description of unit of measurement.
updatedtimestamp (timestamp with time zone) Time when measurement sampled.
lastupdatedsample (integer) Value of most recently collected measurement.
Table: MonitoringYrMeta
Stores 1 record for each observable per host.
Columns:
id (integer) Unique identifier for host observable.
hostkey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.observable (text) Name of monitored metric. The handle of the measurement promise.
global (boolean)
expected_min (real) Minimum expected value.
expected_max (real) Maximum expected value.
unit (text) Unit of measurement.
description (text) Description of unit of measurement.
lastupdatedsample (integer) Value of most recently collected measurement.
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: 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: PromiseLog
History of promises executed on hosts.
Columns:
id (integer)
HostKey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.ChangeTimeStamp (timestamp) The GMT time on the host when this state was first perceived.
Note causes of change:
- A change in the promise signature/hash for example, altering the promise handle, promisees, or moving the promise to a different bundle
- A change in the policy releaseId (cf_promises_release_id)
- A change in promise outcome
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.
Example query:
SELECT hostkey,
policyfile,
releaseid,
promisehash,
namespace,
bundlename,
promisetype,
promiser,
stackpath,
promisehandle,
promiseoutcome,
logmessages,
promisees,
changetimestamp
FROM promiselog;
Output:
-[ RECORD 1 ]---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
hostkey | SHA=70138d580b9fd292ff856746df2fe7f9ded29db9ffca0c4d83acbbb97cde4d42
policyfile | /var/cfengine/inputs/lib/bundles.cf
releaseid | f90866033a826aa05cf10fdc8d34a532a9cd465b
promisehash | 04659a0501f471eb1794cead6cd7a3291b78dcb195063821a7dcb4dbe7f7f804
namespace | default
bundlename | prunedir
promisetype | files
promiser | /var/cfengine/outputs
stackpath | /default/cfe_internal_management/methods/'CFEngine_Internals'/default/cfe_internal_core_main/methods/'any'/default/cfe_internal_log_rotation/methods/'Prune old log files'/default/prunedir/files/'/var/cfengine/output
s'[1]
promisehandle |
promiseoutcome | REPAIRED
logmessages | {"Deleted file '/var/cfengine/outputs/cf_demohub_a10042_cfengine_com__1535846669_Sun_Sep__2_00_04_29_2018_0x7f4da3549700'"}
promisees | {}
changetimestamp | 2018-10-02 00:04:52+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: 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: Status
Statuses of report collection. cf-hub records all collection attempts and whether they are FAILEDC or CONSUMED. CONSUMED means next one will be delta. FAILEDC means next one will be REBASE.
Columns:
host (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.ts (timestamp) Timestamp of last data provided by client during report collection. This is used by delta queries to request a start time.
status (
FAILEDC
,CONSUMED
) CFEngine uses incremental diffs to report it's state.ChangeOperation
is a diff state describing current entry.FAILEDC
- 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.CONSUMED
- 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.
lstatus (text) Deprecated
type (text) Deprecated
who (integer) Deprecated
whr integer Deprecated
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: 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: Variables_dictionary
Inventory attributes, these data are using in List of inventory attributes API
Columns:
- Id (integer)
Auto incremental ID - Attribute_name (text) Attribute name
- Category (text) (
Hardware
,Software
,Network
,Security
,User defined
) Attribute category - Readonly (integer) (
0
,1
) Is attribute readonly - Type (text) Type of the attribute. List of supported variable types.
- convert_function (text)
Convert function. Emp.:
cf_clearSlist
- to transform string like{"1", "2"}
to1, 2
- keyname (text) Key name
- Enabled (integer) (
0
,1
) Is attribute enabled for the API
Example query:
SELECT attribute_name,
category,
readonly,
type,
convert_function,
enabled
FROM variables_dictionary;
Output:
-[ RECORD 1 ]---|-----------------------------------------------------
attribute_name | Architecture
category | Software
readonly | 1
type | string
convert_function|
enabled | 1
-[ RECORD 2 ]---|-----------------------------------------------------
attribute_name | IPv4 addresses
category | Network
readonly | 1
type | slist
convert_function| cf_clearSlist
enabled | 1
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: v_hosts
V_hosts table contains information about hosts.
Columns:
HostKey (text) Unique host identifier. All tables can be joined by
HostKey
to connect data concerning same hosts.iscallcollected (boolean) Is host call collected
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,
iscallcollected,
lastreporttimestamp,
firstreporttimestamp
FROM hosts;
Output:
-[ RECORD 1 ]--------|-----------------------
hostkey | SHA=a4dd...
iscallcollected | t
lastreporttimestamp | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:40:20+00
-[ RECORD 2 ]--------|-----------------------
hostkey | SHA=3b94...
iscallcollected | f
lastreporttimestamp | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:34:20+00
-[ RECORD 3 ]--------|-----------------------
hostkey | SHA=2aab...
iscallcollected | f
lastreporttimestamp | 2015-03-10 14:20:20+00
firstreporttimestamp | 2015-03-10 13:40:20+00
Table: vm_hosts
vm_hosts table contains basic information about hosts managed by CFEngine. In this table data are cached what gives a better query performance
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
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. This should be done when the host is no longer active, activities such as a new bootstrap can cause the host to reappear.
If host is found and scheduled for deletion, status code 202 ACCEPTED
is returned.
If host is not found, status code 404 NOT FOUND
is returned.
Other response codes are also possible (access denied, server error, etc.).
Only users with the admin role are allowed to delete hosts.
Reporting data associated with the host is immediately purged.
This includes SQL tables like agentstatus
, hosts
, contexts
, variables
, etc.
In order to completely delete the host, a deletion job is scheduled by adding the host to the internal table KeysPendingForDeletion
.
To see what hosts are pending deletion, run the query SELECT HostKey FROM KeysPendingForDeletion;
against the cfsettings
database.
After 5-10 minutes (one reporting iteration based on the hub schedule), the main thread of cf-hub will pick up the deletion job. The hostkey is then removed from:
- "Last seen" database, which contains network connection info (
/var/cfengine/state/cf_lastseen.lmdb
). - Public key directory, containing cryptographic keys exchaned during bootstrap (
/var/cfengine/ppkeys
). - The previously mentioned
KeysPendingForDeletion
table.
Depending on the configuration
of trustkeysfrom
for the hub hosts may re-appear
and resume being collected from after being deleted.
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
Get count of bootstrapped hosts by date range
URI: https://hub.cfengine.com/api/host-count
Method: POST
Parameters:
- from (string)
Timestamp marking the start of the interval for which to fetch data.
Emp: 2017-11-28
- to (string)
End of data interval to be fetched.
Emp: 2017-12-28
- period (string)
Group data by period. Allowed values:
day, week, month, year
.
Example request (curl):
curl -k --user admin:admin -X POST https://hub.cfengine.com/api/host-count -H 'content-type: application/json' -d '{"period": "month", "from": "2017-11-28", "to" : "2017-12-06"}'
Example response:
HTTP 200 Ok
{
"period": "month",
"data": [
{
"date": "Nov 2017",
"count": 0
},
{
"date": "Dec 2017",
"count": 15
}
]
}
Output:
- period
Period of grouping the data. Allowed values:
day, week, month, year
. - date The date of statistic.
- count The bootstrapped hosts to the hub count.
Import & Export API
Import & Export API provides users the ability to transfer Mission Portal data between hubs.
Get available items to export
This API call provides a list of items available for export. Please note that the role of the user that authenticates to this API will affect what items are available. For example: the API user must have admin role in order to export settings.
URI: https://hub.example/data_transfer/api/exportItems
Method: GET
Example request (curl):
curl -k --user <username>:<password> \
-X GET \
https://hub.example/data_transfer/api/exportItems
Example response:
HTTP 200 Ok
[
{
"id": "categorizations",
"name": "Host categorizations"
},
{
"id": "dashboards",
"name": "Dashboards"
},
{
"id": "reports",
"name": "Reports"
},
{
"id": "settingsScripts",
"name": "Custom notification scripts"
},
{
"id": "users",
"name": "Users"
},
{
"id": "settingsRoles",
"name": "Roles"
},
{
"id": "settingsPreferences",
"name": "Preferences"
},
{
"id": "settingsAuthentication",
"name": "LDAP authentication settings"
},
{
"id": "settingsMail",
"name": "Mail settings"
},
{
"id": "settingsVCS",
"name": "Version control repository"
}
]
Output:
- id Item id. Use this id in export API call.
- name Name of export item.
Export
URI: https://hub.example/data_transfer/api/export
Method: GET
Parameters:
item_id (array) Item id to be exported. List of item ids you can obtain through List of items to export call described below.
encryptionKey (string) Encryption key to encrypt sensitive data. Please save this key to be able to import the data.
exportOnlyUserItems (string)
true
- export only user items.false
- export whole system data
Example request (curl):
curl -k -g --user <username>:<password> \
-X GET \
'https://hub.example/index.php/data_transfer/api/export?encryptionKey=key&exportOnlyUserItems=true&items[]=categorizations&items[]=dashboards&items[]=settingsAuthentication&items[]=settingsMail'
Example response:
HTTP 200 Ok
{
"name": "export_12-14-2018_15:19:40.381400.phar",
}
Output:
- name Name of export file.
- url Url of export file.
Download export file
URI: https://hub.example/data_transfer/api/download/:file_name:
Method: GET
Parameters:
- file_name (string) File name to be downloaded.
Example request (curl):
curl -k -g --user <username>:<password> \
-X GET \
--output /save/file/here/export_12-14-2018_15:19:40.381400.phar \
'https://hub.example/index.php/data_transfer/api/download/export_12-14-2018_15:19:40.381400.phar'
Example response:
HTTP 200 Ok
Raw file contetnt
Output headers:
- Cache-Control: must-revalidate, post-check=0, pre-check=0
- Pragma: public
- Content-Description: File Transfer
- Content-Disposition: attachment; filename="export_12-14-2018_16:04:46.093500.phar"
- Content-Length: 337801
- Content-Type: application/octet-stream
Analyze import file
This API call allows you to see short summary of file content.
URI: https://hub.example/data_transfer/api/analyzeImportFile
Method: POST
Parameters:
- file (form data file) File to be analyzed.
Example request (curl):
curl -k --user <username>:<password> \
-X POST \
-F file=@/path/to/file.phar \
'https://hub.example/index.php/data_transfer/api/analyzeImportFile'
Example response:
HTTP 200 Ok
{
"categorizations": 3,
"dashboards": "4, Widgets: 21 , Alerts: 31, Rules: 7",
"settingsAuthentication": "yes",
"settingsMail": "yes"
}
Import
URI: https://hub.example/data_transfer/api/import
Method: POST
Parameters:
- file (form data file) File to be analyzed.
- encryptionKey (string) Encryption key that was set while export.
- skipDuplicates (number)
Merge conflict strategy:
1
- skip duplicate items.0
- overwrite duplicate items.
Example request (curl):
curl -k --user <username>:<password> \
-X POST \
-F file=@/path/to/file.phar \
-F encryptionKey=key \
-F skipDuplicates=1 \
'https://hub.example/index.php/data_transfer/api/analyzeImportFile'
Example response:
HTTP 200 Ok
Health diagnostic API
This API provides access to health diagnostic information.
Get health diagnostic status
URI: https://hub.cfengine.com/api/health-diagnostic/status
Method: GET
Example response:
{
"hostsNeverCollected": 1,
"hostNotRecentlyCollected": 0,
"hostsUsingSameIdentity": 0,
"agentNotRunRecently": 2,
"lastAgentRunUnsuccessful": 0,
"totalFailed": 3,
"total": "50642"
}
List of health diagnostic report categories
URI: https://hub.cfengine.com/api/health-diagnostic/report_ids
Method: GET
Example response:
[
"hostsNeverCollected",
"notRecentlyCollected",
"hostsUsingSameIdentity",
"agentNotRunRecently",
"lastAgentRunUnsuccessful"
]
Get health diagnostic report data
URI: https://hub.cfengine.com/api/health-diagnostic/report/:report_id
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:
- report_id (string) Report id. List of report ids you can obtain through List of health diagnostic report categories
- 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.
CURL Request Example:
curl -k --user <username>:<password> -X POST \
https://hub.cfengine.com/api/health-diagnostic/report/agentNotRunRecently \
-H 'Content-Type: application/json' \
-d '{"limit": 50}'
Example response:
{
"data": [
{
"header": [
{
"columnName": "key",
"columnType": "STRING"
},
{
"columnName": "Host name",
"columnType": "STRING"
},
{
"columnName": "Last report collected",
"columnType": "STRING"
},
{
"columnName": "Last agent local execution time",
"columnType": "STRING"
}
],
"query": "SELECT h.Hostkey as key,h.hostname as \"Host name\", lastreporttimestamp as \"Last report collected\", agentstatus.lastagentlocalexecutiontimestamp as \"Last agent local execution time\" \n FROM vm_hosts h \n LEFT JOIN agentstatus ON agentstatus.Hostkey = h.Hostkey WHERE h.HostKey IN (SELECT result.hostkey FROM (SELECT agentstatus.HostKey \n FROM agentstatus \n LEFT JOIN vm_hosts ON vm_hosts.hostkey = agentstatus.hostkey\n WHERE extract(epoch from (lastReportTimeStamp::timestamp - lastagentlocalexecutiontimestamp::timestamp)) > agentexecutioninterval::int * 1.3) AS result \n WHERE hostkey IS NOT NULL AND HostKey NOT IN (SELECT hostkey FROM hosts_not_reported) AND HostKey NOT IN (SELECT Hosts_view.HostKey \n FROM vm_hosts Hosts_view \n WHERE Hosts_view.lastreporttimestamp < to_timestamp('1549559891')) AND HostKey NOT IN (SELECT SameHosts.HostKey \n FROM (\n SELECT remotehostkey as HostKey FROM lastseenhostslogs GROUP BY remotehostkey HAVING COUNT(distinct remotehostip) > 1\n ) AS SameHosts))",
"queryTimeMs": 1408,
"rowCount": 2,
"rows": [
[
"SHA=aasdsfdgddswrdfgddfdfgwerdffb86",
"SHA=aasdsfdgddswrdfgddfdfgwerdffb86",
"2019-02-27 15:16:52.987126+00",
"2019-02-27 15:05:56.567979+00"
],
[
"SHA=fe7f992547addc96fe167bacd6de37681c188709ce9f01fb995f03124ef2a934",
"vagrant-ubuntu-trusty-64",
"2019-03-05 10:26:08+00",
"2019-03-04 08:38:30+00"
]
]
}
],
"meta": {
"count": 1,
"page": 1,
"timestamp": 1551782115,
"total": 1
}
}
List of health diagnostic dismissed hosts
URI: https://hub.cfengine.com/api/health-diagnostic/dismiss/:report_id
Method: GET
Parameters
- report_id (string) Report id. List of report ids you can obtain through List of health diagnostic report categories
- offset (integer) Number of results to skip for the processed query.
- limit (integer) Limit the number of results in the query.
CURL Request Example:
curl -k --user <username>:<password> -X GET \
https://hub.cfengine.com/api/health-diagnostic/dismiss/notRecentlyCollected?limit=3&offset=0
Example response:
{
"data": [
{
"header": [
{
"columnName": "hostkey",
"columnType": "STRING"
},
{
"columnName": "hostname",
"columnType": "STRING"
},
{
"columnName": "ipaddress",
"columnType": "STRING"
},
{
"columnName": "lastreporttimestamp",
"columnType": "STRING"
},
{
"columnName": "firstreporttimestamp",
"columnType": "STRING"
}
],
"query": "SELECT * FROM m_hosts WHERE hostkey IN (SELECT hostkey FROM health_diagnostics_dismissed WHERE report_type = 'notRecentlyCollected' AND username = 'admin')",
"queryTimeMs": 26,
"rowCount": 50,
"rows": [
[
"SHA=aasdsfdgddswrdfgddfdfgdffb8922",
"SHA=aasdsfdgddswrdfgddfdfgdffb8922",
null,
"2019-02-27 10:32:12.813777+00",
"2019-02-27 10:32:12.813777+00"
],
[
"SHA=aasdsfdgddswrdfgddfdfgdffb8930",
"SHA=aasdsfdgddswrdfgddfdfgdffb8930",
null,
"2019-02-27 10:32:12.813777+00",
"2019-02-27 10:32:12.813777+00"
],
[
"SHA=aasdsfdgddswrdfgddfdfgdffb8925",
"SHA=aasdsfdgddswrdfgddfdfgdffb8925",
null,
"2019-02-27 10:32:12.813777+00",
"2019-02-27 10:32:12.813777+00"
]
]
}
],
"meta": {
"count": 1,
"page": 1,
"timestamp": 1553087363,
"total": 1
}
}
Dismiss hosts from health diagnostic
URI: https://hub.cfengine.com/api/health-diagnostic/dismiss/:report_id
Method: POST
Parameters
- report_id (string) Report id. List of report ids you can obtain through List of health diagnostic report categories
- hosts (array) Array of host keys to dismiss
CURL Request Example:
curl -k --user admin:admin -X POST \
https://hub.cfengine.com/api/health-diagnostic/dismiss/notRecentlyCollected \
-H 'Content-Type: application/json' \
-d '{"hosts": ["SHA=aasdsfdgddswrdfgddfdfgwerdffb86", "SHA=fe7f992547addc96fe167bacd6de37681c188709ce9f01fb995f03124ef2a934"]}'
Example response:
HTTP 201 CREATED
Remove hosts from dismissed list
URI: https://hub.cfengine.com/api/health-diagnostic/dismiss/:report_id
Method: DELETE
Parameters
- report_id (string) Report id. List of report ids you can obtain through List of health diagnostic report categories
- hosts (array) Array of host keys to remove from dismissed list
CURL Request Example:
curl -k --user admin:admin -X POST \
https://hub.cfengine.com/api/health-diagnostic/dismiss/notRecentlyCollected \
-H 'Content-Type: application/json' \
-d '{"hosts": ["SHA=aasdsfdgddswrdfgddfdfgwerdffb86", "SHA=fe7f992547addc96fe167bacd6de37681c188709ce9f01fb995f03124ef2a934"]}'
Example response:
HTTP 202 ACCEPTED
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": [
{
"hostIdentifier": "default.sys.fqhost",
"rbacEnabled": true,
"logLevel": "error",
"ldapEnabled": true,
"blueHostHorizon": 900,
"sketchActivationAlertTimeout": 60,
"sameHostsNumberOfRuns": 3
}
]
}
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.
- 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.
- sameHostsNumberOfRuns (integer)
Number of samples used to identify a duplicate identity. Default value is 3.
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.
- 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.
- sameHostsNumberOfRuns (integer)
Number of samples used to identify a duplicate identity. Default value is 3.
Example Request Body:
{
"hostIdentifier": "default.sys.fqhost",
"rbacEnabled": false,
"logLevel": "error",
"ldapEnabled": true,
"blueHostHorizon": 900,
"sketchActivationAlertTimeout": 60,
"sameHostsNumberOfRuns": 5
}
Example usage: Example: Configuring LDAP
, Example: Changing The Log Level
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. 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, or pass options to the command line of the underlying
package tool. The behavior on an options
attribute is entirely
dependent on the module, and should not be assumed to be portable between
modules.
The options
attribute is valid in all of the commands 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
Option=-o
Option=APT::Install-Recommends=0
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
Option=--noplugins
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.
Masterfiles Policy Framework
The Masterfiles Policy Framework or MPF also commonly reffered to as simply masterfiles is the policy framework that ships with CFEngine.
The Masterfiles Policy Framework is the default policy that ships with both the CFEngine Enterprise and Community editions. The MPF includes policy to manage cfengine itself, the stdlib, and policy to inventory various aspects of the system.
Overview
update.cf
- The update policy entry.promises.cf
- The main policy entry.lib/
- The standard library.inventory/
- Inventory policy.services
- User defined custom policy.services/main.cf
- Contains an empty bundle agent main where custom policy can be integrated.services/autorun/
- Automatically included policy files.
The MPF is continually updated. You can track its development on github.
Configuration
The most common controls reference variables in the def
bundle in order to
keep the modifications to the distributed policy contained within a single
place. The recommended way to set classes and define variables in the def
bundle is using an augments file. Keeping the modifications to the
distributed policy set makes policy framework upgrades significantly easier.
Note: If you need to make modification to a shipped file consider opening a pull
request to expose the tunable into the def
bundle.
Note: controls/def.cf
contains the defaults and settings for promises.cf
and controls/update_def.cf
contains the defaults and settings for update.cf
.
Since 3.7.8, 3.10.4, and 3.12.0 The class cf_runagent_initated
is defined by
default in the MPF for agent executions initiated by cf-runagent
through
cf-serverd
. Previously the class cfruncommand
was defined. See body server
control cfruncommand
in controls/cf_serverd.cf
.
Update Policy (update.cf)
Synchronizing clients with the policy server happens here, in
update.cf
. Its main job is to copy all the files on the policy
server (usually the hub) under $(sys.masterdir)
(usually
/var/cfengine/masterfiles
) to the local host into $(sys.inputdir)
(usually /var/cfengine/inputs
).
This file should rarely if ever change. Should you ever change it (or
when you upgrade CFEngine), take special care to ensure the old and
the new CFEngine can parse and execute this file successfully. If not,
you risk losing control of your system (that is, if CFEngine cannot
successfully execute update.cf
, it has no mechanism for distributing
new policy files).
By default, the policy defined in update.cf is executed at the
beginning of a cf-execd
scheduled agent run (see schedule
and
exec_command
as defined in body executor control
in
controls/cf_execd.cf
). When the update policy completes
(regardless of success or failure) the policy defined in promises.cf
is activated.
This is a standalone policy file. You can actually run it with
cf-agent -KI -f ./update.cf
but if you don't understand what that
command does, please hold off until you've gone through the CFEngine
documentation. The contents of update.cf
duplicate other things
under lib
sometimes, in order to be completely standalone.
To repeat, when update.cf
is broken, things go bonkers. CFEngine
will try to run a backup failsafe.cf
you can find in the C core
under libpromises/failsafe.cf
(that .cf
file is written into the C
code and can't be modified). If things get to that point, you probably
have to look at why corrupted policies made it into production.
As is typical for CFEngine, the policy and the configuration are mixed. In
controls/update_def.cf
you'll find some very useful settings. Keep referring
to controls/update_def.cf
as you read this. We are skipping the nonessential
ones.
Configure upstream masterfiles location for policy update
Want to get your policy from a place other than /var/cfengine/masterfiles
on
sys.policy_hub
?
With an augments like this:
{
"vars": {
"house": "Gryffindor",
"mpf_update_policy_master_location": "/srv/cfengine/$(sys.flavor)/$(def.house)"
}
}
A CentOS 7 host would copy policy from /srv/cfengine/centos_6/Gryffindor
to
$(sys.inputdir)
(commonly /var/cfengine/inputs
).
History:
- Introduced in 3.12.0.
Append to inputs used by update policy
You can append to the inputs used by the update policy via augments by defining
vars.update_inputs
. The following example will add the policy file
my_updatebundle1.cf
to the list of policy file inputs during the update policy.
{
"vars":{
"update_inputs": [ "my_updatebundle1.cf" ]
}
}
Append to the update bundlesequence
You can specify bundles which should be run at the end of the default update
policy bundlesequence by defining control_common_update_bundlesequence_end
in the vars of an augments file.
For example:
{
"vars":{
"control_common_update_bundlesequence_end": [ "my_updatebundle1", "mybundle2" ]
}
}
Notes:
- The order in which bundles are actuates is not guaranteed.
- The agent will error if a named bundle is not part of inputs.
Specify the agent bundle used for policy update
The MPF uses cfe_internal_update_policy_cpv
to update inputs, modules, and
plugins on remote agents. When new policy is verified by the agent
/var/cfengine/masterfiles/cf_promises_validated
is updated with the current
timestamp. This file is used by remote agents to avoid unnecessary inspection of
all files each time the update policy is triggered.
Override this bundle by setting def.mpf_update_policy_bundle
via augments:
{
"vars": {
"mpf_update_policy_bundle": "MyCustomPolicyUpdateBundle"
}
}
History:
- Introduced in 3.12.0
ignore_missing_bundles
This option allows you to ignore errors when a bundle specified in body common control bundlesequence is not found.
This example illustrates enabling the option via augments.
{
"vars": {
"control_common_ignore_missing_bundles": "true"
}
}
NOTE: The same augments key is used for both update.cf
and promsies.cf
entries.
History:
- Introduced in 3.12.0
ignore_missing_inputs
This option allows you to ignore errors when a file specified in body common control inputs is not found.
This example illustrates enabling the option via augments.
{
"vars": {
"control_common_ignore_missing_inputs": "true"
}
}
NOTE: The same augments key is used for both update.cf
and promsies.cf
entries.
History:
- Introduced in 3.12.0
Verify update transfers
Enable additional verrification after file transfers during policy update by
defining the class cfengine_internal_verify_update_transfers
. When this
class is defined, the update policy will hash the transfered file and compare it
against the hash given by the server
This augments file will enable this behavior for all clients.
{
"classes": {
"cfengine_internal_verify_update_transfers": [ "any" ]
}
}
Encrypted transfers
Note: When using protocol version 2 or greater all communications are encapsulated within a TLS session. This configuration option is only relevant for clients using protocol version 1 (default for versions 3.6 and prior).
To enable encryption during policy updates define the class
cfengine_internal_encrypt_transfers
.
Preserve permissions
By default the MPF does not enforce permissions of inputs. When
masterfiles are copied to inputs, new files are created with default
restrictive permissions. If the class
cfengine_internal_preserve_permissions
is defined the permissions of the
policy server's masterfiles will be preserved when they are copied.
Enable CFEngine Enterprise HA
When the enable_cfengine_enterprise_hub_ha
class is defined the policy to
manage High Availability of Enterprise Hubs is enabled.
Note: This class is not defined by default.
Disable cf_promises_validated check
For non policy hubs the default update policy only performs a full scan of
masterfiles if cf_promises_validated
is repaired. This repair indicates
that the hub has validated new policy that the client needs to refresh.
To disable this check define the
cfengine_internal_disable_cf_promises_validated
class.
It not recommended to disable this check as it both removes a safety mechanism that checks for policy to be valid before allowing clients to download updates, and the increased load on the hub will affect scalability.
If you want to periodically perform a full scan consider adding custom policy to
simply remove $(sys.inputdir)/cf_promises_validated
. This will cause the
file to be repaired during the next update run triggering a full scan.
Automatically remove files not present upstream (SYNC masterfiles)
If the class cfengine_internal_purge_policies
is defined the update
behavior to change from only copying changed files down to performing a
synchronization by purging files on the client that do not exist on the server.
This augments file will enable this behavior for all clients.
{
"classes": {
"cfengine_internal_purge_policies": [ "any" ]
}
}
Disable limiting robot agents
By default the MPF (Masterfiles Policy Framework) contains active policy that is intended to remediate a pathological condition where multiple agent component daemons (like cf-execd) are running concurrently.
Define the class mpf_disable_cfe_internal_limit_robot_agents
to disable this automatic remediation.
{
"classes": {
"mpf_disable_cfe_internal_limit_robot_agents": [ "any" ]
}
}
History:
- Introduced in 3.15.0, 3.12.3, 3.10.7
Automatically deploy masterfiles from Version Control
On a CFEngine Enterprise Hub during the update policy if the class
cfengine_internal_masterfiles_update
is defined masterfiles will be
automatically deployed from an upstream version control repository using
the
settings defined via Mission Portal or
directly in /opt/cfengine/dc-scripts
.
Note: Any policy in the distribution location (/var/cfengine/masterfiles) will be deleted the first time this tooling runs. Be wary of local modifications before enabling.
Policy Permissions
By default the policy enforces permissions of 0600
meaning that inputs are
only readable by their owner. If you are distributing scripts with your
masterfiles, be sure there is a policy to ensure they are executable when you
expect them to be.
Agent binary upgrades
Remote agents can upgrade their own binaries using the built in binary upgrade
policy. Packages must be placed in /var/cfengine/master_software_updates
in
the appropriate platform directory. Clients will automatically download and
install packages when the trigger_upgrade
class is defined during a run of
update.cf
.
This augments file will defines trigger_upgrade
on hosts with IPv4 addresses in 192.0.2.0/24 or 203.0.113.0/24 or hosts that have a cfengine 3.10 class not for cfengine 3.10.2.
{
"classes": {
"trigger_upgrade": [
"ipv4_10_10_1",
"ipv4_10_10_2",
"cfengine_3_10_(?!2$)\d+"
]
}
}
Notes:
- This policy is specific to CFEngine Enterprise.
- The negative look ahead regular expression is useful because it automatically turns off on hosts after they reach the target version.
Configure path that software is served from for autonomous agent upgrades
def.master_software_updates
defines the path that cfengine policy servers
share software updates from. Remote agents access this path via the
master_software_updates
shortcut. By default this path is
$(sys.workdir)/master_software_updates
. This path can be overridden via
vars.dir_master_software_updates
in augments.
For example:
{
"vars": {
"dir_master_software_updates": "/srv/cfengine-software-updates/"
}
}
History: - Introduced 3.15.0, 3.12.3, 3.10.8
Files considered for copy during policy updates
The default update policy only copies files that match regular expressions
listed in def.input_name_patterns
.
This augments file ensures that only files ending in .cf
, .dat
,
.mustache
, .json
, .yaml
and the file
cf_promises_release_id
will be considered by the default update policy.
{
"vars": {
"input_name_patterns": [ ".*\\.cf", ".*\\.dat", ".*\\.txt", ".*\\.conf",
".*\\.mustache", ".*\\.sh", ".*\\.pl", ".*\\.py", ".*\\.rb",
".*\\.sed", ".*\\.awk", "cf_promises_release_id", ".*\\.json",
".*\\.yaml", ".*\\.csv" ]
}
}
Note: This filter does not apply to bootstrap operations. During bootstrap the embedded failsafe policy is used and it decides which files should be copied.
Configuring component management
The Masterfiles Policy Framework ships with policy to manage the components of CFEngine.
By default, for hosts without systemd, this policy defaults to ensuring that components are running.
On systemd hosts, the policy to manage component units is disabled by default.
Enable management of components on systemd hosts
To allow the Masterfiles Policy Framework to actively manage cfengine systemd units and state define the mpf_enable_cfengine_systemd_component_management
.
This example illustrates enabling management of components on systemd hosts having a class matching redhat_8
via augments.
{
"classes:" {
"mpf_enable_cfengine_systemd_component_management": [ "redhat_8" ]
}
}
When enabled, the policy will render systemd unit files in /etc/systemd/system
for managed services. Mustache templates for service units are in the templates directory in the root of the Masterfiles Policy Framework.
When enabled, the policy will make sure that all units are enabled, unless they have been disabled by a persistent class or are explicitly listed as an agent to be disabled.
Enable or disable CFEngine components
Using persistent classes
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.
This augments file will ensure that cf-monitord
is disabled on hosts that have
server1
or the redhat
class defined.
{
"classes": {
"persistent_disable_cf_monitord": [ "server1", "redhat" ]
}
}
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
.
This augments file will ensure that cf-monitord
is not disabled on redhat
hosts.
{
"classes": {
"clear_persistent_disable_cf_monitord": [ "redhat" ]
}
}
Using augments
agents_to_be_disabled
Description: list of agents to disable.
This augments file is a way to specify that cf-monitord
should be disabled on all hosts.
{
"vars": {
"agents_to_be_disabled": [ "cf-monitord" ]
}
}
Main Policy (promises.cf)
The following settings are defined in controls/def.cf
can be set from an
augments file.
dmidecode inventory
When dmidecode is present, some key system attributes are inventoried. The
inventoried attributes can be overridden by defining
def.cfe_autorun_inventory_demidecode[dmidefs]
via augments. dmidecode queries
each key in dmidefs and tags the result with the value prefixed with
attribute_name=
Note, as the dmidefs are overridden, you must supply all
desired inventory attributes.
For example:
{
"vars": {
"cfe_autorun_inventory_dmidecode": {
"dmidefs": {
"bios-vendor": "BIOS vendor",
"bios-version": "BIOS version",
"system-serial-number": "System serial number",
"system-manufacturer": "System manufacturer",
"system-version": "System version",
"system-product-name": "System product name",
"bios-release-date": "BIOS release date",
"chassis-serial-number": "Chassis serial number",
"chassis-asset-tag": "Chassis asset tag",
"baseboard-asset-tag": "Baseboard asset tag"
}
}
}
}
History: - Introduced 3.13.0, 3.12.1, 3.10.5
mailto
The address that cf-execd
should email agent output to.
mailfrom
The address that output mailed from cf-execd
should come from.
smtpserver
The SMTP server that cf-execd
should use to send emails.
acl
def.acl
is a list of of network ranges that should be allowed to connect to cf-serverd. It is also used in the default access promises to allow hosts access to policy and modules that should be distributed.
Here is an example setting the acl from augments:
{
"vars": {
"acl": [ "24.124.0.0/16", "192.168.33.0/24" ]
}
}
See Also: Configure networks allowed to make collect calls (client initiated reporting)
ignore_missing_bundles
This option allows you to ignore errors when a bundle specified in body common control bundlesequence is not found.
This example illustrates enabling the option via augments.
{
"vars": {
"control_common_ignore_missing_bundles": "true"
}
}
NOTE: The same augments key is used for both update.cf
and promises.cf
entries.
History:
- Introduced in 3.12.0
ignore_missing_inputs
This option allows you to ignore errors when a file specified in body common control inputs is not found.
This example illustrates enabling the option via augments.
{
"vars": {
"control_common_ignore_missing_inputs": "true"
}
}
NOTE: The same augments key is used for both update.cf
and promises.cf
entries.
History:
- Introduced in 3.12.0
trustkeysfrom
The list of network ranges that cf-serverd
should trust keys from. This is
should only be open on policy servers while new hosts are expected to be
bootstrapped. It should be empty after your hosts have been bootstrapped to
avoid unwanted hosts from being able to bootstrap.
By default the MPF configures cf-serverd
to trust keys from any host. This is
convenient for simplified bootstrapping. After initial deployment it is
recommended that this setting be reviewed and adjusted appropriately according
to the needs of your infrastructure.
The augments file (def.json
) can be used to override the
default setting. For example it can be restricted to 127.0.0.1
to prevent
keys from any foreign host from being automatically accepted.
{
"vars": {
"trustkeysfrom": [ "127.0.0.1" ]
}
}
Prevent automatic trust for any host by specifying an empty value:
{
"vars": {
"trustkeysfrom": [ "" ]
}
}
services_autorun
When the services_autorun
class is defined bundles tagged with
autorun
are actuated in lexical order.
Example definition of services_autorun
using Augments (def.json):
{
"classes": {
"services_autorun": [ "any::" ]
}
}
Example policy with bundle tagged for execution when services_autorun
is defined:
bundle agent example
{
meta:
"tags" slist => { "autorun" };
reports:
"I will report when 'services_autorun' is defined."
}
Note: .cf
files located in services/autorun/
are automatically
included in inputs even when the services_autorun
class is not
defined. Bundles tagged with autorun
are not required to be placed in
services/autorun/
in order to be automatically actuated.
History:
- Added in CFEngine 3.6.0
postgresql_full_maintenance
On CFEngine Enterprise policy hubs this class is defined by default on Sundays
at 2am. To adjust when postgres maintenance operations run edit
controls/def.cf
directly.
postgresql_vacuum
On CFEngine Enterprise policy hubs this class is defined by default at 2am when
postgresql_maintenance_supported
is defined except for Sundays.
To adjust when postgres maintenance operations run edit controls/def.cf
directly.
enable_cfengine_enterprise_hub_ha
Set this class when you want to enable the CFEngine Enterprise HA policies.
This class can be defined by an augments file. For example:
{
"classes" {
"enable_cfengine_enterprise_hub_ha": [ "hub001" ]
}
}
enable_cfe_internal_cleanup_agent_reports
This class enables policy that cleans up report diffs when they exceed
def.max_client_history_size
. By default is is off unless a CFEngine
Enterprise agent is detected.
Configure splaytime
splaytime
is the maximum number of minutes exec_commad
should wait before executing.
Note: splaytime
should be less than the scheduled interval plus agent run time. So for example if your agent run time is over 1 minute and you are running the default execution schedule of 5 mintues your splay time should be set to 3.
Configure it via augments by defining control_executor_splaytime
:
{
"vars": {
"control_executor_splaytime": "3"
}
}
Configure agent expiration
cf-agents spawned by cf-execd are killed after this number of minutes of not returning data.
Example configuration via augments:
{
"vars": {
"control_executor_agent_expireafter": "15"
}
}
Configure agent execution schedule
The execution scheduled is expressesd as a list of classes. If any of the classes are defined when cf-execd wakes up then exec_command is triggered. By default this is set to a list of time based classes for every 5th minute. This results in a 5 minute execution schedule.
Example configuration via augments:
{
"vars": {
"control_executor_schedule": [ "Min00", "Min30" ]
}
}
The above configuration would result in exec_command being triggered at the top and half hour and sleeping for up to splaytime
before agent execution.
Allow connections from the classic/legacy protocol
By default since 3.9.0 cf-serverd
disallows connections from the classic protocol by default. To allow clients using the legacy protocol (versions prior to 3.7.0 by default) define control_server_allowlegacyconnects
as a list of networks.
Example definition in augments file:
{
"vars": {
"control_server_allowlegacyconnects": [ "0.0.0.0/0" ]
}
}
Configure users allowed to initate execution via cf-runagent
cf-serverd only allows specified users to request unscheduled execution remotely via cf-runagent.
By default the MPF allows root
to request unscheduled execution of non policy servers and does not allow any users to request unscheduled execution from policy servers.
To configure the list of users allowed to request unscheduled execution from non policy servers define vars.control_server_allowusers_non_policy_server
. This example allows the users hubmanager
and cfoperator
to request unscheduled execution from policy servers and no users are allowed to request unscheduled runs from non policy servers.
{
"vars": {
"control_server_allowusers_non_policy_server": [ ],
"control_server_allowusers_policy_server": [ "hubmanager", "cfoperator" ]
}
}
History:
- Added in 3.13.0, 3.12.1
Configure retention for files in log directories
By default the MPF rotates managed log files when they reach 1M in size. To configure this limit via augments define vars.mpf_log_file_max_size
.
For example:
{
"vars": {
"mpf_log_file_max_size": "10M"
}
}
By default the MPF keeps up to 10 rotated log files. To configure this limit via augments define vars.mpf_log_file_retention
.
For example:
{
"vars": {
"mpf_log_file_retention": "5"
}
}
By default the MPF retains log files in log directories (outputs
, reports
and application logs in Enterprise) for 30 days. This can be configured by setting vars.mpf_log_dir_retention
via augments.
For example:
{
"vars": {
"mpf_log_dir_retention": "7"
}
}
Configure retention of assets generated by asynchronous query api or scheduled reports
By default the MPF is configured to retain reports generated by the asynchronous query api and scheduled reports generated by CFEngine Enterprise. This can be configured by setting vars.purge_scheduled_reports_older_than_days
via augments.
{
"vars": {
"purge_scheduled_reports_older_than_days": "30"
}
}
Adjust the maximum amount of client side report data to retain (CFEngine Enterprise)
Enterprise agents cache detailed information about each agent run locally. The
data is purged when the data is reported to a hub. If the volume of data exceeds
def.max_client_history_size
then the client will purge the local data in order
to keep report collection from timing out.
The default 50M threshold can be configured using an augments file, for example:
{
"vars": {
"max_client_history_size": "5M"
}
}
Enterprise hub pull collection schedule
By default Enterprise hubs initiate pull collection once every 5 minutes. This can be overridden in the MPF by defining def.control_hub_hub_schedule
as a list of classes that should trigger collection when defined.
Here we set the schedule to initiate pull collection once every 30 minutes via augments.
{
"vars": {
"control_hub_hub_schedule": [ "Min00", "Min30" ]
}
}
History:
- MPF override introduced in 3.13.0, 3.12.2
Configure maximum age in hours of old reports for cf-hub to collect
By default cf-hub instructs clients to expire reports older than 6 hours in order to prevent a build up of reports that could cause a condition where the client is never able to send all reports within the collection window.
You can adjust this time by setting vars.control_hub_client_history_timeout
For example:
{
"vars": {
"control_hub_client_history_timeout": "72"
}
}
History:
- MPF override introduced in 3.13.0, 3.12.2
Exclude hosts from hub initiated report collection
You may want to exclude some hosts like community agents, hosts behind NAT, and
hosts using client initiated reporting from hub initiated report collection. To
exclude hosts from hub initiated report collection define
def.control_hub_exclude_hosts
in an augments file.
For example to completely disable hub initiated report collection:
{
"vars": {
"control_hub_exclude_hosts": [ "0.0.0.0/0" ]
}
}
History:
- MPF override introduced in 3.13.0, 3.12.2
Change port used for enterprise report collection
By default cf-hub collects reports by connecting to port 5308. You can change this default by setting vars.control_hub_port
in augments.
For example:
{
"vars": {
"control_hub_port": "8035"
}
}
History:
- Added in 3.13.0, 3.12.2
Enable client initiated reporting
In the default configuration for Enterprise report collection the hub periodically polls agents that are bootstrapped to collect reports. Sometimes it may be desirable or necessary for the client to initiate report collection.
To enable client initiated reporting define the class
client_initiated_reporting_enabled
. You may also want to configure the report
interval (how frequently an agent will try to report it's data to the hub) by default it is set to 5. The
reporting interval def.control_server_call_collect_interval
and the class can
be defined in an augments file.
For example:
{
"classes" {
"client_initiated_reporting_enabled": [ "any" ]
},
"vars": {
"control_server_call_collect_interval": "1",
}
}
Configure client initiated reporting timeout
By default cf-serverd holds an open connection for client initiated for 30 seconds. In some environments this value may need to be increased in order for report collection to finish. Once the connection has been open for longer than the specified seconds it is closed.
The window of time can be controled by setting def.control_server_collect_window
.
For example, enable client initated reporting for all hosts with a 10 minute interval and hold the connection open for 90 seconds.
{
"classes" {
"client_initiated_reporting_enabled": [ "any" ]
},
"vars": {
"control_server_collect_window": "90",
"control_server_call_collect_interval": "10"
}
}
History:
- Added in 3.10.6, 3.12.2, 3.13.1
Configure MPF to automatically restart components on relevant data change
While the agent itsef will reload its config upon notice of policy change this bundle specifically handles changes to variables used in the MPF which may come from external data sources which are unknown to the components themselves.
Currently only cf-serverd
and cf-monitord
are handled. cf-execd
is
NOT automatically restarted.
To enable this functionality define the class mpf_augments_control_enabled
{
"classes":{
"mpf_augments_control_enabled": [ "any" ]
}
}
Notes: In order for custom ACLs to leverage augments and support data based
restart you should use variables prefixed with control_server_
.
For example changes to vars.control_server_my_access_rules
when mpf_augments_control_enabled
is defined will result
in cf-serverd
restarting.
{
"classes": {
"mpf_augments_control_enabled": [ "any" ]
},
"vars": {
"control_server_my_access_rules" : {
"/var/repo/": {
"admit": "def.acl"
}
}
}
}
History: Added 3.11.0
Configure maxconnections for cf-serverd
maxconnections
in body server control
configures the maximum number of
connections allowed by cf-serverd. Recommended to be set greater than the number
of hosts bootstrapped.
This can be configured via augments:
{
"vars":{
"control_server_maxconnections": "1000"
}
}
History: Added 3.11.0
Configure networks allowed to make collect_calls (client initiated reporting)
By default the hub allows collect calls (client initiated reporting) from the
networks defined in def.acl
To configure which networks are allowed to
initiate report collection define
def.mpf_access_rules_collect_calls_admit_ips
.
For example to allow client initiated reporting for hosts coming from
24.124.0.0/16
:
{
"vars": {
"mpf_access_rules_collect_calls_admit_ips": [ "24.124.0.0/16" ]
}
}
See Also: Generic acl
Configure Enterprise Measurement/Monitoring Collection
Metrics recorded by measurement promises in cf-monitord
are only collected by
default for policy servers. In order to collect metrics for non policy servers
simply define default_data_select_host_monitoring_include
via in an augments file.
For example to collect all measurements for remote agents and only cpu and memory related probes on policy servers:
{
"vars": {
"default_data_select_host_monitoring_include": [ ".*" ],
"default_data_select_policy_hub_monitoring_include": [ "mem_.*", "cpu.*" ]
}
}
History:
- Added in 3.10.2, 3.11.0
Configure Enterprise Mission Portal Docroot
Primarily for developer conveniance, this setting allows you to easily disable the enforcement that the webapp consists of the packaged files in the docroot used for Mission Portal.
{
"classes": {
"mpf_disable_mission_portal_docroot_sync_from_share_gui": [ "any" ]
}
}
Append to the main bundlesequence
You can specify bundles which should be run at the end of the default
bundlesequence by defining control_common_bundlesequence_end
in the vars
of an augments file.
For example:
{
"vars":{
"control_common_bundlesequence_end": [ "mybundle1", "mybundle2" ]
}
"inputs": [ "services/mybundles.cf" ]
}
Notes:
- The order in which bundles are actuates is not guaranteed.
- The agent will error if a named bundle is not part of inputs.
History: Added in 3.10.0
Configure abortclasses
via augments
Configure a list of regular expressions that result in cf-agent terminating itself upon definition of a matching class.
For example, abort execution if any class starting with error_
or abort_
is defined:
{
"vars":{
"control_agent_abortclasses": [ "error.*", "abort.**" ]
}
}
History: Added in 3.15.0, 3.12.3
Configure abortbundleclasses
via augments
Configure a list of regular expressions that match classes which if defined lead to termination of current bundle.
If no list is defined, then a default of abortbundle
is used.
For example, abort execution if any class starting with bundle_error_
or bundle_abort_
is defined:
{
"vars":{
"control_agent_abortbundleclasses": [ "bundle_error.*", "bundle_abort.**" ]
}
}
History: Added in 3.15.0, 3.12.3
Configure files_single_copy
via augments
Specify a list of regular expressions that when matched will prevent the agent from performing subsequent copy operations on the same promiser.
For example, to only allow any file to be copied a single time:
{
"vars":{
"control_agent_files_single_copy": [ ".*" ]
}
}
History: Added in 3.11.0, 3.10.2
Disable automatic policy hub detection
During bootstrap, if the executing host finds the IP address of the target on
itself it automatically classifies the host as a policy server by ensuring the
$(sys.statedir)/am_policy_hub
file exists. When this file exists, the
am_policy_hub
and policy_server
classes are defined. To help avoid
accidental declassification, the MPF contains policy to regularly check if the
host is bootstrapped to an IP found on itself, and if so, to ensure the proper
state file exists.
To disable this check, define mpf_auto_am_policy_hub_state_disabled
.
For example, to define this class via augments, place the following in your def.json.
{
"classes":{
"mpf_auto_am_policy_hub_state_disabled": [ "any" ]
}
}
History: Added in 3.15.0, 3.12.3, 3.10.7
Configure default repository for file backups
By default the agent creates a backup of a file before it is edited in the same
directory as the edited file. Defining the
mpf_control_agent_default_repository
class will cause these backups to be
placed in $(sys.workdir)/backups
. Customize the backup directory by setting
def.control_agent_default_backup
.
For example:
{
"classes": {
"mpf_control_agent_default_repository": [ "any" ]
},
"vars": {
"control_agent_default_repository": "/var/cfengine/edit_backups"
}
}
History: Added in 3.10.1
Configure periodic package inventory refresh interval
CFEngine refreshes software inventory when it makes changes via packages promises. Additionally, by default, CFEngine periodically refreshes it's internal cache of packages installed (every 60 minutes) and package updates that are available (once a day) according to the default package manager in order to pick up changes made outside packages promises.
{
"vars": {
"package_module_query_installed_ifelapsed": "5",
"package_module_query_updates_ifelapsed": "60"
}
}
WARNING: Be ware of setting package_module_query_update_ifelapsed
too low,
especially with public repositories or you may be banned for abuse.
History: Added in 3.15.0, 3.12.3
Enable logging of Enterprise License utilization
If the class enable_log_cfengine_enterprise_license_utilization
is defined on
an enterprise hub license utilization will be logged by the hub in
$(sys.workdir)/log/license_utilization.log
Example enabling the class from an augments file:
{
"classes": {
"enable_log_cfengine_enterprise_license_utilization": [ "enterprise_edition" ]
}
}
History: Added in 3.11, 3.10.2
Enable external watchdog
Note: This feature is not enabled by default.
If the class cfe_internal_core_watchdog_enabled
is defined, the feature is
enabled and the watchdog will be active. If the class
cfe_internal_core_watchdog_disabled
is defined, the feature is disabled and
the watchdog will not be active.
By default if enabled the watchdog tries to remediate the following conditions:
History:
- start cf-execd if not running (3.8.0)
- restart if processes not resulting in updated logs (3.12.0)
Modules
Modules executed by the usemodule()
function are expected to be found in
$(sys.workdir)/modules
the modules are distributed to all remote agents by in
the default policy.
Templates
For convenience the templates
shortcut is provided and by default the path is
set to $(sys.workdir/templates)
unless $(def.template_dir)
is overridden via
augments.
- NOTE: The templates directory is not currently managed by default policy. Unlike modules templates are not distributed to all hosts by default.
Copy a template from the templates directory:
files:
"$(def.dir_templates)/mytemplate.mustache" -> { "myservice" }
copy_from => remote_dcp("templates/mytemplate.mustache", $(sys.policy_server) ),
comment => "mytemplate is necessary in order to render myservice configuration file.";
Override the path for $(def.dir_templates)
by setting vars.dir_templates
in
the augments file (def.json
):
{
"vars": {
"dir_templates": "/var/cfengine/mytemplates"
}
}
Note: When overriding the templates directory a change to the augments alone
will not cause cf-serverd
to reload its configuration and update the access
control lists as necessary. cf-serverd
will only automatically reload its
config when it notices a change in policy.
History: Added in 3.11.
Recommendations
The MPF includes policy that inspects the system and makes recommendations about
the configuration of the system. When cfengine_recommendations_enabled
is
defined bundles tagged cfengine_recommendation
are executed in lexical order.
cfengine_recommendations_enabled
is defined by default when
cfengine_recommendations_disabled
is not defined.
To disable cfengine recommendations define cfengine_recommendations_disabled
.
This snippet disables recommendations via augments.
{
"classes": [
"cfengine_recommendations_disabled"
]
}
History:
- Recommendations added in 3.12.1
update.cf
$(sys.inputdir)/update.cf
is responsible for updating
policy from the hub as well as ensuring various core
cfenigne components are running.
common bodies
cfengine_update_controls
Prototype: cfengine_update_controls
Implementation:
bundle common cfengine_update_controls
{
vars:
"update_def_inputs"
slist => {
"controls/update_def.cf",
"controls/update_def_inputs.cf",
};
reports:
DEBUG|DEBUG_cfengine_update_controls::
"DEBUG $(this.bundle): update def inputs='$(update_def_inputs)'";
}
agent bundles
cfengine_internal_standalone_self_upgrade
Prototype: cfengine_internal_standalone_self_upgrade
Description: Manage desired version state and execution of policy to reach the target version.
Implementation:
bundle agent cfengine_internal_standalone_self_upgrade
{
methods:
"cfengine_internal_standalone_self_upgrade_state_data";
"cfengine_internal_standalone_self_upgrade_execution";
}
cfengine_internal_standalone_self_upgrade_state_data
Prototype: cfengine_internal_standalone_self_upgrade_state_data
Description: Clear stale recorded desired version information from state
Implementation:
bundle agent cfengine_internal_standalone_self_upgrade_state_data
{
vars:
"binary_upgrade_entry"
string => "$(this.promise_dirname)/standalone_self_upgrade.cf";
"desired_pkg_data_path" string =>
"$(cfengine_internal_standalone_self_upgrade_execution.desired_pkg_data_path)";
files:
# We consider the data stale if it's older than the policy that generated it
"$(desired_pkg_data_path)" -> { "ENT-4317" }
delete => u_tidy,
if => isnewerthan( $(binary_upgrade_entry) , $(desired_pkg_data_path) );
}
cfengine_internal_standalone_self_upgrade_execution
Prototype: cfengine_internal_standalone_self_upgrade_execution
Description: Manage the version of CFEngine that is currently installed. This policy executes a stand alone policy as a sub agent. If systemd is found we assume that it is necessary to escape the current unit via systemd-run.
If the running version matches either the desired version information in state or the version supplied from augments, then we skip running the standalone upgrade policy.
Implementation:
bundle agent cfengine_internal_standalone_self_upgrade_execution
{
vars:
"exec_prefix"
string => ifelse( isexecutable("/bin/systemd-run"), "/bin/systemd-run --unit=cfengine-upgrade --scope ", # trailing space in commands important
isexecutable( "/usr/bin/systemd-run" ), "/usr/bin/systemd-run --unit=cfengine-upgrade --scope ",
"");
"desired_pkg_data_path" -> { "ENT-3592" }
string => "$(sys.statedir)/MPF/desired-cfengine-package-version.json";
"desired_pkg_data" -> { "ENT-3592" }
data => readjson( $(desired_pkg_data_path), inf ),
if => fileexists( $(desired_pkg_data_path) );
"local_update_log_dir"
string => translatepath("$(sys.workdir)/software_updates/update_log"),
comment => "This directory is used for logging the current version of cfengine running.";
classes:
# If we are running the version desired by the self upgrade policy
"at_desired_version_by_policy_specification" -> { "ENT-3592" }
expression => strcmp( "$(desired_pkg_data[version])", "$(sys.cf_version)" );
# If we are running the version explicitly defined by the user
"at_desired_version_by_user_specification" -> { "ENT-3592" }
expression => strcmp( "$(def.cfengine_software_pkg_version)", "$(sys.cf_version)" );
"at_desired_version"
or => { "at_desired_version_by_user_specification", "at_desired_version_by_policy_specification" };
files:
enterprise_edition::
"$(local_update_log_dir)/$(sys.cf_version)_is_running" -> { "ENT-4352" }
comment => "This results in a record of the first time the enterprise
agent of a given version is seen to run on a host.",
handle => "cfe_internal_update_bins_files_version_is_running",
create => "true";
commands:
trigger_upgrade.!at_desired_version::
'$(exec_prefix)$(sys.cf_agent) --inform --timestamp --file "$(this.promise_dirname)$(const.dirsep)standalone_self_upgrade.cf" --define trigger_upgrade,update_cf_initiated >"$(sys.workdir)$(const.dirsep)outputs/standalone_self_upgrade_$(sys.cdate).log"'
handle => "standalone_self_upgrade",
contain => in_shell;
reports:
trigger_upgrade.(inform_mode|verbose_mode|DEBUG|DEBUG_cfengine_internal_standalone_self_upgrade)::
"Skipped self upgrade because we are running the desired version $(sys.cf_version)" -> { "ENT-3592" }
if => "at_desired_version";
}
common bodies
control
Prototype: control
Implementation:
body common control
{
bundlesequence => {
"update_def",
"u_cfengine_enterprise",
@(u_cfengine_enterprise.def),
"cfe_internal_dc_workflow",
"cfe_internal_update_policy",
"cfengine_internal_standalone_self_upgrade",
"cfe_internal_update_processes",
@(update_def.bundlesequence_end), # Define control_common_update_bundlesequnce_end via augments
};
version => "update.cf $(update_def.current_version)";
inputs => {
"cfe_internal/update/lib.cf",
"cfe_internal/update/systemd_units.cf",
@(cfengine_update_controls.update_def_inputs),
"cfe_internal/update/cfe_internal_dc_workflow.cf",
"cfe_internal/update/cfe_internal_local_git_remote.cf",
"cfe_internal/update/cfe_internal_update_from_repository.cf",
"cfe_internal/update/update_policy.cf",
"cfe_internal/update/update_processes.cf",
@(update_def.augments_inputs)
};
any::
ignore_missing_bundles => "$(update_def.control_common_ignore_missing_bundles)";
ignore_missing_inputs => "$(update_def.control_common_ignore_missing_inputs)";
}
agent bodies
control
Prototype: control
Implementation:
body agent control
{
ifelapsed => "1";
skipidentify => "true";
}
classes bodies
u_kept_successful_command
Prototype: u_kept_successful_command
Description: Set command to "kept" instead of "repaired" if it returns 0
Implementation:
body classes u_kept_successful_command
{
kept_returncodes => { "0" };
failed_returncodes => { "1" };
}
contain bodies
in_shell
Prototype: in_shell
Implementation:
body contain in_shell
{
useshell => "true";
}
promises.cf
$(sys.inputdir)/promises.cf
is the default policy run by the agent. It is
responsible for specifying additional policy files that should be included as
part of the policy and the order in which to run bundles.
Policy
common bodies
inventory
Prototype: inventory
Description: Set up inventory inputs
This bundle creates the inputs for inventory bundles.
Inventory bundles are simply common bundles loaded before anything else in promises.cf
Tested to work properly against 3.5.x
Implementation:
{
classes:
"other_unix_os" expression => "!(windows|macos|linux|freebsd|aix)";
"specific_linux_os" expression => "redhat|debian|suse|sles";
vars:
# This list is intended to grow as needed
debian::
"inputs" slist => { "inventory/any.cf", "inventory/linux.cf", "inventory/lsb.cf", "inventory/debian.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_linux", "inventory_lsb", "inventory_debian", "inventory_os" };
redhat::
"inputs" slist => { "inventory/any.cf", "inventory/linux.cf", "inventory/lsb.cf", "inventory/redhat.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_linux", "inventory_lsb", "inventory_redhat", "inventory_os" };
suse|sles::
"inputs" slist => { "inventory/any.cf", "inventory/linux.cf", "inventory/lsb.cf", "inventory/suse.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_linux", "inventory_lsb", "inventory_suse", "inventory_os" };
windows::
"inputs" slist => { "inventory/any.cf", "inventory/windows.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_windows", "inventory_os" };
macos::
"inputs" slist => { "inventory/any.cf", "inventory/macos.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_macos", "inventory_os" };
freebsd::
"inputs" slist => { "inventory/any.cf", "inventory/freebsd.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_freebsd", "inventory_os" };
linux.!specific_linux_os::
"inputs" slist => { "inventory/any.cf", "inventory/linux.cf", "inventory/lsb.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_linux", "inventory_lsb", "inventory_os" };
aix::
"inputs" slist => { "inventory/any.cf", "inventory/generic.cf", "inventory/aix.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_generic", "inventory_aix", "inventory_os" };
other_unix_os::
"inputs" slist => { "inventory/any.cf", "inventory/generic.cf", "inventory/os.cf" };
"bundles" slist => { "inventory_control", "inventory_any", "inventory_autorun", "inventory_generic", "inventory_os" };
reports:
verbose_mode::
"$(this.bundle): loading inventory module '$(inputs)'";
}
cfe_internal_inputs
Prototype: cfe_internal_inputs
Description: Include internal self management policies
Implementation:
}
{
vars:
any::
"input[cfe_internal_management]"
string => "cfe_internal/CFE_cfengine.cf",
comment => "This policy activates internal management policies
for both core and enterprise";
"input[core_main]"
string => "cfe_internal/core/main.cf",
comment => "This policy activates other core policies";
"input[core_limit_robot_agents]"
string => "cfe_internal/core/limit_robot_agents.cf",
comment => "The policy here ensures that we don't have too many
cf-monitord or cf-execd processes";
"input[core_log_rotation]"
string => "cfe_internal/core/log_rotation.cf",
comment => "This policy ensures that various cfengine log files
do not grow without bound and fill up the disk";
"input[core_host_info_report]"
string => "cfe_internal/core/host_info_report.cf",
comment => "This policy produces a text based host info report
and serves as a functional example of using mustache templates";
"input[cfengine_internal_core_watchdog]"
string => "cfe_internal/core/watchdog/watchdog.cf",
comment => "This policy configures external watchdogs to ensure that
cf-execd is always running.";
enterprise_edition.(policy_server|am_policy_hub)::
"input[enterprise_hub_specific]"
string => "cfe_internal/enterprise/CFE_hub_specific.cf",
comment => "Policy relating to CFEngine Enterprise Hub, for example
software updates, webserver configuration, and alerts";
@if minimum_version(3.12)
"input[enterprise_hub_federation]"
string => "cfe_internal/enterprise/federation/federation.cf",
comment => "Policy relating to CFEngine Federated Reporting";
@endif
enterprise_edition::
"input[enterprise_knowledge]"
string => "cfe_internal/enterprise/CFE_knowledge.cf",
comment => "Settings mostly releated to CFEngine Enteprise Mission Portal";
"input[enterprise_main]"
string => "cfe_internal/enterprise/main.cf",
comment => "This policy activates other enterprise specific policies";
"input[change_management]"
string => "cfe_internal/enterprise/file_change.cf",
comment => "This policy monitors critical system files for change";
"input[enterprise_mission_portal]"
string => "cfe_internal/enterprise/mission_portal.cf",
comment => "This policy manages Mission Portal related configurations.";
any::
"inputs" slist => getvalues("input");
}
cfengine_stdlib
Prototype: cfengine_stdlib
Description: Include the standard library
Implementation:
any::
{
vars:
!cfengine_3_7::
# CFEngine 3.6 can include through a secondary file
# CFEngine version 3.6 and prior use the split library to avoid syntax
# errors introduced by new functionality. For example new functions.
# This also works for 3.8 because local_libdir should be set to lib
# instead of lib/3.8
"inputs" slist => { "$(sys.local_libdir)/stdlib.cf" };
# As part of ENT-2719 3.12.2 introduced package_method attributes for
# specifying the interpreter and specifying the module path. These
# attributes are not known in previous versions and must not be seen by
# the parser or they will be seen as syntax errors. A cleaner way to do
# this using the minimum_version macro is possible, but that would break
# masterfiles compatibility in 3.12 with 3.7 binaries since 3.7 binaries
# do not support major.minor.patch with minimum_version, only major.minor.
windows.cfengine_3_12.!(cfengine_3_12_0|cfengine_3_12_1)::
"inputs" slist => { "$(sys.local_libdir)/stdlib.cf",
"$(sys.local_libdir)/packages-ENT-3719.cf" };
@if minimum_version(3.14)
windows::
"inputs" slist => { "$(sys.local_libdir)/stdlib.cf",
"$(sys.local_libdir)/packages-ENT-3719.cf" };
@endif
cfengine_3_7::
# CFEngine 3.7 has local_libdir set to $(sys.inputdir)/lib/3.7, but with
# the @if macro support we can re-unify the split library for 3.7+ so we
# specify the unified lib relative to local_libdir.
"inputs" slist => { "$(sys.local_libdir)/../stdlib.cf" };
reports:
verbose_mode::
"$(this.bundle): defining inputs='$(inputs)'";
}
cfengine_controls
Prototype: cfengine_controls
Description: Include various agent control policies
Implementation:
"inputs" slist => { "$(sys.local_libdir)/../stdlib.cf" };
{
vars:
"def_inputs"
slist => {
"controls/def.cf",
"controls/def_inputs.cf",
},
comment => "We strictly order the def inputs because they should be parsed first";
"input[cf_agent]"
string => "controls/cf_agent.cf",
comment => "Agent control options";
"input[cf_execd]"
string => "controls/cf_execd.cf",
comment => "Executor (scheduler) control options";
"input[cf_monitord]"
string => "controls/cf_monitord.cf",
comment => "Monitor/Measurement control options";
"input[cf_serverd]"
string => "controls/cf_serverd.cf",
comment => "Server control options";
"input[cf_runagent]"
string => "controls/cf_runagent.cf",
comment => "Runagent (remote activation request) control options";
enterprise_edition::
"input[cf_hub]" -> { "CFEngine Enterprise" }
string => "controls/cf_hub.cf",
comment => "Hub (agent report collection) control options";
"input[reports]" -> { "CFEngine Enterprise" }
string => "controls/reports.cf",
comment => "Report collection options";
any::
"inputs" slist => getvalues(input);
reports:
DEBUG|DEBUG_cfengine_controls::
"DEBUG $(this.bundle)";
"$(const.t)defining inputs='$(inputs)'";
}
services_autorun
Prototype: services_autorun
Description: Include autorun policy and discover autorun bundles if enabled
Implementation:
"inputs" slist => getvalues(input);
{
vars:
!cfengine_3_7.services_autorun::
# 3.8+ can use local_libdir and will use the common lib as it supports the
# @if macro
"inputs" slist => { "$(sys.local_libdir)/autorun.cf" };
"found_inputs" slist => lsdir("$(this.promise_dirname)/services/autorun", ".*\.cf", "true");
"bundles" slist => { "autorun" }; # run loaded bundles
cfengine_3_7.services_autorun::
# We have to point 3.7 at the unified library because sys.local_libdir in
# 3.7 binaries it is set to a version specific path. However since 3.7
# knows about the @if macro it is safe to share the same policy as 3.8+
"inputs" slist => { "$(sys.local_libdir)/../autorun.cf" };
"found_inputs" slist => lsdir("$(this.promise_dirname)/services/autorun", ".*\.cf", "true");
"bundles" slist => { "autorun" }; # run loaded bundles
!services_autorun::
# If services_autorun is not enabled, then we should not extend inputs
# automatically.
"inputs" slist => { };
"found_inputs" slist => {};
"bundles" slist => { "services_autorun" }; # run self
reports:
DEBUG|DEBUG_services_autorun::
"DEBUG $(this.bundle): Services Autorun Disabled"
ifvarclass => "!services_autorun";
"DEBUG $(this.bundle): Services Autorun Enabled"
ifvarclass => "services_autorun";
"DEBUG $(this.bundle): adding input='$(inputs)'"
ifvarclass => isvariable("inputs");
"DEBUG $(this.bundle): adding input='$(found_inputs)'"
ifvarclass => isvariable("found_inputs");
}
common bodies
control
Prototype: control
Description: Control options common to all agents
Implementation:
body common control
{
bundlesequence => {
# Common bundle first (Best Practice)
inventory_control,
@(inventory.bundles),
def,
@(cfengine_enterprise_hub_ha.classification_bundles),
# Design Center
cfsketch_run,
# autorun system
services_autorun,
@(services_autorun.bundles),
# Agent bundle
cfe_internal_management, # See cfe_internal/CFE_cfengine.cf
main,
@(cfengine_enterprise_hub_ha.management_bundles),
@(def.bundlesequence_end),
};
inputs => {
# File definition for global variables and classes
@(cfengine_controls.def_inputs),
# Inventory policy
@(inventory.inputs),
# Design Center
"sketches/meta/api-runfile.cf",
@(cfsketch_g.inputs),
# CFEngine internal policy for the management of CFEngine itself
@(cfe_internal_inputs.inputs),
# Control body for all CFEngine robot agents
@(cfengine_controls.inputs),
# COPBL/Custom libraries. Eventually this should use wildcards.
@(cfengine_stdlib.inputs),
# autorun system
@(services_autorun.inputs),
"services/main.cf",
};
version => "CFEngine Promises.cf 3.12.8a.ff51a7d2f";
# From 3.7 onwards there is a new package promise implementation using package
# modules in which you MUST provide package modules used to generate
# software inventory reports. You can also provide global default package module
# instead of specifying it in all package promises.
(debian).!disable_inventory_package_refresh::
package_inventory => { $(package_module_knowledge.platform_default) };
# We only define pacakge_invetory on redhat like systems that have a
# python version that works with the package module.
(redhat|centos|suse|sles|opensuse|amazon_linux).cfe_yum_package_module_supported.!disable_inventory_package_refresh::
package_inventory => { $(package_module_knowledge.platform_default) };
(debian|redhat|suse|sles|opensuse|amazon_linux)::
package_module => $(package_module_knowledge.platform_default);
# CFEngine 3.12.2+ and 3.14+ have new package module on Windows
windows.cfengine_3_12.!(cfengine_3_12_0|cfengine_3_12_1)::
package_inventory => { $(package_module_knowledge.platform_default) };
package_module => $(package_module_knowledge.platform_default);
@if minimum_version(3.14)
windows::
package_inventory => { $(package_module_knowledge.platform_default) };
package_module => $(package_module_knowledge.platform_default);
@endif
alpinelinux::
package_module => $(package_module_knowledge.platform_default);
any::
ignore_missing_bundles => "$(def.control_common_ignore_missing_bundles)";
ignore_missing_inputs => "$(def.control_common_ignore_missing_inputs)";
}
controls/
This directory contains policy related to the internal control and functioning of the various CFEngine components.
Note: Many of the tuneables specified in these files have been exposed in the
def
bundle for use via the augments
file. It is reccomended that direct
modifications to these files be limited in order to ease policy framework
upgrades. If you are altering one of these files, please consider making a pull
request to expose the tunable.
controls/cf_monitord.cf
This is where body monitor control
is defined. body monitor control
is where
various settings related to cf-monitord
can be tuned.
monitor bodies
control
Prototype: control
Implementation:
body monitor control
{
any::
forgetrate => "0.7";
histograms => "true";
# tcpdump => "false";
# tcpdumpcommand => "/usr/sbin/tcpdump -t -n -v";
}
controls/update_def_inputs.cf
This is where the list of update related policy files to include as defined from the augments file is included.
common bodies
u_cfengine_enterprise
Prototype: u_cfengine_enterprise
Description: Inputs specific to CFEngine Enterpise
Implementation:
bundle common u_cfengine_enterprise
{
vars:
enable_cfengine_enterprise_hub_ha::
"input[ha_update]"
string => "cfe_internal/enterprise/ha/ha_update.cf";
"input[ha_def]"
string => "cfe_internal/enterprise/ha/ha_def.cf";
"def"
slist => { "ha_def", "ha_update" };
"inputs" slist => getvalues(input);
!enable_cfengine_enterprise_hub_ha::
"inputs" slist => { };
"def" slist => { "$(this.bundle)" };
}
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(u_cfengine_enterprise.inputs) };
}
controls/cf_serverd.cf
This is where body server control
is defined. body server control
is where
various settings related to cf-serverd
can be tuned.
server bundles
access_rules
Prototype: access_rules
Implementation:
bundle server access_rules()
{
access:
any::
"$(def.dir_masterfiles)/"
handle => "server_access_grant_access_policy",
shortcut => "masterfiles",
comment => "Grant access to the policy updates",
if => isdir( "$(def.dir_masterfiles)/" ),
admit => { @(def.acl) };
"$(def.dir_bin)/"
handle => "server_access_grant_access_binary",
comment => "Grant access to binary for cf-runagent",
if => isdir( "$(def.dir_bin)/" ),
admit => { @(def.acl) };
"$(def.dir_data)/"
handle => "server_access_grant_access_data",
shortcut => "data",
comment => "Grant access to data directory",
if => isdir( "$(def.dir_data)/" ),
admit => { @(def.acl) };
"$(def.dir_modules)/"
handle => "server_access_grant_access_modules",
shortcut => "modules",
comment => "Grant access to modules directory",
if => isdir( "$(def.dir_modules)/" ),
admit => { @(def.acl) };
"$(def.dir_plugins)/"
handle => "server_access_grant_access_plugins",
comment => "Grant access to plugins directory",
if => isdir( "$(def.dir_plugins)/" ),
admit => { @(def.acl) };
"$(def.dir_templates)/"
handle => "server_access_grant_access_templates",
shortcut => "templates",
comment => "Grant access to templates directory",
if => isdir( "$(def.dir_templates)/" ),
admit => { @(def.acl) };
policy_server|am_policy_hub::
"$(def.dir_master_software_updates)/" -> { "ENT-4953" }
handle => "server_access_grant_access_master_software_updates",
shortcut => "master_software_updates",
comment => "Grant access for hosts to download cfengine packages for self upgrade",
if => isdir( "$(sys.workdir)/master_software_updates" ),
admit => { @(def.acl) };
enterprise_edition.policy_server::
"collect_calls"
resource_type => "query",
admit_ips => { @(def.mpf_access_rules_collect_calls_admit_ips) };
!windows::
"$(def.cf_runagent_shell)"
handle => "server_access_grant_access_shell_cmd",
comment => "Grant access to shell for cfruncommand",
admit => { @(def.policy_servers) };
policy_server.enable_cfengine_enterprise_hub_ha::
"$(sys.workdir)/ppkeys/"
handle => "server_access_grant_access_ppkeys_hubs",
comment => "Grant access to ppkeys for HA hubs",
if => isdir( "$(sys.workdir)/ppkeys/" ),
admit => { @(def.policy_servers) };
# Allow slave hub to synchronize cf_robot and appsettings, application
# config config, ldap config settings, and ldap api config settings.
# Files are containing configuration that must be the same on all hubs.
"$(sys.workdir)/httpd/htdocs/application/config/cf_robot.php"
handle => "server_access_grant_access_cf_robot",
comment => "Grant access to cf_robot file for HA hubs",
admit => { @(def.policy_servers) };
"$(sys.workdir)/httpd/htdocs/application/config/appsettings.php"
handle => "server_access_grant_access_appsettings",
comment => "Grant access to appsettings for HA hubs",
admit => { @(def.policy_servers) };
"$(sys.workdir)/httpd/htdocs/application/config/config.php" -> { "ENT-4944" }
handle => "server_access_grant_access_application_config_config_php",
comment => "Grant access to application config for HA hubs",
admit => { @(def.policy_servers) };
"$(sys.workdir)/httpd/htdocs/ldap/config/settings.php" -> { "ENT-4944" }
handle => "server_access_grant_access_ldap_config_settings_php",
comment => "Grant access to ldap config settings for HA hubs",
admit => { @(def.policy_servers) };
"$(sys.workdir)/httpd/htdocs/api/config/config.php" -> { "ENT-4944" }
handle => "server_access_grant_access_api_config_settings_php",
comment => "Grant access to LDAP api config for HA hubs",
admit => { @(def.policy_servers) };
# Allow access to notification_scripts directory so passive hub
# will be able to synchronize its content. Once passive hub will
# be promoted to act as a master all the custom scripts will be
# accessible.
"/opt/cfengine/notification_scripts/"
handle => "server_access_grant_access_notification scripts",
comment => "Grant access to notification scripts",
if => isdir( "/opt/cfengine/notification_scripts/" ),
admit => { @(def.policy_servers) };
# When HA is enabled clients are updating active hub IP address
# using data stored in master_hub.dat file.
"$(ha_def.master_hub_location)"
handle => "server_access_grant_access_policy_server_dat",
comment => "Grant access to policy_server.dat",
admit => { @(def.acl) };
# Hubs keys working in HA configuration are stored in ppkeys_hubs directory.
# In order to perform failover while active hub is down clients needs to
# have all hubs keys. This gives ability to connect to slave hub promoted to active role
# once active is down.
"$(ha_def.hubs_keys_location)/"
handle => "server_access_grant_access_to_clients",
comment => "Grant access to hubs' keys to clients",
if => isdir("$(ha_def.hubs_keys_location)/"),
admit => { @(def.acl) };
windows::
"c:\program files\cfengine\bin\cf-agent.exe"
handle => "server_access_grant_access_agent",
comment => "Grant access to the agent (for cf-runagent)",
admit => { @(def.policy_servers) };
roles:
# Use roles to allow specific remote cf-runagent users to
# define certain soft-classes when running cf-agent on this host
".*" authorize => { "root" };
}
server bodies
control
Prototype: control
Implementation:
body server control
{
# List of ciphers the server accepts. For Syntax help see man page
# for "openssl ciphers". Default is "AES256-GCM-SHA384:AES256-SHA"
#allowciphers => "AES256-GCM-SHA384:AES256-SHA";
# List of hosts that may connect (change the ACL in def.cf)
allowconnects => { "127.0.0.1" , "::1", @(def.acl) };
# Out of them, which ones should be allowed to have more than one
# connection established at the same time?
allowallconnects => { "127.0.0.1" , "::1", @(def.acl) };
# Out of the hosts in allowconnects, trust new keys only from the
# following ones. SEE COMMENTS IN def.cf
trustkeysfrom => { @(def.trustkeysfrom) };
## List of the hosts not using the latest protocol that we'll accept connections from
## (absence of this option or empty list means allow none)
allowlegacyconnects => { @(def.control_server_allowlegacyconnects) };
# Maximum number of concurrent connections.
# Suggested value >= total number of hosts
maxconnections => "$(def.control_server_maxconnections)";
# Allow connections from nodes which are out-of-sync
denybadclocks => "false";
enterprise_edition.client_initiated_reporting_enabled::
# How often cf-serverd should try to establish a reverse tunnel for report
# collection
call_collect_interval => "$(def.control_server_call_collect_interval)";
# The time in seconds that a collect-call tunnel will remain open for hub
# to complete report transfer.
collect_window => "$(def.control_server_collect_window)";
# The MPF provides different variables for configuring which users are
# allowed to initiate execution via cf-runagent.
!policy_server::
allowusers => { @(def.control_server_allowusers_non_policy_server) };
policy_server::
allowusers => { @(def.control_server_allowusers_policy_server) };
windows::
cfruncommand => "$(sys.cf_agent) -I -D cf_runagent_initiated -f \"$(sys.update_policy_path)\" &
$(sys.cf_agent) -I -D cf_runagent_initiated";
!windows.!cfengine_3_7::
# In 3.10 the quotation is properly closed when EOF is reached. It is left
# open so that arguments (like -K and --remote-bundles) can be appended.
# 3.10.x does not automatically append -I -Dcfruncommand
cfruncommand => "$(def.cf_runagent_shell) -c \'
$(sys.cf_agent) -I -D cf_runagent_initiated -f $(sys.update_policy_path) ;
$(sys.cf_agent) -I -D cf_runagent_initiated";
!windows.cfengine_3_7::
# 3.7.x does not properly close open quotes when EOF is reached preventing
# policy execution, so we must provide a trailing quote for cfruncommand.
# 3.7.x Automatically appends -I -Dcfruncommand to the cfruncommand. This
# results in an error about not being allowed to specify inputs for remote
# execution from the update policy. In order to preserve the ability for
# policy to know its execution provenance we define a different class
# `cf_runagent_initiated` that does not elicit the error.
cfruncommand => "$(def.cf_runagent_shell) -c \'
$(sys.cf_agent) -I -D cf_runagent_initiated -f $(sys.update_policy_path) ;
$(sys.cf_agent) -I -D cf_runagent_initiated\'";
# Bind to all interfaces including ipv6
# Adding this on windows will force ipv6 only
bindtointerface => "::";
}
controls/cf_hub.cf
This is where body hub control
is defined. body hub control
is where
various settings related to cf-hub
can be tuned.
Note: cf-hub
is only available in CFEngine Enterprise.
hub bodies
control
Prototype: control
Implementation:
body hub control
{
enterprise_edition.policy_server::
exclude_hosts => { @(def.control_hub_exclude_hosts) };
# exclude_hosts => { "192.168.12.21", "10.10", "10.12.*" };
# cf-hub initiates a pull collection round if one of the listed classes is defined.
hub_schedule => { @(def.control_hub_hub_schedule) };
# port => "5308";
# Hub will discard accumulated reports on the clients
# and download only information about current state of the client
# in case of not successfully downloading the reports for defined
# period of time. Default value is 6 hours.
# Was introduced in CFEngine 3.6.4
# client_history_timeout => 6; # [hours]
}
controls/update_def.cf
This is where most common variables and classes are defined for the update
policy. Note its variable scope can be augmented with def.json
.
common bodies
update_def
Prototype: update_def
Implementation:
bundle common update_def
{
classes:
!feature_def_json_preparse::
"have_augments_file" expression => fileexists($(augments_file)), scope => "bundle";
"have_augments_classes" expression => isvariable("augments[classes]"), scope => "bundle";
have_augments_classes.!feature_def_json_preparse::
"$(augments_classes_data_keys)"
expression => classmatch("$(augments[classes][$(augments_classes_data_keys)])"),
meta => { "augments_class", "derived_from=$(augments_file)" };
any::
"sys_policy_hub_port_exists" expression => isvariable("sys.policy_hub_port");
vars:
"current_version" string => "3.12.8a.ff51a7d2f";
!feature_def_json_preparse::
"augments_file" string => "$(this.promise_dirname)/../../def.json";
"defvars" slist => variablesmatching("default:update_def\..*", "defvar");
have_augments_file.!feature_def_json_preparse::
"augments" data => readjson($(augments_file), 100k), ifvarclass => "have_augments_file";
"override_vars" slist => getindices("augments[vars]");
"override_data_$(override_vars)" data => mergedata("augments[vars][$(override_vars)]");
"override_data_s_$(override_vars)" string => format("%S", "override_data_$(override_vars)");
have_augments_classes.!feature_def_json_preparse::
"augments_classes_data" data => mergedata("augments[classes]");
"augments_classes_data_keys" slist => getindices("augments_classes_data");
any::
# MPF Controls
# Because in some versions of cfengine bundlesequence in body common
# control does not support does not support iteration over data containers
# we must first pick out the bundles into a shallow container that we can
# then get a regular list from using getvalues().
"tbse" data => mergedata( "def.control_common_update_bundlesequence_end" );
# Since we have @(def.update_bundlesequence_end) in body common control
# bundlesequence we must have a list variable defined. It can be empty, but it
# must be defined. If it is not defined the agent will error complaining
# that '@(def.bundlesequence_end) is not a defined bundle.
# As noted in CFE-2460 getvalues behaviour varies between versions. 3.7.x
# getvalues will return an empty list when run on a non existant data
# container. On 3.9.1 it does not return an empty list.
# So we initialize it as an empty list first to be safe.
"bundlesequence_end" slist => {};
"bundlesequence_end" slist => getvalues( tbse );
"augments_inputs"
slist => {},
ifvarclass => not( isvariable( "def.update_inputs" ) );
"augments_inputs"
slist => { @(def.update_inputs) },
ifvarclass => isvariable( "def.update_inputs" );
# Begin change
# When parsing the augments_file from policy, we set input_name_patterns
# based on the data extracted from within policy
"input_name_patterns"
slist => getvalues("override_data_input_name_patterns"),
comment => "JSON-sourced filename patterns to match when updating the policy
(see update/update_policy.cf)",
handle => "common_def_json_vars_input_name_patterns_without_feature_def_json_preparse",
ifvarclass => and(isvariable("override_data_input_name_patterns"), "!feature_def_json_preparse"),
meta => { "defvar" };
# Default the input name patterns, if we don't find it defined in def
# (from the augments_file).
"input_name_patterns"
slist => { ".*\.cf",".*\.dat",".*\.txt", ".*\.conf", ".*\.mustache",
".*\.sh", ".*\.pl", ".*\.py", ".*\.rb", ".*\.sed", ".*\.awk",
"cf_promises_release_id", ".*\.json", ".*\.yaml",
".*\.ps1" },
comment => "Filename patterns to match when updating the policy
(see update/update_policy.cf)",
handle => "common_def_vars_input_name_patterns_policy_default",
ifvarclass => and(and(not(isvariable("override_data_acl")),
not(isvariable("input_name_patterns"))),
not(isvariable("def.input_name_patterns"))),
meta => { "defvar" };
# define based on data in def (which comes from augments file), if
# present and input_name_patterns is not yet defined.).
"input_name_patterns"
slist => { @(def.input_name_patterns) },
comment => "Filename patterns to match when updating the policy
(see update/update_policy.cf)",
handle => "common_def_vars_input_name_patterns_from_def_with_feature_def_json_preparse",
ifvarclass => and( isvariable("def.input_name_patterns"),
not(isvariable("input_name_patterns"))),
meta => { "defvar" };
# the permissions for your masterfiles, which will propagate to inputs
"masterfiles_perms_mode" string => "0600",
handle => "common_def_vars_masterfiles_perms_mode",
meta => { "defvar" };
"dc_scripts" string => "$(sys.workdir)/httpd/htdocs/api/dc-scripts",
comment => "Directory where design center scripts are located on Enterprise Hub";
"DCWORKFLOW" string => "/opt/cfengine",
comment => "Directory where DC Workflow related things are stored";
"masterfiles_staging" string => "$(DCWORKFLOW)/masterfiles_staging",
comment => "Directory where Mission Portal Design Center
activations are staged for commit to upstream repository";
"local_masterfiles_git" string => "$(DCWORKFLOW)/masterfiles.git",
comment => "Local bare git repository, serves as OOTB upstream repo";
"cf_git" string => "$(sys.workdir)/bin/git",
comment => "Path to git binary installed with CFEngine Enterprise";
"cf_apache_user" string => "cfapache",
comment => "User that CFEngine Enterprise webserver runs as",
handle => "common_def_vars_cf_cfapache_user";
"cf_apache_group" string => "cfapache",
comment => "Group that CFEngine Enterprise webserver runs as",
handle => "common_def_vars_cf_cfapache_group";
# Hubs get the explicit path because they perform local copies (especially
# during bootstrap) when cf-serverd may not be available. Else we use the
# "masterfiles" shortcut.
"mpf_update_policy_master_location" -> { "ENT-3692" }
string => ifelse( "am_policy_hub", "/var/cfengine/masterfiles",
"masterfiles");
# Here we allow the masterfiles location to be overridden via augments. If
# augments overides the value, use that. Note: Since hubs do not perform
# copies to themselves over cf-serverd, this should be a fully qualified
# path or special considerations must be made for hub bootstrapping.
"mpf_update_policy_master_location" -> { "ENT-3692" }
comment => "Directory where clients should get policy from.",
string => "$(def.mpf_update_policy_master_location)",
if => isvariable( "def.mpf_update_policy_master_location" );
# enable_cfengine_enterprise_hub_ha is defined below
# Disabled by default
enable_cfengine_enterprise_hub_ha::
"standby_servers" slist => filter("$(sys.policy_hub)", "ha_def.ips", false, true, 10);
"policy_servers" slist => { "$(sys.policy_hub)", "@(standby_servers)" };
!enable_cfengine_enterprise_hub_ha::
"policy_servers" slist => {"$(sys.policy_hub)"};
any::
"control_common_ignore_missing_bundles" -> { "CFE-2773" }
string => ifelse( strcmp( $(control_common_ignore_missing_bundles), "true" ),
"true",
"false");
"control_common_ignore_missing_inputs" -> { "CFE-2773" }
string => ifelse( strcmp( $(control_common_ignore_missing_inputs), "true" ),
"true",
"false");
classes:
# Trigger binary upgrade from policy hub
# Disable by default
"trigger_upgrade" or => {
"!any",
};
# Update masterfiles from Git
# Enabled for enterprise users by default
# you can also request it from the command line with
# -Dcfengine_internal_masterfiles_update
# NOTE THAT ENABLING THIS BY DEFAULT *WILL* OVERWRITE THE HUB'S $(sys.workdir)/masterfiles
#"cfengine_internal_masterfiles_update" expression => "enterprise.!(cfengine_3_4|cfengine_3_5)";
"cfengine_internal_masterfiles_update" expression => "!any";
# Transfer policies and binaries with encryption
# you can also request it from the command line with
# -Dcfengine_internal_encrypt_transfers
# NOTE THAT THIS CLASS ALSO NEEDS TO BE SET IN def.cf
"cfengine_internal_encrypt_transfers" expression => "!any";
# Purge policies that don't exist on the server side.
# you can also request it from the command line with
# -Dcfengine_internal_purge_policies
# NOTE THAT THIS CLASS ALSO NEEDS TO BE SET IN def.cf
"cfengine_internal_purge_policies" expression => "!any";
# Preserve permissions of the policy server's masterfiles.
# you can also request it from the command line with
# -Dcfengine_internal_preserve_permissions
# NOTE THAT THIS CLASS ALSO NEEDS TO BE SET IN def.cf
"cfengine_internal_preserve_permissions" expression => "!any";
# Disable checking of cf_promises_validated before updating clients.
# Disabling checking of cf_promises_validated ensures that remote agents
# will **always** scan all of masterfiles for any changes and update
# accordingly. This is not recommended as it both removes a safety
# mechanism that checks for policy to be valid before allowing clients to
# download updates, and the increased load on the hub will affect
# scalability. Consider using time_based, select_class, or dist based classes
# instead of any to retain some of the benefits. **DISABLE WITH CAUTION**
"cfengine_internal_disable_cf_promises_validated"
expression => "!any",
comment => "When cf_promises_validated is disabled remote agents will
always scan all of masterfiles for changes. Disabling this
is not recommended as it will increase the load on the policy
server and increases the possibility for remote agents to
receive broken policy.";
# Enable CFEngine Enterprise HA Policy
"enable_cfengine_enterprise_hub_ha" expression => "!any";
#"enable_cfengine_enterprise_hub_ha" expression => "enterprise_edition";
# Enable failover to node which is outside cluster
#"failover_to_replication_node_enabled" expression => "enterprise_edition";
any::
# TODO Remove from MPF after 3.12 EOL
"cfredis_in_enterprise" -> { "ENT-2797" }
or => { "cfengine_3_7", "cfengine_3_8", "cfengine_3_9", "cfengine_3_10", "cfengine_3_11" };
"cfconsumer_in_enterprise" -> { "ENT-2797" }
or => { "cfengine_3_7", "cfengine_3_8", "cfengine_3_9", "cfengine_3_10", "cfengine_3_11" };
reports:
DEBUG|DEBUG_update_def::
"DEBUG: $(this.bundle)";
"$(const.t) override request $(override_vars) to '$(override_data_s_$(override_vars))'; new value '$($(override_vars))'"
ifvarclass => isvariable("override_data_$(override_vars)");
"$(const.t) defined class role/byname $(extra_roles[byname][$(roles_byname_keys)]) because of classmatch('$(roles_byname_keys)')"
ifvarclass => "$(extra_roles[byname][$(roles_byname_keys)])";
"$(const.t) defined class role/byrole $(roles_byrole_keys) because of classmatch('$(extra_roles[byrole][$(roles_byrole_keys)])')"
ifvarclass => "$(roles_byrole_keys)";
"$(const.t) $(defvars) = $($(defvars))";
"DEBUG $(this.bundle): Agent parsed augments_file"
ifvarclass => "have_augments_file.feature_def_json_preparse";
"DEBUG $(this.bundle): Policy parsed augments_file"
ifvarclass => "have_augments_file.!feature_def_json_preparse";
"DEBUG $(this.bundle): input_name_pattern = '$(input_name_patterns)'";
}
controls/def.cf
This is where most common variables and classes are defined. Note its variable scope can be augmented with def.json
.
common bodies
def
Prototype: def
Implementation:
bundle common def
{
classes:
"_workaround_CFE_2333" -> { "https://tracker.mender.io/browse/CFE-2333" }
or => { "cfengine_3_7_3", "cfengine_3_8_1", "cfengine_3_8_2" };
# If the augments_file is parsed from C then we do not need ot do this work
# from policy
!(feature_def_json_preparse)|(_workaround_CFE_2333)::
"have_augments_file" expression => fileexists($(augments_file)), scope => "bundle";
"have_augments_classes" expression => isvariable("augments[classes]"), scope => "bundle";
"have_augments_inputs" expression => isvariable("augments[inputs]"), scope => "bundle";
have_augments_classes.!(feature_def_json_preparse)|(_workaround_CFE_2333)::
"$(augments_classes_data_keys)"
expression => classmatch("$(augments[classes][$(augments_classes_data_keys)])"),
meta => { "augments_class", "derived_from=$(augments_file)" };
vars:
!(feature_def_json_preparse)|(_workaround_CFE_2333)::
"augments_file" string => "$(this.promise_dirname)/../../def.json";
"defvars" slist => variablesmatching("default:def\..*", "defvar");
have_augments_file.!feature_def_json_preparse|(_workaround_CFE_2333)::
"augments" data => readjson($(augments_file), 100k), ifvarclass => "have_augments_file";
"augments_inputs" slist => getvalues("augments[inputs]");
"override_vars" slist => getindices("augments[vars]");
"override_data_$(override_vars)" data => mergedata("augments[vars][$(override_vars)]");
"override_data_s_$(override_vars)" string => format("%S", "override_data_$(override_vars)");
any::
"augments_inputs"
slist => {},
ifvarclass => not( isvariable( "augments_inputs" ) ),
comment => "It's important that we define this list, even if it's empty
or we get errors about the list being unresolved.";
have_augments_classes.!(feature_def_json_preparse)|(_workaround_CFE_2333)::
"augments_classes_data" data => mergedata("augments[classes]");
"augments_classes_data_keys" slist => getindices("augments_classes_data");
any::
# Begin change
# Your domain name, for use in access control
# Note: this default may be inaccurate!
"domain"
string => "$(sys.domain)",
comment => "Define a global domain for all hosts",
handle => "common_def_vars_domain",
ifvarclass => not(isvariable("domain"));
# Mail settings used by body executor control found in controls/cf_execd.cf
"mailto"
string => "root@$(def.domain)",
ifvarclass => not(isvariable("mailto"));
"mailfrom"
string => "root@$(sys.uqhost).$(def.domain)",
ifvarclass => not(isvariable("mailfrom"));
"smtpserver"
string => "localhost",
ifvarclass => not(isvariable("smtpserver"));
# List here the IP masks that we grant access to on the server
# Only define here if we are not capable of parsing augments from C
"acl"
slist => getvalues("override_data_acl"),
comment => "JSON-sourced: Define an acl for the machines to be granted accesses",
handle => "common_def_json_vars_acl",
ifvarclass => and(isvariable("override_data_acl"), "!feature_def_json_preparse"),
meta => { "defvar" };
"acl"
slist => {
# Allow everything in my own domain.
# Note that this:
# 1. requires def.domain to be correctly set
# 2. will cause a DNS lookup for every access
# ".*$(def.domain)",
# Assume /16 LAN clients to start with
"$(sys.policy_hub)/16",
# Uncomment below if HA is used
#"@(def.policy_servers)"
# "2001:700:700:3.*",
# "217.77.34.18",
# "217.77.34.19",
},
comment => "Define an acl for the machines to be granted accesses",
handle => "common_def_vars_acl",
ifvarclass => and(not(isvariable("override_data_acl")), not(isvariable("acl"))),
meta => { "defvar" };
# Out of the hosts in allowconnects, trust new keys only from the
# following ones. This is open by default for bootstrapping.
# Only define here if we are not capable of parsing augments from C
"trustkeysfrom"
slist => getvalues("override_data_trustkeysfrom"),
comment => "JSON-sourced: define from which machines keys can be trusted",
ifvarclass => and(isvariable("override_data_trustkeysfrom"), "!feature_def_json_preparse"),
meta => { "defvar" };
"trustkeysfrom"
slist => {
# COMMENT THE NEXT LINE OUT AFTER ALL MACHINES HAVE BEEN BOOTSTRAPPED.
"0.0.0.0/0", # allow any IP
},
comment => "Define from which machines keys can be trusted",
ifvarclass => and(not(isvariable("override_data_trustkeysfrom")),
not(isvariable("trustkeysfrom"))),
meta => { "defvar" };
## List of the hosts not using the latest protocol that we'll accept connections from
## (absence of this option or empty list means allow none)
"control_server_allowlegacyconnects"
slist => {},
ifvarclass => not( isvariable( "control_server_allowlegacyconnects" ) );
# Users authorized to request executions via cf-runagent
"control_server_allowusers_non_policy_server"
slist => { "root" },
ifvarclass => not( isvariable( "control_server_allowusers_non_policy_server" ) );
"control_server_allowusers_policy_server"
slist => {},
ifvarclass => not( isvariable( "control_server_allowusers_policy_server" ) );
# Executor Controls
## Default splaytime to 4 unless it's already defined (via augments)
"control_executor_splaytime"
string => "4",
if => not( isvariable( "control_executor_splaytime" ) ),
comment => "Splaytime controls the number of minutes hosts execution
should be splayed over. This value should be less than the
number of minutes between scheduled executions";
## Default agent_expireafter to 120 unless it's already defined (via augments)
"control_executor_agent_expireafter" -> { "ENT-4208" }
string => "120",
if => not( isvariable( "control_executor_agent_expireafter" ) ),
comment => "This controls the number of minutes after no data has been
recieved by cf-execd from a cf-agent process before that
cf-agent process is killed.";
## Default schedule unless it's already defined (via augments)
"control_executor_schedule_value"
slist => {
"Min00", "Min05", "Min10", "Min15",
"Min20", "Min25", "Min30", "Min35",
"Min40", "Min45", "Min50", "Min55",
},
if => not( isvariable( control_executor_schedule) ),
comment => "This variable defines the list of classes that should
trigger exec_command if any of them are defined.";
# schedule cant use a data structure directly, so we must use an
# intermediary variable to convert it to list
"control_executor_schedule_value"
slist => getvalues(control_executor_schedule),
if => not( isvariable( control_executor_schedule_value) ),
comment => "This variable defines the list of classes that should
trigger exec_command if any of them are defined.";
# Agent Controls
"control_agent_abortclasses" -> { "ENT-4823" }
slist => { },
comment => "The control body has a variable, so a valid list must be defined or the agent will error",
if => not( isvariable( $(this.promiser) ));
"control_agent_abortbundleclasses" -> { "ENT-4823" }
slist => { "abortbundle" },
comment => "The control body has a variable, so a valid list must be defined or the agent will error",
if => not( isvariable( $(this.promiser) ));
"control_agent_default_repository"
string => ifelse( isvariable( "control_agent_default_repository"),
$(control_agent_default_repository),
"$(sys.workdir)/backups"),
if => "mpf_control_agent_default_repository";
"control_agent_maxconnections"
int => "30",
ifvarclass => not( isvariable( "control_agent_maxconnections" ) );
# Because in some versions of cfengine bundlesequence in body common
# control does not support does not support iteration over data containers
# we must first pick out the bundles into a shallow container that we can
# then get a regular list from using getvalues().
"tbse" data => mergedata( "def.control_common_bundlesequence_end" );
# Since we have @(def.bundlesequence_end) in body common control
# bundlesequence we must have a list variable defined. It can be empty, but it
# must be defined. If it is not defined the agent will error complaining
# that '@(def.bundlesequence_end) is not a defined bundle.
# As noted in CFE-2460 getvalues behaviour varies between versions. 3.7.x
# getvalues will return an empty list when run on a non existant data
# container. On 3.9.1 it does not return an empty list.
# So we initialize it as an empty list first to be safe.
"bundlesequence_end" slist => {};
"bundlesequence_end" slist => getvalues( tbse );
"control_common_ignore_missing_bundles" -> { "CFE-2773" }
string => ifelse( strcmp( $(control_common_ignore_missing_bundles), "true" ),
"true",
"false");
"control_common_ignore_missing_inputs" -> { "CFE-2773" }
string => ifelse( strcmp( $(control_common_ignore_missing_inputs), "true" ),
"true",
"false");
# Agent controls
@if minimum_version(3.18)
# TODO When 3.18 is the oldest supported LTS, redact this macro and associated protections
"control_agent_files_single_copy" -> { "CFE-3622" }
slist => { },
if => not( isvariable( "control_agent_files_single_copy" ) ),
comment => "Default files_single_copy to an empty list if it is not
defined. It is expected that users can override the default
by setting this value from the augments file (def.json).";
@endif
"control_server_maxconnections"
int => "200",
ifvarclass => not( isvariable( "control_server_maxconnections" ) );
# Package inventory refresh
"package_module_query_installed_ifelapsed" -> { "CFE-2771" }
string => "60", # 1 hour
if => not( isvariable( $(this.promiser) ));
"package_module_query_updates_ifelapsed" -> { "CFE-2771" }
string => "1440", # 1 day
if => not( isvariable( $(this.promiser) ));
debian::
"environment_vars"
handle => "common_def_vars_environment_vars",
comment => "Environment variables of the agent process. The values
of environment variables are inherited by child commands.",
slist => {
"DEBIAN_FRONTEND=noninteractive",
# "APT_LISTBUGS_FRONTEND=none",
# "APT_LISTCHANGES_FRONTEND=none",
};
# End change #
any::
"dir_masterfiles" string => translatepath("$(sys.masterdir)"),
comment => "Define masterfiles path",
handle => "common_def_vars_dir_masterfiles";
"dir_reports" string => translatepath("$(sys.workdir)/reports"),
comment => "Define reports path",
handle => "common_def_vars_dir_reports";
"dir_bin" string => translatepath("$(sys.bindir)"),
comment => "Define binary path",
handle => "common_def_vars_dir_bin";
"dir_data"
string => ifelse( isvariable( "def.dir_data"),
$(def.dir_data),
"$(sys.workdir)/data"),
comment => "Define data path",
handle => "common_def_vars_dir_data";
"dir_modules" string => translatepath("$(sys.workdir)/modules"),
comment => "Define modules path",
handle => "common_def_vars_dir_modules";
"dir_plugins" string => translatepath("$(sys.workdir)/plugins"),
comment => "Define plugins path",
handle => "common_def_vars_dir_plugins";
"dir_templates"
string => ifelse( isvariable( "def.dir_templates"),
$(def.dir_templates),
"$(sys.workdir)/templates"),
comment => "Define templates path",
handle => "common_def_vars_dir_templates";
"cf_apache_user" string => "cfapache",
comment => "User that CFEngine Enterprise webserver runs as",
handle => "common_def_vars_cf_cfapache_user";
"cf_apache_group" string => "cfapache",
comment => "Group that CFEngine Enterprise webserver runs as",
handle => "common_def_vars_cf_cfapache_group";
policy_server|am_policy_hub::
# Only hubs serve software updates
"dir_master_software_updates" -> { "ENT-4953" }
string => "$(sys.workdir)/master_software_updates",
handle => "common_def_vars_dir_serve_master_software_updates",
comment => "Path where software updates are served from the policy hub.
This variable is overridable via augments as
vars.dir_master_software_updates. All remote agents request this path
via the master_software_updates shortcut.",
if => not( isvariable( "def.dir_master_software_updates" ));
solaris::
"cf_runagent_shell"
string => "/usr/bin/sh",
comment => "Define path to shell used by cf-runagent",
handle => "common_def_vars_solaris_cf_runagent_shell";
!(windows|solaris)::
"cf_runagent_shell"
string => "/bin/sh",
comment => "Define path to shell used by cf-runagent",
handle => "common_def_vars_cf_runagent_shell";
any::
"base_log_files" slist =>
{
"$(sys.workdir)/cf3.$(sys.uqhost).runlog",
"$(sys.workdir)/promise_summary.log",
};
"enterprise_log_files" slist =>
{
"$(sys.workdir)/cf_notkept.log",
"$(sys.workdir)/cf_repair.log",
"$(sys.workdir)/state/cf_value.log",
"$(sys.workdir)/outputs/dc-scripts.log",
"$(sys.workdir)/state/promise_log.jsonl",
"$(sys.workdir)/state/classes.jsonl",
};
"hub_log_files" slist =>
{
"$(sys.workdir)/httpd/logs/access_log", # Mission Portal
"$(sys.workdir)/httpd/logs/error_log", # Mission Portal
"/var/log/postgresql.log",
};
"max_client_history_size" -> { "cf-hub", "CFEngine Enterprise" }
int => "50M",
unless => isvariable(max_client_history_size),
comment => "The threshold of report diffs which will trigger purging of
diff files.";
enterprise.!am_policy_hub::
# CFEngine's own log files
"cfe_log_files" slist => { @(base_log_files), @(enterprise_log_files) };
enterprise.am_policy_hub::
# CFEngine's own log files
"cfe_log_files" slist => { @(base_log_files), @(enterprise_log_files), @(hub_log_files) };
!enterprise::
# CFEngine's own log files
"cfe_log_files" slist => { @(base_log_files) };
# Directories where logs are rotated and old files need to be purged.
any::
"log_dir[outputs]" string => "$(sys.workdir)/outputs";
"log_dir[reports]" string => "$(sys.workdir)/reports";
# TODO ENT-6845 - move package module logs to $(sys.workdir)/log/something
windows::
"log_dir[package_logs]" string => "$(const.dirsep)cfengine_package_logs";
enterprise.am_policy_hub::
"log_dir[application]" string => "$(sys.workdir)/httpd/htdocs/application/logs";
any::
"cfe_log_dirs" slist => getvalues( log_dir );
# MPF controls
"mpf_log_dir_retention"
string => "30",
if => not( isvariable ( mpf_log_dir_retention ) ),
comment => "The default log file retention in cfe_log_dirs is 30 days
unless it's already been defined (augments).";
"mpf_log_file_retention"
string => "10",
if => not( isvariable( mpf_log_file_retention) ),
comment => "This is the number of rotated versions of mpf log files to
save";
"mpf_log_file_max_size"
string => "1M",
if => not( isvariable( mpf_log_file_max_size) ),
comment => "When individual mpf log files reach this size they should be
rotated so that we don't fill up the disk";
"purge_scheduled_reports_older_than_days" -> { "ENT-4404" }
string => "30",
if => not( isvariable( purge_scheduled_reports_older_than_days ) ),
comment => "This controls the maximum age of artifacts generated by the
asynchronous query API and scheduled reports.";
# Enterprise HA Related configuration
# enable_cfengine_enterprise_hub_ha is defined below
# Disabled by default
enable_cfengine_enterprise_hub_ha::
"standby_servers" slist => filter("$(sys.policy_hub)", "ha_def.ips", false, true, 10);
"policy_servers" slist => { "$(sys.policy_hub)", "@(standby_servers)" };
!enable_cfengine_enterprise_hub_ha::
"policy_servers" slist => {"$(sys.policy_hub)"};
enterprise_edition.policy_server::
"control_hub_exclude_hosts"
slist => { "" },
unless => isvariable(control_hub_exclude_hosts);
"control_hub_hub_schedule"
comment => "By default Enterprise hubs initiate pull collection once every 5 minutes.",
slist => { "Min00", "Min05", "Min10", "Min15", "Min20",
"Min25", "Min30", "Min35", "Min40", "Min45",
"Min50", "Min55" },
unless => isvariable(control_hub_hub_schedule);
"control_hub_port"
comment => "cf-hub performs pull collection on port 5308, unless
overridden by augments",
string => "5308",
unless => isvariable(control_hub_port);
"control_hub_client_history_timeout"
comment => "cf-hub instructs clients to discard reports older than this
many hours to avoid a condition where a build up of reports
causes a client to never be fully collected from",
string => "6",
unless => isvariable(control_hub_port);
"mpf_access_rules_collect_calls_admit_ips"
slist => { @(def.acl) },
unless => isvariable(mpf_access_rules_collect_calls_admit_ips);
enterprise_edition.client_initiated_reporting_enabled::
"control_server_call_collect_interval"
string => "5",
unless => isvariable(control_server_call_collect_interval);
"control_server_collect_window" -> { "ENT-4102" }
string => "30",
unless => isvariable(control_server_collect_window);
enterprise_edition.policy_server::
"default_data_select_host_monitoring_include"
comment => "Most people have monitoring systems, so instead of collecting data people won't use we save the work unless its requested.",
slist => { },
unless => isvariable( default_data_select_host_monitoring_include );
"default_data_select_policy_hub_monitoring_include"
comment => "Collect all the monitoring data from the hub itself. It can be useful in diagnostics.",
slist => { ".*" },
unless => isvariable( default_data_select_policy_hub_monitoring_include );
classes:
"_have_control_agent_files_single_copy" -> { "CFE-3622"}
expression => isvariable( "def.control_agent_files_single_copy" );
"cfengine_recommendations_enabled"
expression => "!cfengine_recommendations_disabled";
### Enable special features policies. Set to "any" to enable.
# Auto-load files in "services/autorun" and run bundles tagged "autorun".
# Disabled by default!
"services_autorun" -> { "jira:CFE-2135" }
comment => "This class enables the automatic parsing running of bundles
tagged with 'autorun'. Evaluation limitations require that
this class is set at the beginning of the agent run, so it
must be defined in the augments file (def.json), or as an
option to the agent with --define or -D. Changing the
expression here will *NOT* work correctly. Setting the class
here will result in an error due to the autorun bundle not
being found.",
expression => "!any";
# Internal CFEngine log files rotation
"cfengine_internal_rotate_logs" expression => "any";
# Enable or disable agent email output (also see mailto, mailfrom and
# smtpserver)
"cfengine_internal_agent_email" expression => "any";
"cfengine_internal_disable_agent_email" expression => "!any";
# Enable or disable external watchdog to ensure cf-execd is running
"cfe_internal_core_watchdog_enabled" expression => "!any";
"cfe_internal_core_watchdog_disabled" expression => "!any";
# Transfer policies and binaries with encryption
# you can also request it from the command line with
# -Dcfengine_internal_encrypt_transfers
# NOTE THAT THIS CLASS ALSO NEEDS TO BE SET IN update.cf
"cfengine_internal_encrypt_transfers" expression => "!any";
# Purge policies that don't exist on the server side.
# you can also request it from the command line with
# -Dcfengine_internal_purge_policies
# NOTE THAT THIS CLASS ALSO NEEDS TO BE SET IN update.cf
"cfengine_internal_purge_policies" expression => "!any";
# Preserve permissions of the policy server's masterfiles.
# you can also request it from the command line with
# -Dcfengine_internal_preserve_permissions
# NOTE THAT THIS CLASS ALSO NEEDS TO BE SET IN update.cf
"cfengine_internal_preserve_permissions" expression => "!any";
# Allow the hub to edit sudoers in order for the Apache user to
# run passwordless sudo cf-runagent. Enable this if you want the
# Mission Portal to be able to troubleshoot failed Design Center
# sketch activations on a host.
"cfengine_internal_sudoers_editing_enable" expression => "!any";
# Class defining which versions of cfengine are (not) supported
# by this policy version.
# Also note that this policy will only be run on enterprise policy_server
"postgresql_maintenance_supported"
expression => "(policy_server.enterprise.!enable_cfengine_enterprise_hub_ha)|(policy_server.enterprise.enable_cfengine_enterprise_hub_ha.hub_active)";
# This class is for PosgreSQL maintenance
# pre-defined to every Sunday at 2 a.m.
# This can be changed later on.
"postgresql_full_maintenance" expression => "postgresql_maintenance_supported.Sunday.Hr02.Min00_05";
# Run vacuum job on database
# pre-defined to every night except Sunday when full cleanup is executed.
"postgresql_vacuum" expression => "postgresql_maintenance_supported.!Sunday.Hr02.Min00_05";
# Enable CFEngine Enterprise HA Policy
"enable_cfengine_enterprise_hub_ha" expression => "!any";
#"enable_cfengine_enterprise_hub_ha" expression => "enterprise_edition";
# Enable failover to node which is outside cluster
#"failover_to_replication_node_enabled" expression => "enterprise_edition";
# Enable cleanup of agent report diffs when they exceed
# `def.max_client_history_size`
"enable_cfe_internal_cleanup_agent_reports" -> { "cf-hub", "CFEngine Enterprise" }
expression => "enterprise_edition",
comment => "If reports are not collected for an extended period of time
the disk may fill up or cause additional collection
issues.";
# Enable paths to POSIX tools instead of native tools when possible.
"mpf_stdlib_use_posix_utils" expression => "any";
# TODO Remove from MPF after 3.12 EOL
"cfredis_in_enterprise" -> { "ENT-2797" }
or => { "cfengine_3_7", "cfengine_3_8", "cfengine_3_9", "cfengine_3_10", "cfengine_3_11" };
"cfconsumer_in_enterprise" -> { "ENT-2797" }
or => { "cfengine_3_7", "cfengine_3_8", "cfengine_3_9", "cfengine_3_10", "cfengine_3_11" };
reports:
DEBUG|DEBUG_def::
"DEBUG: $(this.bundle)";
"$(const.t) def.json was found at $(augments_file)"
ifvarclass => fileexists( $(augments_file) );
"$(const.t) override request $(override_vars) to '$(override_data_s_$(override_vars))'; new value '$($(override_vars))'"
ifvarclass => isvariable("override_data_$(override_vars)");
"$(const.t) defined class '$(augments_classes_data_keys)' because of classmatch('$(augments[classes][$(augments_classes_data_keys)])')"
ifvarclass => "$(augments_classes_data_keys)";
"$(const.t) $(defvars) = $($(defvars))";
"DEBUG $(this.bundle): Agent parsed augments_file"
ifvarclass => "have_augments_file.feature_def_json_preparse";
"DEBUG $(this.bundle): Policy parsed augments_file"
ifvarclass => "have_augments_file.!feature_def_json_preparse";
}
inventory_control
Prototype: inventory_control
Description: Inventory control bundle
This common bundle is for controlling whether some inventory bundles are disabled.
Implementation:
{
vars:
"lldpctl_exec" string => ifelse(fileexists("/usr/sbin/lldpctl"), "/usr/sbin/lldpctl",
fileexists("/usr/local/bin/lldpctl"), "/usr/local/bin/lldpctl",
"/usr/sbin/lldpctl");
"lldpctl_json" string => "$(lldpctl_exec) -f json",
unless => isvariable("def.lldpctl_json");
"lldpctl_json" string => "$(def.lldpctl_json)",
if => isvariable("def.lldpctl_json");
"lsb_exec" string => "/usr/bin/lsb_release";
"mtab" string => "/etc/mtab";
"proc" string => "/proc";
vars:
freebsd::
"dmidecoder" string => "/usr/local/sbin/dmidecode";
!freebsd::
"dmidecoder" string => "/usr/sbin/dmidecode";
classes:
# setting this disables all the inventory modules except package_refresh
"disable_inventory" expression => "!any";
# disable specific inventory modules below
# by default disable the LSB inventory if the general inventory
# is disabled or the binary is missing. Note that the LSB
# binary is typically not very fast.
"disable_inventory_lsb" expression => "disable_inventory";
"disable_inventory_lsb" not => fileexists($(lsb_exec));
# by default disable the dmidecode inventory if the general
# inventory is disabled or the binary does not exist. Note that
# typically this is a very fast binary.
"disable_inventory_dmidecode" expression => "disable_inventory";
"disable_inventory_dmidecode" not => fileexists($(dmidecoder));
# by default disable the LLDP inventory if the general inventory
# is disabled or the binary does not exist. Note that typically
# this is a reasonably fast binary but still may require network
# I/O.
"disable_inventory_LLDP" expression => "disable_inventory";
"disable_inventory_LLDP" not => fileexists($(lldpctl_exec));
# by default run the package inventory refresh every time, even
# if disable_inventory is set
"disable_inventory_package_refresh" expression => "!any";
# by default disable the mtab inventory if the general inventory
# is disabled or $(mtab) is missing. Note that this is very
# fast.
"disable_inventory_mtab" expression => "disable_inventory";
"disable_inventory_mtab" not => fileexists($(mtab));
# by default disable the fstab inventory if the general
# inventory is disabled or $(sys.fstab) is missing. Note that
# this is very fast.
"disable_inventory_fstab" expression => "disable_inventory";
"disable_inventory_fstab" not => fileexists($(sys.fstab));
# by default disable the proc inventory if the general
# inventory is disabled or /proc is missing. Note that
# this is typically fast.
"disable_inventory_proc" expression => "disable_inventory|freebsd";
"disable_inventory_proc" not => isdir($(proc));
# by default don't run the CMDB integration every time, even if
# disable_inventory is not set
"disable_inventory_cmdb" expression => "any";
reports:
verbose_mode.disable_inventory::
"$(this.bundle): All inventory modules disabled";
verbose_mode.!disable_inventory_lsb::
"$(this.bundle): LSB module enabled";
verbose_mode.!disable_inventory_dmidecode::
"$(this.bundle): dmidecode module enabled";
verbose_mode.!disable_inventory_LLDP::
"$(this.bundle): LLDP module enabled";
verbose_mode.!disable_inventory_mtab::
"$(this.bundle): mtab module enabled";
verbose_mode.!disable_inventory_fstab::
"$(this.bundle): fstab module enabled";
verbose_mode.!disable_inventory_proc::
"$(this.bundle): proc module enabled";
verbose_mode.!disable_inventory_package_refresh::
"$(this.bundle): package_refresh module enabled";
verbose_mode.!disable_inventory_cmdb::
"$(this.bundle): CMDB module enabled";
DEBUG|DEBUG_def::
"Executor Schedule: $(def.control_executor_schedule_value)";
}
controls/reports.cf
This is where report settings for CFEngine Enterprise are found. Control which
variables and classes should be collected by central reporting based on tags
that should be included or excluded. It also controls which measuremtnts taken
by cf-monitord
will be collected for central reporting.
server bundles
report_access_rules
Prototype: report_access_rules
Implementation:
bundle server report_access_rules
{
vars:
enterprise::
"query_types" slist => {"delta", "rebase", "full"};
access:
!policy_server.enterprise::
"$(query_types)"
handle => "report_access_grant_$(query_types)_for_hosts",
comment => "Grant $(query_types) reporting query for the hub on the hosts",
resource_type => "query",
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", "::1", @(def.policy_servers) };
}
report_data_select bodies
default_data_select_host
Prototype: default_data_select_host
Description: Data authorized by non policy servers for collection by cf-hub
By convention variables and classes known to be internal, (having no reporting value) should be prefixed with an underscore. By default cf-hub explicitly excludes these variables and classes from collection.
Implementation:
body report_data_select default_data_select_host
{
metatags_include => { "inventory", "report" };
metatags_exclude => { "noreport" };
promise_handle_exclude => { "noreport_.*" };
monitoring_include => { @(def.default_data_select_host_monitoring_include) };
}
default_data_select_policy_hub
Prototype: default_data_select_policy_hub
Description: Data authorized by policy servers for collection by cf-hub
By convention variables and classes known to be internal, (having no reporting value) should be prefixed with an underscore. By default cf-hub explicitly excludes these variables and classes from collection.
Implementation:
body report_data_select default_data_select_policy_hub
{
metatags_include => { "inventory", "report" };
metatags_exclude => { "noreport" };
promise_handle_exclude => { "noreport_.*" };
monitoring_include => { @(def.default_data_select_policy_hub_monitoring_include) };
}
controls/cf_agent.cf
This is where body agent control
is defined. body agent control
is where
various settings related to cf-agent
can be tuned.
agent bodies
control
Prototype: control
Implementation:
body agent control
{
# Global default for time that must elapse before promise will be rechecked.
# Don't keep any promises.
any::
# Minimum time (in minutes) which should have passed since the last time
# the promise was verified before it is checked again.
ifelapsed => "1";
# Do not send IP/name during server connection if address resolution is broken.
# Comment it out if you do NOT have a problem with DNS
skipidentify => "true";
# explicitly not supported (but they should break long before)
abortclasses => { "cfengine_3_3", "cfengine_3_4", @(def.control_agent_abortclasses) };
# The abortbundleclasses slist contains regular expressions that match
# classes which if defined lead to termination of current bundle.
abortbundleclasses => { @(def.control_agent_abortbundleclasses) };
# Maximum number of outgoing connections to a remote cf-serverd.
maxconnections => "$(def.control_agent_maxconnections)";
_have_control_agent_files_single_copy::
# CFE-3622
# File patterns which allow a file to be copied over only a single time
# per agent run.
files_single_copy => { @(def.control_agent_files_single_copy) };
mpf_control_agent_default_repository::
# Location to backup files before they are edited by cfengine
default_repository => "$(def.control_agent_default_repository)";
# Environment variables based on Distro
debian::
environment => { @(def.environment_vars) };
}
controls/cf_runagent.cf
This is where body runagent control
is defined. body runagent control
is where
various settings related to cf-runagent
can be tuned.
See Also: Run agent on multiple hosts sharing a class
runagent bodies
control
Prototype: control
Implementation:
body runagent control
{
# A list of hosts to contact when using cf-runagent
any::
hosts => { "127.0.0.1" };
# , "myhost.example.com:5308", ...
}
controls/cf_execd.cf
This is where body executor control
is defined. body executor control
is where
various settings related to cf-execd
can be tuned.
executor bodies
control
Prototype: control
Implementation:
body executor control
{
any::
splaytime => "$(def.control_executor_splaytime)"; # activity will be spread over this many time slices
agent_expireafter => "$(def.control_executor_agent_expireafter)";
cfengine_internal_agent_email.!cfengine_internal_disable_agent_email::
mailto => "$(def.mailto)";
mailfrom => "$(def.mailfrom)";
smtpserver => "$(def.smtpserver)";
# Filter log entries in emails based on regular expressions.
# mailfilter_include => { ".*include pattern.*" };
# mailfilter_exclude => { ".*exclude pattern.*" };
any::
# Default:
#
# schedule => { "Min00", "Min05", "Min10", "Min15", "Min20",
# "Min25", "Min30", "Min35", "Min40", "Min45",
# "Min50", "Min55" };
schedule => { @(def.control_executor_schedule_value) };
# The full path and command to the executable run by default (overriding builtin).
# cf-twin needs its own safe environment because of the update mechanism
windows::
exec_command => "$(sys.cf_agent) -Dfrom_cfexecd,cf_execd_initiated -f \"$(sys.update_policy_path)\" & $(sys.cf_agent) -Dfrom_cfexecd,cf_execd_initiated";
!windows::
exec_command => "$(sys.cf_agent) -Dfrom_cfexecd,cf_execd_initiated -f \"$(sys.update_policy_path)\" ; $(sys.cf_agent) -Dfrom_cfexecd,cf_execd_initiated";
}
controls/def_inputs.cf
This is where the list of policy files to include as defined from the augments file is included.
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(def.augments_inputs) };
}
cfe_internal/
This directory contains policy related to the internal control and functioning of CFEngine and its various components. This directory provides policeis that function at a higher level than those found in controls. For example, policy related to log rotation can be found here, while settings related to access control rules are organized under controls.
Note: Many of the tuneables specified in these files have been exposed in the
def
bundle for use via the augments
file. It is reccomended that direct
modifications to these files be limited in order to ease policy framework
upgrades. If you are altering one of these files, please consider making a pull
request to expose the tunable.
cfe_internal/CFE_cfengine.cf
common bodies
cfe_internal_management_file_control
Prototype: cfe_internal_management_file_control
Implementation:
bundle common cfe_internal_management_file_control
{
vars:
"inputs" slist => { };
cfengine_recommendations_enabled::
"input[cfengine_recommendations]"
string => "$(this.promise_dirname)/recommendations.cf";
any::
"inputs" slist => getvalues( input );
}
agent bundles
cfe_internal_management
Prototype: cfe_internal_management
Implementation:
bundle agent cfe_internal_management
{
vars:
any::
"policy[cfe_internal_core_main]"
string => "cfe_internal_core_main",
comment => "Activate policies related to basic CFEngine operations";
enterprise_edition::
"policy[cfe_internal_enterprise_main]"
string => "cfe_internal_enterprise_main",
comment => "Activate policies related to CFEngine Enterprise operations";
# TODO: Scope this more tightly to mission portal role
enterprise_edition.policy_server::
"policy[cfe_internal_enterprise_mission_portal]"
string => "Activate policies related to CFEngine Enterprise Mission Portal";
any::
"bundles" slist => getindices(policy);
"recommendation_bundles"
slist => sort( bundlesmatching( "cfengine_recommendations" ), lex);
methods:
#
# CFEngine internals
#
"CFEngine_Internals"
usebundle => "$(bundles)";
"CFEngine Recommendations"
usebundle => $(recommendation_bundles),
if => isvariable( recommendation_bundles );
reports:
DEBUG|DEBUG_cfe_internal_management::
"DEBUG $(this.bundle): Should actuate $(bundles)";
}
file bodies
control
Prototype: control
Implementation:
body file control
{
cfengine_recommendations_enabled::
inputs => { @(cfe_internal_management_file_control.inputs) };
}
cfe_internal/update/
This directory contains internal management polcies related to the default update policy like ensuring inputs is a copy of masterfiles from your policy server and ensuring the CFEngine components are running. Some policies contained in this directory are only aplicable to CFEngine Enterprise and others are used in both Community and Enterprise editions.
cfe_internal/update/update_processes.cf
common bodies
cfe_internal_process_knowledge
Prototype: cfe_internal_process_knowledge
Implementation:
bundle common cfe_internal_process_knowledge
{
vars:
!windows::
"bindir" string => "$(sys.bindir)",
comment => "Use a system variable";
}
agent bundles
cfe_internal_update_processes
Prototype: cfe_internal_update_processes
Description: Determine which cfengine components should be managed, and what their state should be.
By default all the relevant services will run on each host. For example all hosts will run cf-execd, cf-serverd, and cf-monitord. Individual services can be disabled:
If persistent_disable_COMPONENT ( persistent_disable_cf_serverd, persistent_disable_cf_monitord) is defined the service will be disabled.
If the component is found in def.agents_to_be_disabled it will be disabled.
To enable component management on hosts with systemd define the class
mpf_enable_cfengine_systemd_component_management
.
Implementation:
bundle agent cfe_internal_update_processes
{
classes:
"systemd_supervised"
scope => "bundle",
expression => "systemd.!(cfengine_3_7|cfengine_3_8|cfengine_3_9)",
comment => "As of 3.10 the runalerts service is supervised by systemd
when available. Prior to 3.10 the service is supervised
directly by CFEngine policy.";
reports:
inform.systemd_supervised.!mpf_enable_cfengine_systemd_component_management::
"NOTE: You have defined a class to persistently disable a cfengine
component on a systemd managed host, but you have not defined
mpf_enable_cfengine_systemd_component_management in order to enable
management"
if => classmatch( "persistent_disable_cf_.*" );
"NOTE: You have explicitly listed components that should be disabled in def.agents_to_be_disabled.
This host is managed by systemd and requires the class
mpf_enable_cfengine_systemd_component_management in order to enable
active management"
if => some( ".*", @(def.agents_to_be_disabled) );
vars:
any::
# By default the core components are expected to be running in all cases.
"agent[cf_execd]" string => "cf-execd";
"agent[cf_serverd]" string => "cf-serverd";
"agent[cf_monitord]" string => "cf-monitord";
policy_server.enterprise_edition::
"agent[cf_hub]"
string => "cf-hub",
comment => "cf-hub is only relevant on Enterprise hubs";
systemd::
# On systemd hosts the cfengine3 service acts as an umbrella for other
# services.
"agent[cfengine3]"
string => "cfengine3",
comment => "systemd hosts use the cfengine3 service as an umbrella.
systemd_supervised hosts additionally have individual units
for each managed service.";
systemd_supervised.enterprise_edition.policy_server::
# Only enterprise systemd supervised hosts these additional service
# definitions for each component.
"agent[cf_postgres]" string => "cf-postgres";
"agent[cf_runalerts]" string => "cf-runalerts";
"agent[cf_apache]" string => "cf-apache";
cfredis_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"agent[cf_redis_server]" -> { "ENT-2797" }
string => "cf-redis-server";
cfconsumer_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"agent[cf_consumer]" -> { "ENT-2797" }
string => "cf-consumer";
any::
# We get a consolidated list of all agents for the executing host.
"all_agents" slist => getvalues( agent );
# We use def.agents_to_be_disabled if it exists, otherwise we default to
# no agents being disabled.
"agents_to_be_disabled"
comment => "CFE processes that should not be enabled",
handle => "cfe_internal_update_processes_vars_agents_to_be_disabled",
slist => { @(def.agents_to_be_disabled) },
ifvarclass => isvariable( "def.agents_to_be_disabled" );
"agents_to_be_disabled"
comment => "The default agents that should not be enabled.",
handle => "cfe_internal_update_processes_vars_default_agents_to_be_disabled",
slist => { },
ifvarclass => not( isvariable("def.agents_to_be_disabled") );
# An agent is disabled if there is a persistently defined disablement
# class OR if the agent is found in a list of agents to be specifically
# disabled.
"disabled[$(all_agents)]"
string => "$(all_agents)",
ifvarclass => or( canonify( "persistent_disable_$(all_agents)" ),
some( "$(all_agents)", agents_to_be_disabled ));
systemd_supervised.policy_server.enterprise.hub_passive|(ha_replication_only_node.!failover_to_repliacation_node_enabled)::
# We want the enterprise components cf-runalerts, and cf-hub to be
# disabled if running on a passive hub or replication only hub.
"disabled[cf_runalerts]" string => "cf-runalerts";
"disabled[cf_hub]" string => "cf-hub";
any::
# First we get the consolidated list of agents to be disabled.
"agents_to_be_disabled" slist => getvalues( disabled );
# Any agent that is not explicitly disabled should be enabled.
"agents_to_be_enabled" slist => difference( all_agents, agents_to_be_disabled );
methods:
systemd.!systemd_supervised::
# TODO Remove from policy.
# This makes sure the cfengine3 (umbrella) unit is active. It does not
# make any assertions about individual components. Furthermore, since
# commit 6a7fe6b3fa466e55b29eca75cd53ff8b2883ff0e (introduced in 3.14)
# this policy won't be run because systemd_supervised is defined any time
# systemd is defined.
"CFENGINE systemd service"
usebundle => maintain_cfe_systemd,
comment => "Call a bundle to maintain CFEngine with systemd",
handle => "cfe_internal_update_processes_methods_maintain_systemd";
systemd_supervised.mpf_enable_cfengine_systemd_component_management::
"CFEngine systemd Unit Definitions"
usebundle => cfe_internal_systemd_unit_files;
"CFEngine systemd Unit States"
usebundle => cfe_internal_systemd_service_unit_state;
am_policy_hub.enterprise.!systemd_supervised::
"TAKING CARE CFE HUB PROCESSES"
usebundle => maintain_cfe_hub_process,
comment => "Call a bundle to maintian HUB processes",
handle => "cfe_internal_update_processes_methods_maintain_hub";
!windows.!systemd_supervised::
"DISABLING CFE AGENTS"
usebundle => disable_cfengine_agents("$(agents_to_be_disabled)"),
comment => "Call a bundle to disable CFEngine given processes",
handle => "cfe_internal_update_processes_methods_disabling_cfe_agents";
"CHECKING FOR PERSISTENTLY DISABLED CFE AGENTS"
usebundle => disable_cfengine_agents($(all_agents)),
ifvarclass => canonify("persistent_disable_$(all_agents)"),
comment => "Call a bundle to disable CFEngine given processes if persistent_disable_x is set",
handle => "cfe_internal_update_processes_methods_maybe_disabling_cfe_agents";
"ENABLING CFE AGENTS"
usebundle => enable_cfengine_agents("$(agents_to_be_enabled)"),
comment => "Call a bundle to enable CFEngine given processes",
handle => "cfe_internal_update_processes_methods_enabling_cfe_agents";
windows::
"CFENGINE on Windows"
usebundle => maintain_cfe_windows,
comment => "Call a bundle to maintain CFEngine on Windows",
handle => "cfe_internal_update_processes_methods_maintain_windows";
}
maintain_cfe_hub_process
Prototype: maintain_cfe_hub_process
Implementation:
bundle agent maintain_cfe_hub_process
{
vars:
am_policy_hub::
"file_check" string => translatepath("$(cfe_internal_update_policy_cpv.inputs_dir)/promises.cf"),
comment => "Path to a policy file",
handle => "cfe_internal_maintain_cfe_hub_process_vars_file_check";
#
classes:
am_policy_hub::
"files_ok" expression => fileexists("$(file_check)"),
comment => "Check for $(sys.workdir)/inputs/promises.cf",
handle => "cfe_internal_maintain_cfe_hub_process_classes_files_ok";
am_policy_hub.enable_cfengine_enterprise_hub_ha::
"ha_run_hub_process"
or => { "!ha_replication_only_node",
"ha_replication_only_node.failover_to_replication_node_enabled" };
"ha_kill_hub_process"
or => { "ha_replication_only_node.!failover_to_replication_node_enabled" };
#
processes:
am_policy_hub::
"$(cfe_internal_process_knowledge.bindir)/vacuumdb"
restart_class => "no_vacuumdb",
comment => "Monitor vacuumdb process",
handle => "cfe_internal_maintain_cfe_hub_process_processes_check_vacuumdb",
ifvarclass => "nova|enterprise";
am_policy_hub.cfredis_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"$(cfe_internal_process_knowledge.bindir)/redis-server" -> { "ENT-2797" }
restart_class => "start_redis_server",
comment => "Monitor redis-server process",
handle => "cfe_internal_maintain_cfe_hub_process_processes_redis",
ifvarclass => "nova|enterprise";
am_policy_hub.cfconsumer_in_enterprise::
"$(cfe_internal_process_knowledge.bindir)/cf-consumer" -> { "ENT-2797" }
restart_class => "start_cf_consumer",
comment => "Monitor cf-consumer process",
handle => "cfe_internal_maintain_cfe_hub_process_processes_cf_consumer",
ifvarclass => "(nova|enterprise).no_vacuumdb";
am_policy_hub.!enable_cfengine_enterprise_hub_ha::
"$(cfe_internal_process_knowledge.bindir)/postgres"
restart_class => "start_postgres_server",
comment => "Monitor postgres process",
handle => "cfe_internal_maintain_cfe_hub_process_processes_postgres",
ifvarclass => "nova|enterprise";
am_policy_hub.!enable_cfengine_enterprise_hub_ha.files_ok.!windows|ha_run_hub_process::
"cf-hub" restart_class => "start_hub",
comment => "Monitor cf-hub process",
handle => "cfe_internal_maintain_cfe_hub_process_processes_cf_hub",
ifvarclass => and( "(nova|enterprise).no_vacuumdb",
"!persistent_disable_cf_hub" ); # Don't start it if it's persistently disabled
am_policy_hub.ha_kill_hub_process::
"cf-hub" signals => { "term" },
comment => "Terminate cf-hub on backup HA node outside cluster",
handle => "cfe_internal_kill_hub_process_on_inactive_ha_node";
#
files:
"/var/log/postgresql.log"
comment => "Ensure postgres.log file is there with right permissions",
handle => "cfe_internal_maintain_cfe_hub_process_files_create_postgresql_log",
create => "true",
perms => u_mo("0600","cfpostgres");
#
commands:
!windows.am_policy_hub.start_redis_server.cfredis_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"$(cfe_internal_process_knowledge.bindir)/redis-server $(cfe_internal_update_policy_cpv.redis_conf_file)" -> { "ENT-2797" }
contain => u_in_dir("/"),
comment => "Start redis process",
classes => u_kept_successful_command,
handle => "cfe_internal_maintain_cfe_hub_process_commands_start_redis";
!windows.am_policy_hub.!enable_cfengine_enterprise_hub_ha.start_postgres_server::
"$(cfe_internal_process_knowledge.bindir)/pg_ctl -D $(cfe_internal_update_policy_cpv.postgresdb_dir) -l $(cfe_internal_update_policy_cpv.postgresdb_log) start"
contain => u_postgres,
comment => "Start postgres process",
classes => u_kept_successful_command,
handle => "cfe_internal_maintain_cfe_hub_process_commands_start_postgres";
!windows.am_policy_hub.start_cf_consumer.cfconsumer_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"$(cfe_internal_process_knowledge.bindir)/cf-consumer"
comment => "Start cf-consumer process",
classes => u_kept_successful_command,
handle => "cfe_internal_maintain_cfe_hub_process_commands_start_cf-consumer";
!windows.am_policy_hub.start_hub::
"$(sys.cf_hub)"
comment => "Start cf-hub process",
classes => u_kept_successful_command,
handle => "cfe_internal_maintain_cfe_hub_process_commands_start_cf_hub";
}
disable_cfengine_agents
Prototype: disable_cfengine_agents(process)
Arguments:
process
: string, used to set promise attributestring
ofvars
promiser cprocess: string, used to set promise attributeexpression
: string, used to set promise attributeexpression
ofclasses
promiser disable_$(cprocess), used as promiser of typeprocesses
Implementation:
bundle agent disable_cfengine_agents(process)
{
vars:
!windows::
"cprocess" string => canonify("$(process)"),
comment => "Canonify a given process",
handle => "cfe_internal_disable_cfengine_agents_vars_cprocess";
#
classes:
!windows::
"disable_$(cprocess)" expression => strcmp("$(process)","$(process)"),
comment => "Create a class to disable a given process",
handle => "cfe_internal_disable_cfengine_agents_classes_disable_process";
#
processes:
!windows::
"$(cfe_internal_process_knowledge.bindir)/$(process)"
signals => { "term" },
comment => "Terminate $(process)",
handle => "cfe_internal_disable_cfengine_agents_processes_terminate_process",
ifvarclass => "disable_$(cprocess)";
}
enable_cfengine_agents
Prototype: enable_cfengine_agents(process)
Arguments:
process
: string, used to set promise attributestring
ofvars
promiser cprocess ofclasses
promiser enable_$(cprocess), used as promiser of typeprocesses
ofcommands
promiser $(sys.$(cprocess)), used as promiser of typereports
, used as promiser of typereports
, used as promiser of typereports
Implementation:
bundle agent enable_cfengine_agents(process)
{
vars:
!windows::
"cprocess" string => canonify("$(process)"),
comment => "Canonify a given process",
handle => "cfe_internal_enable_cfengine_agents_vars_cprocess";
classes:
!windows::
"enable_$(cprocess)" expression => "!persistent_disable_$(cprocess)",
comment => "Create a class to enable a given process",
handle => "cfe_internal_enable_cfengine_agents_classes_enable_process";
#
processes:
!windows::
"$(cfe_internal_process_knowledge.bindir)/$(process)"
restart_class => "restart_$(cprocess)",
comment => "Create a class to restart a process",
handle => "cfe_internal_enable_cfengine_agents_processes_restart_process",
ifvarclass => "enable_$(cprocess)";
#
commands:
!windows::
"$(sys.$(cprocess))"
comment => "Restart a process",
handle => "cfe_internal_enable_cfengine_agents_commands_restart_process",
classes => u_kept_successful_command,
ifvarclass => and( "restart_$(cprocess)",
isvariable( "sys.$(cprocess)" ) );
reports:
"The process $(process) is persistently disabled. Run with '-Dclear_persistent_disable_$(cprocess)' to re-enable it."
ifvarclass => and( "persistent_disable_$(cprocess)",
isvariable( "sys.$(cprocess)" ));
"The process $(process) has been re-enabled. Run with '-Dset_persistent_disable_$(cprocess)' to disable it persistently again."
ifvarclass => and( "clear_persistent_disable_$(cprocess)",
isvariable( "sys.$(cprocess)" )),
classes => u_clear_always("persistent_disable_$(cprocess)");
"The process $(process) has been disabled persistently. Run with '-Dclear_persistent_disable_$(cprocess)' to re-enable it."
ifvarclass => "set_persistent_disable_$(cprocess)",
classes => u_always_forever("persistent_disable_$(cprocess)");
}
maintain_cfe_windows
Prototype: maintain_cfe_windows
Implementation:
bundle agent maintain_cfe_windows
{
vars:
windows::
"file_check" string => translatepath("$(cfe_internal_update_policy_cpv.inputs_dir)/promises.cf"),
comment => "Path to a policy file",
handle => "cfe_internal_maintain_cfe_windows_vars_file_check";
#
classes:
windows::
"files_ok" expression => fileexists("$(file_check)"),
comment => "Check for /var/cfengine/masterfiles/promises.cf",
handle => "cfe_internal_maintain_cfe_windows_classes_files_ok";
#
processes:
files_ok::
"cf-serverd" restart_class => "start_server",
comment => "Monitor cf-serverd process",
handle => "cfe_internal_maintain_cfe_windows_processes_cf_serverd";
"cf-monitord" restart_class => "start_monitor",
comment => "Monitor cf-monitord process",
handle => "cfe_internal_maintain_cfe_windows_processes_cf_monitord";
#
services:
files_ok.windows::
"CfengineNovaExec"
service_policy => "start",
service_method => u_bootstart,
comment => "Start the executor windows service now and at boot time",
handle => "cfe_internal_maintain_cfe_windows_services_windows_executor";
#
commands:
start_server::
"$(sys.cf_serverd)"
action => u_ifwin_bg,
comment => "Start cf-serverd process",
classes => u_kept_successful_command,
handle => "cfe_internal_maintain_cfe_windows_commands_start_cf_serverd";
start_monitor|restart_monitor::
"$(sys.cf_monitord)"
action => u_ifwin_bg,
comment => "Start cf-monitord process",
classes => u_kept_successful_command,
handle => "cfe_internal_maintain_cfe_windows_commands_start_cf_monitord";
}
maintain_cfe_systemd
Prototype: maintain_cfe_systemd
Implementation:
bundle agent maintain_cfe_systemd
{
classes:
systemd::
"restart_cfe"
not => returnszero("/bin/systemctl -q is-active cfengine3", "noshell"),
comment => "Check running status of CFEngine using systemd",
handle => "cfe_internal_maintain_cfe_systemd_classes_restart_cfe";
commands:
restart_cfe::
"/bin/systemctl -q start cfengine3"
comment => "Start CFEngine using systemd",
handle => "cfe_internal_maintain_cfe_systemd_commands_start_cfe";
}
classes bodies
u_clear_always
Prototype: u_clear_always(theclass)
Arguments:
theclass
Implementation:
body classes u_clear_always(theclass)
{
cancel_kept => { $(theclass) };
cancel_notkept => { $(theclass) };
cancel_repaired => { $(theclass) };
}
u_always_forever
Prototype: u_always_forever(theclass)
Arguments:
theclass
Implementation:
body classes u_always_forever(theclass)
{
promise_kept => { $(theclass) };
promise_repaired => { $(theclass) };
repair_failed => { $(theclass) };
repair_denied => { $(theclass) };
repair_timeout => { $(theclass) };
persist_time => 999999999;
scope => "namespace";
}
cfe_internal/update/systemd_units.cf
agent bundles
cfe_internal_systemd_unit_files
Prototype: cfe_internal_systemd_unit_files
Description: This bundle is responsible for ensuring the systemd units are in place with the proper content and permissions. Any time a unit is repaired systemd is reloaded and the repaired unit(s) are restarted. the cfe_internal_systemd_unit_state bundle is responsible for making sure the service units are in the appropriate state. So a service may be restarted because of a change, and subsequently stopped if the desired state was for it to not be running.
Implementation:
bundle agent cfe_internal_systemd_unit_files
{
vars:
systemd::
"systemctl" string => "/bin/systemctl";
"unit_dir"
string => "/etc/systemd/system",
comment => "This is the directory where the systemd units should be
placed.";
"unit_template_dir"
string => "$(this.promise_dirname)/../../templates",
comment => "This is where the templates for the units exist. In the
templates dir in the root of masterfiles.";
"service_units"
slist => { @(cfe_internal_update_processes.all_agents) },
handle => "systemd_core_units",
comment => "These are the services which should have systemd units on all systems.";
files:
systemd::
"$(unit_dir)/$(service_units).service"
create => "true",
edit_template => "$(unit_template_dir)/$(service_units).service.mustache",
template_method => "mustache",
classes => u_results( "bundle", "cfe_systemd_service_unit_$(service_units)" ),
perms => u_mog("644", "root", "root"),
comment => "We need to make each service units content is correct.";
commands:
systemd::
"$(systemctl)"
args => "daemon-reload",
handle => "cfe_internal_systemd_unit_files_reload_when_changed",
if => classmatch("cfe_systemd_service_unit_.*_repaired"),
comment => "We need to reload the systemd configuration after any unit
is changed in order for systemd to recognize the change.";
"$(systemctl)"
args => "restart $(service_units).service",
handle => "cfe_internal_systemd_unit_restart_when_changed",
if => and(classify("cfe_systemd_service_unit_$(service_units)_repaired"),
returnszero("$(systemctl) --quiet is-active $(service_units)", noshell)),
comment => "We need to restart any units which have been changed in
order for the new configuration to be in effect. But we only
want to restart them if they are currently running.";
}
cfe_internal_systemd_service_unit_state
Prototype: cfe_internal_systemd_service_unit_state
Description: This bundle is responsible for managing the various cfengine components units state. It uses information from cfe_internal_update_processes.agents_to_be_enabled, cfe_internal_update_processes.agents_to_be_disabled to determine which units should be running or not.
Implementation:
bundle agent cfe_internal_systemd_service_unit_state
{
vars:
"enabled" slist => { @(cfe_internal_update_processes.agents_to_be_enabled) };
"disabled"
slist => { @(cfe_internal_update_processes.agents_to_be_disabled) };
methods:
"Disabled Components"
usebundle => u_systemd_services( $(disabled), "stop");
"Enabled Components"
usebundle => u_systemd_services( $(enabled), "start" );
}
cfe_internal/update/cfe_internal_update_from_repository.cf
common bodies
cfe_internal_update_from_repository_file_control
Prototype: cfe_internal_update_from_repository_file_control
Implementation:
bundle common cfe_internal_update_from_repository_file_control
{
vars:
"inputs" slist => { "$(this.promise_dirname)/lib.cf" };
}
agent bundles
cfe_internal_update_from_repository
Prototype: cfe_internal_update_from_repository
Implementation:
bundle agent cfe_internal_update_from_repository
{
methods:
am_policy_hub.cfengine_internal_masterfiles_update::
"Update staged masterfiles from VCS"
usebundle => cfe_internal_masterfiles_stage,
handle => "cfe_internal_update_from_repository_methods_masterfiles_fetch",
comment => "Grab the latest updates from upstream VCS repo before deploying masterfiles";
}
cfe_internal_masterfiles_stage
Prototype: cfe_internal_masterfiles_stage
Implementation:
bundle agent cfe_internal_masterfiles_stage
{
commands:
"$(update_def.dc_scripts)/masterfiles-stage.sh"
classes => u_kept_successful_command_results("bundle", "masterfiles_deploy"),
handle => "masterfiles_update_stage";
reports:
masterfiles_deploy_not_kept::
"Masterfiles deployment failed, for more info see '$(sys.workdir)/outputs/dc-scripts.log'";
(DEBUG|DEBUG_cfe_internal_masterfiles_stage).(masterfiles_deploy_kept|masterfiles_deploy_repaired)::
"DEBUG $(this.bundle): Masterfiles deployed successfully";
}
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(cfe_internal_update_from_repository_file_control.inputs) };
}
cfe_internal/update/cfe_internal_local_git_remote.cf
common bodies
cfe_internal_local_git_remote_file_control
Prototype: cfe_internal_local_git_remote_file_control
Implementation:
bundle common cfe_internal_local_git_remote_file_control
{
vars:
"inputs" slist => { "$(this.promise_dirname)/lib.cf" };
}
agent bundles
cfe_internal_local_git_remote
Prototype: cfe_internal_local_git_remote
Implementation:
bundle agent cfe_internal_local_git_remote
{
files:
"$(update_def.local_masterfiles_git)/."
handle => "cfe_internal_local_git_remote_files_have_local_git_remote_masterfiles_git_perms",
perms => u_mog($(update_def.masterfiles_perms_mode),
$(update_def.cf_apache_user),
$(update_def.cf_apache_group)),
depth_search => u_cfe_internal_recurse("inf"),
comment => "Apache needs the ability to write to this repository in order to
support sketch activation deployments from Mission Portal",
classes => u_if_else("did_perms_local_git_remote_clone",
"failed_perms_local_git_remote_clone");
reports:
inform_mode.did_perms_local_git_remote_clone::
"$(this.bundle): set the permissions of the Git clone $(update_def.local_masterfiles_git)";
inform_mode.failed_perms_local_git_remote_clone::
"$(this.bundle): failed to set the permissions of the Git clone $(update_def.local_masterfiles_git)";
}
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(cfe_internal_local_git_remote_file_control.inputs) };
}
cfe_internal/update/lib.cf
agent bundles
u_systemd_services
Prototype: u_systemd_services(service, state)
Description: Manage standard systemd services
Arguments:
service
ofvars
promiser systemctl ofvars
promiser call_systemctl ofvars
promiser systemd_properties ofvars
promiser systemd_service_info ofclasses
promiser $(state) ofclasses
promiser non_disabling ofclasses
promiser service_enabled ofclasses
promiser service_active ofclasses
promiser service_loaded ofclasses
promiser service_notfound ofclasses
promiser can_stop_service ofclasses
promiser can_start_service ofclasses
promiser can_reload_service ofclasses
promiser request_start ofclasses
promiser request_stop ofclasses
promiser request_reload ofclasses
promiser request_restart ofclasses
promiser action_custom ofclasses
promiser action_start ofclasses
promiser action_stop ofclasses
promiser action_reload ofclasses
promiser action_restart ofclasses
promiser action_enable ofclasses
promiser action_disable, used as promiser of typecommands
, used as promiser of typecommands
, used as promiser of typecommands
, used as promiser of typecommands
, used as promiser of typecommands
, used as promiser of typecommands
, used as promiser of typecommands
, used as promiser of typereports
, used as promiser of typereports
, used as promiser of typereports
state
ofvars
promiser systemctl ofvars
promiser call_systemctl ofvars
promiser systemd_properties ofvars
promiser systemd_service_info, used as promiser of typeclasses
ofclasses
promiser non_disabling ofclasses
promiser service_enabled ofclasses
promiser service_active ofclasses
promiser service_loaded ofclasses
promiser service_notfound ofclasses
promiser can_stop_service ofclasses
promiser can_start_service ofclasses
promiser can_reload_service: string, used to set promise attributeexpression
ofclasses
promiser request_start: string, used to set promise attributeexpression
ofclasses
promiser request_stop: string, used to set promise attributeexpression
ofclasses
promiser request_reload: string, used to set promise attributeexpression
ofclasses
promiser request_restart ofclasses
promiser action_custom ofclasses
promiser action_start ofclasses
promiser action_stop ofclasses
promiser action_reload ofclasses
promiser action_restart ofclasses
promiser action_enable ofclasses
promiser action_disable ofcommands
promiser $(call_systemctl) -q start $(service) ofcommands
promiser $(call_systemctl) -q stop $(service) ofcommands
promiser $(call_systemctl) -q reload $(service) ofcommands
promiser $(call_systemctl) -q restart $(service) ofcommands
promiser $(call_systemctl) -q enable $(service) ofcommands
promiser $(call_systemctl) -q disable $(service), used as promiser of typecommands
, used as promiser of typereports
ofreports
promiser DEBUG $(this.bundle): Service $(service) unit file is not loaded; doing nothing ofreports
promiser DEBUG $(this.bundle): Could not find service: $(service)
Implementation:
bundle agent u_systemd_services(service,state)
{
vars:
# We explicitly guard for systemd to avoid unnecessary agent time in
# pre-eval
systemd::
"systemctl" string => "/bin/systemctl";
"call_systemctl" string => "$(systemctl) --no-ask-password --global --system";
"systemd_properties" string => "-pLoadState,CanStop,UnitFileState,ActiveState,LoadState,CanStart,CanReload";
"systemd_service_info" slist => string_split(execresult("$(call_systemctl) $(systemd_properties) show $(service)", "noshell"), "\n", "10");
classes:
systemd::
# define a class named after the desired state
"$(state)" expression => "any";
"non_disabling" or => { "start", "stop", "restart", "reload" };
# 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
"service_enabled" expression => reglist(@(systemd_service_info), "UnitFileState=enabled");
"service_active" expression => reglist(@(systemd_service_info), "ActiveState=active");
"service_loaded" expression => reglist(@(systemd_service_info), "LoadState=loaded");
"service_notfound" expression => reglist(@(systemd_service_info), "LoadState=not-found");
"can_stop_service" expression => reglist(@(systemd_service_info), "CanStop=yes");
"can_start_service" expression => reglist(@(systemd_service_info), "CanStart=yes");
"can_reload_service" expression => reglist(@(systemd_service_info), "CanReload=yes");
"request_start" expression => strcmp("start", "$(state)");
"request_stop" expression => strcmp("stop", "$(state)");
"request_reload" expression => strcmp("reload", "$(state)");
"request_restart" expression => strcmp("restart", "$(state)");
"action_custom" expression => "!(request_start|request_stop|request_reload|request_restart)";
"action_start" expression => "request_start.!service_active.can_start_service";
"action_stop" expression => "request_stop.service_active.can_stop_service";
"action_reload" expression => "request_reload.service_active.can_reload_service";
"action_restart" or => {
"request_restart",
# 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";
reports:
DEBUG|DEBUG_u_systemd_service::
"DEBUG $(this.bundle): using systemd layer to $(state) $(service)";
"DEBUG $(this.bundle): Service $(service) unit file is not loaded; doing nothing"
ifvarclass => "systemd.!service_loaded";
"DEBUG $(this.bundle): Could not find service: $(service)"
ifvarclass => "systemd.service_notfound";
}
classes bodies
u_results
Prototype: u_results(scope, class_prefix)
Description: Define classes prefixed with class_prefix
and suffixed with
appropriate outcomes: _kept, _repaired, _not_kept, _error, _failed,
_denied, _timeout, _reached
Arguments:
scope
: The scope in which the class should be defined (bundle
ornamespace
)class_prefix
: The prefix for the classes defined
This body can be applied to any promise and sets global
(namespace
) or local (bundle
) classes based on its outcome. For
instance, with class_prefix
set to abc
:
if the promise is to change a file's owner to
nick
and the file was already owned bynick
, the classesabc_reached
andabc_kept
will be set.if the promise is to change a file's owner to
nick
and the file was owned byadam
and the change succeeded, the classesabc_reached
andabc_repaired
will be set.
This body is a simpler, more consistent version of the body
scoped_classes_generic
, which see. The key difference is that
fewer classes are defined, and only for outcomes that we can know.
For example this body does not define "OK/not OK" outcome classes,
since a promise can be both kept and failed at the same time.
It's important to understand that promises may do multiple things, so a promise is not simply "OK" or "not OK." The best way to understand what will happen when your specific promises get this body is to test it in all the possible combinations.
Suffix Notes:
_reached
indicates the promise was tried. Any outcome will result in a class with this suffix being defined._kept
indicates some aspect of the promise was kept_repaired
indicates some aspect of the promise was repaired_not_kept
indicates some aspect of the promise was not kept. error, failed, denied and timeout outcomes will result in a class with this suffix being defined_error
indicates the promise repair encountered an error_failed
indicates the promise failed_denied
indicates the promise repair was denied_timeout
indicates the promise timed out
Example:
bundle agent example
{
commands:
"/bin/true"
classes => results("bundle", "my_class_prefix");
reports:
my_class_prefix_kept::
"My promise was kept";
my_class_prefix_repaired::
"My promise was repaired";
}
See also: scope
, scoped_classes_generic
, classes_generic
Implementation:
body classes u_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" };
}
depth_search bodies
u_cfe_internal_recurse
Prototype: u_cfe_internal_recurse(d)
Arguments:
d
: string, used in the value of attributedepth
Implementation:
body depth_search u_cfe_internal_recurse(d)
{
depth => "$(d)";
xdev => "true";
}
classes bodies
u_kept_successful_command_results
Prototype: u_kept_successful_command_results(scope, class_prefix)
Description: Set command to "kept" instead of "repaired" if it returns 0 and define classes suffixed with the appropriate outcomes.
Arguments:
scope
: The scope in which the class should be defined (bundle
ornamespace
)class_prefix
: The prefix for the classes defined
See also: scope
, scoped_classes_generic
, classes_generic
, results
TODO After cfengine_3_10 is no longer supported inherit from u_results and add
in the return code differences.
Implementation:
body classes u_kept_successful_command_results(scope, class_prefix)
{
kept_returncodes => { "0" };
failed_returncodes => { "1" };
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" };
}
service_method bodies
u_systemd_services
Prototype: u_systemd_services
Implementation:
body service_method u_systemd_services
{
service_autostart_policy => "none";
service_dependence_chain => "ignore";
service_type => "generic";
service_args => "";
}
perms bodies
u_mog
Prototype: u_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 u_mog(mode,user,group)
{
owners => { "$(user)" };
groups => { "$(group)" };
mode => "$(mode)";
}
cfe_internal/update/cfe_internal_dc_workflow.cf
agent bundles
cfe_internal_dc_workflow
Prototype: cfe_internal_dc_workflow
Implementation:
bundle agent cfe_internal_dc_workflow
{
methods:
am_policy_hub.enterprise::
"Local Git Repository"
usebundle => cfe_internal_local_git_remote,
handle => "cfe_internal_dc_workflow_methods_local_git_repo",
comment => "Local git repository for OOTB Enterprise functionality";
am_policy_hub.enterprise.cfengine_internal_masterfiles_update::
"Masterfiles from VCS"
usebundle => cfe_internal_update_from_repository,
handle => "cfe_internal_dc_workflow_methods_masterfiles_from_vcs",
comment => "Update masterfiles from upstream VCS automatically
for best OOTB Enterprise experience";
}
cfe_internal/update/update_policy.cf
agent bundles
cfe_internal_update_policy
Prototype: cfe_internal_update_policy
Description: This bundle is responsible for activating the policy to update inputs.
Implementation:
bundle agent cfe_internal_update_policy
{
classes:
# Define classes if we see a user is requesting a custom policy update bundle
"have_user_specified_update_bundle"
expression => isvariable( "def.mpf_update_policy_bundle" );
# Define classes if we are able to find the specific bundle they requested
# (otherwise we may get an error about undefined bundle)
"have_found_user_specified_update_bundle"
expression => some(".*", "found_matching_user_specified_bundle");
"missing_user_specified_update_bundle"
not => some(".*", "found_matching_user_specified_bundle");
vars:
"default_policy_update_bundle" string => "cfe_internal_update_policy_cpv";
# Look for a bundle that matches what the user wants
"found_matching_user_specified_bundle"
slist => bundlesmatching( "$(def.mpf_update_policy_bundle)" );
methods:
# Use the user specified bundle when it's found
have_found_user_specified_update_bundle::
"User specified policy update bundle"
usebundle => $(found_matching_user_specified_bundle);
# Fall back to stock policy update bundle if we have not found one
# specified by user
!have_found_user_specified_update_bundle::
"Stock policy update"
usebundle => cfe_internal_update_policy_cpv;
reports:
inform_mode|verbose_mode|DEBUG|DEBUG_cfe_internal_update_policy::
# Report a human readable way to understand the policy behavior
"Found user specified update bundle."
if => "have_user_specified_update_bundle";
"User specified update bundle: $(def.mpf_update_policy_bundle)"
if => "have_user_specified_update_bundle";
"User specified update bundle MISSING! Falling back to $(default_policy_update_bundle)."
if => and( "have_user_specified_update_bundle",
"missing_user_specified_update_bundle"
);
}
cfe_internal_setup_python_symlink
Prototype: cfe_internal_setup_python_symlink(symlink_path)
Description: Create the /var/cfengine/bin/python symlink pointing to some installed python (if any)
Arguments:
symlink_path
ofvars
promiser path ofvars
promiser _path_folders ofvars
promiser path_folders ofvars
promiser abs_path_folders ofvars
promiser abs_path_folders ofvars
promiser exact_version_globs ofvars
promiser generic_python_globs ofvars
promiser python_exact[$(exact_version_globs)] ofvars
promiser python_generic[$(generic_python_globs)] ofvars
promiser python_platform_fallback[/usr/libexec/platform-python] ofvars
promiser python_exact_sorted ofvars
promiser pythons ofvars
promiser python, used as promiser of typefiles
, used as promiser of typefiles
Implementation:
bundle agent cfe_internal_setup_python_symlink(symlink_path)
{
vars:
"path" string => getenv("PATH", 1024);
"_path_folders" slist => splitstring("$(path)", ":", 128);
"path_folders" slist => filter("$(sys.bindir)",
@(_path_folders),
false, true, 128);
windows::
"abs_path_folders" -> {"CFE-2309"}
slist => filter("([A-Z]|//):.*", path_folders, "true", "false", 128),
comment => "findfiles() complains about relative directories";
!windows::
"abs_path_folders" -> {"CFE-2309"}
slist => filter("/.*", path_folders, "true", "false", 128),
comment => "findfiles() complains about relative directories";
any::
"exact_version_globs" slist => maplist("$(this)/python[23]", @(abs_path_folders)),
comment => "Looking for Python 2 and/or Python 3 in the $PATH folders";
"generic_python_globs" slist => maplist("$(this)/python", @(abs_path_folders)),
comment => "Looking for the 'python' symlink/executable which can be any
version of Python (usually Python 2 for backwards compatibility)";
"python_exact[$(exact_version_globs)]" slist => findfiles("$(exact_version_globs)");
"python_generic[$(generic_python_globs)]" slist => findfiles("$(generic_python_globs)");
"python_platform_fallback[/usr/libexec/platform-python]" -> { "CFE-3291" }
slist => { "/usr/libexec/platform-python" };
"python_exact_sorted" slist => reverse(sort(getvalues(@(python_exact)), "lex")),
comment => "Prefer higher major versions of Python";
"pythons" slist => getvalues(mergedata(@(python_exact_sorted),
getvalues(@(python_generic)),
getvalues(@(python_platform_fallback)))),
comment => "Prefer exact versions over unknown";
"python" string => nth(@(pythons), 0),
if => isgreaterthan(length(@(pythons)), 0),
comment => "Taking the first item from the list (sorted by preference)";
files:
"$(symlink_path)"
delete => u_tidy,
if => not(isvariable("python"));
"$(symlink_path)"
link_from => u_ln_s("$(python)"),
move_obstructions => "true",
if => isvariable("python");
}
cfe_internal_update_policy_cpv
Prototype: cfe_internal_update_policy_cpv
Description: Update inputs from masterfiles when cf_promises_validated changes
Implementation:
bundle agent cfe_internal_update_policy_cpv
{
vars:
"inputs_dir" string => translatepath("$(sys.inputdir)"),
comment => "Directory containing CFEngine policies",
handle => "cfe_internal_update_policy_vars_inputs_dir";
"master_location" -> { "ENT-3692" }
string => "$(update_def.mpf_update_policy_master_location)",
comment => "The path to request updates from the policy server.",
handle => "cfe_internal_update_policy_vars_master_location";
windows::
"modules_dir" string => "/var/cfengine/modules", # NB! NOT $(sys.workdir) on Windows !
comment => "Directory containing CFEngine modules",
handle => "cfe_internal_update_policy_vars_modules_dir_windows";
"plugins_dir" string => "/var/cfengine/plugins", # NB! NOT $(sys.workdir) on Windows !
comment => "Directory containing CFEngine plugins",
handle => "cfe_internal_update_policy_vars_plugins_dir_windows";
!windows::
"modules_dir" string => translatepath("$(sys.workdir)/modules"),
comment => "Directory containing CFEngine modules",
handle => "cfe_internal_update_policy_vars_modules_dir";
"plugins_dir" string => translatepath("$(sys.workdir)/plugins"),
comment => "Directory containing CFEngine plugins",
handle => "cfe_internal_update_policy_vars_plugins_dir";
any::
"file_check" string => translatepath("$(inputs_dir)/promises.cf"),
comment => "Path to a policy file",
handle => "cfe_internal_update_vars_file_check";
"ppkeys_file" string => translatepath("$(sys.workdir)/ppkeys/localhost.pub"),
comment => "Path to public key file",
handle => "cfe_internal_update_policy_vars_ppkeys_file";
"postgresdb_dir" string => "$(sys.workdir)/state/pg/data",
comment => "Directory where Postgres database files will be stored on hub -",
handle => "cfe_internal_update_policy_postgresdb_dir";
"postgresdb_log" string => "/var/log/postgresql.log",
comment => "File where Postgres database files will be logging -",
handle => "cfe_internal_update_policy_postgresdb_log_file";
"python_symlink" string => "$(sys.bindir)/python",
comment => "Symlink to Python we found (if any)",
handle => "cfe_internal_update_policy_python_symlink";
cfredis_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"redis_conf_file" -> { "ENT-2797" }
string => translatepath("$(sys.workdir)/config/redis.conf"),
comment => "Path to Redis configuration file",
handle => "cfe_internal_update_policy_redis_conf_file";
classes:
"validated_updates_ready"
expression => "cfengine_internal_disable_cf_promises_validated",
comment => "If cf_promises_validated is disabled, then updates are
always considered validated.";
any::
"local_files_ok" expression => fileexists("$(file_check)"),
comment => "Check for $(sys.masterdir)/promises.cf",
handle => "cfe_internal_update_classes_files_ok";
# create a global files_ok class
"cfe_internal_trigger" expression => "local_files_ok",
classes => u_if_else("files_ok", "files_ok");
files:
!am_policy_hub:: # policy hub should not alter inputs/ uneccessary
"$(inputs_dir)/cf_promises_validated"
comment => "Check whether a validation stamp is available for a new policy update to reduce the distributed load",
handle => "cfe_internal_update_policy_check_valid_update",
copy_from => u_rcp("$(master_location)/cf_promises_validated", @(update_def.policy_servers)),
action => u_immediate,
classes => u_if_repaired("validated_updates_ready");
!am_policy_hub.!windows::
"$(modules_dir)"
comment => "Always update modules files on client side",
handle => "cfe_internal_update_policy_files_update_modules",
copy_from => u_rcp("$(modules_dir)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
perms => u_m("755"),
action => u_immediate;
"$(plugins_dir)"
comment => "Always update plugins files on client side",
handle => "cfe_internal_update_policy_files_update_plugins",
copy_from => u_rcp("$(plugins_dir)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
perms => u_m("755"),
action => u_immediate;
!am_policy_hub.windows::
"$(sys.workdir)\modules"
comment => "Always update modules files on client side (Windows)",
handle => "cfe_internal_update_policy_files_update_modules_windows",
copy_from => u_rcp("$(modules_dir)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
perms => u_m("755"),
action => u_immediate;
"$(sys.workdir)\plugins"
comment => "Always update plugins files on client side (Windows)",
handle => "cfe_internal_update_policy_files_update_plugins_windows",
copy_from => u_rcp("$(plugins_dir)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
perms => u_m("755"),
action => u_immediate;
am_policy_hub|validated_updates_ready:: # policy hub should always put masterfiles in inputs in order to check new policy
"$(inputs_dir)"
comment => "Copy policy updates from master source on policy server if a new validation was acquired",
handle => "cfe_internal_update_policy_files_inputs_dir",
copy_from => u_rcp("$(master_location)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
file_select => u_input_files,
action => u_immediate,
classes => u_results("bundle", "update_inputs");
update_inputs_not_kept::
"$(inputs_dir)/cf_promises_validated" -> { "CFE-2587" }
delete => u_tidy,
comment => "If there is any problem copying to $(inputs_dir) then purge
the cf_promises_validated file must be purged so that
subsequent agent runs will perform a full scan.";
!policy_server.enable_cfengine_enterprise_hub_ha::
"$(sys.workdir)/policy_server.dat"
comment => "Copy policy_server.dat file from server",
handle => "cfe_internal_update_ha_policy_server",
copy_from => u_rcp("$(sys.workdir)/state/master_hub.dat", @(update_def.policy_servers)),
action => u_immediate,
classes => u_if_repaired("replica_failover"); # not needed ?
am_policy_hub::
"$(master_location)/."
comment => "Make sure masterfiles folder has right file permissions",
handle => "cfe_internal_update_policy_files_sys_workdir_masterfiles",
perms => u_m($(update_def.masterfiles_perms_mode)),
depth_search => u_recurse_basedir("inf"),
action => u_immediate;
methods:
debian|redhat|amazon_linux|suse|sles|opensuse::
# Only needed on distros with Python-based package modules
"setup_python_symlink" usebundle => cfe_internal_setup_python_symlink("$(python_symlink)");
}
perms bodies
u_m
Prototype: u_m(p)
Arguments:
p
: string, used in the value of attributemode
Implementation:
body perms u_m(p)
{
mode => "$(p)";
}
u_mo
Prototype: u_mo(p, o)
Arguments:
p
: string, used in the value of attributemode
o
Implementation:
body perms u_mo(p,o)
{
mode => "$(p)";
owners => {"$(o)"};
}
u_shared_lib_perms
Prototype: u_shared_lib_perms
Implementation:
body perms u_shared_lib_perms
{
!hpux::
mode => "0644";
hpux::
mode => "0755"; # Mantis 1114, Redmine 1179
}
file_select bodies
u_cf3_files
Prototype: u_cf3_files
Implementation:
body file_select u_cf3_files
{
leaf_name => { "cf-.*" };
file_result => "leaf_name";
}
u_input_files
Prototype: u_input_files
Implementation:
body file_select u_input_files
{
leaf_name => { @(update_def.input_name_patterns) };
file_result => "leaf_name";
}
copy_from bodies
u_rcp
Prototype: u_rcp(from, server)
Arguments:
from
: string, used in the value of attributesource
server
Implementation:
body copy_from u_rcp(from,server)
{
source => "$(from)";
compare => "digest";
trustkey => "false";
!am_policy_hub::
servers => { "$(server)" };
!am_policy_hub.sys_policy_hub_port_exists::
portnumber => "$(sys.policy_hub_port)";
cfengine_internal_encrypt_transfers::
encrypt => "true";
cfengine_internal_purge_policies::
purge => "true";
cfengine_internal_preserve_permissions::
preserve => "true";
cfengine_internal_verify_update_transfers::
verify => "true";
}
u_cp
Prototype: u_cp(from)
Arguments:
from
: string, used in the value of attributesource
Implementation:
body copy_from u_cp(from)
{
source => "$(from)";
compare => "digest";
}
u_cp_nobck
Prototype: u_cp_nobck(from)
Arguments:
from
: string, used in the value of attributesource
Implementation:
body copy_from u_cp_nobck(from)
{
source => "$(from)";
compare => "digest";
copy_backup => "false";
}
action bodies
u_immediate
Prototype: u_immediate
Implementation:
body action u_immediate
{
ifelapsed => "0";
}
depth_search bodies
u_recurse
Prototype: u_recurse(d)
Arguments:
d
: string, used in the value of attributedepth
Implementation:
body depth_search u_recurse(d)
{
depth => "$(d)";
exclude_dirs => { "\.svn", "\.git", "git-core" };
}
u_recurse_basedir
Prototype: u_recurse_basedir(d)
Arguments:
d
: string, used in the value of attributedepth
Implementation:
body depth_search u_recurse_basedir(d)
{
include_basedir => "true";
depth => "$(d)";
exclude_dirs => { "\.svn", "\.git", "git-core" };
}
classes bodies
u_if_repaired
Prototype: u_if_repaired(x)
Arguments:
x
Implementation:
body classes u_if_repaired(x)
{
promise_repaired => { "$(x)" };
}
u_if_repaired_then_cancel
Prototype: u_if_repaired_then_cancel(y)
Arguments:
y
Implementation:
body classes u_if_repaired_then_cancel(y)
{
cancel_repaired => { "$(y)" };
}
u_if_else
Prototype: u_if_else(yes, no)
Arguments:
yes
no
Implementation:
body classes u_if_else(yes,no)
{
# promise_kept => { "$(yes)" };
promise_repaired => { "$(yes)" };
repair_failed => { "$(no)" };
repair_denied => { "$(no)" };
repair_timeout => { "$(no)" };
}
contain bodies
u_in_shell
Prototype: u_in_shell
Implementation:
body contain u_in_shell
{
useshell => "true";
}
u_in_shell_and_silent
Prototype: u_in_shell_and_silent
Implementation:
body contain u_in_shell_and_silent
{
useshell => "true";
no_output => "true";
}
u_postgres
Prototype: u_postgres
Implementation:
body contain u_postgres
{
useshell => "useshell";
exec_owner => "cfpostgres";
exec_group => "cfpostgres";
chdir => "/tmp";
no_output => "true";
}
action bodies
u_ifwin_bg
Prototype: u_ifwin_bg
Implementation:
body action u_ifwin_bg
{
windows::
background => "true";
}
service_method bodies
u_bootstart
Prototype: u_bootstart
Implementation:
body service_method u_bootstart
{
service_autostart_policy => "boot_time";
}
contain bodies
u_in_dir
Prototype: u_in_dir(s)
Arguments:
s
: string, used in the value of attributechdir
Implementation:
body contain u_in_dir(s)
{
chdir => "$(s)";
}
u_silent_in_dir
Prototype: u_silent_in_dir(s)
Arguments:
s
: string, used in the value of attributechdir
Implementation:
body contain u_silent_in_dir(s)
{
chdir => "$(s)";
no_output => "true";
}
link_from bodies
u_ln_s
Prototype: u_ln_s(x)
Arguments:
x
: string, used in the value of attributesource
Implementation:
body link_from u_ln_s(x)
{
link_type => "symlink";
source => "$(x)";
when_no_source => "force";
}
delete bodies
u_tidy
Prototype: u_tidy
Implementation:
body delete u_tidy
{
dirlinks => "delete";
rmdirs => "true";
}
cfe_internal/update/update_bins.cf
agent bundles
cfe_internal_update_bins
Prototype: cfe_internal_update_bins
Implementation:
bundle agent cfe_internal_update_bins
{
vars:
enterprise::
"cf_components" slist => { "cf-key", "cf-monitord", "cf-promises",
"cf-runagent", "cf-serverd", "cf-hub", },
comment => "Define cfengine robot agents",
handle => "cfe_internal_update_bins_vars_cf_components";
"master_software_location" string => "/var/cfengine/master_software_updates",
comment => "The Cfengine binary updates directory on the policy host",
handle => "cfe_internal_update_bins_vars_master_software_location";
!hpux::
"package_dir"
string => "$(sys.flavour)_$(sys.arch)",
comment => "The directory within software updates to look for packages";
hpux::
"package_dir"
string => "$(sys.class)_$(sys.arch)",
comment => "The directory within software updates to look for packages.
On HPUX sys.flavor includes versions, so we use sys.class
instead.";
enterprise::
"local_software_dir" string => translatepath("$(sys.workdir)/software_updates/$(package_dir)"),
comment => "Local directory containing binary updates for this host",
handle => "cfe_internal_update_bins_vars_local_software_dir";
"local_update_log_dir" string => translatepath("$(sys.workdir)/software_updates/update_log"),
comment => "Local directory to store update log for this host",
handle => "cfe_internal_update_bins_vars_local_update_log_dir";
!windows.enterprise::
# backup script for cf-upgrade
# the script should have 2 conditions, BACKUP and RESTORE
# BACKUP and RESTORE status is $(const.dollar)1 variable in the script
# see more details at bundle edit_line u_backup_script
"backup_script" string => "/tmp/cf-upgrade_backup.sh";
# a single compressed backup file for cf-upgrade
# this backup_file is passed to backup_script as $(const.dollar)2 variable
# cf-upgrade will extract this file if return signal of upgrade command is not 0
"backup_file" string => "/tmp/cfengine-nova-$(sys.cf_version).tar.gz";
# install script for cf-upgrade
# each distribution has its own way to upgrade a package
# see more details at bundle edit_line u_install_script
"install_script" string => "/tmp/cf-upgrade_install.sh";
(solarisx86|solaris).enterprise::
# to automatically remove or install packages on Solaris
# admin_file is a must to have to avoid pop-up interaction
# see more details at bundle edit_line u_admin_file
"admin_file" string => "/tmp/cf-upgrade_admin_file";
(solarisx86|solaris).enterprise::
"novapkg" string => "CFEcfengine-nova",
comment => "Name convention of Nova package on Solaris",
handle => "cfe_internal_update_bins_vars_novapkg_solaris",
ifvarclass => "nova_edition";
aix.enterprise::
"novapkg" string => "cfengine.cfengine-nova",
comment => "Name convention of Nova package on AIX",
handle => "cfe_internal_update_bins_vars_novapkg_aix",
ifvarclass => "nova_edition";
!(solarisx86|solaris|aix).enterprise::
"novapkg" string => "cfengine-nova",
comment => "Name convention of Nova package for all but not Solaris",
handle => "cfe_internal_update_bins_vars_novapkg_not_solaris_aix",
ifvarclass => "nova_edition";
solaris.!sunos_i86pc.enterprise::
"pkgarch" string => "sparc",
comment => "Name convention of package arch on Solaris",
handle => "cfe_internal_update_bins_vars_pkgarch_solaris";
solarisx86.enterprise::
"pkgarch" string => "i386",
comment => "Name convention of package arch on Solaris",
handle => "cfe_internal_update_bins_vars_pkgarch_solarisx86";
redhat.i686.enterprise::
"pkgarch" string => "i386",
comment => "Name convention of package arch on 32-bit RHEL",
handle => "cfe_internal_update_bins_vars_pkgarch_redhat_32b";
redhat.(x86_64|ppc64).enterprise::
"pkgarch" string => "$(sys.arch)",
comment => "Name convention of package arch on RHEL x86_64 and ppc64",
handle => "cfe_internal_update_bins_vars_pkgarch_redhat_x86_64_and_ppc64";
(freebsd|netbsd|debian|aix).enterprise::
"pkgarch" string => "*",
comment => "Name convention of package arch on *BSD/Debian",
handle => "cfe_internal_update_bins_vars_pkgarch_bsd_debian_aix";
!(solarisx86|solaris|freebsd|netbsd|debian|aix|redhat).enterprise::
"pkgarch" string => "$(sys.arch)",
comment => "Name convention of package arch for other system except Solaris and *BSD",
handle => "cfe_internal_update_bins_vars_pkgarch_not_solaris_bsd_debian_aix_redhat_32b";
#
classes:
"have_software_dir" expression => fileexists($(local_software_dir));
#
packages:
!am_policy_hub.linux.enterprise.trigger_upgrade.!bootstrap_mode::
"$(novapkg)"
comment => "Update Nova package to a newer version (package is there)",
handle => "cfe_internal_update_bins_packages_nova_update_linux_pkg_there",
package_policy => "update",
package_select => "==", # picks the newest Nova available
package_architectures => { "$(pkgarch)" },
package_version => "$(update_def.current_version)-1",
package_method => u_generic( "$(local_software_dir)" ),
ifvarclass => "nova_edition.have_software_dir",
classes => u_if_else("bin_update_success", "bin_update_fail");
!am_policy_hub.(solaris|solarisx86).enterprise.trigger_upgrade.!bootstrap_mode::
"$(novapkg)"
comment => "Update Nova package to a newer version (package is there)",
handle => "cfe_internal_update_bins_packages_nova_update_solaris_pkg_there",
package_policy => "update",
package_select => "==", # picks the newest Nova available
package_architectures => { "$(pkgarch)" },
package_version => "$(update_def.current_version)",
package_method => u_generic( "$(local_software_dir)" ),
ifvarclass => "nova_edition.have_software_dir",
classes => u_if_else("bin_update_success", "bin_update_fail");
!am_policy_hub.windows.enterprise.trigger_upgrade.!bootstrap_mode::
"$(novapkg)"
comment => "Update Nova package to a newer version (package is there)",
handle => "cfe_internal_update_bins_packages_nova_update_windows_only_pkg_there",
package_policy => "update",
package_select => "==", # picks the newest Nova available
package_architectures => { "$(pkgarch)" },
package_version => "$(update_def.current_version)",
package_method => u_generic( "$(local_software_dir)" ),
ifvarclass => "nova_edition.have_software_dir",
classes => u_if_else("bin_update_success", "bin_update_fail");
!am_policy_hub.aix.enterprise.trigger_upgrade.!bootstrap_mode::
"$(novapkg)"
comment => "Update Nova package to a newer version (package is there)",
handle => "cfe_internal_update_bins_packages_nova_update_aix_only_pkg_there",
package_policy => "update",
package_select => "==", # picks the newest Nova available
package_architectures => { "$(pkgarch)" },
package_version => "$(update_def.current_version).0",
package_method => u_generic( "$(local_software_dir)" ),
ifvarclass => "nova_edition.have_software_dir",
classes => u_if_else("bin_update_success", "bin_update_fail");
#
files:
enterprise::
"$(local_update_log_dir)/$(sys.nova_version)_is_running"
comment => "Create an empty file about a version that is running",
handle => "cfe_internal_update_bins_files_version_is_running",
create => "true";
am_policy_hub.enterprise::
"$(master_software_location)/cf-upgrade/linux.i386/."
comment => "Prepare a directory for cf-upgrade",
handle => "cfe_internal_update_bins_files_linux_i386",
create => "true";
"$(master_software_location)/cf-upgrade/linux.x86_64/."
comment => "Prepare a directory for cf-upgrade",
handle => "cfe_internal_update_bins_files_linux_x86_64",
create => "true";
!am_policy_hub.enterprise.trigger_upgrade::
"$(admin_file)"
comment => "Create solaris admin_file to automate remove and install packages",
handle => "cfe_internal_update_bins_files_solaris_admin_file",
create => "true",
edit_defaults => u_empty_no_backup,
edit_line => u_admin_file,
perms => u_m("0644"),
ifvarclass => "solarisx86|solaris";
"$(backup_script)"
comment => "Create a backup script for cf-upgrade",
handle => "cfe_internal_update_bins_files_backup_script",
create => "true",
ifvarclass => "!windows",
edit_defaults => u_empty_no_backup,
edit_line => u_backup_script,
perms => u_m("0755");
"$(install_script)"
comment => "Create an install script for cf-upgrade",
handle => "cfe_internal_update_bins_files_install_script",
create => "true",
ifvarclass => "!windows",
edit_defaults => u_empty_no_backup,
edit_line => u_install_script,
perms => u_m("0755");
"$(local_software_dir)/."
create => "true",
handle => "cfe_internal_update_bins_files_local_software_dir_presence",
comment => "Ensure the local software directory exists for new binaries
to be downloaded to";
"$(local_software_dir)"
comment => "Copy binary updates from master source on policy server",
handle => "cfe_internal_update_bins_files_pkg_copy",
copy_from => u_pcp("$(master_software_location)/$(package_dir)", @(update_def.policy_servers)),
depth_search => u_recurse("1"), # Nova updates should be in root dir
action => u_immediate,
classes => u_if_repaired("bin_newpkg");
bin_update_success.enterprise.trigger_upgrade::
"$(local_update_log_dir)/upgraded_binary_from_$(sys.nova_version)"
comment => "Create an empty file after successfully upgrade the binary",
handle => "cfe_internal_update_bins_files_update_from_log",
create => "true";
reports:
DEBUG|DEBUG_cfe_internal_update_bins::
"DEBUG $(this.bundle): Evaluating binaries for upgrade using internal update mechanism"
ifvarclass => "trigger_upgrade";
}
edit_line bundles
u_admin_file
Prototype: u_admin_file
Implementation:
bundle edit_line u_admin_file
{
insert_lines:
sunos_5_8::
"mail=
instance=unique
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=nocheck
setuid=nocheck
conflict=nocheck
action=nocheck
basedir=default";
solaris.!sunos_5_8::
"mail=
instance=overwrite
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";
}
u_backup_script
Prototype: u_backup_script
Implementation:
bundle edit_line u_backup_script
{
insert_lines:
linux::
"#!/bin/sh
if [ $(const.dollar)1 = \"BACKUP\" ]; then
tar cfzS $(const.dollar)2 $(sys.workdir) > /dev/null
fi
if [ $(const.dollar)1 = \"RESTORE\" ]; then
tar xfz $(const.dollar)2
fi";
solarisx86|solaris::
"#!/bin/sh
if [ $(const.dollar)1 = \"BACKUP\" ]; then
tar cf $(const.dollar)2 $(sys.workdir); gzip $(const.dollar)2
fi
if [ $(const.dollar)1 = \"RESTORE\" ]; then
gunzip $(const.dollar)2.gz; tar xf $(const.dollar)2
fi";
}
u_install_script
Prototype: u_install_script
Implementation:
bundle edit_line u_install_script
{
insert_lines:
redhat|suse|sles::
"#!/bin/sh
/bin/rpm -U $(const.dollar)1";
debian::
"#!/bin/sh
/usr/bin/dpkg --force-confdef --force-confnew --install $(const.dollar)1 > /dev/null";
solarisx86|solaris::
"#!/bin/sh
pkgname=`pkginfo -d $(const.dollar)1 | awk '{print $(const.dollar)2}'`
/usr/sbin/pkgrm -n -a $(cfe_internal_update_bins.admin_file) $pkgname
/usr/sbin/pkgadd -n -a $(cfe_internal_update_bins.admin_file) -d $(const.dollar)1 all
$(sys.workdir)/bin/cf-execd || true
exit 0";
}
common bodies
u_common_knowledge
Prototype: u_common_knowledge
Description: standalone common packages knowledge bundle
This common bundle defines general things about platforms.
Implementation:
bundle common u_common_knowledge
{
vars:
"list_update_ifelapsed_now" string => "10080";
}
edit_defaults bodies
u_empty_no_backup
Prototype: u_empty_no_backup
Implementation:
body edit_defaults u_empty_no_backup
{
empty_file_before_editing => "true";
edit_backup => "false";
}
copy_from bodies
u_pcp
Prototype: u_pcp(from, server)
Arguments:
from
: string, used in the value of attributesource
server
Implementation:
body copy_from u_pcp(from,server)
{
source => "$(from)";
compare => "digest";
trustkey => "false";
purge => "true";
!am_policy_hub::
servers => { "$(server)" };
cfengine_internal_encrypt_transfers::
encrypt => "true";
}
package_method bodies
u_generic
Prototype: u_generic(repo)
Arguments:
repo
: string, used in the value of attributepackage_add_command
: string, used in the value of attributepackage_add_command
Implementation:
body package_method u_generic(repo)
{
debian::
package_changes => "individual";
package_list_command => "/usr/bin/dpkg -l";
# package_list_update_command => "/usr/bin/apt-get update";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "ii\s+([^\s:]+).*";
# package_list_version_regex => "ii\s+[^\s]+\s+([^\s]+).*";
package_list_version_regex => "ii\s+[^\s]+\s+(\d+\.\d+((\.|-)\d+)+).*";
package_installed_regex => ".*"; # all reported are installed
package_file_repositories => { "$(repo)" };
package_version_equal_command => "/usr/bin/dpkg --compare-versions '$(v1)' eq '$(v2)'";
package_version_less_command => "/usr/bin/dpkg --compare-versions '$(v1)' lt '$(v2)'";
debian.x86_64::
package_name_convention => "$(name)_$(version)_amd64.deb";
debian.i686::
package_name_convention => "$(name)_$(version)_i386.deb";
debian::
package_add_command => "/usr/bin/dpkg --force-confdef --force-confnew --install";
package_delete_command => "/usr/bin/dpkg --purge";
debian::
package_update_command => "$(sys.workdir)/bin/cf-upgrade -b $(cfe_internal_update_bins.backup_script) -s $(cfe_internal_update_bins.backup_file) -i $(cfe_internal_update_bins.install_script)";
redhat|SuSE|suse|sles::
package_changes => "individual";
package_list_command => "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\"";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "[^|]+\|[^|]+\|\s+([^\s|]+).*";
package_list_version_regex => "[^|]+\|[^|]+\|[^|]+\|\s+([^\s|]+).*";
package_list_arch_regex => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*";
package_installed_regex => "i.*";
package_file_repositories => { "$(repo)" };
package_name_convention => "$(name)-$(version).$(arch).rpm";
package_add_command => "/bin/rpm -ivh ";
package_delete_command => "/bin/rpm -e --nodeps";
package_verify_command => "/bin/rpm -V";
package_noverify_regex => ".*[^\s].*";
package_version_less_command => "$(sys.bindir)/rpmvercmp '$(v1)' lt '$(v2)'";
package_version_equal_command => "$(sys.bindir)/rpmvercmp '$(v1)' eq '$(v2)'";
(redhat|SuSE|suse|sles)::
package_update_command => "$(sys.workdir)/bin/cf-upgrade -b $(cfe_internal_update_bins.backup_script) -s $(cfe_internal_update_bins.backup_file) -i $(cfe_internal_update_bins.install_script)";
redhat.!redhat_4::
package_list_update_command => "/usr/bin/yum --quiet check-update";
redhat_4::
package_list_update_command => "/usr/bin/yum check-update";
SuSE|suse|sles::
package_list_update_command => "/usr/bin/zypper list-updates";
windows::
package_changes => "individual";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_file_repositories => { "$(repo)" };
package_installed_regex => ".*";
package_name_convention => "$(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";
freebsd::
package_changes => "individual";
package_list_command => "/usr/sbin/pkg_info";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "^(\S+)-(\d+\.?)+";
package_list_version_regex => "^\S+-((\d+\.?)+\_\d)";
package_file_repositories => { "$(repo)" };
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version).tbz";
package_delete_convention => "$(name)-$(version)";
package_add_command => "/usr/sbin/pkg_add";
package_delete_command => "/usr/sbin/pkg_delete";
netbsd::
package_changes => "individual";
package_list_command => "/usr/sbin/pkg_info";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "^(\S+)-(\d+\.?)+";
package_list_version_regex => "^\S+-((\d+\.?)+\nb\d)";
package_file_repositories => { "$(repo)" };
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version).tgz";
package_delete_convention => "$(name)-$(version)";
package_add_command => "/usr/sbin/pkg_add";
package_delete_command => "/usr/sbin/pkg_delete";
solarisx86|solaris::
package_changes => "individual";
package_list_command => "/usr/bin/pkginfo -l";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_multiline_start => "\s*PKGINST:\s+[^\s]+";
package_list_name_regex => "\s*PKGINST:\s+([^\s]+)";
package_list_version_regex => "\s*VERSION:\s+([^\s]+)";
package_list_arch_regex => "\s*ARCH:\s+([^\s]+)";
package_file_repositories => { "$(repo)" };
package_installed_regex => "\s*STATUS:\s*(completely|partially)\s+installed.*";
package_name_convention => "$(name)-$(version)-$(arch).pkg";
package_delete_convention => "$(name)";
# Cfengine appends path to package and package name below, respectively
package_add_command => "/bin/sh $(repo)/add_scr $(repo)/admin_file";
package_delete_command => "/usr/sbin/pkgrm -n -a $(repo)/admin_file";
(solarisx86|solaris)::
package_update_command => "$(sys.workdir)/bin/cf-upgrade -b $(cfe_internal_update_bins.backup_script) -s $(cfe_internal_update_bins.backup_file) -i $(cfe_internal_update_bins.install_script)";
aix::
package_changes => "individual";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_command => "/usr/bin/lslpp -lc";
package_list_name_regex => "[^:]+:([^:]+):[^:]+:.*";
package_list_version_regex => "[^:]+:[^:]+:([^:]+):.*";
package_file_repositories => { "$(repo)" };
package_installed_regex => "[^:]+:[^:]+:[^:]+:[^:]*:(COMMITTED|APPLIED):.*";
package_name_convention => "$(name)-$(version).bff";
package_delete_convention => "$(name)";
package_add_command => "/usr/bin/rm -f $(repo)/.toc && /usr/sbin/geninstall -IqacgXNY -d $(repo) cfengine.cfengine-nova$";
package_update_command => "/usr/bin/rm -f $(repo)/.toc && /usr/sbin/geninstall -IqacgXNY -d $(repo) cfengine.cfengine-nova$";
# package_add_command => "/bin/sh -c /usr/sbin/inutoc $(repo) && /usr/sbin/installp -qacgXNYd $(repo) cfengine.cfengine-nova$";
# package_update_command => "/bin/sh -c /usr/sbin/inutoc $(repo) && /usr/sbin/installp -qacgXNYd $(repo) cfengine.cfengine-nova$";
package_delete_command => "/usr/sbin/installp -ug cfengine.cfengine-nova$";
}
cfe_internal/core/watchdog/watchdog.cf
agent bundles
cfe_internal_core_watchdog
Prototype: cfe_internal_core_watchdog(state)
Description: Configure external watchdog processes to keep cf-execd running
Arguments:
state
: (enabled|disabled) The state to keep the watchdog configuration in
Implementation:
bundle agent cfe_internal_core_watchdog(state)
{
meta:
"description"
string => "Configure external watchdog processes (like cron, or monit) to
make sure that cf-execd is always running";
classes:
"invalid_state"
not => regcmp("(enabled|disabled)", "$(state)");
"have_cron_d"
expression => isdir("/etc/cron.d");
"use_cfe_internal_core_watchdog_cron_d"
expression => "have_cron_d._stdlib_path_exists_pgrep";
# We use the aix specific watchdog implementation when it's aix and we are
# not using the cron.d implementation.
"use_cfe_internal_core_watchdog_aix"
expression => "!use_cfe_internal_core_watchdog_cron_d.aix";
methods:
use_cfe_internal_core_watchdog_cron_d::
"any" usebundle => cfe_internal_core_watchdog_cron_d( $(state) );
use_cfe_internal_core_watchdog_aix::
"any" usebundle => cfe_internal_core_watchdog_aix( $(state) );
reports:
DEBUG|DEBUG_cfe_internal_core_watchdog::
"DEBUG $(this.bundle): Watchdog '$(state)'";
"DEBUG $(this.bundle): Invalid state '$(state)' only enabled|disabled allowed"
ifvarclass => "invalid_state";
!(use_cfe_internal_core_watchdog_cron_d|aix)::
"WARNING $(this.bundle): Currently only supports /etc/cron.d on systems that have pgrep in the the stdlib paths bundle and AIX hosts.";
}
cfe_internal_core_watchdog_aix
Prototype: cfe_internal_core_watchdog_aix(state)
Description: Manage watchdog state on aix
Arguments:
state
: enabled|disabled- When enabled a cron job will be present to start cf-execd if it's not running.
- When disabled cron jobs ending with
# CFEngine watchdog
will not be present.
Implementation:
bundle agent cfe_internal_core_watchdog_aix(state)
{
classes:
any::
# Define a class for whatever the desired state is
"$(state)"
expression => "any";
vars:
"my_statedir" string => "$(sys.statedir)/MPF/$(this.bundle)";
commands:
# We need to know about the current crontab before making any changes
"/usr/bin/crontab -l > $(my_statedir)/root-crontab"
handle => "aix_crontab_get_state",
if => isdir( "$(my_statedir)" ),
contain => in_shell_and_silent;
files:
enabled::
# We need a place to track state for processing changes to cron entries
# with proper signaling.
"$(my_statedir)/."
create => "true";
# The watchdog script takes care of detecting conditions and launching
# necessary components.
"$(sys.bindir)/watchdog"
create => "true",
template_method => "mustache",
perms => mog( "700", "root", "system" ),
edit_template => "$(this.promise_dirname)/templates/watchdog.mustache";
# When enabled we make sure there is a cron entry to execute the watchdog
# script.
# NOTE The text `# CFEngine watchdog` is used to locate the specific entry in cron when disabling
"$(my_statedir)/root-crontab"
create => "true",
edit_line => lines_present( "* * * * * $(sys.bindir)/watchdog >/dev/null 2>&1 # CFEngine watchdog"),
classes => results( "bundle", "root_crontab" ),
depends_on => { "aix_crontab_get_state" };
disabled::
"$(my_statedir)/root-crontab"
edit_line => delete_lines_matching(".*# CFEngine watchdog"),
classes => results( "bundle", "root_crontab" ),
depends_on => { "aix_crontab_get_state" };
commands:
root_crontab_repaired::
# We use crontab to load the desired entries so that crond will be
# signaled and the changes will be respected.
"/usr/bin/crontab $(my_statedir)/root-crontab";
}
cfe_internal_core_watchdog_cron_d
Prototype: cfe_internal_core_watchdog_cron_d(state)
Description: Use a cron job installed in /etc/cron.d to watch and make sure that cf-execd is always running.
Arguments:
state
: (enabled|disabled) The state to keep the watchdog configuration in. Enabled manages the cron job, disabled removes it.
Implementation:
bundle agent cfe_internal_core_watchdog_cron_d(state)
{
classes:
any::
# Define a class for whatever the desired state is
"$(state)"
expression => "any";
vars:
any::
"template"
string => "$(this.promise_dirname)/../../../templates/cfengine_watchdog.mustache";
"cron_d_watchdog" string => "/etc/cron.d/cfengine_watchdog";
files:
enabled::
"$(cron_d_watchdog)"
create => "true";
"$(cron_d_watchdog)"
edit_template => "$(template)",
handle => "cfe_internal_core_watchdog_enable_cron_d_file_content",
template_method => "mustache";
disabled::
"$(cron_d_watchdog)"
delete => tidy;
}
cfe_internal/core/watchdog
The watchdog implements a process external to CFEngine which is responsible for identifying symptoms of pathology that result in CFEngine degrading into an inoperable non-recoverable state.
cron.d Watchdog
The generic cron.d watchdog looks for a running cf-execd
processes and starts one if not found.
AIX Watchdog
The AIX watchdog is implemented as a shell script rendered via mustache template.
When enabled the policy ensures that the watchdog script is available and executed via root's crontab.
When disabled the policy ensures that the cron job as identified with a trailing string # CFEngine watchdog
is not active.
The watchdog logs to /var/cfengine/watchdog.log
. Note, this log file is not automatically rotated or purged.
The watchdog records observation artifacts to /var/cfengine/watchdog-archives/.
.
If there is less than 500MB of free space, the watchdog will clean up old archives, preserving the oldest and most recent collection.
Symptoms of pathology
The following conditions are included in the watchdog checks:
/var/cfengine/bin/cf-execd
is running/var/cfengine/bin/cf-execd
is not running more than oncecf-execd
has not timed out long-running (as defined byagent_expireafter
), non-responsivecf-agent
processes based on inspection of$(sys.workdir)/outputs/.*
/var/cfengine/bin/cf-agent
processes currently running do not exceed 300 seconds of execution time/var/cfengine/bin/cf-agent
processes currently running do not exceed concurrency of 3cf-check
does not observe any critical integrity issues in embedded databases
If the pathology threshold (default 0) is breached the watchdog collects observations about the environment into an archive which is intended for submission to CFEngine Support. After the archive has been prepared the watchdog terminates all CFEngine processes, purges outputs ($(sys.workdir)/outputs/*
), local databases ($(sys.statedir)/*.lmdb*
), and then the watchdog will try to re-start the CFEngine processes. The agent will try up to 10 times, with a delay of 10 seconds between each attempt to ensure that cf-execd
, cf-serverd
, and cf-monitord
are all running.
cfe_internal/core/
This directory contains internal management polcies related to CFEngine Core/Community functionality like log rotation.
cfe_internal/enterprise/
This directory contains internal management polcies related to CFEngine Enterprise functionality like Mission Portal.
inventory/
The CFEngine 3 inventory modules are pieces of CFEngine policy that are loaded
and used by promises.cf
in order to inventory the system.
CFEngine Enterprise has specific functionality to show and use inventory data, but users of the Community Version can use them as well locally on each host.
How It Works
The inventory modules are called in promises.cf
:
body common control
{
bundlesequence => {
# Common bundle first (Best Practice)
inventory_control,
@(inventory.bundles),
...
As you see, this calls the inventory_control
bundle, and then each
bundle in the list inventory.bundles
. That list is built in the
top-level common inventory
bundle, which will load the right things
for some common cases. The any.cf
inventory module is always loaded;
the rest are loaded if they are appropriate for the platform. For
instance, Debian systems will load debian.cf
and linux.cf
and
lsb.cf
but may load others as needed.
The effect for users is that the right inventory modules will be loaded and evaluated.
The inventory_control
bundle lives in def.cf
and defines what
inventory modules should be disabled. You can simply set
disable_inventory
to avoid the whole system, or you can look for the
disable_inventory_xyz
class to disable module xyz
.
Any inventory module works the same way, by doing some discovery work
and then tagging its classes and variables with the report
or
inventory
tags. For example:
vars:
"ports" slist => { @(mon.listening_ports) },
meta => { "inventory", "attribute_name=Ports listening" };
This defines a reported attribute "Ports listening" which contains a list of strings representing the listening ports. More on this in a second.
Your Very Own Inventory Module
The good news is, writing an inventory module is incredibly easy.
They are just CFEngine bundles. You can see a simple one that collects
the listening ports in any.cf
:
bundle agent cfe_autorun_inventory_listening_ports
# @brief Inventory the listening ports
#
# This bundle uses `mon.listening_ports` and is always enabled by
# default, as it runs instantly and has no side effects.
{
vars:
"ports" slist => { @(mon.listening_ports) },
meta => { "inventory", "attribute_name=Ports listening" };
}
Well, the slist copy is a CFEngine detail (we get the listening ports from the
monitoring daemon), so just assume that the data is correct. What's important is
the second line that starts with meta
.
That defines metadata for the promise that CFEngine will use to determine that
this data is indeed inventory data and should be reported to the CFEngine
Enterprise Hub.
That's it. Really. The comments are optional but nice to have. You don't have to
put your new bundle in a file under the inventory
directory, either. The
variables and classes can be declared anywhere as long as they have the right
tags. So you can use the services
directory or whatever else makes sense to
you.
CFEngine Enterprise vs. Community
In CFEngine Enterprise, the reported data is aggregated in the hub and reported across the whole host population.
In CFEngine Community, users can use the classesmatching()
and
variablesmatching()
functions to collect all the inventory variables
and classes and report them in other ways.
Implementation Best Practice for CFEngine Enterprise
It is important that inventory variables and classes are continually defined. Only inventory variables and classes defined during the last reported run are available for use by the inventory reporting interface.
Inventory items that change frequently can create a burden on the Enterprise reporting infrastructure. Generally, inventory attributes should change infrequently.
If you wish to inventory attributes that frequently change or are expensive to discover consider implementing a sample interval and caching mechanism.
What Modules Are Available?
As soon as you use the promises.cf
provided in the parent directory,
quite a few inventory modules will be enabled (if appropriate for your
system). Here's the list of modules and what they provide. Note they
are all enabled by code in def.cf
as explained above.
Package Inventory
- lives in:
any.cf
- applies to: All systems
- runs: package modules in order to report on packages installed and patches available
- disable: define the class
disable_inventory_package_refresh
. Note this also disables the default package inventory used by the new packages promise implementation. This will cause thepackagesmatching()
andpackageupdatesmatching()
functions to rely on data supplied by the legacy package promise implementation.
LSB
- lives in:
lsb.cf
- applies to: LSB systems (most Linux distributions, basically)
- runs:
lsb_release -a
- sample data:
Distributor ID: Ubuntu
Description: Ubuntu 14.04 LTS
Release: 14.04
Codename: trusty
provides:
- classes
lsb_$(os)
,lsb_$(os)_$(release)
,lsb_$(os)_$(codename)
- variables:
inventory_lsb.os
(Distributor ID),inventory_lsb.codename
,inventory_lsb.release
,inventory_lsb.flavor
,inventory_lsb.description
- classes
sample output:
% cf-agent -KI -binventory_control,inventory_lsb
R: inventory_lsb: OS = Ubuntu, codename = trusty, release = 14.04, flavor = Ubuntu_14_04, description = Ubuntu 14.04 LTS
SUSE
- lives in:
suse.cf
- applies to: SUSE Linux
- provides classes:
suse_pure
andsuse_derived
Debian
- lives in:
debian.cf
- applies to: Debian and its derivatives
- provides:
- variables:
inventory_debian.mint_release
andinventory_debian.mint_codename
- classes:
debian_pure
,debian_derived
,linuxmint
,lmde
,linuxmint_$(mint_release)
,linuxmint_$(mint_codename)
,$(mint_codename)
- variables:
Red Hat
- lives in:
redhat.cf
- applies to: Red Hat and its derivatives
- provides classes:
redhat_pure
,redhat_derived
Windows
- lives in:
windows.cf
Mac OS X
- lives in:
macos.cf
Generic (unknown OS)
- lives in:
generic.cf
(seeany.cf
for generally applicable inventory modules)
LLDP
- lives in:
any.cf
- runs
inventory_control.lldpctl_exec
through a Perl filter - provides variables:
cfe_autorun_inventory_LLDP.K
for eachK
returned by the LLDB executable
mtab
- lives in:
any.cf
- parses:
/etc/mtab
provides classes:
have_mount_FSTYPE
andhave_mount_FSTYPE_MOUNTPOINT
sample output (note this is verbose mode with
-v
because there's a lot of output):
% cf-agent -Kv -binventory_control,cfe_autorun_inventory_mtab|grep 'cfe_autorun_inventory_mtab: we have'
R: cfe_autorun_inventory_mtab: we have a ext4 mount under /
...
R: cfe_autorun_inventory_mtab: we have a cgroup mount under /sys/fs/cgroup/systemd
R: cfe_autorun_inventory_mtab: we have a tmpfs mount under /run/shm
fstab
- lives in:
any.cf
- parses:
sys.fstab
provides classes:
have_fs_FSTYPE
have_fs_MOUNTPOINT
andhave_fs_FSTYPE_MOUNTPOINT
sample output (note this is verbose mode with
-v
because there's a LOT of output):
% cf-agent -Kv -binventory_control,cfe_autorun_inventory_fstab|grep 'cfe_autorun_inventory_fstab: we have'
R: cfe_autorun_inventory_fstab: we have a ext4 fstab entry under /
R: cfe_autorun_inventory_fstab: we have a cifs fstab entry under /backups/load
R: cfe_autorun_inventory_fstab: we have a auto fstab entry under /mnt/cdrom
CMDB
- lives in:
any.cf
- parses:
me.json
(which is copied from the policy server; see implementation) - provides classes:
CLASS
for each CLASS found under theclasses
key in the JSON data - provides variables:
inventory_cmdb_load.VARNAME
for each VARNAME found under thevars
key in the JSON data
DMI decoding
- lives in:
any.cf
- runs:
dmidecode
provides variables:
cfe_autorun_inventory_dmidecode.dmi[K]
for each key K in thedmidecode
outputsample output (sudo is needed to access the DMI):
% sudo /var/cfengine/bin/cf-agent -KI -binventory_control,cfe_autorun_inventory_dmidecode
R: cfe_autorun_inventory_dmidecode: Obtained BIOS vendor = 'Intel Corp.'
R: cfe_autorun_inventory_dmidecode: Obtained BIOS version = 'BLH6710H.86A.0146.2013.1555.1888'
R: cfe_autorun_inventory_dmidecode: Obtained System serial number = ''
R: cfe_autorun_inventory_dmidecode: Obtained System manufacturer = ''
R: cfe_autorun_inventory_dmidecode: Obtained System version = ''
R: cfe_autorun_inventory_dmidecode: Obtained CPU model = 'Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz'
Listening ports
- lives in:
any.cf
- provides variables:
cfe_autorun_inventory_listening_ports.ports
as a copy ofmon.listening_ports
Disk space
- lives in:
any.cf
- provides variables:
cfe_autorun_inventory_disk.free
as a copy ofmon.value_diskfree
Available memory
- lives in:
any.cf
- provides variables:
cfe_autorun_inventory_memory.free
as a copy ofmon.value_mem_free
andcfe_autorun_inventory_memory.total
as a copy ofmon.value_mem_total
Load average
- lives in:
any.cf
- provides variables:
cfe_autorun_inventory_loadaverage.value
as a copy ofmon.value_loadavg
procfs
- lives in:
any.cf
- parses:
consoles
,cpuinfo
,modules
,partitions
,version
- provides variables:
cfe_autorun_inventory_proc.console_count
,cfe_autorun_inventory_proc.cpuinfo[K]
for each CPU info key,cfe_autorun_inventory_proc.paritions[K]
for each partition key provides classes:
_have_console_CONSOLENAME
,have_module_MODULENAME
sample output (note this is verbose mode with
-v
because there's a LOT of output):
% cf-agent -Kv -binventory_control,cfe_autorun_inventory_proc|grep 'cfe_autorun_inventory_proc: we have'
R: cfe_autorun_inventory_proc: we have console tty0
R: cfe_autorun_inventory_proc: we have module snd_seq_midi
...
R: cfe_autorun_inventory_proc: we have module ghash_clmulni_intel
R: cfe_autorun_inventory_proc: we have cpuinfo flags = fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx lahf_lm ida arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid
...
R: cfe_autorun_inventory_proc: we have cpuinfo model name = Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz
R: cfe_autorun_inventory_proc: we have partitions sr0 with 1048575 blocks
...
R: cfe_autorun_inventory_proc: we have partitions sda with 468851544 blocks
R: cfe_autorun_inventory_proc: we have kernel version 'Linux version 3.11.0-15-generic (buildd@roseapple) (gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu8) ) #25-Ubuntu SMP Thu Jan 30 17:22:01 UTC 2014'
inventory/suse.cf
This policy is inventory related to suse hosts.
common bodies
inventory_suse
Prototype: inventory_suse
Description: SUSE inventory bundle
This common bundle is for SUSE Linux inventory work.
Implementation:
bundle common inventory_suse
{
classes:
"suse_pure" expression => "(sles|sled).!opensuse",
comment => "pure SUSE",
meta => { "inventory", "attribute_name=none" };
"suse_derived" expression => "opensuse.!suse_pure",
comment => "derived from SUSE",
meta => { "inventory", "attribute_name=none" };
}
inventory/linux.cf
This policy is inventory related to linux hosts.
common bodies
inventory_linux
Prototype: inventory_linux
Description: Linux inventory
This common bundle is for Linux inventory work.
Provides: os_release_id, and os_release_version based on parsing of /etc/os-release systemd class based on linktarget of /proc/1/cmdline
Implementation:
bundle common inventory_linux
{
vars:
has_os_release::
"os_release_info" string => readfile("/etc/os-release", "512"),
comment => "Read /etc/os-release" ;
os_release_has_id::
"os_release_id" string => canonify("$(id_array[1])");
os_release_has_version::
"os_release_version" string => canonify("$(version_array[1])");
has_proc_1_cmdline::
"proc_1_cmdline_split" slist => string_split(readfile("/proc/1/cmdline", "512"), " ", "2"),
comment => "Read /proc/1/cmdline and split off arguments";
"proc_1_cmdline" string => nth("proc_1_cmdline_split", 0),
comment => "Get argv[0] of /proc/1/cmdline";
# this is the same as the original file for non-links
"proc_1_process" string => filestat($(proc_1_cmdline), "linktarget");
any::
"proc_routes" data => data_readstringarrayidx("/proc/net/route",
"#[^\n]*","\s+",40,4k),
ifvarclass => fileexists("/proc/net/route");
"routeidx" slist => getindices("proc_routes");
"dgw_ipv4_iface" string => "$(proc_routes[$(routeidx)][0])",
comment => "Name of the interface where default gateway is routed",
ifvarclass => strcmp("$(proc_routes[$(routeidx)][1])", "00000000");
@if minimum_version(3.10)
linux::
"nfs_servers" -> { "CFE-3259" }
comment => "NFS servers (to list hosts impacted by NFS outages)",
slist => maplist( regex_replace( $(this) , ":.*", "", "g"),
# NFS server is before the colon (:), that's all we want
# e.g., nfs.example.com:/vol/homedir/user1 /home/user1 ...
# ^^^^^^^^^^^^^^^
grep( ".* nfs .*",
readstringlist("/proc/mounts", "", "\n", inf, inf)
)
),
if => fileexists( "/proc/mounts" );
"nfs_server[$(nfs_servers)]"
string => "$(nfs_servers)",
meta => { "inventory", "attribute_name=NFS Server" };
@endif
classes:
any::
"has_os_release" expression => fileexists("/etc/os-release"),
comment => "Check if we can get more info from /etc/os-release";
"os_release_has_id" expression => regextract('^ID="?([^"\s]+)"?$',
$(os_release_info),
"id_array"),
comment => "Extract ID= line from os-release to id_array";
"os_release_has_version" expression => regextract('^VERSION_ID="?([^"]+)"?$',
$(os_release_info),
"version_array"),
comment => "Extract VERSION_ID= line from os-release to version_array";
"has_proc_1_cmdline" expression => fileexists("/proc/1/cmdline"),
comment => "Check if we can read /proc/1/cmdline";
os_release_has_id::
"$(os_release_id)" expression => "any";
os_release_has_version::
"$(os_release_id)_$(os_release_version)" expression => "any";
has_proc_1_cmdline::
"systemd" expression => strcmp(lastnode($(proc_1_process), "/"), "systemd"),
comment => "Check if (the link target of) /proc/1/cmdline is systemd";
reports:
(DEBUG|DEBUG_inventory_linux).has_os_release::
"DEBUG $(this.bundle)";
"$(const.t)OS release ID = $(os_release_id), OS release version = $(os_release_version)";
}
inventory/any.cf
This policy is inventory related policy that can be run on any OS. This inventory processes is done by populating variables and data structures with useful information gathered from the system. In the CFEngine Enterprise Edition these inventoried variables and classes are automatically collected into a centralized reporting system. In the CFEngine Community Edition these variables are availbale from within policy, and may be reporting in numerous ways.
common bodies
inventory_any
Prototype: inventory_any
Description: Do inventory for any OS
This common bundle is for any OS work not handled by specific bundles.
Implementation:
bundle common inventory_any
{
vars:
"release_data" string => "$(this.promise_dirname)/../cf_promises_release_id";
"data"
data => readjson( $(release_data), inf ),
if => fileexists( $(release_data) );
"id"
string => "$(data[releaseId])",
meta => { "inventory", "attribute_name=Policy Release Id" };
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): Inventory Policy Release Id=$(id)";
}
agent bundles
inventory_autorun
Prototype: inventory_autorun
Description: Autorun some inventory bundles
This agent bundle runs other "autorun" inventory agent bundles explicitly. It will use bundlesmatching() when CFEngine 3.5 and earlier are no longer supported.
Implementation:
bundle agent inventory_autorun
{
methods:
!disable_inventory_cmdb::
"cmdb" usebundle => cfe_autorun_inventory_cmdb(),
handle => "cfe_internal_autorun_inventory_cmdb";
!disable_inventory_LLDP::
"LLDP" usebundle => cfe_autorun_inventory_LLDP(),
handle => "cfe_internal_autorun_inventory_LLDP";
!disable_inventory_package_refresh::
"packages_refresh" usebundle => cfe_autorun_inventory_packages(),
handle => "cfe_internal_autorun_inventory_packages";
!disable_inventory_policy_servers::
"Inventory Policy Servers"
handle => "cfe_internal_autorun_inventory_policy_servers",
usebundle => cfe_autorun_inventory_policy_servers;
!disable_inventory_proc::
"proc" usebundle => cfe_autorun_inventory_proc(),
handle => "cfe_internal_autorun_inventory_proc";
"proc_cpuinfo" usebundle => cfe_autorun_inventory_proc_cpuinfo(),
handle => "cfe_internal_autorun_inventory_proc_cpuinfo";
!disable_inventory_cpuinfo::
"cpuinfo" usebundle => cfe_autorun_inventory_cpuinfo(),
handle => "cfe_internal_autorun_inventory_cpuinfo";
!disable_inventory_fstab::
"fstab" usebundle => cfe_autorun_inventory_fstab(),
handle => "cfe_internal_autorun_inventory_fstab";
!disable_inventory_mtab::
"mtab" usebundle => cfe_autorun_inventory_mtab(),
handle => "cfe_internal_autorun_inventory_mtab";
!disable_inventory_dmidecode::
"dmidecode" usebundle => cfe_autorun_inventory_dmidecode(),
handle => "cfe_internal_autorun_inventory_dmidecode";
!disable_inventory_aws::
"aws" usebundle => cfe_autorun_inventory_aws(),
handle => "cfe_internal_autorun_inventory_aws";
!disable_inventory_aws|disable_inventory_aws_ec2_metadata::
"aws" usebundle => cfe_autorun_inventory_aws_ec2_metadata(),
handle => "cfe_internal_autorun_inventory_ec2_metadata";
any::
"listening ports" usebundle => cfe_autorun_inventory_listening_ports(),
handle => "cfe_internal_autorun_listening_ports";
"disk" usebundle => cfe_autorun_inventory_disk(),
handle => "cfe_internal_autorun_disk";
"memory" usebundle => cfe_autorun_inventory_memory(),
handle => "cfe_internal_autorun_memory";
"loadaverage" usebundle => cfe_autorun_inventory_loadaverage(),
handle => "cfe_internal_autorun_loadaverage";
"ipv4 addresses"
usebundle => cfe_autorun_inventory_ipv4_addresses,
handle => "cfe_internal_autorun_ipv4_addresses";
}
cfe_autorun_inventory_listening_ports
Prototype: cfe_autorun_inventory_listening_ports
Description: Inventory the listening ports
This bundle uses mon.listening_ports
and is always enabled by
default, as it runs instantly and has no side effects.
Implementation:
bundle agent cfe_autorun_inventory_listening_ports
{
vars:
"ports" -> { "ENT-150" }
slist => sort( "mon.listening_ports", "int"),
meta => { "inventory", "attribute_name=Ports listening" },
ifvarclass => some("[0-9]+", "mon.listening_ports"),
comment => "We only want to inventory the listening ports if we have
values that make sense.";
}
cfe_autorun_inventory_ipv4_addresses
Prototype: cfe_autorun_inventory_ipv4_addresses
Description: Inventory ipv4 addresses This will filter the loopback address (127.0.0.1, as it is likely not very interesting)
Implementation:
bundle agent cfe_autorun_inventory_ipv4_addresses
{
vars:
"filter_reg"
string => "127\.0\.0\.1",
comment => "Addresses that match this regular expression will be filtered
from the inventory for ipv4 addresses";
# Strings are displayed more beautifully in Mission Portal than lists, so
# we first generate the list of addresses to be inventoried and then do
# inventory using an array.
"ipv4_addresses"
slist => filter( $(filter_reg), "sys.ip_addresses", "true", "true", 999999999);
"ipv4[$(ipv4_addresses)]"
string => "$(ipv4_addresses)",
meta => { "inventory", "attribute_name=IPv4 addresses" };
reports:
DEBUG|DEBUG_cfe_autorun_inventory_ipv4_addresses::
"DEBUG $(this.bundle)";
"$(const.t)Inventorying: '$(ipv4_addresses)'";
}
cfe_autorun_inventory_disk
Prototype: cfe_autorun_inventory_disk
Description: Inventory the disk (Enterprise only)
Implementation:
bundle agent cfe_autorun_inventory_disk
{
vars:
enterprise::
"free" -> { "ENT-5190" }
string => "$(mon.value_diskfree)",
meta => { "inventory", "attribute_name=Disk free (%)" },
if => isvariable( "mon.value_diskfree" );
}
cfe_autorun_inventory_memory
Prototype: cfe_autorun_inventory_memory
Description: Inventory the memory (Enterprise only)
Implementation:
bundle agent cfe_autorun_inventory_memory
{
vars:
@if minimum_version(3.11)
# The `with` attribute is necessary for this to work in a single promise.
enterprise_edition.windows::
# wmic returns "TotalVisibleMemorySize=10760224" so split on = and take
# the second item (0-based with nth())
"total" -> { "ENT-4188" }
meta => { "inventory", "attribute_name=Memory size (MB)" },
string => format( "%d", eval("$(with)/1024", "math", "infix" )),
if => not( isvariable( "total" ) ),
with => nth( string_split( execresult("wmic OS get TotalVisibleMemorySize /format:list", useshell ),
"=", 2), 1);
"totalPhysical" -> { "CFE-2896" }
meta => { "inventory", "attribute_name=Physical memory (MB)" },
string => format( "%d", eval("$(with)/1024", "math", "infix" )),
if => not( isvariable( "total" ) ),
with => nth( string_split( execresult("wmic ComputerSystem get TotalPhysicalMemory /format:list", useshell ),
"=", 2), 1);
# This is a volatile metric, perhaps not well suited for inventory
"free"
meta => { "report" },
string => format( "%d", eval("$(with)/1024", "math", "infix" )),
if => not( isvariable( "free" ) ),
with => nth( string_split( execresult("wmic OS get FreePhysicalMemory /format:list", useshell ),
"=", 2), 1);
@endif
enterprise_edition.aix::
"total" -> { "CFE-2797", "CFE-2803" }
string => execresult("/usr/bin/lparstat -i | awk '/Online Memory/ { print $4 }'", "useshell"),
meta => { "inventory", "attribute_name=Memory size (MB)" };
enterprise_edition.hpux::
"total" -> { "ENT-4188" }
string => execresult( "machinfo | awk '/^Memory =/ {print $3}'", useshell ),
meta => { "inventory", "attribute_name=Memory size (MB)" };
enterprise_edition.!(aix|windows|hpux)::
"total" string => "$(mon.value_mem_total)",
meta => { "inventory", "attribute_name=Memory size (MB)" },
if => isvariable( "mon.value_mem_total" );
"free" string => "$(mon.value_mem_free)",
if => and( not( isvariable( "free" ) ),
isvariable( "mon.value_mem_free" )),
meta => { "report" };
}
cfe_autorun_inventory_timezone
Prototype: cfe_autorun_inventory_timezone
Description: Inventory timezone and GMT offset
Implementation:
bundle agent cfe_autorun_inventory_timezone
{
vars:
"_now" int => now();
"timezone" -> { "ENT-6161" }
string => strftime( localtime, "%Z", $(_now) ),
meta => { "inventory", "attribute_name=Timezone" };
"gmt_offset" -> { "ENT-6161" }
string => strftime( localtime, "%z", $(_now) ),
meta => { "inventory", "attribute_name=Timezone GMT Offset" };
}
cfe_autorun_inventory_loadaverage
Prototype: cfe_autorun_inventory_loadaverage
Description: Inventory the loadaverage (Enterprise only)
Implementation:
bundle agent cfe_autorun_inventory_loadaverage
{
vars:
enterprise::
"value" -> { "ENT-5190" }
string => "$(mon.value_loadavg)",
meta => { "report" },
if => isvariable( "mon.value_loadavg" );
}
cfe_autorun_inventory_proc
Prototype: cfe_autorun_inventory_proc
Description: Do procfs inventory
This bundle will parse these /proc files: consoles, cpuinfo, meminfo, modules, partitions, version, vmstat. There are some general patterns you can follow to extend it for other /proc items of interest.
Contributions welcome. /proc/net and /proc/sys in general are of
wide interest, if you're looking for something fun. For instance,
the network interfaces could be extracted here without calling
ifconfig
.
Implementation:
bundle agent cfe_autorun_inventory_proc
{
vars:
"basefiles" slist => { "consoles", "cpuinfo", "modules", "partitions", "version" };
"files[$(basefiles)]" string => "$(inventory_control.proc)/$(basefiles)";
_have_proc_consoles::
"console_count" int => readstringarrayidx("consoles",
"$(files[consoles])",
"\s*#[^\n]*",
"\s+",
500,
50000);
"console_idx" slist => getindices("consoles");
_have_proc_modules::
"module_count" int => readstringarrayidx("modules",
"$(files[modules])",
"\s*#[^\n]*",
"\s+",
2500,
250000);
"module_idx" slist => getindices("modules");
_have_proc_cpuinfo::
# this will extract all the keys in one bunch, so you won't get
# detailed info for processor 0 for example
"cpuinfo_count" int => readstringarrayidx("cpuinfo_array",
"$(files[cpuinfo])",
"\s*#[^\n]*",
"\s*:\s*",
500,
50000);
"cpuinfo_idx" slist => getindices("cpuinfo_array");
"cpuinfo[$(cpuinfo_array[$(cpuinfo_idx)][0])]" string => "$(cpuinfo_array[$(cpuinfo_idx)][1])";
"cpuinfo_keys" slist => getindices("cpuinfo");
_have_proc_partitions::
"partitions_count" int => readstringarrayidx("partitions_array",
"$(files[partitions])",
"major[^\n]*",
"\s+",
500,
50000);
"partitions_idx" slist => getindices("partitions_array");
"partitions[$(partitions_array[$(partitions_idx)][4])]" string => "$(partitions_array[$(partitions_idx)][3])";
"partitions_keys" slist => getindices("partitions");
_have_proc_version::
"version" string => readfile("$(files[version])", 2048);
classes:
"have_proc" expression => isdir($(inventory_control.proc));
have_proc::
"_have_proc_$(basefiles)"
expression => fileexists("$(files[$(basefiles)])");
_have_proc_consoles::
"have_console_$(consoles[$(console_idx)][0])"
expression => "any",
scope => "namespace";
_have_proc_modules::
"have_module_$(modules[$(module_idx)][0])"
expression => "any",
scope => "namespace";
reports:
_have_proc_consoles.verbose_mode::
"$(this.bundle): we have console $(consoles[$(console_idx)][0])";
_have_proc_modules.verbose_mode::
"$(this.bundle): we have module $(modules[$(module_idx)][0])";
_have_proc_cpuinfo.verbose_mode::
"$(this.bundle): we have cpuinfo $(cpuinfo_keys) = $(cpuinfo[$(cpuinfo_keys)])";
_have_proc_partitions.verbose_mode::
"$(this.bundle): we have partitions $(partitions_keys) with $(partitions[$(partitions_keys)]) blocks";
_have_proc_version.verbose_mode::
"$(this.bundle): we have kernel version '$(version)'";
}
cfe_autorun_inventory_proc_cpuinfo
Prototype: cfe_autorun_inventory_proc_cpuinfo
Implementation:
bundle agent cfe_autorun_inventory_proc_cpuinfo
{
classes:
"_have_cpuinfo" expression => isvariable("default:cfe_autorun_inventory_proc.cpuinfo_idx");
# So that we don't inventory non dereferenced variables we check to see
# if we have the info first This is only necessary because its currently
# invalid to do isvariable on an array key that contains a space
# Ref: redmine#7088 https://dev.cfengine.com/issues/7088
"have_cpuinfo_cpu_cores" expression => strcmp("cpu cores", "$(default:cfe_autorun_inventory_proc.cpuinfo_array[$(default:cfe_autorun_inventory_proc.cpuinfo_idx)][0])");
"have_cpuinfo_model_name" expression => strcmp("model name", "$(default:cfe_autorun_inventory_proc.cpuinfo_array[$(default:cfe_autorun_inventory_proc.cpuinfo_idx)][0])");
vars:
_have_cpuinfo::
"cpuinfo_physical_cores"
string => "$(default:cfe_autorun_inventory_proc.cpuinfo[cpu cores])",
ifvarclass => "have_cpuinfo_cpu_cores";
"cpuinfo_cpu_model_name"
string => "$(default:cfe_autorun_inventory_proc.cpuinfo[model name])",
ifvarclass => "have_cpuinfo_model_name";
# We need to be able to count the number of unique physical id lines in
# /proc/cpu in order to get a physical processor count.
"cpuinfo_lines" slist => readstringlist(
"$(default:cfe_autorun_inventory_proc.files[cpuinfo])",
"\s*#[^\n]*",
"\n",
500,
50000);
"cpuinfo_processor_lines"
slist => grep("processor\s+:\s\d+", "cpuinfo_lines"),
comment => "The number of processor entries in $(default:cfe_autorun_inventory_proc.files[cpuinfo]). If no
'physical id' entries are found this is the processor count";
"cpuinfo_processor_lines_count"
int => length("cpuinfo_processor_lines");
"cpuinfo_physical_id_lines"
slist => grep("physical id.*", "cpuinfo_lines"),
comment => "This identifies which physical socket a logical core is on,
the count of the unique physical id lines tells you how
many physical sockets you have. THis would not be present
on systems that are not multicore.";
"cpuinfo_physical_id_lines_unique"
slist => unique("cpuinfo_physical_id_lines");
"cpuinfo_physical_id_lines_unique_count"
int => length("cpuinfo_physical_id_lines_unique");
# If we have physical id lines in cpu info use that for socket inventory,
# else we should use the number of processor lines. physical id lines
# seem to only be present when multiple cores are active.
"cpuinfo_physical_socket_inventory"
string => ifelse(isgreaterthan( length("cpuinfo_physical_id_lines"), 0 ), "$(cpuinfo_physical_id_lines_unique_count)",
"$(cpuinfo_processor_lines_count)"),
meta => { "inventory", "attribute_name=CPU sockets" };
reports:
DEBUG|DEBUG_cfe_autorun_inventory_proc::
"DEBUG $(this.bundle)";
"$(const.t)cpuinfo[$(default:cfe_autorun_inventory_proc.cpuinfo_array[$(default:cfe_autorun_inventory_proc.cpuinfo_idx)][0])] = $(default:cfe_autorun_inventory_proc.cpuinfo[$(default:cfe_autorun_inventory_proc.cpuinfo_array[$(default:cfe_autorun_inventory_proc.cpuinfo_idx)][0])])";
"$(const.t)CPU physical cores: '$(cpuinfo_physical_cores)'"
ifvarclass => "have_cpuinfo_cpu_cores";
"$(const.t)CPU model name: '$(cpuinfo_cpu_model_name)'"
ifvarclass => "have_cpuinfo_model_name";
"$(const.t)CPU Physical Sockets: '$(cpuinfo_physical_socket_inventory)'";
}
cfe_autorun_inventory_cpuinfo
Prototype: cfe_autorun_inventory_cpuinfo
Implementation:
bundle agent cfe_autorun_inventory_cpuinfo
{
classes:
"_have_proc_cpu_model_name" expression => isvariable("default:cfe_autorun_inventory_proc_cpuinfo.cpuinfo_cpu_model_name");
"_have_proc_cpu_physical_cores" expression => isvariable("default:cfe_autorun_inventory_proc_cpuinfo.cpuinfo_physical_cores");
# We only accept dmidecode values that don't look like cfengine variables,
# (starting with dollar), or that have an apparent empty value.
"_have_dmidecode_cpu_model_name"
not => regcmp("($(const.dollar)\(.*\)|^$)", "$(default:cfe_autorun_inventory_dmidecode.dmi[processor-version])");
vars:
_have_proc_cpu_physical_cores::
"cpuinfo_physical_cores"
string => "$(default:cfe_autorun_inventory_proc.cpuinfo[cpu cores])",
#ifvarclass => "have_cpuinfo_cpu_cores",
meta => { "inventory", "attribute_name=CPU physical cores", "derived-from=$(default:cfe_autorun_inventory_proc.files[cpuinfo])" };
_have_proc_cpu_model_name::
"cpu_model"
string => "$(default:cfe_autorun_inventory_proc_cpuinfo.cpuinfo_cpu_model_name)",
meta => { "inventory", "attribute_name=CPU model", "derived-from=$(default:cfe_autorun_inventory_proc.files[cpuinfo])" };
_have_dmidecode_cpu_model_name.!_have_proc_cpu_model_name::
"cpu_model"
string => "$(default:cfe_autorun_inventory_dmidecode.dmi[processor-version])",
meta => { "inventory", "attribute_name=CPU model", "derived-from=$(inventory_control.dmidecoder) -s processor-version" };
reports:
DEBUG|DEBUG_cfe_autorun_inventory_cpuinfo::
"DEBUG $(this.bundle)";
"$(const.t) CPU model: $(cpu_model)";
"$(const.t) CPU physical cores: $(cpuinfo_physical_cores)";
}
common bodies
cfe_autorun_inventory_aws
Prototype: cfe_autorun_inventory_aws
Description: inventory AWS EC2 instances
Provides: ec2_instance class based on Amazon markers in dmidecode's system-uuid, bios-version or bios-vendor
Implementation:
bundle common cfe_autorun_inventory_aws
{
classes:
!disable_inventory_aws::
"ec2_instance" -> { "CFE-2924" }
comment => "See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html",
scope => "namespace",
expression => regcmp("^[eE][cC]2.*", "$(cfe_autorun_inventory_dmidecode.dmi[system-uuid])"),
if => isvariable("cfe_autorun_inventory_dmidecode.dmi[system-uuid]");
"ec2_instance" -> { "CFE-2924" }
expression => regcmp(".*[aA]mazon.*", "$(cfe_autorun_inventory_dmidecode.dmi[bios-version])"),
scope => "namespace",
if => isvariable("cfe_autorun_inventory_dmidecode.dmi[bios-version]");
"ec2_instance" -> { "CFE-2924" }
expression => regcmp(".*[aA]mazon.*", "$(cfe_autorun_inventory_dmidecode.dmi[bios-vendor])"),
scope => "namespace",
if => isvariable("cfe_autorun_inventory_dmidecode.dmi[bios-vendor]");
"ec2_instance" -> { "CFE-2924" }
expression => regline( "^ec2.*", "/sys/hypervisor/uuid" ),
scope => "namespace",
if => fileexists("/sys/hypervisor/uuid");
reports:
(DEBUG|DEBUG_inventory_aws)::
"DEBUG $(this.bundle)";
"$(const.t)+ec2_instance"
if => "ec2_instance";
}
agent bundles
cfe_autorun_inventory_aws_ec2_metadata
Prototype: cfe_autorun_inventory_aws_ec2_metadata
Description: Inventory ec2 metadata Provides:
Implementation:
bundle agent cfe_autorun_inventory_aws_ec2_metadata
{
methods:
!(disable_inventory_aws|disable_inventory_aws_ec2_metadata)::
"cfe_autorun_inventory_aws_ec2_metadata_cache";
"cfe_aws_ec2_metadata_from_cache";
}
cfe_autorun_inventory_aws_ec2_metadata_cache
Prototype: cfe_autorun_inventory_aws_ec2_metadata_cache
Description: Cache ec2 metadata from http request for one day
Provides cache of ec2 instance metadata for inventory
Implementation:
bundle agent cfe_autorun_inventory_aws_ec2_metadata_cache
{
vars:
ec2_instance.!(disable_inventory_aws|disable_inventory_aws_ec2_metadata)::
"curl" string => "$(paths.curl)";
"curl_opts" string => "--max-time 1 --connect-timeout 1 --silent";
"URL" string => "http://169.254.169.254/latest/dynamic/instance-identity/document";
"cache" string => "$(sys.statedir)/aws_ec2_metadata";
commands:
ec2_instance.!(disable_inventory_aws|disable_inventory_aws_ec2_metadata)::
"$(curl)"
args => "$(curl_opts) $(URL) -o $(cache)",
if => and( not( fileexists( $(cache) )),
isexecutable( $(curl) ));
files:
ec2_instance.!(disable_inventory_aws|disable_inventory_aws_ec2_metadata)::
"$(cache)"
delete => tidy,
file_select => days_old(1);
}
cfe_aws_ec2_metadata_from_cache
Prototype: cfe_aws_ec2_metadata_from_cache
Description: Inventory ec2 metadata from cache
Provides inventory for EC2 Region, EC2 Instance ID, EC2 Instance Type, EC2 Image ID, and EC2 Availability Zone
Implementation:
bundle agent cfe_aws_ec2_metadata_from_cache
{
classes:
ec2_instance.!(disable_inventory_aws|disable_inventory_aws_ec2_metadata)::
"have_cached_instance_identity"
expression => fileexists( $(cfe_autorun_inventory_aws_ec2_metadata_cache.cache) );
vars:
have_cached_instance_identity.ec2_instance.!(disable_inventory_aws|disable_inventory_aws_ec2_metadata)::
"data" data => readjson( $(cfe_autorun_inventory_aws_ec2_metadata_cache.cache), 100K);
"region" string => "$(data[region])", meta => { "inventory", "attribute_name=EC2 Region" };
"instanceId" string => "$(data[instanceId])", meta => { "inventory", "attribute_name=EC2 Instance ID" };
"instanceType" string => "$(data[instanceType])", meta => { "inventory", "attribute_name=EC2 Instance Type" };
"imageId" string => "$(data[imageId])", meta => { "inventory", "attribute_name=EC2 Image ID" };
"availabilityZone" string => "$(data[availabilityZone])", meta => { "inventory", "attribute_name=EC2 Availability Zone" };
reports:
DEBUG|DEBUG_inventory_ec2_metadata|DEBUG_inventory_ec2_metadata_from_cache::
"DEBUG $(this.bundle):";
"$(const.t)Inventory 'EC2 Region' = '$(region)'";
"$(const.t)Inventory 'EC2 Instance ID' = '$(instanceId)'";
"$(const.t)Inventory 'EC2 Instance Type' = '$(instanceType)'";
"$(const.t)Inventory 'EC2 Image ID' = '$(imageId)'";
"$(const.t)Inventory 'EC2 Availability Zone' = '$(availabilityZone)'";
}
cfe_autorun_inventory_mtab
Prototype: cfe_autorun_inventory_mtab
Description: Do mtab inventory
The mtab format is simple: each line looks like this format:
/dev/sda1 / ext4 rw,noatime,data=ordered 0 0
(in order: DEV
MOUNTPOINT FSTYPE OPTIONS DUMP-FREQ PASS
). Some older Unices have
a different format and it's really not portable, so enable this only
if you know you want it. It's very handy if you want to check if a
file system is mounted.
Implementation:
bundle agent cfe_autorun_inventory_mtab
{
vars:
have_mtab::
"mount_count" int => readstringarrayidx("mounts",
$(inventory_control.mtab),
"\s*#[^\n]*",
"\s+",
500,
50000);
"idx" slist => getindices("mounts");
classes:
"have_mtab" expression => fileexists($(inventory_control.mtab));
# define classes like have_mount_ext4__var for a ext4 /var mount
"have_mount_$(mounts[$(idx)][2])_$(mounts[$(idx)][1])"
expression => "any",
scope => "namespace";
# define classes like have_mount_ext4 if there is a ext4 mount
"have_mount_$(mounts[$(idx)][2])"
expression => "any",
scope => "namespace";
reports:
verbose_mode::
"$(this.bundle): we have a $(mounts[$(idx)][2]) mount under $(mounts[$(idx)][1])";
}
cfe_autorun_inventory_fstab
Prototype: cfe_autorun_inventory_fstab
Description: Do fstab inventory
The fstab format is simple: each line looks like this format:
/dev/sda1 / auto noatime 0 1
(in order: DEV MOUNTPOINT FSTYPE
OPTIONS DUMP-FREQ PASS
). Note the FSTYPE is not known from the
fstab.
Solaris has 'MOUNTDEV FSCKDEV MOUNTPOINT FSTYPE PASS MOUNT-AD-BOOT OPTIONS' but is not supported here. Contributions welcome.
Implementation:
bundle agent cfe_autorun_inventory_fstab
{
vars:
have_fstab::
"mount_count" int => readstringarrayidx("mounts",
$(sys.fstab),
"\s*#[^\n]*",
"\s+",
500,
50000);
"idx" slist => getindices("mounts");
classes:
"have_fstab" expression => fileexists($(sys.fstab));
# define classes like have_fs_ext4__var for a ext4 /var entry
"have_fs_$(mounts[$(idx)][2])_$(mounts[$(idx)][1])"
expression => "any",
scope => "namespace";
# define classes like have__var for a /var entry
"have_fs_$(mounts[$(idx)][1])"
expression => "any",
scope => "namespace";
# define classes like have_fs_ext4 if there is a ext4 entry
"have_fs_$(mounts[$(idx)][2])"
expression => "any",
scope => "namespace";
reports:
verbose_mode::
"$(this.bundle): we have a $(mounts[$(idx)][2]) fstab entry under $(mounts[$(idx)][1])";
}
cfe_autorun_inventory_dmidecode
Prototype: cfe_autorun_inventory_dmidecode
Description: Do hardware related inventory
This agent bundle runs dmidecode when present to inventory: - BIOS vendor - BIOS version - System serial number - System manufacturer - System version - System product name - Physical memory (MB)
On windows where powershell is available this bundle runs gwmi to inventory: - BIOS vendor - BIOS version - System serial number - System manufacturer
Implementation:
bundle agent cfe_autorun_inventory_dmidecode
{
vars:
"dmidefs" data => parsejson('
{
"bios-vendor": "BIOS vendor",
"bios-version": "BIOS version",
"system-serial-number": "System serial number",
"system-manufacturer": "System manufacturer",
"system-version": "System version",
"system-product-name": "System product name",
"system-uuid": "System UUID",
}');
# We override dmidefs from augments when we can.
"dmidefs" -> { "CFE-2927" }
data => mergedata( "def.cfe_autorun_inventory_dmidecode[dmidefs]" ),
if => isvariable( "def.cfe_autorun_inventory_dmidecode[dmidefs]");
# other dmidecode variables you may want:
# baseboard-asset-tag
# baseboard-manufacturer
# baseboard-product-name
# baseboard-serial-number
# baseboard-version
# bios-release-date
# chassis-asset-tag
# chassis-manufacturer
# chassis-serial-number
# chassis-type
# chassis-version
# processor-family
# processor-frequency
# processor-manufacturer
# system-uuid
#"processor-version": "CPU model" <- Collected by default, but not by iterating over the list
"dmivars" slist => getindices(dmidefs);
have_dmidecode::
"decoder" string => "$(inventory_control.dmidecoder)";
# Redhat 4 can support the -s option to dmidecode if
# kernel-utils-2.4-15.el4 or greater is installed.
have_dmidecode.!(redhat_4|redhat_3)::
"dmi[$(dmivars)]" string => execresult("$(decoder) -s $(dmivars)",
"useshell"),
meta => { "inventory", "attribute_name=$(dmidefs[$(dmivars)])" };
# We do not want to inventory the model name from here, as inventory for
# CPU info has been abstracted away from DMI so we just collect it
# manually.
"dmi[processor-version]" string => execresult("$(decoder) -s processor-version",
"useshell");
windows.powershell::
"dmi[bios-vendor]" string => $(bios_array[1]),
meta => { "inventory", "attribute_name=BIOS vendor" };
"dmi[system-serial-number]" string => $(bios_array[2]),
meta => { "inventory", "attribute_name=System serial number" };
"dmi[bios-version]" string => $(bios_array[3]),
meta => { "inventory", "attribute_name=BIOS version" };
"dmi[system-version]" string => $(bios_array[4]),
meta => { "inventory", "attribute_name=System version" };
"dmi[processor-version]" string => $(processor_array[1]);
"split_pscomputername"
slist => string_split($(system_array[1]), "PSComputerName\s.*", 2),
comment => "Work around weird appearance of PSComputerName into System manufacturer";
"dmi[system-manufacturer]" string => nth(split_pscomputername, 0),
meta => { "inventory", "attribute_name=System manufacturer" };
classes:
"have_dmidecode" expression => fileexists($(inventory_control.dmidecoder));
windows.powershell::
"bios_match" expression => regextract(".*Manufacturer\s+:\s([a-zA-Z0-9 ]+)\n.*SerialNumber\W+([a-zA-Z0-9 ]+).*SMBIOSBIOSVersion\W+([a-zA-Z0-9 ]+).*Version\W+([a-zA-Z0-9 -]+)",
execresult("gwmi -query 'SELECT SMBIOSBIOSVersion, Manufacturer, SerialNumber, Version FROM WIN32_BIOS'", "powershell"),
"bios_array");
"processor_match" expression => regextract(".*Name\W+(.*)",
execresult("gwmi -query 'SELECT Name FROM WIN32_PROCESSOR'", "powershell"),
"processor_array");
"system_match" expression => regextract(".*Manufacturer\W+(.*)",
execresult("gwmi -query 'SELECT Manufacturer FROM WIN32_COMPUTERSYSTEM'", "powershell"),
"system_array");
# BEGIN Inventory Total Physical Memory MB
vars:
"total_physical_memory_MB" -> { "CFE-2896" }
string => readfile( "$(sys.statedir)/inventory-$(this.bundle)-total-physical-memory-MB.txt", 100),
meta => { "inventory", "attribute_name=Physical memory (MB)" },
if => fileexists( "$(sys.statedir)/inventory-$(this.bundle)-total-physical-memory-MB.txt" );
commands:
have_dmidecode::
"$(decoder) -t 17 | $(paths.awk) '/Size.*MB/ {s+=$2} END {print s}' > '$(sys.statedir)/inventory-$(this.bundle)-total-physical-memory-MB.txt'" -> { "CFE-2896" }
contain => in_shell,
if => not( fileexists( "$(sys.statedir)/inventory-$(this.bundle)-total-physical-memory-MB.txt") );
files:
"$(sys.statedir)/inventory-$(this.bundle)-total-physical-memory-MB.txt" -> { "CFE-2896" }
delete => tidy,
file_select => older_than(0, 0, 1, 0, 0, 0),
comment => "Clear the cached value for total physical memory MB once a day.";
# END Inventory Total Physical Memory MB
reports:
DEBUG|DEBUG_cfe_autorun_inventory_dmidecode::
"DEBUG $(this.bundle): Obtained $(dmidefs[$(dmivars)]) = '$(dmi[$(dmivars)])'";
"DEBUG $(this.bundle): Obtained Physical memory (MB) = '$(total_physical_memory_MB)'";
}
cfe_autorun_inventory_LLDP
Prototype: cfe_autorun_inventory_LLDP
Description: Do LLDP-based inventory
This agent bundle runs lldpctl to discover information. See http://vincentbernat.github.io/lldpd/ to run this yourself for testing, and your Friendly Network Admin may be of help too.
Implementation:
bundle agent cfe_autorun_inventory_LLDP
{
classes:
"lldpctl_exec_exists" expression => fileexists($(inventory_control.lldpctl_exec));
vars:
!disable_inventory_LLDP.lldpctl_exec_exists::
# TODO When CFE-3108 is DONE, migrate to capturing only stdout
"info" -> { "CFE-3109", "CFE-3108" }
data => parsejson(execresult("$(inventory_control.lldpctl_json) 2>/dev/null", "useshell")),
if => not(isvariable("def.lldpctl_json")),
comment => "Not all versions of lldpctl support json, and because an
absent lldpd will result in an error on stderr resulting noisy logs and
failure to parse the json we redirect to dev null";
"info" -> { "CFE-3109" }
data => parsejson(execresult($(inventory_control.lldpctl_json), "noshell")),
if => isvariable("def.lldpctl_json"),
comment => "For safety, we do not run lldpctl in a shell if the path to
lldpctl is customized via augments";
}
cfe_autorun_inventory_packages
Prototype: cfe_autorun_inventory_packages
Description: Package inventory auto-refresh
This bundle is for refreshing the package inventory. It runs on startup, unless disabled. Other package methods can be added below.
Implementation:
bundle agent cfe_autorun_inventory_packages
{
classes:
"have_patches" or => { "community_edition", # not in Community
fileexists("$(sys.workdir)/state/software_patches_avail.csv") };
"have_inventory" and => { "have_patches",
fileexists("$(sys.workdir)/state/software_packages.csv"),
};
vars:
# if we have the patches, 7 days; otherwise keep trying
"refresh" string => ifelse("have_inventory", "10080",
"0");
packages:
# The legacy implementation (package_method) of the packages type promise
# requires a packages promise to be triggered in order to generate package
# inventory. The following promises ensure that package inventory data
# exists. As package modules become available the package_methods should be
# removed.
suse|sles::
"cfe_internal_non_existing_package"
package_policy => "add",
package_method => inventory_zypper($(refresh)),
action => if_elapsed_day;
aix::
"cfe_internal_non_existing_package"
package_policy => "add",
package_method => inventory_lslpp($(refresh)),
action => if_elapsed_day;
gentoo::
"cfe_internal_non_existing_package"
package_policy => "add",
package_method => emerge,
action => if_elapsed_day;
!redhat.!debian.!gentoo.!(suse|sles).!aix::
"cfe_internal_non_existing_package"
package_policy => "add",
package_method => generic,
action => if_elapsed_day;
reports:
DEBUG|DEBUG_cfe_autorun_inventory_packages::
"DEBUG $(this.bundle): refresh interval is $(refresh)";
"DEBUG $(this.bundle): we have the inventory files."
ifvarclass => "have_inventory";
"DEBUG $(this.bundle): we don't have the inventory files."
ifvarclass => "!have_inventory";
}
cfe_autorun_inventory_policy_servers
Prototype: cfe_autorun_inventory_policy_servers
Description: Inventory policy servers
Implementation:
bundle agent cfe_autorun_inventory_policy_servers
{
vars:
!disable_inventory_policy_servers::
"_primary_policy_server" -> { "ENT-6212" }
string => "$(sys.policy_hub)",
meta => { "inventory", "attribute_name=Primary Policy Server" };
"_policy_servers" -> { "ENT-6212" }
slist => { @(def.policy_servers) },
if => isgreaterthan( length( "def.policy_servers"), 0),
meta => { "inventory", "attribute_name=Policy Servers" };
}
cfe_autorun_inventory_cmdb
Prototype: cfe_autorun_inventory_cmdb
Description: Copy and load the CMDB inventory
This bundle is for refreshing the CMDB inventory. It copies the file me.json from the server, then loads it to create variables and classes.
Implementation:
bundle agent cfe_autorun_inventory_cmdb
{
vars:
"cmdb_dir" string => "$(sys.workdir)/cmdb",
comment => "CMDB directory location",
meta => { "cmdb" };
"cmdb_file" string => "$(cmdb_dir)/me.json",
comment => "CMDB file location",
meta => { "cmdb" };
files:
"$(cmdb_file)"
copy_from => inventory_cmdb_copy_from,
classes => inventory_scoped_classes_generic("bundle", "cmdb_file");
methods:
cmdb_file_ok::
"load CMDB file" usebundle => inventory_cmdb_load($(cmdb_file));
}
inventory_cmdb_load
Prototype: inventory_cmdb_load(file)
Description: Load the CMDB inventory
This bundle is for loading the CMDB inventory.
Arguments:
file
ofclasses
promiser have_cmdb_data ofclasses
promiser $(ckeys): string, used to set promise attributedata
ofvars
promiser cmdb ofvars
promiser cmdb_string ofvars
promiser bkeys ofvars
promiser vkeys_$(bkeys) ofvars
promiser $(vkeys_$(bkeys)) ofvars
promiser ckeys, used as promiser of typereports
ofreports
promiser DEBUG $(this.bundle): Got CMDB key = $(vkeys_$(bkeys)), CMDB value = $((vkeys_$(bkeys))) ofreports
promiser DEBUG $(this.bundle): Got CMDB class = $(ckeys), used as promiser of typereports
Implementation:
bundle agent inventory_cmdb_load(file)
{
classes:
"have_cmdb_data" expression => isvariable("cmdb");
"$(ckeys)" expression => "any", scope => "namespace";
vars:
"cmdb"
data => readjson($(file), "999999"),
ifvarclass => fileexists( $(file) );
"cmdb_string"
string => format("%S", cmdb),
ifvarclass => isvariable( cmdb );
"bkeys" slist => getindices("cmdb[vars]");
"vkeys_$(bkeys)" slist => getindices("cmdb[vars][$(bkeys)]");
"$(vkeys_$(bkeys))" string => nth("cmdb[vars][$(bkeys)]", $(vkeys));
"ckeys" slist => getindices("cmdb[classes]");
reports:
DEBUG|DEBUG_inventory_cmdb_load::
"DEBUG $(this.bundle): Got CMDB data from $(file): $(cmdb_string)"
ifvarclass => "have_cmdb_data";
"DEBUG $(this.bundle): Got CMDB key = $(vkeys_$(bkeys)), CMDB value = $((vkeys_$(bkeys)))"
ifvarclass => "have_cmdb_data";
"DEBUG $(this.bundle): Got CMDB class = $(ckeys)"
ifvarclass => "have_cmdb_data";
"DEBUG $(this.bundle): Could not read the CMDB data from $(file)"
ifvarclass => "!have_cmdb_data";
}
package_method bodies
inventory_lslpp
Prototype: inventory_lslpp(update_interval)
Description: AIX lslpp installation method for inventory purposes only
Arguments:
update_interval
: how often to update the package and patch list
Implementation:
body package_method inventory_lslpp(update_interval)
{
package_changes => "individual";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => $(update_interval);
package_list_command => "/usr/bin/lslpp -Lqc"; # list RPMs too
package_list_version_regex => "[^:]+:[^:]+:([^:]+):.*";
# Make sure version is not included in the name, that indicates RPM
# packages, which we should ignore.
package_list_name_regex => "[^:]+:(([^-:]|-[^0-9])+):.*";
package_installed_regex => "[^:]+:(([^-:]|-[^0-9])+):[^:]+:[^:]+:.*";
package_name_convention => "$(name)-$(version).+";
package_add_command => "/usr/bin/true";
package_update_command => "/usr/bin/true";
package_patch_command => "/usr/bin/true";
package_delete_command => "/usr/bin/true";
package_verify_command => "/usr/bin/true";
}
inventory_zypper
Prototype: inventory_zypper(update_interval)
Description: SUSE zypper installation method for inventory purposes only
Arguments:
update_interval
: how often to update the package and patch list
This package method is a copy of the SUSE zypper method just for inventory purposes.
Implementation:
body package_method inventory_zypper(update_interval)
{
package_changes => "bulk";
package_list_command => "$(paths.path[rpm]) -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\"";
# 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 => $(update_interval);
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) --help >/dev/null 2>&1 ; /bin/true";
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$";
}
copy_from bodies
inventory_cmdb_copy_from
Prototype: inventory_cmdb_copy_from
Description: Copy from the CMDB source
Implementation:
body copy_from inventory_cmdb_copy_from
{
!cfe_inventory_cmdb_override_file::
source => "me.json";
servers => { "$(sys.policy_hub)" };
cfe_inventory_cmdb_override_file::
source => "$(sys.inputdir)/me.json";
any::
compare => "digest";
encrypt => "true";
verify => "true";
}
classes bodies
inventory_scoped_classes_generic
Prototype: inventory_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
Copy of scoped_classes_generic
, which see.
Implementation:
body classes inventory_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)_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" };
}
contain bodies
inventory_in_shell
Prototype: inventory_in_shell
Description: run command in shell
Copy of in_shell
, which see.
Implementation:
body contain inventory_in_shell
{
useshell => "true"; # canonical "useshell" but this is backwards-compatible
}
inventory/macos.cf
This policy is inventory related to macos hosts.
common bodies
inventory_macos
Prototype: inventory_macos
Description: Mac OS X inventory bundle
This common bundle is for Mac OS X inventory work.
Implementation:
bundle common inventory_macos
{
}
inventory/lsb.cf
This policy is inventory related to lsb hosts.
agent bundles
inventory_lsb
Prototype: inventory_lsb
Description: LSB inventory bundle
This common bundle is for LSB inventory work.
Implementation:
bundle agent inventory_lsb
{
classes:
"have_lsb" expression => fileexists($(lsb_exec));
"_inventory_lsb_found" expression => regcmp("^[1-9][0-9]*$", $(dim)),
scope => "namespace";
_inventory_lsb_found::
"lsb_$(os)" expression => "any",
comment => "LSB Distributor ID",
depends_on => { "inventory_lsb_os" },
scope => "namespace",
meta => { "inventory", "attribute_name=none" };
"lsb_$(os)_$(release)" expression => "any",
comment => "LSB Distributor ID and Release",
depends_on => { "inventory_lsb_os", "inventory_lsb_release" },
scope => "namespace",
meta => { "inventory", "attribute_name=none" };
"lsb_$(os)_$(codename)" expression => "any",
comment => "LSB Distributor ID and Codename",
depends_on => { "inventory_lsb_os", "inventory_lsb_codename" },
scope => "namespace",
meta => { "inventory", "attribute_name=none" };
vars:
"lsb_exec" string => "$(inventory_control.lsb_exec)";
have_lsb::
"data" string => execresult("$(lsb_exec) -a", "noshell");
"dim" int => parsestringarray(
"lsb",
$(data),
"\s*#[^\n]*",
"\s*:\s+",
"15",
"4095"
);
_inventory_lsb_found::
"lsb_keys" slist => getindices("lsb");
"os" string => canonify("$(lsb[Distributor ID][1])"),
handle => "inventory_lsb_os",
comment => "LSB-provided OS name",
meta => { "inventory", "attribute_name=none" };
"codename" string => canonify("$(lsb[Codename][1])"),
handle => "inventory_lsb_codename",
comment => "LSB-provided OS code name",
meta => { "inventory", "attribute_name=none" };
"release" string => "$(lsb[Release][1])",
handle => "inventory_lsb_release",
comment => "LSB-provided OS release",
meta => { "inventory", "attribute_name=none" };
"flavor" string => canonify("$(lsb[Distributor ID][1])_$(lsb[Release][1])"),
handle => "inventory_lsb_flavor",
comment => "LSB-provided OS flavor",
meta => { "inventory", "attribute_name=none" };
"description" string => "$(lsb[Description][1])",
handle => "inventory_lsb_description",
comment => "LSB-provided OS description",
meta => { "inventory", "attribute_name=none" };
reports:
(DEBUG|DEBUG_inventory_lsb)._inventory_lsb_found::
"DEBUG $(this.bundle): OS = $(os), codename = $(codename), release = $(release), flavor = $(flavor), description = $(description)";
"DEBUG $(this.bundle): got $(dim) LSB keys";
"DEBUG $(this.bundle): prepared LSB key $(lsb_keys) = '$(lsb[$(lsb_keys)][1])'";
(DEBUG|DEBUG_inventory_lsb).!_inventory_lsb_found::
"DEBUG $(this.bundle): LSB inventory not found";
}
inventory/redhat.cf
This policy is inventory related to redhat hosts.
common bodies
inventory_redhat
Prototype: inventory_redhat
Description: Red Hat inventory bundle
This common bundle is for Red Hat Linux inventory work.
Implementation:
bundle common inventory_redhat
{
classes:
"redhat_pure" expression => "redhat.!(centos|oracle|fedora)",
comment => "pure Red Hat",
meta => { "inventory", "attribute_name=none" };
"redhat_derived" expression => "redhat.!redhat_pure",
comment => "derived from Red Hat",
meta => { "inventory", "attribute_name=none" };
"inventory_redhat_have_python_symlink" expression => fileexists("$(sys.bindir)/python");
inventory_redhat_have_python_symlink::
"cfe_yum_package_module_supported" -> { "CFE-2602" }
comment => "Here we see if the version of python found is acceptable for
the yum package module. We use this guard to prevent errors
related to missing python modules.",
expression => returnszero("$(sys.bindir)/python -V 2>&1 | grep ^Python | cut -d' ' -f 2 | ( IFS=. read v1 v2 v3 ; [ $v1 -ge 3 ] || [ $v1 -eq 2 -a $v2 -ge 4 ] )",
useshell);
}
inventory/freebsd.cf
This policy is inventory related to freebsd hosts.
common bodies
inventory_freebsd
Prototype: inventory_freebsd
Description: FreeBSD inventory bundle
This common bundle is for FreeBSD inventory work.
Implementation:
bundle common inventory_freebsd
{
}
inventory/os.cf
This policy is inventory related to os hosts.
common bodies
inventory_os
Prototype: inventory_os
Implementation:
bundle common inventory_os
{
vars:
_inventory_lsb_found::
"description" string => "$(inventory_lsb.os) $(inventory_lsb.release)",
meta => { "inventory", "attribute_name=OS" };
!_inventory_lsb_found.windows::
"description" string => "$(sys.release)",
meta => { "inventory", "attribute_name=OS" };
!_inventory_lsb_found.!windows::
"description" string => "$(sys.flavor) (LSB missing)",
meta => { "inventory", "attribute_name=OS" };
}
inventory/windows.cf
This policy is inventory related to windows hosts.
common bodies
inventory_windows
Prototype: inventory_windows
Description: Windows inventory bundle
This common bundle is for Windows inventory work.
Implementation:
bundle common inventory_windows
{
}
inventory/debian.cf
This policy is inventory related to debian hosts.
common bodies
inventory_debian
Prototype: inventory_debian
Description: Debian inventory
This common bundle is for Debian inventory work.
Implementation:
bundle common inventory_debian
{
vars:
has_lsb_release::
"lsb_release_info" string => readfile("/etc/lsb-release","256"),
comment => "Read more OS info" ;
has_etc_linuxmint_info::
"linuxmint_info" string => readfile("/etc/linuxmint/info","1024"),
comment => "Read Linux Mint specific info" ;
"lm_info_count"
int => parsestringarray("mint_info", # array to populate
"$(linuxmint_info)", # data to parse
"\s*#[^\n]*", # comments
"=", # split
100, # maxentries
2048) ; # maxbytes
"mint_release" string => "$(mint_info[RELEASE][1])" ;
"mint_codename" string => "$(mint_info[CODENAME][1])" ;
classes:
any::
"debian_derived_evaluated"
scope => "bundle",
or => { "has_os_release", "has_lsb_release", "has_etc_linuxmint_info" } ;
"linuxmint"
expression => "has_etc_linuxmint_info",
comment => "this is a Linux Mint system, of some sort",
meta => { "inventory", "attribute_name=none" } ;
has_lsb_release::
"linuxmint"
expression => regcmp("(?ms).*^DISTRIB_ID=LinuxMint$.*", "$(lsb_release_info)"),
comment => "this is a Linux Mint system, of some sort",
meta => { "inventory", "attribute_name=none" } ;
linuxmint.has_os_release::
"lmde"
expression => regcmp('(?ms).*^NAME="Linux Mint LMDE"$.*', "$(inventory_linux.os_release_info)"),
comment => "this is a Linux Mint Debian Edition",
meta => { "inventory", "attribute_name=none", "derived-from=inventory_linux.os_release_info" } ;
linuxmint.has_lsb_release::
"lmde"
expression => regcmp('(?ms).*^DISTRIB_DESCRIPTION="LMDE.*', "$(lsb_release_info)"),
comment => "this is a Linux Mint Debian Edition",
meta => { "inventory", "attribute_name=none", "derived-from=inventory_debian.lsb_release_info" } ;
has_etc_linuxmint_info::
"lmde"
expression => regcmp('(?ms).*^DESCRIPTION="LMDE.*',"$(linuxmint_info)"),
comment => "this is a Linux Mint Debian Edition",
meta => { "inventory", "attribute_name=none", "derived-from=inventory_debian.linuxmint_info" } ;
debian_derived_evaluated.has_etc_linuxmint_info.!lmde::
# These need to be evaluated only after debian_derived_evaluated is defined
# to ensure that the mint_info array has been evaluated as well.
# Failing to do that will create meaningless classes
# On non-LMDE Mint systems, this will create classes like, e.g.:
# linuxmint_14, nadia, linuxmint_nadia
"linuxmint_$(mint_release)" expression => "any",
meta => { "inventory", "attribute_name=none" } ;
"$(mint_codename)" expression => "any",
meta => { "inventory", "attribute_name=none" } ;
"linuxmint_$(mint_codename)" expression => "any",
meta => { "inventory", "attribute_name=none" } ;
debian_derived_evaluated::
"debian_pure" expression => "debian.!(ubuntu|linuxmint)",
comment => "pure Debian",
meta => { "inventory", "attribute_name=none" };
"debian_derived" expression => "debian.!debian_pure",
comment => "derived from Debian",
meta => { "inventory", "attribute_name=none" };
any::
"has_lsb_release" expression => fileexists("/etc/lsb-release"),
comment => "Check if we can get more info from /etc/lsb-release";
"has_etc_linuxmint_info" expression => fileexists("/etc/linuxmint/info"),
comment => "If this is a Linux Mint system, this *could* be available";
}
inventory/generic.cf
This policy is inventory related to generic hosts.
common bodies
inventory_generic
Prototype: inventory_generic
Description: Generic (unknown OS) inventory
This common bundle is for unknown operating systems, not handled by specific bundles.
Implementation:
bundle common inventory_generic
{
}
services/autorun/
This directory is a user entry. Policy files placed in the root of this directory are automatically added to inputs when the class services_autorun
is defined.
services/
This directory is the suggested place to add your custom policies.
services/main.cf
This directory is the suggested place to add your custom policies.
standalone_self_upgrade.cf
$(sys.inputdir)/standalone_self_upgrade.cf
is an independent policy set entry
like promises.cf
and update.cf
. The policy is executed by an independent
agent executed from the update.cf
entry when the class trigger_upgrade
is
defined and the host is not seen to be running the desired version of the agent.
The policy is designed for use with Enterprise packages, but can be customized
for use with community packages.
common bodies
standalone_self_upgrade_file_control
Prototype: standalone_self_upgrade_file_control
Implementation:
bundle common standalone_self_upgrade_file_control
{
vars:
"inputs" slist => { "$(this.promise_dirname)$(const.dirsep)cfe_internal$(const.dirsep)update$(const.dirsep)windows_unattended_upgrade.cf" };
}
agent bundles
main
Prototype: main
Description: This bundle drives the self upgrade. It actuates the appropriate bundles to download binaries to the hub for serving to clients, caching the software to remote clients, and managing the version of cfengine installed on non hubs.
Implementation:
bundle agent main
{
classes:
"policy_server_dat_unstable"
expression => isnewerthan( "$(sys.workdir)/policy_server.dat", "$(sys.workdir)/outputs" ),
comment => "If $(sys.workdir)/policy_server.dat is newer than the
outputs directory, it can indicate that the current agent
execution is a result of bootstrap. For stability we want to
skip upgrades during bootstrap. The outputs directory should
be newer than the policy_server.dat on the next agent run
and allow upgrade then.";
reports:
"Running $(this.promise_filename)";
methods:
"cfengine_software";
am_policy_hub|policy_server::
"Master Software Repository Data"
usebundle => cfengine_master_software_content;
!(am_policy_hub|policy_server|policy_server_dat_unstable)::
"Local Software Cache"
usebundle => cfengine_software_cached_locally;
"CFEngine Version"
usebundle => cfengine_software_version;
}
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|ubuntu::
"platform_default" string => "apt_get";
redhat|centos|amazon_linux::
"platform_default" string => "yum";
}
u_common_knowledge
Prototype: u_common_knowledge
Description: standalone common packages knowledge bundle
This common bundle defines general things about platforms.
Implementation:
bundle common u_common_knowledge
{
vars:
"list_update_ifelapsed_now" string => "10080";
}
agent bundles
cfengine_software
Prototype: cfengine_software
Implementation:
bundle agent cfengine_software
{
vars:
# Default desired CFEngine software
"pkg_name" string => "cfengine-nova";
"pkg_version" string => "3.12.8a.ff51a7d2f";
"pkg_release" string => "1";
"pkg_arch" string => "x86_64";
"package_dir" string => "$(sys.flavour)_$(sys.arch)";
!(cfengine_3_7_1|cfengine_3_7_2|cfengine_3_7_3)::
# After 3.7.4 a fix to ifelse and isvarible allows for actuating the function
# even thought the promise references an unresolved variable.
"pkg_name" string => ifelse( isvariable( "def.cfengine_software_pkg_name" ), $(def.cfengine_software_pkg_name), "cfengine-nova");
"pkg_version" string => ifelse( isvariable( "def.cfengine_software_pkg_version" ), $(def.cfengine_software_pkg_version), "3.12.8a.ff51a7d2f");
"pkg_release" string => ifelse( isvariable( "def.cfengine_software_pkg_release" ), $(def.cfengine_software_pkg_release), "1");
"pkg_arch" string => ifelse( isvariable( "def.cfengine_software_pkg_arch" ), $(def.cfengine_software_pkg_arch), "x86_64");
"package_dir" string => ifelse( isvariable( "def.cfengine_software_pkg_dir" ), $(def.cfengine_software_pkg_dir), "$(sys.flavour)_$(sys.arch)");
aix::
"pkg_name" string => "cfengine.cfengine-nova";
"pkg_arch" string => "default";
# TODO ENT-3187
solaris|solarisx86::
"pkg_name" string => "CFE.cfengine-nova";
(debian|ubuntu).64_bit::
"pkg_arch"
string => "amd64",
comment => "On debian hosts it's the standard to use 'amd64' instead of
'x86_64' in package architectures.";
(redhat|centos|suse|sles).32_bit::
"pkg_arch"
string => "i386",
comment => "i686 is the detected architecture, but the package is
compatible from i386 up.";
hpux::
"package_dir"
string => "$(sys.class)_$(sys.arch)",
comment => "The directory within software updates to look for packages.
On HPUX sys.flavor includes versions, so we use sys.class
instead.";
any::
"local_software_dir"
string => translatepath( "$(sys.workdir)/software_updates/$(package_dir)" ),
comment => "So that we converge on the first pass we set this last as
package_dir may vary across platforms.";
reports:
DEBUG|DEBUG_cfengine_software::
"$(this.bundle) pkg_name = $(pkg_name)";
"$(this.bundle) pkg_version = $(pkg_version)";
"$(this.bundle) pkg_release = $(pkg_release)";
"$(this.bundle) pkg_arch = $(pkg_arch)";
"$(this.bundle) package_dir = $(package_dir)";
files:
"$(sys.statedir)/MPF/."
create => "true";
"$(sys.statedir)/MPF/desired-cfengine-package-version.json"
create => "true";
"$(sys.statedir)/MPF/desired-cfengine-package-version.json" -> { "ENT-3592", "ENT-3937" }
edit_line => cfengine_software_version_data;
windows::
"$(sys.bindir)$(const.dirsep)vercmp.ps1"
create => "true",
template_method => "mustache",
edit_template => "$(this.promise_dirname)$(const.dirsep)/templates/vercmp.ps1",
template_data => mergedata( '{}' ),
comment => "We need to use specialized version comparison logic for unattended self upgrades.";
}
edit_line bundles
cfengine_software_version_data
Prototype: cfengine_software_version_data
Description: Populate data file with the package information The data this bundle writes is used by the update policy to determine if the standalone self upgrade policy should be run or not. TODO Consider replacing with inline_mustache after 3.10.x is EOL
Implementation:
bundle edit_line cfengine_software_version_data
{
delete_lines: ".*";
insert_lines: '{
"name": "$(cfengine_software.pkg_name)",
"version": "$(cfengine_software.pkg_version)",
"release": "$(cfengine_software.pkg_release)",
"arch": "$(cfengine_software.pkg_arch)"
}';
}
agent bundles
cfengine_software_cached_locally
Prototype: cfengine_software_cached_locally
Description: Ensure that the internal local software mirror is up to date
Implementation:
bundle agent cfengine_software_cached_locally
{
reports:
inform_mode::
"Ensuring local software cache in $(local_software_dir) is up to date";
vars:
"local_software_dir"
string => "$(cfengine_software.local_software_dir)";
"package_dir"
string => "$(cfengine_software.package_dir)";
"master_software_location" -> { "ENT-4953" }
string => "master_software_updates",
comment => "The Cfengine binary updates directory on the policy server",
handle => "cfe_internal_update_bins_vars_master_software_location";
files:
"$(local_software_dir)/."
create => "true",
comment => "Ensure the local software directory exists for new binaries
to be downloaded to";
# NOTE This is pegged to the single upstream policy hub, it won't fail
# over to a secondary for copying the binarys to update.
"$(local_software_dir)"
comment => "Copy binary updates from master source on policy server",
handle => "cfe_internal_update_bins_files_pkg_copy",
copy_from => u_dsync( "$(master_software_location)/$(package_dir)", $(sys.policy_hub) ),
file_select => plain,
depth_search => u_recurse_basedir(inf),
action => u_immediate,
classes => u_if_repaired("bin_newpkg");
}
cfengine_software_version
Prototype: cfengine_software_version
Description: Ensure the version of CFEngine installed is correct for supported platforms. Different platforms leverage different implementations for self upgrading.
Implementation:
bundle agent cfengine_software_version
{
classes:
"__supported_platform" -> { "ENT-5045", "ENT-5152", "ENT-4094" }
or => {
"redhat.!redhat_4",
"centos.!centos_4",
"debian",
"ubuntu",
"hpux",
"aix",
"SUSE_11",
"SUSE_12",
"windows", # ENT-4094
};
# Add "windows" to __new_implementation classes with ENT-6823
"__new_implementation"
or => { "redhat", "centos", "ubuntu", "debian" };
vars:
"pkg_name" string => "$(cfengine_software.pkg_name)";
"pkg_version" string => "$(cfengine_software.pkg_version)";
"pkg_release" string => "$(cfengine_software.pkg_release)";
"_cf_version_release" string => ifelse( isvariable( "sys.cf_version_release" ), "$(sys.cf_version_release)", "1" );
"pkg_arch" string => "$(cfengine_software.pkg_arch)";
"package_dir" string => "$(cfengine_software.package_dir)";
"local_software_dir" string => "$(cfengine_software.local_software_dir)";
methods:
__supported_platform.__new_implementation::
"Manage CFEngine Version"
usebundle => cfengine_software_version_packages2;
__supported_platform.!__new_implementation::
"Manage CFEngine Version"
usebundle => cfengine_software_version_packages1;
# TODO, remove this and cfe_internal/enterprise/windows_unattended_upgrade.cf
# when ENT-6823 allows us to use msiexec.bat packages module.
"Windows Unattended Upgrade Workaround"
usebundle => windows_unattended_upgrade,
ifvarclass => and(
"windows",
or(
not(strcmp("$(cfengine_software.pkg_version)", "$(sys.cf_version)")),
not(strcmp("$(cfengine_software.pkg_release)", "$(_cf_version_release)"))
)
);
reports:
!__supported_platform.inform_mode::
"$(this.bundle) $(package_dir) is not supported";
}
cfengine_software_version_packages2
Prototype: cfengine_software_version_packages2
Implementation:
bundle agent cfengine_software_version_packages2
{
vars:
"pkg_name" string => "$(cfengine_software.pkg_name)";
"pkg_version" string => "$(cfengine_software.pkg_version)";
"pkg_release" string => "$(cfengine_software.pkg_release)";
"pkg_arch" string => "$(cfengine_software.pkg_arch)";
"package_dir" string => "$(cfengine_software.package_dir)";
"local_software_dir" string => "$(cfengine_software.local_software_dir)";
packages:
(redhat|centos).!__supported::
"$(local_software_dir)/$(cfengine_package_names.my_pkg)"
policy => "present",
package_module => yum,
comment => "Ensure the latest package is installed";
(debian|ubuntu).!__supported::
"$(local_software_dir)/$(cfengine_package_names.my_pkg)"
policy => "present",
package_module => apt_get,
comment => "Ensure the latest package is installed";
# TODO, uncomment the following to enable msiexec packages module (ENT-6823)
# windows::
# "$(local_software_dir)$(const.dirsep)$(cfengine_package_names.my_pkg)"
# policy => "present",
# package_module => msiexec,
# comment => "Ensure the latest package is installed";
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"Running $(this.bundle)";
}
cfengine_software_version_packages1
Prototype: cfengine_software_version_packages1
Implementation:
bundle agent cfengine_software_version_packages1
{
classes:
"cf_upgrade" expression => "(redhat|suse|sles|debian|solaris|solarisx86).!(am_policy_hub|policy_server)";
vars:
# NOTE These logs are not actively used or cleaned up by anything. Their
# use will be phased as platforms migrate to the new packages
# implementation for self upgrades.
"local_update_log_dir"
string => translatepath("$(sys.workdir)/software_updates/update_log"),
comment => "Local directory to store update log for this host.",
handle => "cfe_internal_update_bins_vars_local_update_log_dir";
"local_software_dir" string => "$(cfengine_software.local_software_dir)";
"desired_version" -> { "ENT-4094" }
string => ifelse("linux", "$(cfengine_software.pkg_version)-$(cfengine_software.pkg_release)",
"windows", "$(cfengine_software.pkg_version).$(cfengine_software.pkg_release)", # ENT-4094
"aix", "$(cfengine_software.pkg_version).0",
$(cfengine_software.pkg_version) ),
comment => "The version attribute sometimes contains package release
information and sometimes does not. Here we construct the
version used in the package promise for the given
platform.";
cf_upgrade::
# We only use cf-upgrade for some platforms, the need for it has been
# deprecated by the new packages promise implementation.
# backup script for cf-upgrade
# the script should have 2 conditions, BACKUP and RESTORE
# BACKUP and RESTORE status is $(const.dollar)1 variable in the script
# see more details at bundle edit_line u_backup_script
# NOTE cf-upgrade wants to execute from /tmp by default. This is
# problematic for systems where /tmp is mounted with no-exec.
"backup_script" string => "/tmp/cf-upgrade_backup.sh";
# a single compressed backup file for cf-upgrade
# this backup_file is passed to backup_script as $(const.dollar)2 variable
# cf-upgrade will extract this file if return signal of upgrade command is not 0
"backup_file" string => "/tmp/cfengine-nova-$(sys.cf_version).tar.gz";
# install script for cf-upgrade
# each distribution has its own way to upgrade a package
# see more details at bundle edit_line u_install_script
"install_script" string => "/tmp/cf-upgrade_install.sh";
(solarisx86|solaris).enterprise::
# to automatically remove or install packages on Solaris
# admin_file is a must to have to avoid pop-up interaction
# see more details at bundle edit_line u_admin_file
"admin_file" string => "/tmp/cf-upgrade_admin_file";
files:
# Remote enterprise agents (non policy hubs) that have `trigger_upgrade` defined
cf_upgrade.enterprise.trigger_upgrade::
"$(backup_script)"
comment => "Create a backup script for cf-upgrade",
handle => "cfe_internal_update_bins_files_backup_script",
create => "true",
ifvarclass => "!windows",
edit_defaults => u_empty_no_backup,
edit_line => u_backup_script,
perms => u_m("0755");
"$(install_script)"
comment => "Create an install script for cf-upgrade",
handle => "cfe_internal_update_bins_files_install_script",
create => "true",
ifvarclass => "!windows",
edit_defaults => u_empty_no_backup,
edit_line => u_install_script,
perms => u_m("0755");
"$(admin_file)"
comment => "Create solaris admin_file to automate remove and install packages",
handle => "cfe_internal_update_bins_files_solaris_admin_file",
create => "true",
edit_defaults => u_empty_no_backup,
edit_line => u_admin_file,
perms => u_m("0644"),
ifvarclass => "solarisx86|solaris";
packages:
# Only non policy hubs running are allowed to self upgrade
# We don't upgrade during bootstrap
!(am_policy_hub|policy_server|bootstrap_mode).enterprise_edition::
"$(cfengine_software.pkg_name)"
comment => "Update Nova package to a newer version",
handle => "cfe_internal_update_bins_packages_nova_update",
package_policy => "update",
package_select => "==",
package_architectures => { "$(cfengine_software.pkg_arch)" },
package_version => "$(desired_version)",
package_method => u_generic( $(cfengine_software.local_software_dir) ),
classes => u_if_else("bin_update_success", "bin_update_fail");
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"Running $(this.bundle)";
}
common bodies
cfengine_package_names
Prototype: cfengine_package_names
Implementation:
bundle common cfengine_package_names
{
vars:
"pkg_name" string => "$(cfengine_software.pkg_name)";
"pkg_version" string => "$(cfengine_software.pkg_version)";
"pkg_release" string => "$(cfengine_software.pkg_release)";
"pkg_arch" string => "$(cfengine_software.pkg_arch)";
# Redhat/Centos/Oracle 5, SLES 11 use the same package
"pkg[redhat_5_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el5.centos.x86_64.rpm";
"pkg[centos_5_x86_64]" string => "$(pkg[redhat_5_x86_64])";
"pkg[oracle_5_x86_64]" string => "$(pkg[redhat_5_x86_64])";
"pkg[SuSE_11_x86_64]" string => "$(pkg[redhat_5_x86_64])";
# 32bit RPMs
"pkg[$(cfengine_master_software_content._rpm_dists)_$(cfengine_master_software_content._32bit_arches)]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el5.centos.i386.rpm";
# Redhat/Centos/Oracle 6, SLES 12-15 use the same package
"pkg[redhat_6_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el6.x86_64.rpm";
"pkg[centos_6_x86_64]" string => "$(pkg[redhat_6_x86_64])";
"pkg[oracle_6_x86_64]" string => "$(pkg[redhat_6_x86_64])";
"pkg[SuSE_12_x86_64]" string => "$(pkg[redhat_6_x86_64])";
"pkg[SuSE_15_x86_64]" string => "$(pkg[redhat_6_x86_64])";
# Redhat/Centos/Oracle 7 use the same package
"pkg[redhat_7_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el7.x86_64.rpm";
"pkg[centos_7_x86_64]" string => "$(pkg[redhat_7_x86_64])";
"pkg[oracle_7_x86_64]" string => "$(pkg[redhat_7_x86_64])";
# Redhat/Centos/Oracle 8 use the same package
"pkg[redhat_8_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release).el8.x86_64.rpm";
"pkg[centos_8_x86_64]" string => "$(pkg[redhat_8_x86_64])";
"pkg[oracle_8_x86_64]" string => "$(pkg[redhat_8_x86_64])";
# 64bit Debian
"pkg[debian_7_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian7_amd64.deb";
"pkg[debian_8_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian8_amd64.deb";
"pkg[debian_9_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian9_amd64.deb";
"pkg[debian_10_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian10_amd64.deb";
# 64bit Ubuntu
"pkg[ubuntu_14_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu14_amd64.deb";
"pkg[ubuntu_16_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu16_amd64.deb";
"pkg[ubuntu_18_x86_64]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).ubuntu18_amd64.deb";
# 32bit DEBs
"pkg[$(cfengine_master_software_content._deb_dists)_$(cfengine_master_software_content._32bit_arches)]" string => "$(pkg_name)_$(pkg_version)-$(pkg_release).debian7_i386.deb";
# Windows
"pkg[windows_x86_64]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release)-x86_64.msi";
"pkg[windows_i686]" string => "$(pkg_name)-$(pkg_version)-$(pkg_release)-i686.msi";
"my_pkg"
string => "$(pkg[$(sys.flavor)_$(sys.arch)])",
comment => "The package name for the currently executing platform.";
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"My Package: $(my_pkg)";
}
agent bundles
cfengine_master_software_content
Prototype: cfengine_master_software_content
Implementation:
bundle agent cfengine_master_software_content
{
vars:
"pkg_name" string => "$(cfengine_software.pkg_name)";
"pkg_version" string => "$(cfengine_software.pkg_version)";
"pkg_release" string => "$(cfengine_software.pkg_release)";
"pkg_arch" string => "$(cfengine_software.pkg_arch)";
"package_dir" string => "$(cfengine_software.package_dir)";
"base_url" string => "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-$(pkg_version)/agent";
# Map platform/directory identifier to upstream package URLs
# Better to read in an external explicit data structure?
"_32bit_arches" slist => { "i386", "i586", "i686" };
# Redhat/Centos/Oracle 5 and SuSE 11 all use the same package
"dir[redhat_5_x86_64]" string => "agent_rpm_x86_64";
"dir[centos_5_x86_64]" string => "$(dir[redhat_5_x86_64])";
"dir[oracle_5_x86_64]" string => "$(dir[redhat_5_x86_64])";
"dir[SuSE_11_x86_64]" string => "$(dir[redhat_5_x86_64])";
# All 32bit rpms use the same package
"_rpm_dists" slist => { "redhat_5", "redhat_6", "redhat_7",
"centos_5", "centos_6", "centos_7",
"SuSE_11", "SuSE_10" };
"dir[$(_rpm_dists)_$(_32bit_arches)]" string => "agent_rpm_i386";
# Redhat/Centos/Oracle 6 use the same package
"dir[redhat_6_x86_64]" string => "agent_rhel6_x86_64";
"dir[centos_6_x86_64]" string => "$(dir[redhat_6_x86_64])";
"dir[oracle_6_x86_64]" string => "$(dir[redhat_6_x86_64])";
# Redhat/Centos/Oracle 7 use the same package
"dir[redhat_7_x86_64]" string => "agent_rhel7_x86_64";
"dir[centos_7_x86_64]" string => "$(dir[redhat_7_x86_64])";
"dir[oracle_7_x86_64]" string => "$(dir[redhat_7_x86_64])";
# Redhat/Centos/Oracle 8 use the same package
"dir[redhat_8_x86_64]" string => "agent_rhel8_x86_64";
"dir[centos_8_x86_64]" string => "$(dir[redhat_8_x86_64])";
"dir[oracle_8_x86_64]" string => "$(dir[redhat_8_x86_64])";
# Debian
"dir[debian_7_x86_64]" string => "agent_deb_x86_64";
"dir[debian_8_x86_64]" string => "agent_debian8_x86_64";
"dir[debian_9_x86_64]" string => "agent_debian9_x86_64";
"dir[debian_10_x86_64]" string => "agent_debian10_x86_64";
# Ubuntu
"dir[ubuntu_14_x86_64]" string => "agent_ubuntu14_x86_64";
"dir[ubuntu_16_x86_64]" string => "agent_ubuntu16_x86_64";
"dir[ubuntu_18_x86_64]" string => "agent_ubuntu18_x86_64";
# All 32bit debs use the same package
"_deb_dists" slist => { "debian_4", "debian_5", "debian_6",
"debian_7", "debian_8", "debian_9",
"debian_10", "ubuntu_14", "ubuntu_16",
"ubuntu_18" };
"dir[$(_deb_dists)_$(_32bit_arches)]" string => "agent_deb_i386";
"platform_dir" slist => getindices( dir );
"download_dir" string => "$(sys.workdir)/master_software_updates";
files:
"$(download_dir)/$(platform_dir)/."
create => "true",
comment => "We need a place to download each packge we build";
commands:
# Fetch each package that we don't already have
"/usr/bin/curl"
args => "-s $(base_url)/$(dir[$(platform_dir)])/$(cfengine_package_names.pkg[$(platform_dir)]) --output /var/cfengine/master_software_updates/$(platform_dir)/$(cfengine_package_names.pkg[$(platform_dir)])",
if => not( fileexists( "$(download_dir)/$(platform_dir)/$(cfengine_package_names.pkg[$(platform_dir)])" ) );
reports:
DEBUG|DEBUG_cfengine_master_software_content::
"curl -s $(base_url)/$(dir[$(platform_dir)])/$(cfengine_package_names.pkg[$(platform_dir)]) --output $(download_dir)/$(platform_dir)/$(cfengine_package_names.pkg[$(platform_dir)])";
}
edit_line bundles
u_backup_script
Prototype: u_backup_script
Description: Backup script used by cf-upgrade
Implementation:
bundle edit_line u_backup_script
{
insert_lines:
linux::
"#!/bin/sh
if [ $(const.dollar)1 = \"BACKUP\" ]; then
tar cfzS $(const.dollar)2 $(sys.workdir) > /dev/null
fi
if [ $(const.dollar)1 = \"RESTORE\" ]; then
tar xfz $(const.dollar)2
fi";
solarisx86|solaris::
"#!/bin/sh
if [ $(const.dollar)1 = \"BACKUP\" ]; then
tar cf $(const.dollar)2 $(sys.workdir); gzip $(const.dollar)2
fi
if [ $(const.dollar)1 = \"RESTORE\" ]; then
gunzip $(const.dollar)2.gz; tar xf $(const.dollar)2
fi";
}
u_install_script
Prototype: u_install_script
Description: Install script used by cf-upgrade
Implementation:
bundle edit_line u_install_script
{
insert_lines:
redhat|suse|sles::
"#!/bin/sh
/bin/rpm -U $(const.dollar)1";
debian::
"#!/bin/sh
/usr/bin/dpkg --force-confdef --force-confnew --install $(const.dollar)1 > /dev/null";
solarisx86|solaris::
"#!/bin/sh
pkgname=`pkginfo -d $(const.dollar)1 | awk '{print $(const.dollar)2}'`
/usr/sbin/pkgrm -n -a $(cfengine_software_version_packages1.admin_file) $pkgname
/usr/sbin/pkgadd -n -a $(cfengine_software_version_packages1.admin_file) -d $(const.dollar)1 all
$(sys.workdir)/bin/cf-execd || true
exit 0";
}
u_admin_file
Prototype: u_admin_file
Description: Admin file specification to enable unattended installation
Implementation:
bundle edit_line u_admin_file
{
insert_lines:
sunos_5_8::
"mail=
instance=unique
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=nocheck
setuid=nocheck
conflict=nocheck
action=nocheck
basedir=default";
solaris.!sunos_5_8::
"mail=
instance=overwrite
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";
}
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(standalone_self_upgrade_file_control.inputs) };
}
action bodies
u_immediate
Prototype: u_immediate
Implementation:
body action u_immediate
{
ifelapsed => "0";
}
copy_from bodies
u_dsync
Prototype: u_dsync(from, server)
Arguments:
from
: string, used in the value of attributesource
server
Implementation:
body copy_from u_dsync(from,server)
{
# NOTE policy servers cheat and copy directly from the local file system.
# This works even if cf-serverd is down and it makes sense if your serving
# yourself.
source => "$(from)";
compare => "digest";
trustkey => "false";
purge => "true";
!am_policy_hub::
servers => { "$(server)" };
cfengine_internal_encrypt_transfers::
encrypt => "true";
}
classes bodies
u_if_repaired
Prototype: u_if_repaired(x)
Arguments:
x
Implementation:
body classes u_if_repaired(x)
{
promise_repaired => { "$(x)" };
}
u_if_else
Prototype: u_if_else(yes, no)
Arguments:
yes
no
Implementation:
body classes u_if_else(yes,no)
{
# promise_kept => { "$(yes)" };
promise_repaired => { "$(yes)" };
repair_failed => { "$(no)" };
repair_denied => { "$(no)" };
repair_timeout => { "$(no)" };
}
common bodies
control
Prototype: control
Implementation:
body common control
{
version => "CFEngine Standalone Self Upgrade 3.12.8a.ff51a7d2f";
(debian|ubuntu)::
package_inventory => { $(package_module_knowledge.platform_default) };
# We only define pacakge_inventory on redhat like systems that have a
# python version that works with the package module.
(redhat|centos)::
package_inventory => { $(package_module_knowledge.platform_default) };
(debian|redhat)::
package_module => $(package_module_knowledge.platform_default);
}
depth_search bodies
u_recurse_basedir
Prototype: u_recurse_basedir(d)
Arguments:
d
: string, used in the value of attributedepth
Implementation:
body depth_search u_recurse_basedir(d)
{
include_basedir => "true";
depth => "$(d)";
exclude_dirs => { "\.svn", "\.git", "git-core" };
}
edit_defaults bodies
u_empty_no_backup
Prototype: u_empty_no_backup
Description: Do not create backups and ensure we are promising the entire content of the file.
Implementation:
body edit_defaults u_empty_no_backup
{
empty_file_before_editing => "true";
edit_backup => "false";
}
file_select bodies
plain
Prototype: plain
Description: Select plain, regular files
Implementation:
body file_select plain
{
file_types => { "plain" };
file_result => "file_types";
}
package_method bodies
u_generic
Prototype: u_generic(repo)
Arguments:
repo
: string, used in the value of attributepackage_add_command
: string, used in the value of attributepackage_add_command
Implementation:
body package_method u_generic(repo)
{
debian::
package_changes => "individual";
package_list_command => "/usr/bin/dpkg -l";
# package_list_update_command => "/usr/bin/apt-get update";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "ii\s+([^\s:]+).*";
# package_list_version_regex => "ii\s+[^\s]+\s+([^\s]+).*";
package_list_version_regex => "ii\s+[^\s]+\s+(\d+\.\d+((\.|-)\d+)+).*";
package_installed_regex => ".*"; # all reported are installed
package_file_repositories => { "$(repo)" };
package_version_equal_command => "/usr/bin/dpkg --compare-versions '$(v1)' eq '$(v2)'";
package_version_less_command => "/usr/bin/dpkg --compare-versions '$(v1)' lt '$(v2)'";
debian.x86_64::
package_name_convention => "$(name)_$(version)_amd64.deb";
debian.i686::
package_name_convention => "$(name)_$(version)_i386.deb";
debian::
package_add_command => "/usr/bin/dpkg --force-confdef --force-confnew --install";
package_delete_command => "/usr/bin/dpkg --purge";
redhat|SuSE|suse|sles::
package_changes => "individual";
package_list_command => "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\"";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "[^|]+\|[^|]+\|\s+([^\s|]+).*";
package_list_version_regex => "[^|]+\|[^|]+\|[^|]+\|\s+([^\s|]+).*";
package_list_arch_regex => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*";
package_installed_regex => "i.*";
package_file_repositories => { "$(repo)" };
package_name_convention => "$(name)-$(version).$(arch).rpm";
package_add_command => "/bin/rpm -ivh ";
package_delete_command => "/bin/rpm -e --nodeps";
package_verify_command => "/bin/rpm -V";
package_noverify_regex => ".*[^\s].*";
package_version_less_command => "$(sys.bindir)/rpmvercmp '$(v1)' lt '$(v2)'";
package_version_equal_command => "$(sys.bindir)/rpmvercmp '$(v1)' eq '$(v2)'";
(redhat|SuSE|suse|sles|debian|solarisx86|solaris)::
package_update_command => "$(sys.workdir)/bin/cf-upgrade -b $(cfengine_software_version_packages1.backup_script) -s $(cfengine_software_version_packages1.backup_file) -i $(cfengine_software_version_packages1.install_script)";
redhat.!redhat_4::
package_list_update_command => "/usr/bin/yum --quiet check-update";
redhat_4::
package_list_update_command => "/usr/bin/yum check-update";
SuSE|suse|sles::
package_list_update_command => "/usr/bin/zypper list-updates";
windows::
package_changes => "individual";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_file_repositories => { "$(repo)" };
package_installed_regex => ".*";
package_name_convention => "$(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";
package_version_less_command => '$(sys.winsysdir)$(const.dirsep)WindowsPowerShell$(const.dirsep)v1.0$(const.dirsep)powershell.exe "$(sys.bindir)$(const.dirsep)vercmp.ps1" "$(v1)" "lt" "$(v2)"';
package_version_equal_command => '$(sys.winsysdir)$(const.dirsep)WindowsPowerShell$(const.dirsep)v1.0$(const.dirsep)powershell.exe "$(sys.bindir)$(const.dirsep)vercmp.ps1" "$(v1)" "eq" "$(v2)"';
freebsd::
package_changes => "individual";
package_list_command => "/usr/sbin/pkg_info";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "^(\S+)-(\d+\.?)+";
package_list_version_regex => "^\S+-((\d+\.?)+\_\d)";
package_file_repositories => { "$(repo)" };
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version).tbz";
package_delete_convention => "$(name)-$(version)";
package_add_command => "/usr/sbin/pkg_add";
package_delete_command => "/usr/sbin/pkg_delete";
netbsd::
package_changes => "individual";
package_list_command => "/usr/sbin/pkg_info";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_name_regex => "^(\S+)-(\d+\.?)+";
package_list_version_regex => "^\S+-((\d+\.?)+\nb\d)";
package_file_repositories => { "$(repo)" };
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version).tgz";
package_delete_convention => "$(name)-$(version)";
package_add_command => "/usr/sbin/pkg_add";
package_delete_command => "/usr/sbin/pkg_delete";
solarisx86|solaris::
package_changes => "individual";
package_list_command => "/usr/bin/pkginfo -l";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_multiline_start => "\s*PKGINST:\s+[^\s]+";
package_list_name_regex => "\s*PKGINST:\s+([^\s]+)";
package_list_version_regex => "\s*VERSION:\s+([^\s]+)";
package_list_arch_regex => "\s*ARCH:\s+([^\s]+)";
package_file_repositories => { "$(repo)" };
package_installed_regex => "\s*STATUS:\s*(completely|partially)\s+installed.*";
package_name_convention => "$(name)-$(version)-$(arch).pkg";
package_delete_convention => "$(name)";
# Cfengine appends path to package and package name below, respectively
package_add_command => "/bin/sh $(repo)/add_scr $(repo)/admin_file";
package_delete_command => "/usr/sbin/pkgrm -n -a $(repo)/admin_file";
aix::
package_changes => "individual";
package_list_update_command => "/usr/bin/true";
package_list_update_ifelapsed => "$(u_common_knowledge.list_update_ifelapsed_now)";
package_list_command => "/usr/bin/lslpp -lc";
package_list_name_regex => "[^:]+:([^:]+):[^:]+:.*";
package_list_version_regex => "[^:]+:[^:]+:([^:]+):.*";
package_file_repositories => { "$(repo)" };
package_installed_regex => "[^:]+:[^:]+:[^:]+:[^:]*:(COMMITTED|APPLIED):.*";
package_name_convention => "$(name)-$(version).bff";
package_delete_convention => "$(name)";
# Redirecting the output to '/dev/null' below makes sure 'geninstall' has
# its stdout open even if the 'cf-agent' process that started it
# terminates (e.g. gets killed).
package_add_command => "/usr/bin/rm -f $(repo)/.toc && /usr/sbin/geninstall -IacgXNY -d $(repo) cfengine.cfengine-nova > /dev/null$";
package_update_command => "/usr/bin/rm -f $(repo)/.toc && /usr/sbin/geninstall -IacgXNY -d $(repo) cfengine.cfengine-nova > /dev/null$";
package_delete_command => "/usr/sbin/installp -ug cfengine.cfengine-nova$";
# Internal version comparison model doesn't work for W.X.Y.Z
package_version_less_command => "$(sys.bindir)/rpmvercmp '$(v1)' lt '$(v2)'";
package_version_equal_command => "$(sys.bindir)/rpmvercmp '$(v1)' eq '$(v2)'";
}
package_module bodies
yum
Prototype: yum
Implementation:
body package_module yum
{
query_installed_ifelapsed => "10";
query_updates_ifelapsed => "30";
}
apt_get
Prototype: apt_get
Implementation:
body package_module apt_get
{
query_installed_ifelapsed => "10";
query_updates_ifelapsed => "30";
}
msiexec
Prototype: msiexec
Description: msiexec package module default settings
Implementation:
body package_module msiexec
{
query_installed_ifelapsed => "10";
query_updates_ifelapsed => "30";
interpreter => "$(sys.winsysdir)$(const.dirsep)cmd.exe /c ";
module_path => "$(sys.workdir)$(const.dirsep)modules$(const.dirsep)packages$(const.dirsep)msiexec.bat";
}
perms bodies
u_m
Prototype: u_m(p)
Arguments:
p
: string, used in the value of attributemode
Implementation:
body perms u_m(p)
{
mode => "$(p)";
}
copy_from bodies
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";
}
lib/
This directory contains the standard library akak COPBL or the Community Open Promise Body Library. The bodies and bundles found here are contributed and maintained by the CFEngine community. They codify many common and useful patterns.
This directories contents are expected to be found in the following locations:
$(sys.local_libdir)
(relative to the root of your policy inputs)
lib/databases.cf
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";
}
lib/cfengine_enterprise_hub_ha.cf
common bodies
cfengine_enterprise_hub_ha
Prototype: cfengine_enterprise_hub_ha
Description: Inputs specific to CFEngine Enterpise
Implementation:
bundle common cfengine_enterprise_hub_ha
{
vars:
enable_cfengine_enterprise_hub_ha::
"input[ha_main]"
string => "cfe_internal/enterprise/ha/ha.cf";
"input[ha_def]"
string => "cfe_internal/enterprise/ha/ha_def.cf";
"classification_bundles"
slist => { "ha_def" };
"management_bundles"
slist => { "ha_main" };
"inputs" slist => getvalues(input);
!enable_cfengine_enterprise_hub_ha::
"classification_bundles"
slist => { "$(this.bundle)" };
"management_bundles"
slist => { "$(this.bundle)" };
"inputs"
slist => { };
}
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(cfengine_enterprise_hub_ha.inputs) };
}
lib/cfe_internal.cf
common bodies
cfe_internal_common
Prototype: cfe_internal_common
Description: Select parts of the standard library that are dependant
Implementation:
bundle common cfe_internal_common
{
vars:
"inputs" slist => { "$(this.promise_dirname)/common.cf",
"$(this.promise_dirname)/commands.cf"};
}
agent bundles
cfe_internal_cleanup_agent_reports
Prototype: cfe_internal_cleanup_agent_reports
Description: Cleanup accumulated agent reports
Implementation:
bundle agent cfe_internal_cleanup_agent_reports
{
vars:
any::
# To avoid unnecessary work, we only findfiles if there is not already a
# variable defined.
"diff_files"
slist => findfiles("$(sys.workdir)/state/diff/*.diff"),
unless => isvariable( $(this.promiser) );
"promise_log_files"
slist => findfiles("$(sys.workdir)/state/promise_log/*.csv"),
unless => isvariable( $(this.promiser) );
"previous_state_files" -> { "ENT-3161" }
slist => findfiles("$(sys.workdir)/state/previous_state/*.cache"),
unless => isvariable( $(this.promiser) ),
comment => "The files in this directory record the state at the end of
the previous agent run. They are used in concert with the
promise logs to derive delta reports.";
"untracked_files" -> { "ENT-3161" }
slist => findfiles("$(sys.workdir)/state/untracked/*.idx"),
unless => isvariable( $(this.promiser) ),
comment => "The files in this directory are used in support of the
report_data_select filters. This is a record of all promises
that should not be collected";
"files"
slist => { @(diff_files),
@(promise_log_files),
@(previous_state_files),
@(untracked_files) };
"reports_size[$(files)]"
int => filesize("$(files)"),
unless => isvariable( $(this.promiser) );
"tmpmap"
slist => maparray("$(this.v)", reports_size);
# We need to make sure that we have files before summing or errors are
# produced in the log
have_files::
"total_report_size" real => sum(tmpmap);
classes:
"cfe_internal_purge_reports"
expression => isgreaterthan("$(total_report_size)","$(def.max_client_history_size)"),
comment => "Determine if the current sum of reports exceeds the max desired";
"have_files"
expression => isgreaterthan(length(tmpmap), 0);
files:
cfe_internal_purge_reports::
"$(files)"
delete => tidy,
handle => "cf_cleanup_agent_reports_$(files)";
reports:
DEBUG|DEBUG_cfe_internal_cleanup_agent_reports::
"DEBUG $(this.bundle): Size of '$(files)' = '$(reports_size[$(files)])'";
"DEBUG $(this.bundle): Size of all reports = '$(total_report_size)'";
"DEBUG $(this.bundle): Purge threshold = '$(def.max_client_history_size)'";
"DEBUG $(this.bundle): Client history purge triggered"
ifvarclass => "cfe_internal_purge_reports";
}
file bodies
control
Prototype: control
Description: Include necessary parts of stdlib
Implementation:
body file control
{
inputs => { @(cfe_internal_common.inputs) };
}
lib/files.cf
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" }
}
common bodies
files_common
Prototype: files_common
Description: Enumerate policy files used by this policy file for inclusion to inputs
Implementation:
bundle common files_common
{
vars:
"inputs" slist => { "$(this.promise_dirname)/common.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_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";
}
lines_present
Prototype: lines_present(lines)
Description: Ensure lines
are present in the file. Lines that do not exist are appended to the file
Arguments:
lines
, used as promiser of typeinsert_lines
Implementation:
bundle edit_line lines_present(lines)
{
insert_lines:
"$(lines)"
comment => "Append lines if they don't exist";
}
insert_lines
Prototype: insert_lines(lines)
Description: Alias for lines_present
Arguments:
lines
, used as promiser of typeinsert_lines
Implementation:
bundle edit_line insert_lines(lines)
{
insert_lines:
"$(lines)"
comment => "Append lines if they don't exist";
}
append_if_no_line
Prototype: append_if_no_line(lines)
Description: Alias for lines_present
Arguments:
lines
, used as promiser of typeinsert_lines
Implementation:
bundle edit_line append_if_no_line(lines)
{
insert_lines:
"$(lines)"
comment => "Append lines if they don't exist";
}
append_if_no_lines
Prototype: append_if_no_lines(lines)
Description: Alias for lines_present
Arguments:
lines
, used as promiser of typeinsert_lines
Implementation:
bundle edit_line append_if_no_lines(lines)
{
insert_lines:
"$(lines)"
comment => "Append lines if they don't exist";
}
comment_lines_matching
Prototype: comment_lines_matching(regex, comment)
Description: Comment lines in the file that matching an anchored regex
Arguments:
regex
: Anchored regex that the entire line needs to matchcomment
: A string that is prepended to matching lines
Implementation:
bundle edit_line comment_lines_matching(regex,comment)
{
replace_patterns:
"^($(regex))$"
replace_with => comment("$(comment)"),
comment => "Search and replace string";
}
contains_literal_string
Prototype: contains_literal_string(string)
Description: Ensure the literal string is present in the promised file
Arguments:
string
: The string (potentially multiline) to ensure exists in the promised file.
Implementation:
bundle edit_line contains_literal_string(string)
{
insert_lines:
"$(string)"
insert_type => "preserve_block",
expand_scalars => "false",
whitespace_policy => { "exact_match" };
}
uncomment_lines_matching
Prototype: uncomment_lines_matching(regex, comment)
Description: Uncomment lines of the file where the regex matches the entire text after the comment string
Arguments:
regex
: The regex that lines need to match aftercomment
comment
: The prefix of the line that is removed
Implementation:
bundle edit_line uncomment_lines_matching(regex,comment)
{
replace_patterns:
"^$(comment)\s?($(regex))$"
replace_with => uncomment,
comment => "Uncomment lines matching a regular expression";
}
comment_lines_containing
Prototype: comment_lines_containing(regex, comment)
Description: Comment lines of the file matching a regex
Arguments:
regex
: A regex that a part of the line needs to matchcomment
: A string that is prepended to matching lines
Implementation:
bundle edit_line comment_lines_containing(regex,comment)
{
replace_patterns:
"^((?!$(comment)).*$(regex).*)$"
replace_with => comment("$(comment)"),
comment => "Comment out lines in a file";
}
uncomment_lines_containing
Prototype: uncomment_lines_containing(regex, comment)
Description: Uncomment lines of the file where the regex matches parts of the text after the comment string
Arguments:
regex
: The regex that lines need to match aftercomment
comment
: The prefix of the line that is removed
Implementation:
bundle edit_line uncomment_lines_containing(regex,comment)
{
replace_patterns:
"^$(comment)\s?(.*$(regex).*)$"
replace_with => uncomment,
comment => "Uncomment a line containing a fragment";
}
delete_lines_matching
Prototype: delete_lines_matching(regex)
Description: Delete lines matching a regular expression
Arguments:
regex
: The regular expression that the lines need to match
Implementation:
bundle edit_line delete_lines_matching(regex)
{
delete_lines:
"$(regex)"
comment => "Delete lines matching regular expressions";
}
warn_lines_matching
Prototype: warn_lines_matching(regex)
Description: Warn about lines matching a regular expression
Arguments:
regex
: The regular expression that the lines need to match
Implementation:
bundle edit_line warn_lines_matching(regex)
{
delete_lines:
"$(regex)"
comment => "Warn about lines in a file",
action => warn_only;
}
prepend_if_no_line
Prototype: prepend_if_no_line(string)
Description: Prepend string
if it doesn't exist in the file
Arguments:
string
: The string to be prepended
See also: insert_lines
in
edit_line
Implementation:
bundle edit_line prepend_if_no_line(string)
{
insert_lines:
"$(string)"
location => start,
comment => "Prepend a line to the file if it doesn't already exist";
}
replace_line_end
Prototype: replace_line_end(start, end)
Description: Give lines starting with start
the ending given in end
Whitespaces will be left unmodified. For example,
replace_line_end("ftp", "2121/tcp")
would replace
"ftp 21/tcp"
with
"ftp 2121/tcp"
Arguments:
start
: The string lines have to start withend
: The string lines should end with
Implementation:
bundle edit_line replace_line_end(start,end)
{
field_edits:
"\s*$(start)\s.*"
comment => "Replace lines with $(this.start) and $(this.end)",
edit_field => line("(^|\s)$(start)\s*", "2", "$(end)","set");
}
append_to_line_end
Prototype: append_to_line_end(start, end)
Description: Append end
to any lines beginning with start
end
will be appended to all lines starting with start
and not
already ending with end
. Whitespaces will be left unmodified.
For example, append_to_line_end("kernel", "vga=791")
would replace
kernel /boot/vmlinuz root=/dev/sda7
with
kernel /boot/vmlinuz root=/dev/sda7 vga=791
WARNING: Be careful not to have multiple promises matching the same line, which would result in the line growing indefinitely.
Arguments:
start
: pattern to match lines of interestend
: string to append to matched lines
Example:
files:
"/tmp/boot-options" edit_line => append_to_line_end("kernel", "vga=791");
Implementation:
bundle edit_line append_to_line_end(start,end)
{
field_edits:
"\s*$(start)\s.*"
comment => "Append lines with $(this.start) and $(this.end)",
edit_field => line("(^|\s)$(start)\s*", "2", "$(end)","append");
}
regex_replace
Prototype: regex_replace(find, replace)
Description: Find exactly a regular expression and replace exactly the match with a string. You can think of this like a PCRE powered sed.
Arguments:
find
: The regular expressionreplace
: The replacement string
Implementation:
bundle edit_line regex_replace(find,replace)
{
replace_patterns:
"$(find)"
replace_with => value("$(replace)"),
comment => "Search and replace string";
}
resolvconf
Prototype: resolvconf(search, list)
Description: Adds search domains and name servers to the system resolver configuration.
Use this bundle to modify resolv.conf
. Existing entries for
search
and nameserver
are replaced.
Arguments:
search
: The search domains with spacelist
: An slist of nameserver addresses
Implementation:
bundle edit_line resolvconf(search,list)
{
delete_lines:
"search.*" comment => "Reset search lines from resolver";
"nameserver.*" comment => "Reset nameservers in resolver";
insert_lines:
"search $(search)" comment => "Add search domains to resolver";
"nameserver $(list)" comment => "Add name servers to resolver";
}
resolvconf_o
Prototype: resolvconf_o(search, list, options)
Description: Adds search domains, name servers and options to the system resolver configuration.
Use this bundle to modify resolv.conf
. Existing entries for
search
, nameserver
and options
are replaced.
Arguments:
search
: The search domains with spacelist
: An slist of nameserver addressesoptions
: is an slist of variables to modify the resolver
Implementation:
bundle edit_line resolvconf_o(search,list,options)
{
delete_lines:
"search.*" comment => "Reset search lines from resolver";
"nameserver.*" comment => "Reset nameservers in resolver";
"options.*" comment => "Reset options in resolver";
insert_lines:
"search $(search)" comment => "Add search domains to resolver";
"nameserver $(list)" comment => "Add name servers to resolver";
"options $(options)" comment => "Add options to resolver";
}
manage_variable_values_ini
Prototype: manage_variable_values_ini(tab, sectionName)
Description: Sets the RHS of configuration items in the file of the form
LHS=RHS
If the line is commented out with #
, it gets uncommented first.
Adds a new line if none exists.
Removes any variable value pairs not defined for the ini section.
Arguments:
tab
: An associative array containingtab[sectionName][LHS]="RHS"
. The value is not changed when theRHS
is "dontchange"sectionName
: The section in the file within which values should be modified
See also: set_variable_values_ini()
Implementation:
bundle edit_line manage_variable_values_ini(tab, sectionName)
{
vars:
"index" slist => getindices("$(tab)[$(sectionName)]");
delete_lines:
".*"
select_region => INI_section(escape("$(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(escape("$(sectionName)"));
}
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(escape("$(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(escape("$(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(escape("$(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:
# TODO: refactor once 3.7.x is EOL
"indeces" slist => getindices($(config));
"k" slist => sort("indeces", lex);
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)"),
scope => "bundle";
# if there's more than one comment, just add new (don't know who to use)
"multiple_comments_$(cindex[$(index)])"
expression => isgreaterthan("$(index_comment_matches_$(cindex[$(index)]))","1"),
scope => "bundle";
replace_patterns:
# If the line is commented out, uncomment and replace with
# the correct value
"^\s*#\s*($(index)\s+.*|$(index))$"
comment => "If we find a single commented entry we can uncomment it to
keep the settings near any inline documentation. If there
are multiple comments, then we don't try to replace them and
instead will later append the new value after the first
commented occurrence of $(index).",
handle => "set_config_values_replace_commented_line",
replace_with => value("$(index) $($(v)[$(index)])"),
ifvarclass => "!line_exists_$(cindex[$(index)]).!replace_attempted_$(cindex[$(index)])_reached.!multiple_comments_$(cindex[$(index)])",
classes => results("bundle", "uncommented_$(cindex[$(index)])");
# If the line is there with the wrong value, replace with
# the correct value
"^\s*($(index)\s+(?!$(ev[$(index)])$).*|$(index))$"
comment => "Correct the value $(index)",
replace_with => value("$(index) $($(v)[$(index)])"),
classes => results("bundle", "replace_attempted_$(cindex[$(index)])");
insert_lines:
# If the line doesn't exist, or there is more than one 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)
"$(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.
Note: If the data structure being used for the first parameter is in the current bundle, you can use $(this.bundle).variable
.
Originally set_config_values
by Ed King.
Arguments:
v
: The fully-qualified name (bundlename.variable
) 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";
}
maintain_key_values
Prototype: maintain_key_values(v, sep)
Description: Sets the RHS of configuration items with an giving separator
Contributed by David Lee
Arguments:
v
ofmeta
promiser tags: string, used to set promise attributeslist
ofvars
promiser index ofvars
promiser cindex[$(index)] ofvars
promiser keypat[$(index)]: string, used to set promise attributestring
ofvars
promiser ve[$(index)] ofclasses
promiser $(cindex[$(index)])_key_in_file: string, used to set promise attributereplace_with
ofreplace_patterns
promiser $(keypat[$(index]))(?!$(ve[$(index)])$).*, used as promiser of typeinsert_lines
sep
ofmeta
promiser tags ofvars
promiser index ofvars
promiser cindex[$(index)]: string, used in the value of attributestring
ofvars
promiser keypat[$(index)] ofvars
promiser ve[$(index)] ofclasses
promiser $(cindex[$(index)])_key_in_file ofreplace_patterns
promiser $(keypat[$(index]))(?!$(ve[$(index)])$).*, used as promiser of typeinsert_lines
Implementation:
bundle edit_line maintain_key_values(v,sep)
{
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)");
# Matching pattern for line (basically key-and-separator)
"keypat[$(index)]" string => "\s*$(index)\s*$(sep)\s*";
# Values may contain regexps. Escape them for replace_pattern matching.
"ve[$(index)]" string => escape("$($(v)[$(index)])");
classes:
"$(cindex[$(index)])_key_in_file"
comment => "Dynamic Class created if patterns matching",
expression => regline("^$(keypat[$(index)]).*", "$(edit.filename)");
replace_patterns:
# For convergence need to use negative lookahead on value:
# "key sep (?!value).*"
"^($(keypat[$(index)]))(?!$(ve[$(index)])$).*"
comment => "Replace definition of $(index)",
replace_with => value("$(match.1)$($(v)[$(index)])");
insert_lines:
"$(index)$(sep)$($(v)[$(index)])"
comment => "Insert definition of $(index)",
ifvarclass => "!$(cindex[$(index)])_key_in_file";
}
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
Example:
bundle agent pam_d_su_include
#@brief Ensure /etc/pam.d/su has includes configured properly
{
files:
ubuntu::
"/etc/pam.d/su"
edit_line => converge( "@include", "@include common-auth
**Implementation:**
```cf3
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";
}
converge_prepend
Prototype: converge_prepend(marker, lines)
Description: Converge lines
marked with marker
to start of content.
Any content marked with marker
is removed, then lines
are
inserted at start of content. Every line
should contain marker
.
This is helpful for files where order matters, like pam.d/common-auth
,
and you want the content at the top. If you want to maintain the order
of multiple lines, pass one item with a newline in it like this:
Example:
files:
"/etc/pam.conf"
edit_line => converge_prepend("session", "session required pam_loginuid.so
session required pam_env.so
session required pam_env.so envfile=/etc/default/locale
session required pam_limits.so");
Arguments:
marker
: The marker (not a regular expression; will be escaped)lines
: The lines to insert; all must containmarker
Example:
bundle agent pam_d_su_session
@brief Ensure /etc/pam.d/su has session configured properly
{
files:
ubuntu::
"/etc/pam.d/su"
edit_line => converge_prepend( "session", "session required pam_env.so readenv=1 envfile=/etc/default/locale
session optional pam_mail.so nopen
session required pam_limits.so" );
}
History:
- Introduced in 3.17.0, 3.15.3, 3.12.6
Implementation:
bundle edit_line converge_prepend(marker, lines)
{
vars:
"regex" string => escape($(marker));
delete_lines:
".*$(regex).*" comment => "Delete lines matching the marker";
insert_lines:
"$(lines)" location => start, comment => "Insert the given lines";
}
fstab_option_editor
Prototype: fstab_option_editor(method, mount, option)
Description: Add or remove /etc/fstab
options for a mount
This bundle edits the options field of a mount. The method
is a
field_operation
which can be append
, prepend
, set
, delete
,
or alphanum
. The option is OS-specific.
Arguments:
method
:field_operation
to applymount
: the mount pointoption
: the option to add or remove
Example:
files:
"/etc/fstab" edit_line => fstab_option_editor("delete", "/", "acl");
"/etc/fstab" edit_line => fstab_option_editor("append", "/", "acl");
Implementation:
bundle edit_line fstab_option_editor(method, mount, option)
{
field_edits:
"(?!#)\S+\s+$(mount)\s.+"
edit_field => fstab_options($(option), $(method));
}
agent bundles
file_mustache
Prototype: file_mustache(mustache_file, json_file, target_file)
Description: Make a file from a Mustache template and a JSON file
Arguments:
mustache_file
: the file with the Mustache templatejson_file
: a file with JSON datatarget_file
: the target file to write
Example:
methods:
"m" usebundle => file_mustache("x.mustache", "y.json", "z.txt");
Implementation:
bundle agent file_mustache(mustache_file, json_file, target_file)
{
files:
"$(target_file)"
create => "true",
edit_template => $(mustache_file),
template_data => readjson($(json_file), "100k"),
template_method => "mustache";
}
file_mustache_jsonstring
Prototype: file_mustache_jsonstring(mustache_file, json_string, target_file)
Description: Make a file from a Mustache template and a JSON string
Arguments:
mustache_file
: the file with the Mustache templatejson_string
: a string with JSON datatarget_file
: the target file to write
Example:
methods:
"m" usebundle => file_mustache_jsonstring("x.mustache", '{ "x": "y" }', "z.txt");
Implementation:
bundle agent file_mustache_jsonstring(mustache_file, json_string, target_file)
{
files:
"$(target_file)"
create => "true",
edit_template => $(mustache_file),
template_data => parsejson($(json_string)),
template_method => "mustache";
}
file_tidy
Prototype: file_tidy(file)
Description: Remove a file
Arguments:
file
: to remove
Example:
methods:
"" usebundle => file_tidy("/tmp/z.txt");
Implementation:
bundle agent file_tidy(file)
{
files:
"$(file)" delete => tidy;
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): deleting $(file) with delete => tidy";
}
dir_sync
Prototype: dir_sync(from, to)
Description: Synchronize a directory entire, deleting unknown files
Arguments:
from
: source directoryto
: destination directory
Example:
methods:
"" usebundle => dir_sync("/tmp", "/var/tmp");
Implementation:
bundle agent dir_sync(from, to)
{
files:
"$(to)/."
create => "true",
depth_search => recurse("inf"),
copy_from => copyfrom_sync($(from));
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): copying directory $(from) to $(to)";
}
file_copy
Prototype: file_copy(from, to)
Description: Copy a file
Arguments:
from
: source fileto
: destination file
Example:
methods:
"" usebundle => file_copy("/tmp/z.txt", "/var/tmp/y.txt");
Implementation:
bundle agent file_copy(from, to)
{
files:
"$(to)"
copy_from => copyfrom_sync($(from));
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): copying file $(from) to $(to)";
}
file_make
Prototype: file_make(file, str)
Description: Make a file from a string
Arguments:
file
: targetstr
: the string data
Example:
methods:
"" usebundle => file_make("/tmp/z.txt", "Some text
and some more text here");
Implementation:
bundle agent file_make(file, str)
{
vars:
"len" int => string_length($(str));
summarize::
"summary" string => format("%s...%s",
string_head($(str), 18),
string_tail($(str), 18));
classes:
"summarize" expression => isgreaterthan($(len), 40);
files:
"$(file)"
create => "true",
edit_line => insert_lines($(str)),
edit_defaults => empty;
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): creating $(file) with contents '$(str)'"
ifvarclass => "!summarize";
"DEBUG $(this.bundle): creating $(file) with contents '$(summary)'"
ifvarclass => "summarize";
}
file_make_mog
Prototype: file_make_mog(file, str, mode, owner, group)
Description: Make a file from a string with mode, owner, group
Arguments:
file
: targetstr
: the string datamode
: the file permissions in octalowner
: the file owner as a name or UIDgroup
: the file group as a name or GID
Example:
methods:
"" usebundle => file_make_mog("/tmp/z.txt", "Some text
and some more text here", "0644", "root", "root");
Implementation:
bundle agent file_make_mog(file, str, mode, owner, group)
{
vars:
"len" int => string_length($(str));
summarize::
"summary" string => format("%s...%s",
string_head($(str), 18),
string_tail($(str), 18));
classes:
"summarize" expression => isgreaterthan($(len), 40);
files:
"$(file)"
create => "true",
edit_line => insert_lines($(str)),
perms => mog($(mode), $(owner), $(group)),
edit_defaults => empty;
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): creating $(file) with contents '$(str)', mode '$(mode)', owner '$(owner)' and group '$(group)'"
ifvarclass => "!summarize";
"DEBUG $(this.bundle): creating $(file) with contents '$(summary)', mode '$(mode)', owner '$(owner)' and group '$(group)'"
ifvarclass => "summarize";
}
file_make_mustache
Prototype: file_make_mustache(file, template, data)
Description: Make a file from a mustache template
Arguments:
file
: Target file to rendertemplate
: Path to mustache templatedata
: Data container to use
Example:
vars:
"state" data => datastate();
methods:
"" usebundle => file_make_mustache( "/tmp/z.txt", "/tmp/z.mustache", @(state) );
Implementation:
bundle agent file_make_mustache(file, template, data)
{
files:
"$(file)"
create => "true",
edit_template => "$(template)",
template_method => "mustache",
template_data => @(data);
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): rendering $(file) with template '$(template)'";
}
file_make_mustache_with_perms
Prototype: file_make_mustache_with_perms(file, template, data, mode, owner, group)
Description: Make a file from a mustache template
Arguments:
file
: Target file to rendertemplate
: Path to mustache templatedata
: Data container to usemode
: File permissionsowner
: Target file ownergroup
: Target file group
Example:
vars:
"state" data => datastate();
methods:
"" usebundle => file_make_mustache( "/tmp/z.txt", "/tmp/z.mustache", @(state),
600, "root", "root" );
Implementation:
bundle agent file_make_mustache_with_perms(file, template, data, mode, owner, group)
{
files:
"$(file)"
create => "true",
edit_template => "$(template)",
template_method => "mustache",
perms => mog( $(mode), $(owner), $(group) ),
template_data => @(data);
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): rendering $(file) with template '$(template)'";
}
file_empty
Prototype: file_empty(file)
Description: Make an empty file
Arguments:
file
: target
Example:
methods:
"" usebundle => file_empty("/tmp/z.txt");
Implementation:
bundle agent file_empty(file)
{
files:
"$(file)"
create => "true",
edit_defaults => empty;
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): creating empty $(file) with 0 size";
}
file_hardlink
Prototype: file_hardlink(target, link)
Description: Make a hard link to a file
Arguments:
target
: of linklink
: the hard link's location
Example:
methods:
"" usebundle => file_hardlink("/tmp/z.txt", "/tmp/z.link");
Implementation:
bundle agent file_hardlink(target, link)
{
files:
"$(link)"
move_obstructions => "true",
link_from => linkfrom($(target), "hardlink");
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): $(link) will be a hard link to $(target)";
}
file_link
Prototype: file_link(target, link)
Description: Make a symlink to a file
Arguments:
target
: of symlinklink
: the symlink's location
Example:
methods:
"" usebundle => file_link("/tmp/z.txt", "/tmp/z.link");
Implementation:
bundle agent file_link(target, link)
{
files:
"$(link)"
move_obstructions => "true",
link_from => linkfrom($(target), "symlink");
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): $(link) will be a symlink to $(target)";
}
file bodies
control
Prototype: control
Description: Include policy files used by this policy file as part of inputs
Implementation:
body file control
{
inputs => { @(files_common.inputs) };
}
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*";
@if minimum_version(3.10)
select_end_match_eof => "true";
@endif
}
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" };
aix::
groups => { "system" };
}
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 excluded.
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 excluding 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";
}
lib/packages.cf
See the packages
promises documentation for a
comprehensive reference on the body types and attributes used here.
To use these bodies and bundles, add the following to your policy:
body file control
{
inputs => { "packages.cf" }
}
common bodies
package_module_knowledge
Prototype: package_module_knowledge
Description: common package_module_knowledge bundle
This common bundle defines which package modules are the defaults on different platforms.
Implementation:
bundle common package_module_knowledge
{
vars:
debian::
"platform_default" string => "apt_get";
freebsd::
"platform_default" string => "pkg";
redhat|amazon_linux::
"platform_default" string => "yum";
suse|sles|opensuse::
"platform_default" string => "zypper";
aix::
"platform_default" string => "nimclient";
slackware::
"platform_default" string => "slackpkg";
# CFEngine 3.12.2+ and 3.14+ have new package module on Windows
windows.cfengine_3_12.!(cfengine_3_12_0|cfengine_3_12_1)::
"platform_default" string => "msiexec";
@if minimum_version(3.14)
windows::
"platform_default" string => "msiexec";
@endif
alpinelinux::
"platform_default" string => "apk";
termux::
"platform_default" string => "apt_get";
}
common_knowledge
Prototype: common_knowledge
Description: common packages knowledge bundle
This common bundle defines general things about platforms.
Implementation:
## Packages promises
{
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:
"list_update_ifelapsed" string => "240";
{
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:
"patch_version_regex" string => "^Inst\s+\S+\s+\[\S+\]\s+\((\S+)\s+.*";
{
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:
"rpm3_arch_regex" string => "\S+\s+(\S+).*";
{
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:
"no_locking_option" string => "";
{
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:
p; # Print current buffer.'";
{
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:
"call_zypper" string => "$(paths.zypper)";
{
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:
"brew_version_regex" string => "[\S]+\s([\S]+)";
{
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:
"npm_installed_regex" string => "^[^ /]+ ([\w\d-._~]+@[\d.]+)";
{
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:
"pip_installed_regex" string => "^([[:alnum:]-_]+\s\([\d.]+\))";
{
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:
{
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:
{
packages:
debian::
"$(package)"
package_policy => "delete",
package_method => apt_get_permissive;
redhat::
"$(package)"
package_policy => "delete",
package_method => yum_rpm_permissive;
suse|sles::
"$(package)"
package_policy => "delete",
package_method => zypper;
!debian.!redhat.!(suse|sles)::
"$(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:
package_method => generic;
{
packages:
debian::
"$(package)"
package_policy => "add",
package_method => apt_get_permissive;
redhat::
"$(package)"
package_policy => "add",
package_method => yum_rpm_permissive;
suse|sles::
"$(package)"
package_policy => "add",
package_method => zypper;
!debian.!redhat.!(suse|sles)::
"$(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:
package_method => generic;
{
packages:
debian::
"$(package)"
package_policy => "addupdate",
package_version => "999999999:9999999999",
package_method => apt_get_permissive;
redhat::
"$(package)"
package_policy => "addupdate",
package_version => "999999999",
package_method => yum_rpm_permissive;
suse|sles::
"$(package)"
package_policy => "addupdate",
package_version => "999999999",
package_method => zypper;
!debian.!redhat.!(suse|sles)::
"$(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:
package_method => generic;
{
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:
$(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:
$(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:
$(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|sles::
"$(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|sles)::
"$(package_name)"
package_policy => $(desired),
package_method => generic;
reports:
"(DEBUG|DEBUG_$(this.bundle)).filebased.!(suse|sles).!debian.!redhat.!aix.!solaris_pkgadd"::
"DEBUG $(this.bundle): sorry, can't do file-based installs on $(sys.os)";
}
package_module bodies
apk
Prototype: apk
Implementation:
"platform_default" string => "apt_get";
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
}
apt_get
Prototype: apt_get
Implementation:
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
#default_options => {};
}
zypper
Prototype: zypper
Implementation:
#default_options => {};
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
#default_options => {};
}
nimclient
Prototype: nimclient
Description: Define details used when interfacing with nimclient package module
Example:
bundle agent example_nimclient
{
packages:
"expect.base"
policy => "present",
options => { "lpp_source=lppaix71034" },
package_module => nimclient;
}
Implementation:
#default_options => {};
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
# This would likey be customized based on your infrastructure specifics
# you may for example want to default the lpp_source based on something
# like `oslevel -s` output.
#default_options => {};
}
pkgsrc
Prototype: pkgsrc
Description: Define details used when interfacing with the pkgsrc package module.
Example:
cf3
bundle agent main
{
packages:
"vim"
policy => "present",
package_module => pkgsrc;
}
Implementation:
# like `oslevel -s` output.
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
}
yum
Prototype: yum
Implementation:
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
#default_options => {};
}
slackpkg
Prototype: slackpkg
Implementation:
#default_options => {};
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
#default_options => {};
}
pkg
Prototype: pkg
Implementation:
#default_options => {};
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
#default_options => {};
}
snap
Prototype: snap
Implementation:
#default_options => {};
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
#default_options => {};
}
freebsd_ports
Prototype: freebsd_ports
Description: Define details used when interfacing with the freebsd ports package module.
Note: Ports are expected to be setup prior to trying to use the packages
promise. You may need to ensure that portsnap extract
has been run, e.g.
fileexists("/usr/ports/Mk/bsd.port.mk")
Example:
cf3
bundle agent main
{
packages:
freebsd::
"vim"
policy => "present",
package_module => freebsd_ports;
}
Implementation:
#default_options => {};
{
query_installed_ifelapsed => "$(def.package_module_query_installed_ifelapsed)";
query_updates_ifelapsed => "$(def.package_module_query_updates_ifelapsed)";
}
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 performs recursive upgrade (of dependencies) by default. Set $flags to "--no-deps" to perform 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:
{
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:
package_update_command => "$(paths.path[pip]) install --upgrade $(flags)";
{
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:
package_update_command => "$(npm_knowledge.call_npm) update --prefix $(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 --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:
package_update_command => "$(npm_knowledge.call_npm) update --global";
{
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:
{
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:
# 3) aptitude is a high level interface
{
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:
package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";
{
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:
package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";
{
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:
{
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:
{
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:
{
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:
{
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:
{
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:
{
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:
{
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:
package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
{
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:
{
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:
{
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:
{
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:
{
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:
{
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:
{
package_changes => "individual";
# Could use rpm for this
package_list_command => "/usr/sbin/pkg info";
# Remember to escape special characters like |
package_list_name_regex => "([^\s]+)-.*";
package_list_version_regex => "[^\s]+-([^\s]+).*";
package_name_regex => "([^\s]+)-.*";
package_version_regex => "[^\s]+-([^\s]+).*";
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version)";
package_add_command => "/usr/sbin/pkg install -y";
package_delete_command => "/usr/sbin/pkg delete -y";
}
freebsd_portmaster
Prototype: freebsd_portmaster
Description: FreeBSD portmaster package installation method
This package method interacts with portmaster to build and install packages.
Note that you must use the complete package name as it appears in /usr/ports/*/name, such as 'perl5.14' rather than 'perl5'. Repositories are hard-coded to /usr/ports; alternate locations are unsupported at this time. This method supports both pkg_* and pkgng systems.
Example:
packages:
"perl5.14"
package_policy => "add",
package_method => freebsd_portmaster;
Implementation:
package_delete_command => "/usr/sbin/pkg delete -y";
{
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:
{
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:
{
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:
{
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:
package_update_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
{
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:
package_verify_command => "$(suse_knowledge.call_zypper) --non-interactive verify$";
{
suse|sles::
package_changes => "bulk";
package_list_command => "$(rpm_knowledge.call_rpm) -qa --queryformat \"$(rpm_knowledge.rpm_output_format)\"";
# set it to "0" to avoid caching of list during upgrade
package_list_update_command => "$(suse_knowledge.call_zypper) list-updates";
package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
package_patch_list_command => "$(suse_knowledge.call_zypper) patches";
package_installed_regex => "i.*";
package_list_name_regex => "$(rpm_knowledge.rpm_name_regex)";
package_list_version_regex => "$(rpm_knowledge.rpm_version_regex)";
package_list_arch_regex => "$(rpm_knowledge.rpm_arch_regex)";
package_patch_installed_regex => ".*Installed.*|.*Not Applicable.*";
package_patch_name_regex => "[^|]+\|\s+([^\s]+).*";
package_patch_version_regex => "[^|]+\|[^|]+\|\s+([^\s]+).*";
package_name_convention => "$(name)";
package_add_command => "$(suse_knowledge.call_zypper) --non-interactive install";
package_delete_command => "$(suse_knowledge.call_zypper) --non-interactive remove --force-resolution";
package_update_command => "$(suse_knowledge.call_zypper) --non-interactive update";
package_patch_command => "$(suse_knowledge.call_zypper) --non-interactive patch$"; # $ means no args
package_verify_command => "$(suse_knowledge.call_zypper) --non-interactive verify$";
redhat::
package_changes => "bulk";
package_list_command => "$(rpm_knowledge.call_rpm) -qa --qf '$(rpm_knowledge.rpm3_output_format)'";
package_patch_list_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_offline_options) check-update $(redhat_knowledge.check_update_postproc)";
package_list_name_regex => "$(rpm_knowledge.rpm3_name_regex)";
package_list_version_regex => "$(rpm_knowledge.rpm3_version_regex)";
package_list_arch_regex => "$(rpm_knowledge.rpm3_arch_regex)";
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version).$(arch)";
# just give the package name to rpm to delete, otherwise it gets "name.*" (from package_name_convention above)
package_delete_convention => "$(name)";
# set it to "0" to avoid caching of list during upgrade
package_list_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) check-update $(redhat_knowledge.check_update_postproc)";
package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
package_patch_name_regex => "$(redhat_knowledge.patch_name_regex)";
package_patch_version_regex => "$(redhat_knowledge.patch_version_regex)";
package_patch_arch_regex => "$(redhat_knowledge.patch_arch_regex)";
package_add_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y install";
package_update_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
package_patch_command => "$(redhat_knowledge.call_yum) $(redhat_knowledge.yum_options) -y update";
package_delete_command => "$(rpm_knowledge.call_rpm) -e --nodeps";
package_verify_command => "$(rpm_knowledge.call_rpm) -V";
package_noverify_returncode => "1";
package_version_less_command => "$(redhat_knowledge.rpm_compare_less)";
package_version_equal_command => "$(redhat_knowledge.rpm_compare_equal)";
debian::
package_changes => "bulk";
package_list_command => "$(debian_knowledge.call_dpkg) -l";
package_list_name_regex => "$(debian_knowledge.list_name_regex)";
package_list_version_regex => "$(debian_knowledge.list_version_regex)";
package_installed_regex => ".i.*"; # packages that have been uninstalled may be listed
package_name_convention => "$(name)";
package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
# make correct version comparisons
package_version_less_command => "$(debian_knowledge.dpkg_compare_less)";
package_version_equal_command => "$(debian_knowledge.dpkg_compare_equal)";
debian.have_aptitude::
package_add_command => "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes install";
package_list_update_command => "$(debian_knowledge.call_aptitude) update";
package_delete_command => "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes remove";
package_update_command => "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes install";
package_patch_command => "$(debian_knowledge.call_aptitude) $(debian_knowledge.dpkg_options) --assume-yes install";
package_verify_command => "$(debian_knowledge.call_aptitude) show";
package_noverify_regex => "(State: not installed|E: Unable to locate package .*)";
package_patch_list_command => "$(debian_knowledge.call_aptitude) --assume-yes --simulate --verbose full-upgrade";
package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";
debian.!have_aptitude::
package_add_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
package_list_update_command => "$(debian_knowledge.call_apt_get) update";
package_delete_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes remove";
package_update_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
package_patch_command => "$(debian_knowledge.call_apt_get) $(debian_knowledge.dpkg_options) --yes install";
package_verify_command => "$(debian_knowledge.call_dpkg) -s";
package_noverify_returncode => "1";
package_patch_list_command => "$(debian_knowledge.call_apt_get) --just-print dist-upgrade";
package_patch_name_regex => "$(debian_knowledge.patch_name_regex)";
package_patch_version_regex => "$(debian_knowledge.patch_version_regex)";
freebsd::
package_changes => "individual";
package_list_command => "/usr/sbin/pkg info";
package_list_name_regex => "([^\s]+)-.*";
package_list_version_regex => "[^\s]+-([^\s]+).*";
package_name_regex => "([^\s]+)-.*";
package_version_regex => "[^\s]+-([^\s]+).*";
package_installed_regex => ".*";
package_name_convention => "$(name)-$(version)";
package_add_command => "/usr/sbin/pkg install -y";
package_delete_command => "/usr/sbin/pkg delete";
alpinelinux::
package_changes => "bulk";
package_list_command => "/sbin/apk info -v";
package_list_name_regex => "([^\s]+)-.*";
package_list_version_regex => "[^\s]+-([^\s]+).*";
package_name_regex => ".*";
package_installed_regex => ".*";
package_name_convention => "$(name)";
package_add_command => "/sbin/apk add";
package_delete_command => "/sbin/apk del";
gentoo::
package_changes => "individual";
package_list_command => "/bin/sh -c '/bin/ls -d /var/db/pkg/*/* | cut -c 13-'";
package_list_name_regex => "([^/]+/(?:(?!-\d).)+)-\d.*";
package_list_version_regex => "[^/]+/(?:(?!-\d).)+-(\d.*)";
package_installed_regex => ".*"; # all reported are installed
package_name_convention => "$(name)";
package_list_update_command => "/bin/true"; # I prefer manual syncing
#package_list_update_command => "/usr/bin/emerge --sync"; # if you like automatic
package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
package_add_command => "/usr/bin/emerge -q --quiet-build";
package_delete_command => "/usr/bin/emerge --depclean";
package_update_command => "/usr/bin/emerge --update";
package_patch_command => "/usr/bin/emerge --update";
package_verify_command => "/usr/bin/emerge -s";
package_noverify_regex => ".*(Not Installed|Applications found : 0).*";
archlinux::
package_changes => "bulk";
package_list_command => "/usr/bin/pacman -Q";
package_verify_command => "/usr/bin/pacman -Q";
package_noverify_regex => "error:\b.*\bwas not found";
package_list_name_regex => "(.*)\s+.*";
package_list_version_regex => ".*\s+(.*)";
package_installed_regex => ".*";
package_name_convention => "$(name)";
package_list_update_ifelapsed => "$(common_knowledge.list_update_ifelapsed)";
package_add_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
package_delete_command => "/usr/bin/pacman -Rs --noconfirm";
package_update_command => "/usr/bin/pacman -S --noconfirm --noprogressbar --needed";
}
lib/vcs.cf
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 => { "lib/vcs.cf" };
}
common bodies
vcs_common
Prototype: vcs_common
Description: Enumerate policy files used by this policy file for inclusion to inputs
Implementation:
bundle common vcs_common
{
vars:
"inputs" slist => { "$(this.promise_dirname)/common.cf",
"$(this.promise_dirname)/paths.cf",
"$(this.promise_dirname)/commands.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 happened and so that
we don't accidentally delete something important we stash any
changes.
Note:
1. This promise will fail if user.email is not set
2. We are respecting ignored files.";
}
git_stash_and_clean
Prototype: git_stash_and_clean(repo_path)
Description: Ensure that a given git repo is clean and attempt to save any modifications
Arguments:
repo_path
: Path to the clone
Example:
methods:
"test"
usebundle => git_stash_and_clean("/opt/cfengine/masterfiles_staging_tmp"),
comment => "Ensure that the staging area is a clean clone after attempting to stash any changes";
Implementation:
bundle agent git_stash_and_clean(repo_path)
{
vars:
"stash" string => "CFEngine AUTOSTASH: $(sys.date)";
methods:
"" usebundle => git_stash($(repo_path), $(stash)),
classes => scoped_classes_generic("bundle", "git_stash");
git_stash_ok::
"" usebundle => git_clean($(repo_path));
reports:
git_stash_not_ok::
"$(this.bundle):: Warning: Not saving changes or cleaning. Git stash failed. Perhaps 'user.email' or 'user.name' is not set.";
}
git_commit
Prototype: git_commit(repo_path, message)
Description: executes a commit to the specificed git repository
Arguments:
repo_path
: absolute path to a git repositorymessage
: the message to associate to the commmit
Example:
bundle agent make_git_commit
{
vars:
"repo" string => "/var/git/myrepo";
"msg" string => "dituri added some bundles for common git operations";
methods:
"git_commit" usebundle => git_commit("$(repo)", "$(msg)");
}
Implementation:
bundle agent git_commit(repo_path, message)
{
classes:
"ok_repo" expression => fileexists("$(repo_path)/.git");
methods:
ok_repo::
"git_commit" usebundle => git("$(repo_path)", "commit", '-m "$(message)"');
}
git
Prototype: git(repo_path, subcmd, args)
Description: generic interface to git
Arguments:
repo_path
: absolute path to a new or existing git repositorysubcmd
: any valid git sub-commandargs
: a single string of arguments to pass
This bundle will drop privileges if running as root (uid 0) and the
repository is owned by a different user. Use DEBUG
or DEBUG_git
(from the
command line, -D DEBUG_git
) to see every Git command it runs.
Example:
bundle agent git_rm_files_from_staging
{
vars:
"repo" string => "/var/git/myrepo";
"git_cmd" string => "reset --soft";
"files" slist => { "fileA", "fileB", "fileC" };
methods:
"git_reset" usebundle => git("$(repo)", "$(git_cmd)", "HEAD -- $(files)");
}
Implementation:
bundle agent git(repo_path, subcmd, args)
{
vars:
"oneliner" string => "$(paths.path[git])";
"repo_uid"
string => filestat($(repo_path), "uid"),
comment => "So that we don't mess up permissions, we will just execute
all commands as the current owner of .git";
"repo_gid"
string => filestat($(repo_path), "gid"),
comment => "So that we don't mess up permissions, we will just execute
all commands as the current group of .git";
# We get the passwd entry from the user that owns the repo so
# that we can extract the home directory for later use.
"repo_uid_passwd_ent"
string => execresult("$(paths.getent) passwd $(repo_uid)", noshell),
comment => "We need to extract the home directory of the repo
owner so that it can be used to avoid errors from
unprivledged execution trying to access the root
users git config.";
classes:
"am_root" expression => strcmp($(this.promiser_uid), "0");
# $(repo_uid) must be defined before we try to test this or we will end up
# having at least one pass during evaluation the agent will not know it
# needs to drop privileges, leading to some files like .git/index being
# created with elevated privileges, and subsequently causing the agent to
# not be able to commit as a normal user.
"need_to_drop"
not => strcmp($(this.promiser_uid), $(repo_uid)),
ifvarclass => isvariable( repo_uid );
am_root.need_to_drop::
# This regular expression could be tightened up
# Extract the home directory from the owner of the repository
# into $(repo_uid_passwd[1])
"extracted_repo_uid_home"
expression => regextract( ".*:.*:\d+:\d+:.*:(.*):.*",
$(repo_uid_passwd_ent),
"repo_uid_passwd" ),
ifvarclass => isvariable("repo_uid_passwd_ent");
commands:
am_root.need_to_drop::
# Because cfengine does not inherit the shell environment when
# executing commands, git will look for the root users git
# config and error when the executing user does not have
# access. So we need to set the home directory of the executing
# user.
"$(paths.env) HOME=$(repo_uid_passwd[1]) $(oneliner)"
args => "$(subcmd) $(args)",
classes => kept_successful_command,
contain => setuidgid_dir( $(repo_uid), $(repo_gid), $(repo_path) );
!am_root|!need_to_drop::
"$(oneliner)"
args => "$(subcmd) $(args)",
classes => kept_successful_command,
contain => in_dir( $(repo_path) );
reports:
"DEBUG|DEBUG_$(this.bundle).am_root.need_to_drop"::
"DEBUG $(this.bundle): with dropped privileges to uid '$(repo_uid)' and gid '$(repo_gid)', in directory '$(repo_path)', running Git command '$(paths.env) HOME=\"$(repo_uid_passwd[1])\" $(oneliner) $(subcmd) $(args)'"
ifvarclass => isvariable("repo_uid_passwd[1]");
"DEBUG|DEBUG_$(this.bundle).(!am_root|!need_to_drop)"::
"DEBUG $(this.bundle): with current privileges, in directory '$(repo_path)', running Git command '$(oneliner) $(subcmd) $(args)'";
}
file bodies
control
Prototype: control
Description: Include policy files used by this policy file as part of inputs
Implementation:
body file control
{
inputs => { @(vcs_common.inputs) };
}
lib/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 no_carriage_returns(filename)
{
commands:
_stdlib_path_exists_sed::
"$(paths.sed)" -> { "CFE-3216" }
args => "-i 's/^M//' $(filename)",
comment => "Preferred reference style";
"$(paths[sed])"
args => "-i 's/^M//' $(filename)",
comment => "Alternate array reference style";
}
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";
"path[setfacl]" string => "/usr/bin/setfacl";
freebsd|darwin::
"path[npm]" string => "/usr/local/bin/npm";
"path[pip]" string => "/usr/local/bin/pip";
"path[virtualenv]" string => "/usr/local/bin/virtualenv";
"path[automount]" string => "/usr/sbin/automount";
_have_bin_env::
"path[env]" string => "/bin/env";
!_have_bin_env::
"path[env]" string => "/usr/bin/env";
_have_bin_systemctl::
"path[systemctl]" string => "/bin/systemctl";
!_have_bin_systemctl::
"path[systemctl]" string => "/usr/bin/systemctl";
_have_bin_journalctl::
"path[journalctl]" string => "/bin/journalctl";
!_have_bin_journalctl::
"path[journalctl]" string => "/usr/bin/journalctl";
_have_bin_timedatectl::
"path[timedatectl]" string => "/bin/timedatectl";
!_have_bin_timedatectl::
"path[timedatectl]" string => "/usr/bin/timedatectl";
linux::
"path[lsattr]" string => "/usr/bin/lsattr";
"path[tar]" string => "/bin/tar";
"path[pgrep]" string => "/usr/bin/pgrep";
"path[getent]" string => "/usr/bin/getent";
"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";
coreos::
"path[awk]" string => "/usr/bin/awk";
"path[cat]" string => "/usr/bin/cat";
"path[cksum]" string => "/usr/bin/cksum";
"path[curl]" string => "/usr/bin/curl";
"path[cut]" string => "/usr/bin/cut";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[echo]" string => "/usr/bin/echo";
"path[ip]" string => "/usr/bin/ip";
"path[lsof]" string => "/usr/bin/lsof";
"path[netstat]" string => "/usr/bin/netstat";
"path[ping]" string => "/usr/bin/ping";
"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[wget]" string => "/usr/bin/wget";
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[bhyvectl]" string => "/usr/sbin/bhyvectl";
"path[getfacl]" string => "/bin/getfacl";
"path[setfacl]" string => "/bin/setfacl";
"path[dtrace]" string => "/usr/sbin/dtrace";
"path[service]" string => "/usr/sbin/service";
"path[zpool]" string => "/sbin/zpool";
"path[zfs]" string => "/sbin/zfs";
openbsd::
"path[cksum]" string => "/bin/cksum";
smartos::
"path[npm]" string => "/opt/local/bin/npm";
"path[pip]" string => "/opt/local/bin/pip";
solaris::
"path[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[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/sbin/dig";
"path[echo]" string => "/usr/bin/echo";
"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[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";
solaris.(mpf_stdlib_use_posix_utils.!disable_mpf_stdlib_use_posix_utils)::
"path[awk]" string => "/usr/xpg4/bin/awk";
"path[df]" string => "/usr/xpg4/bin/df";
"path[egrep]" string => "/usr/xpg4/bin/egrep";
"path[find]" string => "/usr/xpg4/bin/find";
"path[grep]" string => "/usr/xpg4/bin/grep";
"path[ls]" string => "/usr/xpg4/bin/ls";
"path[sed]" string => "/usr/xpg4/bin/sed";
"path[sort]" string => "/usr/xpg4/bin/sort";
"path[tr]" string => "/usr/xpg4/bin/tr";
solaris.!(mpf_stdlib_use_posix_utils.!disable_mpf_stdlib_use_posix_utils)::
"path[awk]" string => "/usr/bin/awk";
"path[df]" string => "/usr/bin/df";
"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[sed]" string => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[tr]" string => "/usr/bin/tr";
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[getenforce]" string => "/usr/sbin/getenforce";
"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[restorecon]" string => "/sbin/restorecon";
"path[sed]" string => "/bin/sed";
"path[semanage]" string => "/usr/sbin/semanage";
"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[usermod]" string => "/usr/sbin/usermod";
"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[getenforce]" string => "/usr/sbin/getenforce";
"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[restorecon]" string => "/sbin/restorecon";
"path[sed]" string => "/bin/sed";
"path[semanage]" string => "/usr/sbin/semanage";
"path[sort]" string => "/usr/bin/sort";
"path[test]" string => "/usr/bin/test";
"path[tr]" string => "/usr/bin/tr";
"path[wc]" string => "/usr/bin/wc";
"path[wget]" string => "/usr/bin/wget";
"path[realpath]" string => "/usr/bin/realpath";
#
"path[apt_cache]" string => "/usr/bin/apt-cache";
"path[apt_config]" string => "/usr/bin/apt-config";
"path[apt_get]" string => "/usr/bin/apt-get";
"path[apt_key]" string => "/usr/bin/apt-key";
"path[aptitude]" string => "/usr/bin/aptitude";
"path[dpkg]" string => "/usr/bin/dpkg";
"path[dpkg_divert]" string => "/usr/bin/dpkg-divert";
"path[groupadd]" string => "/usr/sbin/groupadd";
"path[groupdel]" string => "/usr/sbin/groupdel";
"path[groupmod]" string => "/usr/sbin/groupmod";
"path[ifconfig]" string => "/sbin/ifconfig";
"path[ip]" string => "/sbin/ip";
"path[service]" string => "/usr/sbin/service";
"path[svc]" string => "/usr/sbin/service";
"path[update_alternatives]" string => "/usr/bin/update-alternatives";
"path[update_rc_d]" string => "/usr/sbin/update-rc.d";
"path[useradd]" string => "/usr/sbin/useradd";
"path[userdel]" string => "/usr/sbin/userdel";
"path[usermod]" string => "/usr/sbin/usermod";
archlinux||darwin::
"path[sysctl]" string => "/usr/bin/sysctl";
!(archlinux||darwin)::
"path[sysctl]" string => "/sbin/sysctl";
!(suse|sles)::
"path[logger]" string => "/usr/bin/logger";
opensuse::
"path[ls]" string => "/usr/bin/ls";
"path[lsof]" string => "/usr/bin/lsof";
"path[awk]" string => "/usr/bin/awk";
"path[cat]" string => "/usr/bin/cat";
"path[cksum]" string => "/usr/bin/cksum";
"path[crontab]" string => "/usr/bin/crontab";
"path[curl]" string => "/usr/bin/curl";
"path[cut]" string => "/usr/bin/cut";
"path[df]" string => "/usr/bin/df";
"path[diff]" string => "/usr/bin/diff";
"path[dig]" string => "/usr/bin/dig";
"path[dmidecode]" string => "/usr/sbin/dmidecode";
"path[echo]" string => "/usr/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 => "/usr/bin/hostname";
"path[init]" string => "/sbin/init";
"path[iptables]" string => "/usr/sbin/iptables";
"path[ls]" string => "/usr/bin/ls";
"path[lsof]" string => "/usr/bin/lsof";
"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 => "/usr/bin/sed";
"path[sort]" string => "/usr/bin/sort";
"path[test]" string => "/usr/bin/test";
"path[tr]" string => "/usr/bin/tr";
"path[logger]" string => "/usr/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[ip]" string => "/sbin/ip";
"path[rpm]" string => "/usr/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";
suse|sles::
"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");
"_have_bin_timedatectl" expression => fileexists("/bin/timedatectl");
"_have_bin_journalctl" expression => fileexists("/bin/journalctl");
"_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";
}
lib/event.cf
agent bundles
event_register
Prototype: event_register(prefix, type, name, persistence, metadata)
Description: Register a event_$(prefix)_$(type)_$(name)
class with meta=metadata
Arguments:
prefix
: The prefix (usually the issuing bundle name)type
: The event typename
: The event namepersistence
: the time, in minutes, the class should persist on disk (unless collected)metadata
: A slist with the event metadata
This bundle creates a class that conforms to the ad-hoc event protocol defined herein.
See also: event_handle
Implementation:
bundle agent event_register(prefix, type, name, persistence, metadata)
{
vars:
"e" string => "event_$(prefix)_$(type)_$(name)";
"metadata_string" string => format("%S", metadata);
classes:
"$(e)" scope => "namespace",
persistence => $(persistence),
meta => { "event", "prefix=$(prefix)", "type=$(type)", "name=$(name)", @(metadata) };
reports:
inform_mode::
"$(this.bundle): creating event $(e) persistent for $(persistence) minutes with metadata $(metadata_string)";
}
event_handle
Prototype: event_handle(prefix, type)
Description: Handle all the events matching prefix
and type
through delegation
Arguments:
prefix
: A prefix for the event, can be.*
for alltype
: A type for the event, can be.*
for all
This bundle looks for all the event classes matching prefix
and type
, then
for all the bundles that have declared they can handle that prefix and type,
and then passes the corresponding event classes to each bundle.
See also: event_register
Implementation:
bundle agent event_handle(prefix, type)
{
vars:
"events_prefix" slist => classesmatching("event_.*", "prefix=$(prefix)");
"events_type" slist => classesmatching("event_.*", "type=$(type)");
"events" slist => intersection(events_prefix, events_type);
"events_string" string => format("%S", events);
"handlers_prefix" slist => bundlesmatching("default:event_.*", format("event_prefix=(%s|ALL)", escape($(prefix))));
"handlers_type" slist => bundlesmatching("default:event_.*", format("event_type=(%s|ALL)", escape($(type))));
"handlers" slist => intersection(handlers_prefix, handlers_type);
"handlers_string" string => format("%S", handlers);
methods:
"" usebundle => $(handlers)(@(events)),
classes => event_cancel_events(@(events));
reports:
inform_mode::
"$(this.bundle): with prefix $(prefix) and type $(type) found events $(events_string)";
"$(this.bundle): with prefix $(prefix) and type $(type) found handlers $(handlers_string)";
}
event_debug_handler
Prototype: event_debug_handler(events)
Description: Debug all the events matching the meta tags event_prefix
and event_type
Arguments:
events
: The list of events, passed fromevent_handle
This is an event handler that just prints out all the events it finds. To be
registered as a handler, it must have the meta tags
indicated below.
See also: event_handle
, event_register
Implementation:
bundle agent event_debug_handler(events)
{
meta:
"tags" slist => { "event_handler", "event_prefix=.*", "event_type=.*" };
vars:
"events_string" string => format("%S", events);
"tags_string" string => format("%S", "$(this.bundle)_meta.tags");
reports:
inform_mode::
"$(this.bundle): with tags $(tags_string) got events $(events_string)";
}
event_install_handler
Prototype: event_install_handler(events)
Description: Handle all the install events matching the meta tags event_prefix
and event_type
Arguments:
events
: The list of events, passed fromevent_handle
This is an event handler that just prints out all the install events it finds.
To be registered as a handler, it must have the meta tags
indicated below.
The subtlety in event_prefix=ALL
is that we want to match only
event_handle(ANYTHING, "install")
but not event_handle(".*", ANYTHING)
. If
you're confused, just remember: debug handlers use event_prefix=.*
and
everything else uses event_prefix=ALL
.
See also: event_handle
, event_register
Implementation:
bundle agent event_install_handler(events)
{
meta:
"tags" slist => { "event_handler", "event_prefix=ALL", "event_type=install" };
vars:
"events_string" string => format("%S", events);
"tags_string" string => format("%S", "$(this.bundle)_meta.tags");
reports:
inform_mode::
"$(this.bundle): with tags $(tags_string) got events $(events_string)";
}
event_usage_example
Prototype: event_usage_example
Description: Simple demo of event_register and event_handle usage
You can run it like this: cf-agent -K ./event.cf -b event_usage_example
Or for extra debugging, you can run it like this: cf-agent -KI ./event.cf -b event_usage_example
See also: event_handle
, event_register
Expected output with -KI
:
R: event_register: creating event event_event_usage_example_restart_apache persistent for 1440 minutes with metadata { }
R: event_register: creating event event_event_usage_example_install_php persistent for 2880 minutes with metadata { }
R: event_install_handler: with tags { "event_handler", "event_prefix=ALL", "event_type=install" } got events { "event_event_usage_example_install_php" }
R: event_handle: with prefix event_usage_example and type install found events { "event_event_usage_example_install_php" }
R: event_handle: with prefix event_usage_example and type install found handlers { "default:event_install_handler" }
R: event_debug_handler: with tags { "event_handler", "event_prefix=.*", "event_type=.*" } got events { "event_event_usage_example_restart_apache", "event_event_usage_example_install_php" }
R: event_handle: with prefix .* and type .* found events { "event_event_usage_example_restart_apache", "event_event_usage_example_install_php" }
R: event_handle: with prefix .* and type .* found handlers { "default:event_debug_handler" }
Implementation:
bundle agent event_usage_example
{
vars:
"empty" slist => {};
methods:
# register a restart event named "apache" with persistence = 1 day
"" usebundle => event_register($(this.bundle), "restart", "apache", 1440, @(empty)); # 1 day
# register an install event named "php" with persistence = 2 days
"" usebundle => event_register($(this.bundle), "install", "php", 2880, @(empty));
# the following can be run immediately, or up to 2 days later to collect
# the install event above
"" usebundle => event_handle($(this.bundle), "install");
# the following can be run immediately, or up to 1 day later to collect
# the restart event above
"" usebundle => event_handle(".*", ".*");
}
classes bodies
event_cancel_events
Prototype: event_cancel_events(events)
Description: Cancel any events
Arguments:
events
: A slist of events to cancel
Implementation:
body classes event_cancel_events(events)
{
cancel_notkept => { @(events) };
cancel_kept => { @(events) };
cancel_repaired => { @(events) };
}
lib/guest_environments.cf
environment_resources bodies
kvm
Prototype: kvm(name, arch, cpu_count, mem_kb, disk_file)
Description: An environment_resources
body for a KVM virtual machine.
The env_spec
attribute is set to a KVM XML specification.
Arguments:
name
: The name of the virtual machinearch
: The architecturecpu_count
: The number of CPUs the virtual machine should havemem_kb
: The amount of RAM in kilobytedisk_file
: The file on the host system for the virtual machine's harddrive
Example:
bundle agent manage_vm
{
guest_environments:
am_vm_host::
"db_server"
environment_host => atlas,
environment_type => "kvm",
environment_state => "create",
environment_resources => kvm("PSQL1, "x86_64", "4", "4096", "/var/lib/libvirt/images/psql1.iso")
}
Implementation:
body environment_resources kvm(name, arch, cpu_count, mem_kb, disk_file)
{
env_spec =>
"<domain type='kvm'>
<name>$(name)</name>
<memory>$(mem_kb)</memory>
<currentMemory>$(mem_kb)</currentMemory>
<vcpu>$(cpu_count)</vcpu>
<os>
<type arch='$(arch)'>hvm</type>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/bin/kvm</emulator>
<disk type='file' device='disk'>
<source file='$(disk_file)'/>
<target dev='vda' bus='virtio'/>
</disk>
<interface type='network'>
<source network='default'/>
</interface>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='-1' autoport='yes'/>
</devices>
</domain>";
}
lib/commands.cf
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)";
}
lib/cfe_internal_hub.cf
common bodies
cfe_internal_hub_common
Prototype: cfe_internal_hub_common
Description: Enumerate policy files used by this policy file for inclusion to inputs
Implementation:
bundle common cfe_internal_hub_common
{
vars:
"inputs" slist => { "$(this.promise_dirname)/common.cf",
"$(this.promise_dirname)/commands.cf"};
}
agent bundles
cfe_internal_hub_maintain
Prototype: cfe_internal_hub_maintain
Description: Executes reporting database maintenance process By default database clean up interval is 24 hours. Length of log history in database is controlled by modifying "history_length_days" key in report_settings variable.
Intervals less than 6 hours must be used with caution as maintenance process could take a considerable time
Implementation:
bundle agent cfe_internal_hub_maintain
{
vars:
"report_settings"
data => parsejson('[
{
"report": "contexts",
"table": "__ContextsLog",
"history_length_days": 7,
"time_key": "ChangeTimeStamp"
},
{
"report": "variables",
"table": "__VariablesLog",
"history_length_days": 7,
"time_key": "ChangeTimeStamp"
},
{
"report": "software",
"table": "__SoftwareLog",
"history_length_days": 28,
"time_key": "ChangeTimeStamp"
},
{
"report": "software_updates",
"table": "__SoftwareUpdatesLog",
"history_length_days": 28,
"time_key": "ChangeTimeStamp"
},
{
"report": "filechanges",
"table": "__FileChangesLog",
"history_length_days": 365,
"time_key": "ChangeTimeStamp"
},
{
"report": "benchmarks",
"table": "__BenchmarksLog",
"history_length_days": 1,
"time_key": "CheckTimeStamp"
},
{
"report": "promise_executions",
"table": "__PromiseExecutionsLog",
"history_length_days": 7,
"time_key": "ChangeTimeStamp"
}
]');
"diagnostics_settings"
data => parsejson('[
{
"report": "hub_connection_errors",
"table": "__HubConnectionErrors",
"history_length_days": 1,
"time_key": "CheckTimeStamp"
},
{
"report": "diagnostics",
"table": "Diagnostics",
"history_length_days": 1,
"time_key": "TimeStamp"
}
]');
classes:
"enable_cfe_internal_reporting_database_purge_old_history" -> { "postgres", "CFEngine Enterprise" }
expression => "enterprise_edition.Hr00";
"enable_cfe_internal_parition_creation_job" -> { "postgres", "CFEngine Enterprise" }
expression => "enterprise_edition.Hr00";
methods:
enable_cfe_internal_reporting_database_purge_old_history::
"Remove old report history"
usebundle => cfe_internal_database_cleanup_reports(@(report_settings)),
action => if_elapsed_day;
"Remove cf-consumer history"
usebundle => cfe_internal_database_cleanup_consumer_status("3"),
action => if_elapsed_day;
"Remove diagnostics history"
usebundle => cfe_internal_database_cleanup_diagnostics(@(diagnostics_settings)),
action => if_elapsed_day;
"Remove promise log history"
usebundle => cfe_internal_database_cleanup_promise_log("7"),
action => if_elapsed_day;
enable_cfe_internal_parition_creation_job::
"Create new partitions for partitioned tables"
usebundle => cfe_internal_database_partitioning(),
action => if_elapsed_day;
}
cfe_internal_database_cleanup_reports
Prototype: cfe_internal_database_cleanup_reports(settings)
Description: clean up the reporting tables
Arguments:
settings
ofvars
promiser index ofvars
promiser remove_query_$(settings[$(index)][report]) ofcommands
promiser $(sys.bindir)/psql cfdb -c "$(remove_query_$(settings[$(index)][report]))"
Implementation:
bundle agent cfe_internal_database_cleanup_reports (settings)
{
vars:
"index" slist => getindices(settings);
"remove_query_$(settings[$(index)][report])"
comment => "This query will delete rows that have a changetimestamp
$(settings[$(index)][history_length_days]) older than the
hosts most recent changetimestamp in the table.",
string => "WITH z AS
(
SELECT hostkey,
Max($(settings[$(index)][time_key])) AS latest
FROM $(settings[$(index)][table])
GROUP BY hostkey)
DELETE
FROM $(settings[$(index)][table]) t
using z
WHERE z.hostkey = t.hostkey
AND t.$(settings[$(index)][time_key]) <= (z.latest - '$(settings[$(index)][history_length_days]) day'::interval);";
commands:
"$(sys.bindir)/psql cfdb -c \"$(remove_query_$(settings[$(index)][report]))\""
handle => "cf_database_maintain_report_$(settings[$(index)][report])";
}
cfe_internal_database_cleanup_consumer_status
Prototype: cfe_internal_database_cleanup_consumer_status(row_count)
Description: keep up to row_count entries in the database
Arguments:
row_count
ofclasses
promiser has_sql_function_cleanup_historical_data ofclasses
promiser has_sql_table___status ofvars
promiser status_table_name ofvars
promiser remove_query ofvars
promiser delete_future_ts_query: string, used in the value of attributestring
ofvars
promiser remove_query ofcommands
promiser $(sys.bindir)/psql cfdb -c "$(remove_query)" ofcommands
promiser $(sys.bindir)/psql cfdb -c "$(delete_future_ts_query)"
Implementation:
bundle agent cfe_internal_database_cleanup_consumer_status (row_count)
{
classes:
# We probe the database to see if the function is defined
"has_sql_function_cleanup_historical_data"
expression => returnszero( "$(sys.bindir)/psql cfdb -c \"SELECT 'cleanup_historical_data'::regproc;\" > /dev/null 2>&1", useshell ),
if => isexecutable( "$(sys.bindir)/psql" );
"has_sql_table___status" -> { "ENT-4331" }
expression => returnszero( "$(sys.bindir)/psql cfdb -c \"SELECT 'public.__status'::regclass;\" > /dev/null 2>&1", useshell ),
if => isexecutable( "$(sys.bindir)/psql" );
vars:
any::
# The status table changed it's name for 3.12.2 and 3.10.6, this handles
# using the proper table name in the cleanup query
"status_table_name" -> { "ENT-4331" }
string => ifelse( "has_sql_table___status", "__status", "status");
any::
# This is the fallback query to use in case the cleanup_historical_data
# function is not present.
"remove_query" -> { "ENT-4365" }
string => "DELETE FROM $(status_table_name) WHERE ts IN (SELECT ts FROM $(status_table_name) ORDER BY ts DESC OFFSET 50000);";
"delete_future_ts_query" -> { "ENT-4362", "ENT-4992" }
string => "DELETE FROM $(status_table_name) WHERE to_timestamp(ts::bigint) > (now() + interval '2 days')::timestamp;";
has_sql_function_cleanup_historical_data::
"remove_query"
string => "SELECT * FROM cleanup_historical_data('$(status_table_name)', 'ts', $(row_count), 'host');";
commands:
"$(sys.bindir)/psql cfdb -c \"$(remove_query)\""
handle => "cf_database_maintain_consumer_status";
"$(sys.bindir)/psql cfdb -c \"$(delete_future_ts_query)\"" -> { "ENT-4362" }
handle => "cf_database_maintain_consumer_status_no_future_timestamps";
}
cfe_internal_database_cleanup_diagnostics
Prototype: cfe_internal_database_cleanup_diagnostics(settings)
Arguments:
settings
ofvars
promiser index ofvars
promiser remove_query_$(settings[$(index)][report]) ofcommands
promiser $(sys.bindir)/psql cfdb -c "$(remove_query_$(settings[$(index)][report]))"
Implementation:
bundle agent cfe_internal_database_cleanup_diagnostics (settings)
{
vars:
"index" slist => getindices("settings");
"remove_query_$(settings[$(index)][report])"
string => "DELETE FROM $(settings[$(index)][table]) WHERE $(settings[$(index)][time_key]) < (CURRENT_TIMESTAMP - INTERVAL '$(settings[$(index)][history_length_days]) day');";
commands:
"$(sys.bindir)/psql cfdb -c \"$(remove_query_$(settings[$(index)][report]))\""
handle => "cf_database_maintain_diagnostics_$(settings[$(index)][report])";
}
cfe_internal_database_cleanup_promise_log
Prototype: cfe_internal_database_cleanup_promise_log(history_length_days)
Description: clean up promise log files older than history_length_days
Arguments:
history_length_days
: Number of days after which promise logs should be deleted
Implementation:
bundle agent cfe_internal_database_cleanup_promise_log (history_length_days)
{
vars:
"cleanup_query_repaired"
string => "SELECT promise_log_partition_cleanup('REPAIRED', '$(history_length_days) day');";
"cleanup_query_notkept"
string => "SELECT promise_log_partition_cleanup('NOTKEPT', '$(history_length_days) day');";
commands:
"$(sys.bindir)/psql cfdb -c \"$(cleanup_query_repaired)\""
handle => "cf_database_maintain_promise_log_repaired";
"$(sys.bindir)/psql cfdb -c \"$(cleanup_query_notkept)\""
handle => "cf_database_maintain_promise_log_notkept";
}
cfe_internal_database_partitioning
Prototype: cfe_internal_database_partitioning
Description: create any nesesary table partitions for database
Implementation:
bundle agent cfe_internal_database_partitioning()
{
vars:
"promise_outcome"
slist => {"REPAIRED", "NOTKEPT"};
"query_create_promise_log_$(promise_outcome)"
string => "SELECT promise_log_partition_create(NOW() - INTERVAL '7 day', 7 + 3, '$(promise_outcome)');";
commands:
"$(sys.bindir)/psql cfdb -c \"$(query_create_promise_log_$(promise_outcome))\""
handle => "cf_database_create_partition_promise_log_$(promise_outcome)";
}
cfe_internal_postgresql_maintenance
Prototype: cfe_internal_postgresql_maintenance
Description: Vacuum Full PostgreSQL for maintenance
Implementation:
bundle agent cfe_internal_postgresql_maintenance
{
vars:
"vacuum_cfdb_cmd" string => "$(sys.bindir)/vacuumdb --full --dbname=cfdb",
comment => "Vacuum Full PostgreSQL (database: cfdb)",
handle => "cfe_internal_postgresql_maintenance_vacuum_full";
policy_server.enterprise.cfconsumer_in_enterprise::
# TODO Remove after 3.12 EOL
"cf_consumer_pid" -> { "ENT-2797" }
string => readfile("$(sys.workdir)/cf-consumer.pid", 0),
ifvarclass => fileexists( "$(sys.workdir)/cf-consumer.pid" ),
comment => "Read cf-consumer.pid for the main cf-consumer PID";
classes:
cfconsumer_in_enterprise::
# TODO Remove after 3.12 EOL
"cf_consumer_pid_correct" -> { "ENT-2972" }
expression => isvariable("cf_consumer_pid"),
comment => "Check if cf-consumer pid is correctly defined";
processes:
any::
"cf-hub" signals => { "term" },
comment => "Terminate cf-hub while doing PostgreSQL maintenance",
handle => "cfe_internal_postgresql_maintenance_processes_term_cf_hub";
cf_consumer_pid_correct.cfconsumer_in_enterprise::
# TODO Remove from MPF after 3.12 EOL
"cf-consumer" -> { "ENT-2797" }
signals => { "kill" },
process_select => by_pid("$(cf_consumer_pid)"),
comment => "Kill cf-consumer while doing PostgreSQL maintenance",
handle => "cfe_internal_postgresql_maintenance_processes_kill_cf_consumer";
commands:
any::
"$(vacuum_cfdb_cmd)"
comment => "Run vacuum db full command (database: cfdb)",
classes => kept_successful_command,
handle => "cfe_internal_postgresql_maintenance_commands_run_vacuumdb_cmd_full";
}
cfe_internal_postgresql_vacuum
Prototype: cfe_internal_postgresql_vacuum
Description: Vacuum (with analyze) over cfdb database.
Implementation:
bundle agent cfe_internal_postgresql_vacuum
{
vars:
"vacuum_cfdb_cmd"
string => "$(sys.bindir)/vacuumdb --analyze --quiet --dbname=cfdb",
comment => "Vacuum command with update statistics enabled";
commands:
"$(vacuum_cfdb_cmd)"
comment => "Run vacuum db command (database: cfdb)",
handle => "cfe_internal_postgresql_maintenance_commands_run_vacuumdb";
}
file bodies
control
Prototype: control
Description: Include policy files used by this policy file as part of inputs
Implementation:
body file control
{
inputs => { @(cfe_internal_hub_common.inputs) };
}
lib/processes.cf
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" };
}
lib/stdlib.cf
common bodies
stdlib_common
Prototype: stdlib_common
Description: Select which parts of the stdlib should be added to inputs
Implementation:
bundle common stdlib_common
{
vars:
"input[paths]" string => "$(this.promise_dirname)/paths.cf";
"input[common]" string => "$(this.promise_dirname)/common.cf";
"input[commands]" string => "$(this.promise_dirname)/commands.cf";
"input[packages]" string => "$(this.promise_dirname)/packages.cf";
"input[files]" string => "$(this.promise_dirname)/files.cf";
"input[edit_xml]" string => "$(this.promise_dirname)/edit_xml.cf";
"input[services]" string => "$(this.promise_dirname)/services.cf";
"input[processes]" string => "$(this.promise_dirname)/processes.cf";
"input[storage]" string => "$(this.promise_dirname)/storage.cf";
"input[databases]" string => "$(this.promise_dirname)/databases.cf";
"input[users]" string => "$(this.promise_dirname)/users.cf";
"input[monitor]" string => "$(this.promise_dirname)/monitor.cf";
"input[guest_environments]" string => "$(this.promise_dirname)/guest_environments.cf";
"input[bundles]" string => "$(this.promise_dirname)/bundles.cf";
"input[reports]" string => "$(this.promise_dirname)/reports.cf";
"input[cfe_internal]" string => "$(this.promise_dirname)/cfe_internal.cf";
"input[cfe_internal_hub]" string => "$(this.promise_dirname)/cfe_internal_hub.cf";
"input[cfengine_enterprise_hub_ha]" string => "$(this.promise_dirname)/cfengine_enterprise_hub_ha.cf";
# jUnit and TAP reports
"input[testing]" string => "$(this.promise_dirname)/testing.cf";
any::
"inputs" slist => getvalues(input);
reports:
verbose_mode::
"$(this.bundle): adding COPBL stdlib inputs='$(inputs)'";
}
file bodies
control
Prototype: control
Description: Include the standard library files selected to inputs
Implementation:
body file control
{
inputs => { @(stdlib_common.inputs) };
}
lib/common.cf
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" }
}
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 defined if the 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
Arguments:
scope
: The scope in which the class should be defined (bundle
ornamespace
)class_prefix
: The prefix for the classes defined
This body can be applied to any promise and sets global
(namespace
) or local (bundle
) classes based on its outcome. For
instance, with class_prefix
set to abc
:
if the promise is to change a file's owner to
nick
and the file was already owned bynick
, the classesabc_reached
andabc_kept
will be set.if the promise is to change a file's owner to
nick
and the file was owned byadam
and the change succeeded, the classesabc_reached
andabc_repaired
will be set.
This body is a simpler, more consistent version of the body
scoped_classes_generic
, which see. The key difference is that
fewer classes are defined, and only for outcomes that we can know.
For example this body does not define "OK/not OK" outcome classes,
since a promise can be both kept and failed at the same time.
It's important to understand that promises may do multiple things, so a promise is not simply "OK" or "not OK." The best way to understand what will happen when your specific promises get this body is to test it in all the possible combinations.
Suffix Notes:
_reached
indicates the promise was tried. Any outcome will result in a class with this suffix being defined._kept
indicates some aspect of the promise was kept_repaired
indicates some aspect of the promise was repaired_not_kept
indicates some aspect of the promise was not kept. error, failed, denied and timeout outcomes will result in a class with this suffix being defined_error
indicates the promise repair encountered an error_failed
indicates the promise failed_denied
indicates the promise repair was denied_timeout
indicates the promise timed out
Example:
bundle agent example
{
commands:
"/bin/true"
classes => results("bundle", "my_class_prefix");
reports:
my_class_prefix_kept::
"My promise was kept";
my_class_prefix_repaired::
"My promise was repaired";
}
See also: scope
, scoped_classes_generic
, classes_generic
Implementation:
body classes results(scope, class_prefix)
{
scope => "$(scope)";
promise_kept => { "$(class_prefix)_reached",
"$(class_prefix)_kept" };
promise_repaired => { "$(class_prefix)_reached",
"$(class_prefix)_repaired" };
repair_failed => { "$(class_prefix)_reached",
"$(class_prefix)_error",
"$(class_prefix)_not_kept",
"$(class_prefix)_failed" };
repair_denied => { "$(class_prefix)_reached",
"$(class_prefix)_error",
"$(class_prefix)_not_kept",
"$(class_prefix)_denied" };
repair_timeout => { "$(class_prefix)_reached",
"$(class_prefix)_error",
"$(class_prefix)_not_kept",
"$(class_prefix)_timeout" };
}
diff_results
Prototype: diff_results(scope, x)
Description: Define x
prefixed/suffixed with promise outcome with command return codes adjusted to align with diff
.
Arguments:
scope
: The scope the class should be defined with [bundle|namespace].x
: The unique part of the classes to be defined.
From man diff: Exit status is 0 if inputs are the same, 1 if different, 2 if trouble.
Example:
bundle agent example
{
commands:
"/usr/bin/diff"
args => "/tmp/file1 /tmp/file2",
classes => diff_results("diff");
vars:
"c" slist => classesmatching("diff_.*");
reports:
"Found class '$(c)'";
"Files Differ!"
if => "diff_failed|diff_error|diff_not_kept";
"Files are the same."
if => "diff_kept";
}
Implementation:
body classes diff_results(scope, x)
{
inherit_from => results( $(scope), $(x) );
kept_returncodes => { "0" };
failed_returncodes => { "1","2" };
}
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" };
}
u_kept_successful_command
Prototype: u_kept_successful_command
Description: Set command to "kept" instead of "repaired" if it returns 0
Implementation:
body classes u_kept_successful_command
{
kept_returncodes => { "0" };
}
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 unique part of the class that should be defined 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" };
}
lib/reports.cf
printfile bodies
cat
Prototype: cat(file)
Description: Report the contents of a file
Arguments:
file
: The full path of the file to report
Implementation:
body printfile cat(file)
{
file_to_print => "$(file)";
number_of_lines => "inf";
}
head
Prototype: head(file)
Description: Report the first 10 lines of a file
Arguments:
file
: The full path of the file to report
Implementation:
body printfile head(file)
{
file_to_print => "$(file)";
# GNU head defaults to 10, but we start counting from 0
number_of_lines => "10";
}
head_n
Prototype: head_n(file, n)
Description: Report the first 10 lines of a file
Arguments:
file
: The full path of the file to reportn
: The number of lines to report
Implementation:
body printfile head_n(file, n)
{
file_to_print => "$(file)";
number_of_lines => "$(n)";
}
lib/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)";
}
lib/storage.cf
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";
}
lib/examples.cf
agent bundles
probabilistic_usebundle
Prototype: probabilistic_usebundle(probability, bundlename)
Description: activate named bundle probabilistically
Arguments:
probability
: probability that the named bundle will be activated during a given agent executionbundlename
: the bundle to activate based on the probability
Example:
bundle agent example
{
methods:
"Toss Coin"
usebundle => probabilistic_usebundle("50", "heads"),
comment => "Call bundle heads ~ 50% of the time";
"Trick Coin"
usebundle => probabilistic_usebundle("75", "heads"),
comment => "Call bundle heads ~ 75% of the time";
}
Implementation:
bundle agent probabilistic_usebundle(probability, bundlename)
{
classes:
"fifty_fifty"
expression => strcmp("$(probability)", "50"),
comment => "We have to special case 50 because of the way dist classes
work you would always get 50 defined";
"not_fifty_fifty" expression => "!fifty_fifty";
"have_remainder" expression => isvariable("remainder");
fifty_fifty.have_remainder::
"activate_bundle"
dist => { "$(probability)000", "$(remainder)"};
not_fifty_fifty.have_remainder::
"activate_bundle"
dist => { "$(probability)", "$(remainder)"};
vars:
fifty_fifty::
"remainder"
string => format("%d", eval("((100 - $(probability)) * 1000) +1", "math", "infix"));
not_fifty_fifty::
"remainder"
string => format("%d", eval("100 - $(probability)", "math", "infix"));
methods:
fifty_fifty::
"Activate bundle probabilistically"
handle => "probabilistic_usebundle_methods_special_case_fifty_fifty_activate_bundle",
usebundle => $(bundlename),
ifvarclass => "activate_bundle_$(probability)000",
comment => "Activate $(bundlename) $(probability)%ish of the time";
not_fifty_fifty::
"Activate bundle probabilistically"
handle => "probabilistic_usebundle_methods_activate_bundle",
usebundle => $(bundlename),
ifvarclass => "activate_bundle_$(probability)",
comment => "Activate $(bundlename) $(probability)% of the time";
reports:
DEBUG.fifty_fifty::
"$(this.bundle) Special case for 50/50";
"$(this.bundle) activate_bundle_$(probability)000"
ifvarclass => "activate_bundle_$(probability)000";
"$(this.bundle) activate_bundle_$(probability)001"
ifvarclass => "activate_bundle_$(probability)001";
}
lib/services.cf
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" }
}
common bodies
services_common
Prototype: services_common
Description: Enumerate policy files used by this policy file for inclusion to inputs
Implementation:
bundle common services_common
{
vars:
"inputs" slist => { "$(this.promise_dirname)/common.cf",
"$(this.promise_dirname)/paths.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". "enable", "enabled", and "disabled" are also able to be used when systemd is detected.
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.freebsd::
"running" -> { "CFE-3513" }
expression => returnszero("$(init) onestatus > /dev/null", "useshell");
chkconfig.have_init.!freebsd::
"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" -> { "CFE-3238" }
expression => reglist(@(systemd_service_info), "ActiveState=(active|activating)");
"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)");
"request_disable" expression => strcmp("disable", "$(state)");
"request_disabled" expression => strcmp("disabled", "$(state)");
"request_enable" expression => strcmp("enable", "$(state)");
"request_enabled" expression => strcmp("enabled", "$(state)");
"action_custom" expression => "!(request_start|request_stop|request_reload|request_restart|request_disable|request_disabled|request_enable|request_enabled)";
"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|request_enable|request_enabled).!service_enabled";
# Respectively, stopping it implicitly disables it
"action_disable" expression => "(request_disable|request_disabled|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|sles::
"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)]"));
}
file bodies
control
Prototype: control
Description: Include policy files used by this policy file as part of inputs
Implementation:
body file control
{
inputs => { @(services_common.inputs) };
}
service_method bodies
bootstart
Prototype: bootstart
Description: Start the service and all its dependencies at boot time
See also: service_autostart_policy
, service_dependence_chain
Implementation:
body service_method bootstart
{
service_autostart_policy => "boot_time";
service_dependence_chain => "start_parent_services";
windows::
service_type => "windows";
}
force_deps
Prototype: force_deps
Description: Start all dependendencies when this service starts, and stop all dependent services when this service stops.
The service does not get automatically started.
See also: service_autostart_policy
, service_dependence_chain
Implementation:
body service_method force_deps
{
service_dependence_chain => "all_related";
windows::
service_type => "windows";
}
standard_services
Prototype: standard_services
Description: Default services_method for when you wan't to call it explicitly
By default this service_method is not used. The call for standard_services is within the core and not here. In case you use a promise like:
services:
"ssh"
service_policy => "start";
Then this method is skipped and CFEngine calls standard_services bundle directly. This is here as a helper in case you wan't to be explicit with your service promise and point it to standard_services (for readability, documentation, etc).
Do note that any options defined in this method does not apply to service promises without explicit template_method call for standard_services.
Example:
services:
"ssh"
service_policy => "start",
service_method => standard_services;
Implementation:
body service_method standard_services
{
service_bundle => default:standard_services( $(this.promiser), $(this.service_policy) );
}
lib/users.cf
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);
}
lib/autorun.cf
agent bundles
autorun
Prototype: autorun
Implementation:
bundle agent autorun
{
vars:
services_autorun::
"bundles" slist => bundlesmatching(".*", "autorun");
"sorted_bundles"
slist => sort("bundles", "lex"),
comment => "Lexicographically sorted bundles for predictable order";
methods:
services_autorun::
"autorun" usebundle => $(sorted_bundles);
reports:
DEBUG|DEBUG_autorun|DEBUG_services_autorun::
"DEBUG $(this.bundle): found bundle $(sorted_bundles) with tag 'autorun'";
}
file bodies
control
Prototype: control
Implementation:
body file control
{
inputs => { @(services_autorun.found_inputs) };
}
lib/edit_xml.cf
edit_xml bundles
xml_insert_tree_nopath
Prototype: xml_insert_tree_nopath(treestring)
Description: Insert XML tree with no path
This edit_xml
bundle inserts the given XML tree. Use with an
empty XML document.
Arguments:
treestring
: The XML tree, as a string
Example:
files:
"/newfile" edit_xml => xml_insert_tree_nopath('<x>y</x>');
Implementation:
bundle edit_xml xml_insert_tree_nopath(treestring)
{
insert_tree:
'$(treestring)';
}
xml_insert_tree
Prototype: xml_insert_tree(treestring, xpath)
Description: Insert XML tree at the given XPath
This edit_xml
bundle inserts the given XML tree at a specific
XPath. Uses insert_tree
.
Arguments:
treestring
: The XML tree, as a stringxpath
: A valid XPath string
Example:
files:
"/file.xml" edit_xml => xml_insert_tree('<x>y</x>', '/a/b/c');
Implementation:
bundle edit_xml xml_insert_tree(treestring, xpath)
{
insert_tree:
'$(treestring)' select_xpath => "$(xpath)";
}
xml_set_value
Prototype: xml_set_value(value, xpath)
Description: Sets or replaces a value in XML at the given XPath
This edit_xml
bundle sets or replaces the value at a specific
XPath with the given value. Uses set_text
.
Arguments:
value
: The new valuexpath
: A valid XPath string
Example:
files:
"/file.xml" edit_xml => xml_set_value('hello', '/a/b/c');
Implementation:
bundle edit_xml xml_set_value(value, xpath)
{
set_text:
"$(value)"
select_xpath => "$(xpath)";
}
xml_set_attribute
Prototype: xml_set_attribute(attr, value, xpath)
Description: Sets or replaces an attribute in XML at the given XPath
This edit_xml
bundle sets or replaces an attribute at a specific
XPath with the given value. Uses set_attribute
.
Arguments:
attr
: The attribute namevalue
: The new attribute valuexpath
: A valid XPath string
Example:
files:
"/file.xml" edit_xml => xml_set_attribute('parameter', 'ha', '/a/b/c');
Implementation:
bundle edit_xml xml_set_attribute(attr, value, xpath)
{
set_attribute:
"$(attr)"
attribute_value => "$(value)",
select_xpath => "$(xpath)";
}
lib/bundles.cf
common bodies
bundles_common
Prototype: bundles_common
Description: Enumerate policy files used by this policy file for inclusion to inputs
Implementation:
bundle common bundles_common
{
vars:
"inputs" slist => { "$(this.promise_dirname)/paths.cf",
"$(this.promise_dirname)/files.cf",
"$(this.promise_dirname)/commands.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|sles::
"crontab" string => "/var/spool/cron/tabs";
redhat|fedora::
"crontab" string => "/var/spool/cron";
freebsd::
"crontab" string => "/var/cron/tabs";
!(suse|sles|redhat|fedora|freebsd)::
"crontab" string => "/var/spool/cron/crontabs";
any::
# We escape the user supplied values so that we can search to see if the
# entry already exists with slightly different spacing.
"e_mins" string => escape("$(mins)");
"e_hours" string => escape("$(hours)");
"e_commands" string => escape("$(commands)");
classes:
!windows::
# We tolerate existing entries that differ only in whitespace and avoid
# entering duplicate entries.
"present_with_potentially_different_spacing"
expression => regline( "^$(e_mins)\s+$(e_hours)\s+\*\s+\*\s+\*\s+$(e_commands)", "$(crontab)/$(user)");
files:
!windows.!present_with_potentially_different_spacing::
"$(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";
}
collect_vars
Prototype: collect_vars(name, tag, flatten)
Description: bundle to collect tagged variables into a data container
Arguments:
name
: the variable name to create insidecollect_vars
tag
: the tag regex string to match e.g. "beta,gamma=.*"flatten
: to flatten variable values, set to "any" or "true" or "1"
The result will be a map in collect.$(name)
. You can also use
cmerge.$(name)_str
for a string version of the merged containers
(if it fits in a CFEngine string).
The name is variable so you can call this bundle for more than one collection.
Every found variable will be a key in the map, unless you specify
flatten
, in which case they'll be flattened into a top-level array
of data.
The flatten
parameter can be "any" or "true" or "1" to be true.
Example:
body common control
{
inputs => { "$(sys.libdir)/stdlib.cf" };
}
bundle agent main
{
vars:
# the "mymerge" tag is user-defined
"a" data => parsejson('{ "mark": "burgess" }'), meta => { "mymerge" };
"b" data => parsejson('{ "volker": "hilsheimer" }'), meta => { "mymerge" };
methods:
# merge a, b into container collect_vars.all
"go" usebundle => collect_vars("all", "mymerge", "");
# flatten a, b into container collect_vars.flattened
"go_flatten" usebundle => collect_vars("flattened", "mymerge", "true");
reports:
# merged = {"default:main.a":{"mark":"burgess"},"default:main.b":{"volker":"hilsheimer"}}
"merged = $(collect_vars.all_str)";
# flattened = {"mark":"burgess","volker":"hilsheimer"}
"flattened = $(collect_vars.flattened_str)";
}
Implementation:
bundle agent collect_vars(name, tag, flatten)
{
classes:
"flatten" expression => strcmp($(flatten), "any");
"flatten" expression => strcmp($(flatten), "1");
"flatten" expression => strcmp($(flatten), "true");
vars:
"todo_$(name)" slist => variablesmatching(".*", $(tag));
!flatten::
"$(name)"
data => parsejson('{}'),
policy => "free";
# this iterates!
"$(name)"
data => mergedata($(name), '{ "$(todo_$(name))": $(todo_$(name)) }'),
policy => "free";
flatten::
"$(name)"
data => parsejson('[]'),
policy => "free";
# this iterates!
"$(name)"
data => mergedata($(name), "$(todo_$(name))"),
policy => "free";
any::
"$(name)_str"
string => format("%S", $(name)),
policy => "free";
}
run_ifdefined
Prototype: run_ifdefined(namespace, mybundle)
Description: bundle to maybe run another bundle dynamically
Arguments:
namespace
: the namespace, usually$(this.namespace)
mybundle
: the bundle to maybe run
This bundle simply is a way to run another bundle only if it's defined.
Example:
bundle agent run
{
methods:
# does nothing if bundle "runthis" is not defined
"go" usebundle => run_ifdefined($(this.namespace), runthis);
}
Implementation:
bundle agent run_ifdefined(namespace, mybundle)
{
vars:
"bundlesfound" slist => bundlesmatching("^$(namespace):$(mybundle)$");
"count" int => length(bundlesfound);
methods:
"any"
usebundle => $(bundlesfound),
ifvarclass => strcmp(1, $(count));
reports:
verbose_mode::
"$(this.bundle): found matching bundles $(bundlesfound) for namespace '$(namespace)' and bundle '$(mybundle)'";
}
file bodies
control
Prototype: control
Description: Include policy files used by this policy file as part of inputs
Implementation:
body file control
{
inputs => { @(bundles_common.inputs) };
}
lib/feature.cf
agent bundles
feature
Prototype: feature
Description: Finds feature_set_X and feature_unset_X classes and sets/unsets X persistently
Finds all classes named feature_unset_X
and clear class X.
Finds all classes named feature_set_DURATION_X
and sets class X
persistently for DURATION. DURATION can be any digits followed by
k
, m
, or g
.
In inform mode (-I
) it will report what it does.
Example:
Set class xyz
for 10 minutes, class qpr
for 100 minutes, and
ijk
for 90m minutes. Unset class abc
.
cf-agent -I -f ./feature.cf -b feature -Dfeature_set_10_xyz,feature_set_100_qpr,feature_set_90m_ijk,feature_unset_abc
Implementation:
bundle agent feature
{
classes:
"parsed_$(on)" expression => regextract("feature_set_([0-9]+[kmgKMG]?)_(.*)",
$(on),
"extract_$(on)");
"parsed_$(off)" expression => regextract("feature_unset_(.*)",
$(off),
"extract_$(off)");
"$(extract_$(on)[2])" expression => "parsed_$(on)",
persistence => "$(extract_$(on)[1])";
vars:
"on" slist => classesmatching("feature_set_.*");
"off" slist => classesmatching("feature_unset_.*");
"_$(off)" string => "off", classes => feature_cancel("$(extract_$(off)[1])");
reports:
"DEBUG|DEBUG_$(this.bundle)"::
"DEBUG $(this.bundle): $(on) => SET class '$(extract_$(on)[2]) for '$(extract_$(on)[1])'"
ifvarclass => "parsed_$(on)";
"DEBUG $(this.bundle): $(off) => UNSET class '$(extract_$(off)[1])'"
ifvarclass => "parsed_$(off)";
"DEBUG $(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
"DEBUG $(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";
"DEBUG $(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
"DEBUG $(this.bundle): have no $(extract_$(off)[1])" ifvarclass => "!$(extract_$(off)[1])";
}
feature_test
Prototype: feature_test
Description: Finds feature_set_X and feature_unset_X classes and reports X
Note that this bundle is intended to be used exactly like feature
and just show what's defined or undefined.
Example:
Check classes xyz
, qpr
, ijk
, and abc
.
cf-agent -I -f ./feature.cf -b feature_test -Dfeature_set_10_xyz,feature_set_100_qpr,feature_set_90m_ijk,feature_unset_abc
Implementation:
bundle agent feature_test
{
classes:
"parsed_$(on)" expression => regextract("feature_set_([0-9]+[kmgKMG]?)_(.*)",
$(on),
"extract_$(on)");
"parsed_$(off)" expression => regextract("feature_unset_(.*)",
$(off),
"extract_$(off)");
vars:
"on" slist => classesmatching("feature_set_.*");
"off" slist => classesmatching("feature_unset_.*");
reports:
"$(this.bundle): have $(extract_$(on)[2])" ifvarclass => "$(extract_$(on)[2])";
"$(this.bundle): have no $(extract_$(on)[2])" ifvarclass => "!$(extract_$(on)[2])";
"$(this.bundle): have $(extract_$(off)[1])" ifvarclass => "$(extract_$(off)[1])";
"$(this.bundle): have no $(extract_$(off)[1])" ifvarclass => "!$(extract_$(off)[1])";
}
classes bodies
feature_cancel
Prototype: feature_cancel(x)
Description: Undefine class x
when promise is kept or repaired
Used internally by bundle feature
Arguments:
x
Implementation:
body classes feature_cancel(x)
{
cancel_kept => { "$(x)" };
cancel_repaired => { "$(x)" };
}
lib/testing.cf
The testing.cf
library provides bundles for working testing frameworks like
TAP and jUnit.
These bodies are included in the stdlib by default.
agent bundles
testing_ok_if
Prototype: testing_ok_if(classname, message, error, trace, format)
Description: Report outcome for test on classname
in format format
with optional error
and trace
Arguments:
classname
: The class namemessage
: The test messageerror
: The error to report, if the class is not definedtrace
: The error trace detail to report, if the class is not definedformat
: TAP (immediate output) or jUnit or anything else for delayed TAP
See also: testing_junit_report
, testing_tap_report
, testing_ok
, testing_todo
, and testing_skip
Implementation:
bundle agent testing_ok_if(classname, message, error, trace, format)
{
vars:
"next_testing_$(classname)" int => length(classesmatching("testing_.*", "testing_(passed|failed|skipped|todo)"));
"next_testing_$(classname)_failed" int => length(classesmatching("testing_.*", "testing_(passed|failed|skipped|todo)"));
classes:
"tap" expression => strcmp('tap', string_downcase($(format)));
"testing_$(classname)" expression => $(classname), scope => "namespace", meta => { "testing_passed", "error=$(error)", "trace=$(trace)", "message=$(message)" };
"testing_$(classname)_failed" not => $(classname), scope => "namespace", meta => { "testing_failed", "error=$(error)", "trace=$(trace)", "message=$(message)" };
reports:
inform_mode::
"$(this.bundle): adding testing report for class $(classname) at position $(next_testing_$(classname))";
"tap.testing_$(classname)"::
"$(const.n)ok $(message)";
"tap.testing_$(classname)_failed"::
"$(const.n)not ok $(message)";
}
testing_ok
Prototype: testing_ok(classname, message, format)
Description: Report expected success in format format
for classname
and its test
Arguments:
classname
: The class namemessage
: The test messageformat
: TAP (immediate output) or jUnit or anything else for delayed TAP
This bundle calls testing_ok_if
expecting classname
to be defined and thus the
test to be a success; the error and trace reflect that.
See also: testing_junit_report
, testing_tap_report
, testing_ok_if
, testing_todo
, and testing_skip
Implementation:
bundle agent testing_ok(classname, message, format)
{
methods:
"" usebundle => testing_ok_if($(classname), $(message), "unexpected error for $(classname)", "no error trace available", $(format));
}
testing_skip
Prototype: testing_skip(classname, message, format)
Description: Report skipped classname
in format format
Arguments:
classname
: The class namemessage
: The test messageformat
: TAP (immediate output) or jUnit or anything else for delayed TAP
This bundle reports that classname
was skipped regardless of whether it's
defined.
See also: testing_junit_report
, testing_tap_report
, testing_ok_if
, testing_todo
, and testing_ok
Implementation:
bundle agent testing_skip(classname, message, format)
{
vars:
"next_testing_$(classname)" int => length(classesmatching("testing_.*", "testing_(passed|failed|skipped|todo)"));
classes:
"tap" expression => strcmp('tap', string_downcase($(format)));
"testing_$(classname)" scope => "namespace", meta => { "testing_skipped", "message=$(message)" };
reports:
inform_mode::
"$(this.bundle): adding testing skip report for class $(classname) at position $(next_testing_$(classname))";
tap::
"$(const.n)ok # SKIP $(message)";
}
testing_todo
Prototype: testing_todo(classname, message, format)
Description: Report TODO classname
in format format
Arguments:
classname
: The class namemessage
: The test messageformat
: TAP (immediate output) or jUnit or anything else for delayed TAP
This bundle reports that classname
was skipped regardless of whether it's
defined.
See also: testing_junit_report
, testing_tap_report
, testing_ok_if
, testing_skip
, and testing_ok
Implementation:
bundle agent testing_todo(classname, message, format)
{
vars:
"next_testing_$(classname)" int => length(classesmatching("testing_.*", "testing_(passed|failed|skipped|todo)"));
classes:
"tap" expression => strcmp('tap', string_downcase($(format)));
"testing_$(classname)" scope => "namespace", meta => { "testing_todo", "message=$(message)" };
reports:
inform_mode::
"$(this.bundle): adding testing TODO report for class $(classname) at position $(next_testing_$(classname))";
tap::
"$(const.n)ok # TODO $(message)";
}
testing_tap_report
Prototype: testing_tap_report(outfile)
Description: Report all test messages in TAP format to outfile
Arguments:
outfile
: A text file with the final TAP report or empty `` for STDOUT report
Note that the TAP format ignores error messages, trace messages, and class names.
See also: testing_junit_report
, testing_tap_report
, testing_ok_if
, testing_todo
, testing_skip
, testing_tap_bailout
, and testing_ok
Implementation:
bundle agent testing_tap_report(outfile)
{
methods:
"" usebundle => testing_generic_report('TAP', $(outfile));
}
testing_junit_report
Prototype: testing_junit_report(outfile)
Description: Report all test knowledge in jUnit format to outfile
Arguments:
outfile
: A XML file with the final jUnit report or empty `` for STDOUT report
See also: testing_tap_report
, testing_ok_if
, testing_todo
, testing_skip
, and testing_ok
Implementation:
bundle agent testing_junit_report(outfile)
{
methods:
"" usebundle => testing_generic_report('jUnit', $(outfile));
}
testing_generic_report
Prototype: testing_generic_report(format, outfile)
Description: Report all test knowledge in jUnit format to outfile
Arguments:
format
: The output format, eitherjUnit
orTAP
(case is ignored)outfile
: A file with the final report or empty for STDOUT
Note that jUnit output to STDOUT will most likely be truncated due to the 4K limitation on string lengths.
See also: testing_tap_report
, testing_ok_if
, testing_todo
, testing_skip
, and testing_ok
Implementation:
bundle agent testing_generic_report(format, outfile)
{
classes:
"junit" expression => strcmp('junit', string_downcase($(format)));
"tap" expression => strcmp('tap', string_downcase($(format)));
"stdout" expression => strcmp('', $(outfile));
"tofile" not => strcmp('', $(outfile));
vars:
"failed" slist => classesmatching("testing_.*", testing_failed);
"passed" slist => classesmatching("testing_.*", testing_passed);
"skipped" slist => classesmatching("testing_.*", testing_skipped);
"todo" slist => classesmatching("testing_.*", testing_todo);
"count_passed" int => length(passed);
"count_skipped" int => length(skipped);
"count_todo" int => length(todo);
"count_failed" int => length(failed);
"count_total" string => format("%d", sum('[$(count_failed), $(count_passed), $(count_skipped), $(count_todo)]'));
"timestamp" string => strftime("localtime", "%FT%T", now());
"tests_passed" data => '[]';
"tests_passed"
data => mergedata(tests_passed,
format('[{ "testcase": "%s", "test_offset": %d, "test_message": "%s", "tap_message": "ok %s" }]',
regex_replace($(passed), "^testing_", "", "T"),
"$(testing_ok_if.next_$(passed))",
regex_replace(join(",", grep("^message=.*", getclassmetatags($(passed)))), "^message=", "", "T"),
regex_replace(join(",", grep("^message=.*", getclassmetatags($(passed)))), "^message=", "", "T")));
"tests_failed" data => '[]';
"tests_failed"
data => mergedata(tests_failed,
format('[{ "testcase": "%s", "fail_message": "%s", "trace_message": "%s", "test_offset": %d, "test_message": "%s", "tap_message": "not ok %s" }]',
regex_replace($(failed), "^testing_", "", "T"),
regex_replace(join(",", grep("^error=.*", getclassmetatags($(failed)))), "^error=", "", "T"),
regex_replace(join(",", grep("^trace=.*", getclassmetatags($(failed)))), "^trace=", "", "T"),
"$(testing_ok_if.next_$(failed))",
regex_replace(join(",", grep("^message=.*", getclassmetatags($(failed)))), "^message=", "", "T"),
regex_replace(join(",", grep("^message=.*", getclassmetatags($(failed)))), "^message=", "", "T")));
"tests_skipped" data => '[]';
"tests_skipped"
data => mergedata(tests_skipped,
format('[{ "testcase": "%s", "test_offset": %d, "test_message": "%s", "tap_message": "ok # SKIP %s" }]',
regex_replace($(skipped), "^testing_", "", "T"),
"$(testing_skip.next_$(skipped))",
regex_replace(join(",", grep("^message=.*", getclassmetatags($(skipped)))), "^message=", "", "T"),
regex_replace(join(",", grep("^message=.*", getclassmetatags($(skipped)))), "^message=", "", "T")));
"tests_todo" data => '[]';
"tests_todo"
data => mergedata(tests_todo,
format('[{ "testcase": "%s", "test_offset": %d, "test_message": "%s", "tap_message": "ok # TODO %s" }]',
regex_replace($(todo), "^testing_", "", "T"),
"$(testing_todo.next_$(todo))",
regex_replace(join(",", grep("^message=.*", getclassmetatags($(todo)))), "^message=", "", "T"),
regex_replace(join(",", grep("^message=.*", getclassmetatags($(todo)))), "^message=", "", "T")));
inform_mode::
"out" string => format("counts = %d/%d/%d/%d/%d failed = %S, passed = %S, skipped = %S, todo = %S, failed = %S; tests = %S+%S+%S+%S", $(count_total), $(count_passed), $(count_skipped), $(count_todo), $(count_failed), failed, passed, skipped, todo, failed, tests_passed, tests_skipped, tests_todo, tests_failed);
junit.stdout::
"junit_out" string => string_mustache(readfile("$(this.promise_dirname)/templates/junit.mustache", 4k), bundlestate("testing_generic_report"));
tap::
"tap_tests" data => mergedata(tests_passed, tests_failed, tests_skipped, tests_todo);
"tap_results" data => mapdata("json_pipe", "$(def.jq) 'sort_by(.test_offset) | .[] | [(.test_offset|tostring), .tap_message ] | join(\" \") '", tap_tests);
tap.inform_mode::
"tap_tests_info" string => format("%S", tap_tests);
"tap_results_info" string => format("%S", tap_results);
files:
junit.tofile::
"$(outfile)"
create => "true",
template_data => bundlestate("testing_generic_report"),
template_method => "mustache",
edit_template => "$(this.promise_dirname)/templates/junit.mustache";
tap.tofile::
"$(outfile)"
create => "true",
template_data => bundlestate("testing_generic_report"),
template_method => "mustache",
edit_template => "$(this.promise_dirname)/templates/tap.mustache";
reports:
junit.stdout::
"$(const.n)$(junit_out)";
tap.stdout::
"$(const.n)1..$(count_total)" ;
inform_mode::
"$(this.bundle): report summary: $(out)";
tap.inform_mode::
"$(this.bundle): TAP report summary: $(tap_tests_info)";
"$(this.bundle): TAP report results summary: $(tap_results_info)";
}
testing_tap_bailout
Prototype: testing_tap_bailout(reason)
Arguments:
reason
, used as promiser of typereports
Implementation:
bundle agent testing_tap_bailout(reason)
{
reports:
"$(const.n)Bail out! $(reason)";
}
testing_usage_example
Prototype: testing_usage_example
Description: Simple demo of testing_junit_report and testing_tap_report testing.cf usage
You can run it like this: cf-agent -K ./testing.cf -b testing_usage_example
Or for extra debugging, you can run it like this: cf-agent -KI ./testing.cf -b testing_usage_example
You can either use tap
as the format
parameter for any testing bundle, in
which case you get immediate TAP output, OR you can use anything else, in
which case you can still get TAP output but at the end.
So your use cases are:
- format=jUnit, then testing_junit_report(''): all jUnit to STDOUT, output at end
- format=TAP, then testing_tap_report(''): all TAP to STDOUT, immediate output
- format=delayed_TAP, then testing_tap_report(MYFILE): all TAP to MYFILE, output at end
- format=jUnit, then testing_jUnit_report(MYFILE): all jUnit to MYFILE, output at end
Implementation:
bundle agent testing_usage_example
{
classes:
"reported_class" scope => "namespace";
methods:
"" usebundle => testing_ok("reported_class", "ok message", "TAP");
"" usebundle => testing_ok_if("missing_class", "missing message", "error1", "error trace 1", "TAP");
"" usebundle => testing_ok_if("missing_class2", "missing message2", "error2", "error trace 2", "TAP");
"" usebundle => testing_skip("skipped_class", "we skipped this", "TAP");
"" usebundle => testing_todo("todo_class", "we need to do this", "TAP");
# output the reports to some files
# "" usebundle => testing_junit_report("/var/cfengine/outputs/junit.xml");
# "" usebundle => testing_tap_report("/var/cfengine/outputs/tap.txt");
# output the reports to STDOUT
"" usebundle => testing_junit_report('');
"" usebundle => testing_tap_report('');
}
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
with
:string
access
admit
:slist
admit_hostnames
:slist
admit_ips
:slist
admit_keys
:slist
deny
:slist
deny_hostnames
:slist
deny_ips
:slist
deny_keys
:slist
ifencrypted
:boolean
maproot
:slist
report_data_select
: bodyreport_data_select
resource_type
: one ofpath
,literal
,context
,query
,variable
,bundle
shortcut
:string
build_xpath
classes
and
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
dist
:rlist
in range-9.99999E100,9.99999E100
expression
: class expression in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
not
: class expression in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
or
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
persistence
:int
in range0,99999999999
scope
: one ofnamespace
,bundle
select_class
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
xor
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
commands
databases
database_columns
:slist
in range.*
database_operation
: one ofcreate
,delete
,drop
,cache
,verify
,restore
database_rows
:slist
in range.*,.*
database_server
: bodydatabase_server
database_type
: one ofsql
,ms_registry
registry_exclude
:slist
defaults
delete_attribute
delete_lines
delete_select
: bodydelete_select
not_matching
:boolean
select_region
: bodyselect_region
delete_text
delete_tree
field_edits
edit_field
: bodyedit_field
select_region
: bodyselect_region
files
acl
: bodyacl
changes
: bodychanges
copy_from
: bodycopy_from
create
:boolean
delete
: bodydelete
depth_search
: bodydepth_search
edit_defaults
: bodyedit_defaults
edit_line
:bundle
edit_template
:string
in range"?(/.*)
edit_template_string
:string
in range.*
edit_xml
:bundle
file_select
: bodyfile_select
file_type
: one ofregular
,fifo
link_from
: bodylink_from
move_obstructions
:boolean
pathtype
: one ofliteral
,regex
,guess
perms
: bodyperms
rename
: bodyrename
repository
:string
in range"?(/.*)
template_data
:data
template_method
: one ofcfengine
,inline_mustache
,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
:string
set_attribute
set_text
storage
users
description
:string
group_primary
:string
groups_secondary
:slist
in range.*
home_bundle
:bundle
home_bundle_inherit
:boolean
home_dir
:string
in range"?(/.*)
password
: bodypassword
policy
: one ofpresent
,absent
,locked
shell
:string
in range"?(/.*)
uid
:int
in range-99999999999,99999999999
vars
data
:data
ilist
:ilist
in range-99999999999,99999999999
int
:int
in range-99999999999,99999999999
policy
: one offree
,overridable
,constant
,ifdefined
real
:real
in range-9.99999E100,9.99999E100
rlist
:rlist
in range-9.99999E100,9.99999E100
slist
:slist
string
:string
All Body Types
acl
aces
:slist
in range((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
acl_default
: one ofnochange
,access
,specify
,clear
acl_directory_inherit
deprecated: one ofnochange
,parent
,specify
,clear
acl_inherit
: one oftrue
,false
,yes
,no
,on
,off
,nochange
acl_method
: one ofappend
,overwrite
acl_type
: one ofgeneric
,posix
,ntfs
inherit_from
: bodyinherit_from
meta
:slist
specify_default_aces
:slist
in range((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
specify_inherit_aces
deprecated:slist
in range((user|group):[^:]+:[-=+,rwx()dtTabBpcoD]*(:(allow|deny))?)|((all|mask):[-=+,rwx()]*(:(allow|deny))?)
action
action_policy
: one offix
,warn
,nop
audit
:boolean
background
:boolean
expireafter
:int
in range0,99999999999
ifelapsed
:int
in range0,99999999999
inherit_from
: bodyinherit_from
log_failed
:string
in rangestdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)
log_kept
:string
in rangestdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)
log_level
: one ofinform
,verbose
,error
,log
log_priority
: one ofemergency
,alert
,critical
,error
,warning
,notice
,info
,debug
log_repaired
:string
in rangestdout|udp_syslog|("?[a-zA-Z]:\\.*)|(/.*)
log_string
:string
measurement_class
:string
meta
:slist
report_level
: one ofinform
,verbose
,error
,log
agent
abortbundleclasses
:slist
in range.*
abortclasses
:slist
in range.*
addclasses
:slist
in range.*
agentaccess
:slist
in range.*
agentfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
allclassesreport
:boolean
alwaysvalidate
:boolean
bindtointerface
:string
in range.*
checksum_alert_time
:int
in range0,60
childlibpath
:string
in range.*
default_repository
:string
in range"?(/.*)
default_timeout
:int
in range0,99999999999
defaultcopytype
: one ofmtime
,atime
,ctime
,digest
,hash
,binary
dryrun
:boolean
editbinaryfilesize
:int
in range0,99999999999
editfilesize
:int
in range0,99999999999
environment
:slist
in range[A-Za-z0-9_]+=.*
expireafter
:int
in range0,99999999999
files_auto_define
:slist
files_single_copy
:slist
hashupdates
:boolean
hostnamekeys
:boolean
ifelapsed
:int
in range0,99999999999
inform
:boolean
intermittency
:boolean
max_children
:int
in range0,99999999999
maxconnections
:int
in range0,99999999999
mountfilesystems
:boolean
nonalphanumfiles
:boolean
refresh_processes
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repchar
:string
in range.
report_class_log
:boolean
secureinput
:boolean
select_end_match_eof
:boolean
sensiblecount
:int
in range0,99999999999
sensiblesize
:int
in range0,99999999999
skipidentify
:boolean
suspiciousnames
:slist
timezone
:slist
verbose
:boolean
changes
hash
: one ofmd5
,sha1
,sha224
,sha256
,sha384
,sha512
,best
inherit_from
: bodyinherit_from
meta
:slist
report_changes
: one ofall
,stats
,content
,none
report_diffs
:boolean
update_hashes
:boolean
classes
cancel_kept
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
cancel_notkept
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
cancel_repaired
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
failed_returncodes
:slist
in range[-0-9_$(){}\[\].]+
inherit_from
: bodyinherit_from
kept_returncodes
:slist
in range[-0-9_$(){}\[\].]+
meta
:slist
persist_time
:int
in range0,99999999999
promise_kept
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
promise_repaired
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repair_denied
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repair_failed
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repair_timeout
:slist
in range[a-zA-Z0-9_$(){}\[\].:]+
repaired_returncodes
:slist
in range[-0-9_$(){}\[\].]+
scope
: one ofnamespace
,bundle
timer_policy
: one ofabsolute
,reset
common
bundlesequence
:slist
in range.*
bwlimit
:real
in range0,99999999999
cache_system_functions
:boolean
domain
:string
in range.*
fips_mode
:boolean
goal_patterns
:slist
ignore_missing_bundles
:boolean
ignore_missing_inputs
:boolean
inputs
:slist
in range.*
lastseenexpireafter
:int
in range0,99999999999
output_prefix
:string
package_inventory
:slist
in range.*
package_module
:string
in range.*
protocol_version
: one of0
,undefined
,1
,classic
,2
,latest
require_comments
:boolean
site_classes
:clist
in range[a-zA-Z0-9_!&@@$|.()\[\]{}:]+
syslog_host
:string
in range[a-zA-Z0-9_$(){}.:-]+
syslog_port
:int
in range0,99999999999
tls_ciphers
:string
tls_min_version
:string
version
:string
contain
chdir
:string
in range"?(/.*)
chroot
:string
in range"?(/.*)
exec_group
:string
exec_owner
:string
exec_timeout
:int
in range1,3600
inherit_from
: bodyinherit_from
meta
:slist
no_output
:boolean
preview
:boolean
umask
:string
useshell
: one ofnoshell
,useshell
,powershell
,true
,false
,yes
,no
,on
,off
copy_from
check_root
:boolean
collapse_destination_dir
:boolean
compare
: one ofatime
,mtime
,ctime
,digest
,hash
,exists
,binary
copy_backup
: one oftrue
,false
,timestamp
copy_size
:irange
in range0,inf
copylink_patterns
:slist
encrypt
:boolean
findertype
: one ofMacOSX
force_ipv4
:boolean
force_update
:boolean
inherit_from
: bodyinherit_from
link_type
: one ofsymlink
,hardlink
,relative
,absolute
linkcopy_patterns
:slist
meta
:slist
missing_ok
:boolean
portnumber
:string
preserve
:boolean
protocol_version
: one of0
,undefined
,1
,classic
,2
,latest
purge
:boolean
servers
:slist
in range[A-Za-z0-9_.:\-\[\]]+
source
:string
in range.+
stealth
:boolean
timeout
:int
in range1,3600
trustkey
:boolean
type_check
:boolean
verify
:boolean
database_server
db_server_connection_db
:string
db_server_host
:string
db_server_owner
:string
db_server_password
:string
db_server_type
: one ofpostgres
,mysql
inherit_from
: bodyinherit_from
meta
:slist
delete
dirlinks
: one ofdelete
,tidy
,keep
inherit_from
: bodyinherit_from
meta
:slist
rmdirs
:boolean
delete_select
delete_if_contains_from_list
:slist
in range.*
delete_if_match_from_list
:slist
in range.*
delete_if_not_contains_from_list
:slist
in range.*
delete_if_not_match_from_list
:slist
in range.*
delete_if_not_startwith_from_list
:slist
in range.*
delete_if_startwith_from_list
:slist
in range.*
inherit_from
: bodyinherit_from
meta
:slist
depth_search
depth
:int
in range0,99999999999
exclude_dirs
:slist
in range.*
include_basedir
:boolean
include_dirs
:slist
in range.*
inherit_from
: bodyinherit_from
meta
:slist
rmdeadlinks
:boolean
traverse_links
:boolean
xdev
:boolean
edit_defaults
edit_backup
: one oftrue
,false
,timestamp
,rotate
empty_file_before_editing
:boolean
inherit
:boolean
inherit_from
: bodyinherit_from
max_file_size
:int
in range0,99999999999
meta
:slist
recognize_join
:boolean
rotate
:int
in range0,99
edit_field
allow_blank_fields
:boolean
extend_fields
:boolean
field_operation
: one ofprepend
,append
,alphanum
,delete
,set
field_separator
:string
in range.*
field_value
:string
in range.*
inherit_from
: bodyinherit_from
meta
:slist
select_field
:int
in range0,99999999
start_fields_from_zero
:boolean
value_separator
:string
in range^.$
environment_interface
env_addresses
:slist
env_name
:string
env_network
:string
inherit_from
: bodyinherit_from
meta
:slist
environment_resources
env_baseline
:string
in range"?(/.*)
env_cpus
:int
in range0,99999999999
env_disk
:int
in range0,99999999999
env_memory
:int
in range0,99999999999
env_spec
:string
in range.*
inherit_from
: bodyinherit_from
meta
:slist
executor
agent_expireafter
:int
in range0,10080
exec_command
:string
in range"?(/.*)
executorfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
mailfilter_exclude
:slist
mailfilter_include
:slist
mailfrom
:string
in range.*@.*
mailmaxlines
:int
in range0,1000
mailsubject
:string
mailto
:string
in range.*@.*
schedule
:slist
smtpserver
:string
in range.*
splaytime
:int
in range0,99999999999
file
file_select
atime
:irange
in range0,2147483647
ctime
:irange
in range0,2147483647
exec_program
:string
in range"?(/.*)
exec_regex
:string
in range.*
file_result
:string
in range[!*(leaf_name|path_name|file_types|mode|size|owner|group|atime|ctime|mtime|issymlinkto|exec_regex|exec_program|bsdflags)[|&.]*]*
file_types
:olist
in rangeplain,reg,symlink,dir,socket,fifo,door,char,block
inherit_from
: bodyinherit_from
issymlinkto
:slist
leaf_name
:slist
meta
:slist
mtime
:irange
in range0,2147483647
path_name
:slist
in range"?(/.*)
search_bsdflags
:slist
in range[+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+
search_groups
:slist
search_mode
:slist
in range[0-7augorwxst,+-=]+
search_owners
:slist
search_size
:irange
in range0,inf
hub
client_history_timeout
:int
in range1,65535
exclude_hosts
:slist
hub_schedule
:slist
port
:int
in range1,65535
insert_select
inherit_from
: bodyinherit_from
insert_if_contains_from_list
:slist
in range.*
insert_if_match_from_list
:slist
in range.*
insert_if_not_contains_from_list
:slist
in range.*
insert_if_not_match_from_list
:slist
in range.*
insert_if_not_startwith_from_list
:slist
in range.*
insert_if_startwith_from_list
:slist
in range.*
meta
:slist
link_from
copy_patterns
:slist
inherit_from
: bodyinherit_from
link_children
:boolean
link_type
: one ofsymlink
,hardlink
,relative
,absolute
meta
:slist
source
:string
in range.+
when_linking_children
: one ofoverride_file
,if_no_such_file
when_no_source
: one offorce
,delete
,nop
location
before_after
: one ofbefore
,after
first_last
: one offirst
,last
inherit_from
: bodyinherit_from
meta
:slist
select_line_matching
:string
in range.*
match_value
extraction_regex
:string
inherit_from
: bodyinherit_from
meta
:slist
select_line_matching
:string
in range.*
select_line_number
:int
in range0,99999999999
select_multiline_policy
: one ofaverage
,sum
,first
,last
track_growing_file
:boolean
monitor
forgetrate
:real
in range0,1
histograms
:boolean
monitorfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
tcpdump
:boolean
tcpdumpcommand
:string
in range"?(/.*)
mount
edit_fstab
:boolean
inherit_from
: bodyinherit_from
meta
:slist
mount_options
:slist
mount_server
:string
mount_source
:string
in range"?(/.*)
mount_type
: one ofnfs
,nfs2
,nfs3
,nfs4
unmount
:boolean
package_method
inherit_from
: bodyinherit_from
meta
:slist
package_add_command
:string
in range.+
package_arch_regex
:string
package_changes
: one ofindividual
,bulk
package_commands_useshell
:boolean
package_default_arch_command
:string
in range"?(/.*)
package_delete_command
:string
in range.+
package_delete_convention
:string
package_file_repositories
:slist
package_installed_regex
:string
package_list_arch_regex
:string
package_list_command
:string
in range.+
package_list_name_regex
:string
package_list_update_command
:string
package_list_update_ifelapsed
:int
in range-99999999999,99999999999
package_list_version_regex
:string
package_multiline_start
:string
package_name_convention
:string
package_name_regex
:string
package_noverify_regex
:string
package_noverify_returncode
:int
in range-99999999999,99999999999
package_patch_arch_regex
:string
package_patch_command
:string
in range.+
package_patch_installed_regex
:string
package_patch_list_command
:string
in range.+
package_patch_name_regex
:string
package_patch_version_regex
:string
package_update_command
:string
in range.+
package_verify_command
:string
in range.+
package_version_equal_command
:string
in range.+
package_version_less_command
:string
in range.+
package_version_regex
:string
package_module
default_options
:slist
interpreter
:string
module_path
:string
query_installed_ifelapsed
:int
in range-99999999999,99999999999
query_updates_ifelapsed
:int
in range-99999999999,99999999999
password
data
:string
format
: one ofplaintext
,hash
inherit_from
: bodyinherit_from
meta
:slist
perms
bsdflags
:slist
in range[+-]*[(arch|archived|nodump|opaque|sappnd|sappend|schg|schange|simmutable|sunlnk|sunlink|uappnd|uappend|uchg|uchange|uimmutable|uunlnk|uunlink)]+
groups
:slist
in range[a-zA-Z0-9_$.-]+
inherit_from
: bodyinherit_from
meta
:slist
mode
:string
in range[0-7augorwxst,+-=]+
owners
:slist
in range[a-zA-Z0-9_$.-]+
rxdirs
:boolean
printfile
file_to_print
:string
in range"?(/.*)
inherit_from
: bodyinherit_from
meta
:slist
number_of_lines
:int
in range0,99999999999
process_count
in_range_define
:slist
inherit_from
: bodyinherit_from
match_range
:irange
in range0,99999999999
meta
:slist
out_of_range_define
:slist
process_select
command
:string
inherit_from
: bodyinherit_from
meta
:slist
pgid
:irange
in range0,99999999999
pid
:irange
in range0,99999999999
ppid
:irange
in range0,99999999999
priority
:irange
in range-20,+20
process_owner
:slist
process_result
:string
in range[(process_owner|pid|ppid||pgid|rsize|vsize|status|command|ttime|stime|tty|priority|threads)[|&!.]*]*
rsize
:irange
in range0,99999999999
status
:string
stime_range
:irange
in range0,2147483647
threads
:irange
in range0,99999999999
ttime_range
:irange
in range0,99999999999
tty
:string
vsize
:irange
in range0,99999999999
rename
disable
:boolean
disable_mode
:string
in range[0-7augorwxst,+-=]+
disable_suffix
:string
inherit_from
: bodyinherit_from
meta
:slist
newname
:string
rotate
:int
in range0,99
replace_with
inherit_from
: bodyinherit_from
meta
:slist
occurrences
: one ofall
,first
replace_value
:string
in range.*
report_data_select
inherit_from
: bodyinherit_from
meta
:slist
metatags_exclude
:slist
in range.*
metatags_include
:slist
in range.*
monitoring_exclude
:slist
in range.*
monitoring_include
:slist
in range.*
promise_handle_exclude
:slist
in range.*
promise_handle_include
:slist
in range.*
runagent
background_children
:boolean
encrypt
:boolean
force_ipv4
:boolean
hosts
:slist
max_children
:int
in range0,99999999999
output_directory
:string
in range"?(/.*)
output_to_file
:boolean
port
:int
in range1,65535
timeout
:int
in range1,9999
trustkey
:boolean
select_region
include_end_delimiter
:boolean
include_start_delimiter
:boolean
inherit_from
: bodyinherit_from
meta
:slist
select_end
:string
in range.*
select_end_match_eof
:boolean
select_start
:string
in range.*
server
allowallconnects
:slist
allowciphers
:string
allowconnects
:slist
allowlegacyconnects
:slist
allowtlsversion
:string
allowusers
:slist
auditing
:boolean
bindtointerface
:string
call_collect_interval
:int
in range0,99999999999
cfruncommand
:string
in range.+
collect_window
:int
in range0,99999999999
denybadclocks
:boolean
denyconnects
:slist
dynamicaddresses
:slist
hostnamekeys
:boolean
listen
:boolean
logallconnections
:boolean
logencryptedtransfers
:boolean
maxconnections
:int
in range0,99999999999
port
:int
in range1,65535
serverfacility
: one ofLOG_USER
,LOG_DAEMON
,LOG_LOCAL0
,LOG_LOCAL1
,LOG_LOCAL2
,LOG_LOCAL3
,LOG_LOCAL4
,LOG_LOCAL5
,LOG_LOCAL6
,LOG_LOCAL7
skipverify
deprecated:slist
trustkeysfrom
:slist
service_method
inherit_from
: bodyinherit_from
meta
:slist
service_args
:string
service_autostart_policy
: one ofnone
,boot_time
,on_demand
service_bundle
:bundle
service_dependence_chain
: one ofignore
,start_parent_services
,stop_child_services
,all_related
service_type
: one ofwindows
,generic
volume
check_foreign
:boolean
freespace
:string
in range[0-9]+[MBkKgGmb%]
inherit_from
: bodyinherit_from
meta
:slist
scan_arrivals
:boolean
sensible_count
:int
in range0,99999999999
sensible_size
:int
in range0,99999999999