cfe_internal/update/update_policy.cf
copy_from bodies
u_cmdb_data
Prototype: u_cmdb_data
Description: Sync CMDB data from policy server Note: Not all hosts necessarily have CMDB data
Implementation:
body copy_from u_cmdb_data
{
copy_backup => "false";
trustkey => "false";
compare => "digest";
source => "hub_cmdb";
servers => { "$(sys.policy_hub)" };
purge => "true";
@if minimum_version(3.12)
missing_ok => "true";
@endif
}
perms bodies
u_m
Prototype: u_m(p)
Description: Ensure file mode is p
Arguments:
p
: Desired file mode
Implementation:
body perms u_m(p)
{
mode => "$(p)";
#+begin_ENT-951
# Remove after 3.20 is not supported
rxdirs => "true";
@if minimum_version(3.20)
rxdirs => "false";
@endif
#+end
}
u_mo
Prototype: u_mo(p, o)
Description: Ensure file mode is p
and owner is o
Arguments:
p
: Desired file owner (username or uid)o
Implementation:
#########################################################
{
mode => "$(p)";
#+begin_ENT-951
# Remove after 3.20 is not supported
rxdirs => "true";
@if minimum_version(3.20)
rxdirs => "false";
@endif
#+end
!(windows|termux)::
owners => {"$(o)"};
}
u_shared_lib_perms
Prototype: u_shared_lib_perms
Description: Shared library permissions
Implementation:
}
{
#+begin_ENT-951
# Remove after 3.20 is not supported
rxdirs => "true";
@if minimum_version(3.20)
rxdirs => "false";
@endif
#+end
!hpux::
mode => "0644";
hpux::
mode => "0755"; # Mantis 1114, Redmine 1179
}
file_select bodies
u_all
Prototype: u_all
Description: Select all file system entries
Implementation:
hpux::
{
leaf_name => { ".*" };
file_result => "leaf_name";
}
u_cf3_files
Prototype: u_cf3_files
Implementation:
# @brief Select all file system entries
{
leaf_name => { ".*" };
file_result => "leaf_name";
}
u_input_files
Prototype: u_input_files
Description: Select files by extension that we should include when updating inputs
Implementation:
leaf_name => { "cf-.*" };
{
leaf_name => { @(update_def.input_name_patterns),
@(update_def.input_name_patterns_extra) };
file_result => "leaf_name";
}
copy_from bodies
u_rcp
Prototype: u_rcp(from, server)
Description: Ensure file is a copy of from
on server
using digest comparison
Arguments:
from
: The path to copy fromserver
: The remote host to copy from
Implementation:
@(update_def.input_name_patterns_extra) };
{
source => "$(from)";
compare => "digest";
trustkey => "false";
purge => "true"; # CFE-3662
# CFE-2932 For testing, we want to be able to avoid this local copy optimiztion
!am_policy_hub|mpf_skip_local_copy_optimizaton::
servers => { "$(server)" };
!am_policy_hub.(sys_policy_hub_port_exists|mpf_skip_local_copy_optimization)::
portnumber => "$(sys.policy_hub_port)";
cfengine_internal_encrypt_transfers::
encrypt => "true";
cfengine_internal_purge_policies_disabled::
purge => "false";
cfengine_internal_preserve_permissions::
preserve => "true";
cfengine_internal_verify_update_transfers::
verify => "true";
}
u_cp
Prototype: u_cp(from)
Description: Ensure file is a copy from from
on the local server based on digest comparison
Arguments:
from
: string, used in the value of attributesource
Implementation:
cfengine_internal_verify_update_transfers::
{
source => "$(from)";
compare => "digest";
}
u_cp_nobck
Prototype: u_cp_nobck(from)
Description: copy from from
locally with digest comparison and making no backups
Arguments:
from
: string, used in the value of attributesource
Implementation:
source => "$(from)";
{
source => "$(from)";
compare => "digest";
copy_backup => "false";
}
u_cp_missing_ok
Prototype: u_cp_missing_ok(from)
Description: same as u_cp but allow from to be missing
Arguments:
from
: string, used to set promise attributeinherit_from
Implementation:
compare => "digest";
{
inherit_from => u_cp($(from));
missing_ok => "true";
}
action bodies
u_immediate
Prototype: u_immediate
Description: Actuate the promise immediately, ignoring locks
Implementation:
inherit_from => u_cp($(from));
{
ifelapsed => "0";
}
depth_search bodies
u_recurse
Prototype: u_recurse(d)
Description: Search recursively for files up to d
levels excluding common version control data
Arguments:
d
: Maximum depth to search recursively
Implementation:
{
{
depth => "$(d)";
exclude_dirs => { "\.svn", "\.git", "git-core" };
}
u_infinite_client_policy
Prototype: u_infinite_client_policy
Description: Search recursively for files excluding vcs related files and .no-distrib directories
Implementation:
depth => "$(d)";
{
depth => "inf";
exclude_dirs => { "\.svn", "\.git", "git-core", "\.no-distrib" };
}
u_recurse_basedir
Prototype: u_recurse_basedir(d)
Description: Search recursively for files up to d
levels excluding common version control data and including the base directory
Arguments:
d
: Maximum depth to search recursively
Implementation:
{
{
include_basedir => "true";
depth => "$(d)";
exclude_dirs => { "\.svn", "\.git", "git-core" };
}
classes bodies
u_if_repaired
Prototype: u_if_repaired(x)
Description: Define x
if the promise is repaired
Arguments:
x
: Class to define if promise repaired
Implementation:
depth => "$(d)";
{
promise_repaired => { "$(x)" };
}
u_if_repaired_then_cancel
Prototype: u_if_repaired_then_cancel(y)
Description: Cancel class x
if the promise is repaired
Arguments:
y
Implementation:
{
{
cancel_repaired => { "$(y)" };
}
u_if_else
Prototype: u_if_else(yes, no)
Description: define yes
if the promise is repaired, and no
if the promise fails to repair (notkept)
Arguments:
yes
: Class to define if promise repairedno
: Class to undefine if promise notkept
Implementation:
{
{
# promise_kept => { "$(yes)" };
promise_repaired => { "$(yes)" };
repair_failed => { "$(no)" };
repair_denied => { "$(no)" };
repair_timeout => { "$(no)" };
}
contain bodies
u_in_shell
Prototype: u_in_shell
Description: Run command within shell environment
Implementation:
repair_denied => { "$(no)" };
{
useshell => "true";
}
u_in_shell_and_silent
Prototype: u_in_shell_and_silent
Description: Run command within shell environment suppressing output
Implementation:
{
{
useshell => "true";
no_output => "true";
}
u_postgres
Prototype: u_postgres
Description: Run command within postgres users shell environment
Implementation:
useshell => "true";
{
useshell => "useshell";
exec_owner => "cfpostgres";
exec_group => "cfpostgres";
chdir => "/tmp";
no_output => "true";
}
action bodies
u_ifwin_bg
Prototype: u_ifwin_bg
Description: Run command in the background if windows is defined
Implementation:
chdir => "/tmp";
{
windows::
background => "true";
}
service_method bodies
u_bootstart
Prototype: u_bootstart
Description: Attributes for u_bootstart service method
Implementation:
windows::
{
service_autostart_policy => "boot_time";
}
contain bodies
u_in_dir
Prototype: u_in_dir(s)
Description: Run command from within s
Arguments:
s
: Path to change into before running command
Implementation:
{
{
chdir => "$(s)";
}
u_silent_in_dir
Prototype: u_silent_in_dir(s)
Description: Run command from within s
and suppress output
Arguments:
s
: Path to change into before running command
Implementation:
{
{
chdir => "$(s)";
no_output => "true";
}
link_from bodies
u_ln_s
Prototype: u_ln_s(x)
Description: Symlink to x
, even if it does not exist
Arguments:
x
: Path to symlink
Implementation:
chdir => "$(s)";
{
link_type => "symlink";
source => "$(x)";
when_no_source => "force";
}
delete bodies
u_tidy
Prototype: u_tidy
Description: Delete directories and symlinks
Implementation:
source => "$(x)";
{
dirlinks => "delete";
rmdirs => "true";
}
file_select bodies
not_vendored_modules
Prototype: not_vendored_modules(pathname)
Arguments:
pathname
Implementation:
if => "override_vendored_module_$(_vendored_modules)";
{
path_name => { "$(pathname)" };
file_result => "path_name";
}
u_dirs
Prototype: u_dirs
Implementation:
body file_select not_vendored_modules(pathname)
{
path_name => { "$(pathname)" };
file_result => "path_name";
}
u_not_dir
Prototype: u_not_dir
Description: Select directories
Implementation:
body file_select u_dirs
{
file_types => { "dir" };
file_result => "file_types";
}
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;
any::
"CMDB data update" -> { "ENT-6788", "ENT-8847" }
usebundle => cfe_internal_update_cmdb,
action => u_immediate;
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 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
offiles
promiser $(sys.bindir)/python
Implementation:
bundle agent cfe_internal_setup_python_symlink(symlink_path)
{
vars:
"path" string => getenv("PATH", 1024);
"path_folders" slist => splitstring("$(path)", ":", 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");
"$(sys.bindir)/python" -> { "CFE-3512" }
delete => u_tidy,
if => islink( "$(sys.bindir)/python" ),
comment => concat( "We don't want to leave a python that is potentially in $PATH ",
"after having re-named our python symlink that is used for various ",
"modules");
}
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_source" string => "/var/cfengine/masterfiles/modules",
comment => "Directory containing CFEngine modules",
handle => "cfe_internal_update_policy_vars_modules_dir_windows";
!windows::
"modules_dir_source" string => translatepath("$(master_location)/modules"),
comment => "Directory containing CFEngine modules",
handle => "cfe_internal_update_policy_vars_modules_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" -> { "CFE-2602", "CFE-3512" }
string => "$(sys.bindir)/cfengine-selected-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|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_infinite_client_policy,
file_select => u_input_files,
action => u_immediate,
classes => u_results("bundle", "update_inputs"),
move_obstructions => "true";
# Note that here we do not filter with `update_def.input_name_patterns` so
# that we copy any and all modules scripts.
"$(inputs_dir)/modules"
comment => "Copy any files in modules from master source on policy server if a new validation was acquired",
handle => "cfe_internal_update_policy_files_modules_dir",
copy_from => u_rcp("$(modules_dir_source)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
action => u_immediate;
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)/." -> { "CFE-951" }
comment => "Make sure masterfiles folder has right file permissions",
handle => "cfe_internal_update_policy_files_sys_workdir_masterfiles_dirs",
perms => u_m($(update_def.masterfiles_perms_mode_dirs)),
file_select => u_dirs,
depth_search => u_recurse_basedir("inf"),
action => u_immediate;
"$(master_location)/." -> { "CFE-951" }
comment => "Make sure masterfiles folder has right file permissions",
handle => "cfe_internal_update_policy_files_sys_workdir_masterfiles_not_dir",
perms => u_m($(update_def.masterfiles_perms_mode_not_dir)),
file_select => u_not_dir,
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" -> { "CFE-2602" }
usebundle => cfe_internal_setup_python_symlink("$(python_symlink)");
any::
# Install vendored and user provided modules to $(sys.workdir) from $(sys.inputdir)
"modules_presence";
}
cfe_internal_update_cmdb
Prototype: cfe_internal_update_cmdb
Description: Ensure local cache of CMDB data is up to date
Implementation:
bundle agent cfe_internal_update_cmdb
{
classes:
"have_cf_reactor" expression => fileexists("$(sys.bindir)/cf-reactor");
methods:
policy_server.enterprise_edition.(!have_cf_reactor|cmdb_data_files_updates_done_in_policy)::
"cfe_internal_update_cmdb_data_distribution";
@if feature(host_specific_data_load)
# Only hosts with this feature, introduced in 3.18.0 can use the data.
# Don't pull CMDB data on policy_hub self bootstrap because
# there will be no cf-serverd listening to serve files yet.
enterprise_edition.!(bootstrap_mode):: # ENT-6840
"cfe_internal_update_cmdb_data_consumption" -> { "ENT-8847" }
action => u_immediate;
@endif
}
cfe_internal_update_cmdb_data_distribution
Prototype: cfe_internal_update_cmdb_data_distribution
Description: Ensure data is ready for agents to download
Implementation:
bundle agent cfe_internal_update_cmdb_data_distribution
{
classes:
"_have_cmdb_next_request_state_file" -> { "ENT-9933" }
expression => fileexists( "$(_cmdb_next_request_state_file)" );
vars:
!bootstrap_mode.(policy_server.enterprise_edition)::
# The API response for host specific data from cmdb tells us the timestamp of the last data change
# We store this timestamp and use it for the next request.
"_cmdb_next_request_state_file"
string => "$(sys.statedir)/cmdb_next_request_from.dat";
!bootstrap_mode.(policy_server.enterprise_edition._have_cmdb_next_request_state_file)::
# If we have the timestamp from a previous response we use it, else we start from 0
"_cmdb_previous_next_request_from"
string => readfile( $(_cmdb_next_request_state_file), inf ),
if => regline( "^\d+$", $(_cmdb_next_request_state_file) );
"_cmdb_previous_next_request_from"
string => "0",
unless => regline( "^\d+$", $(_cmdb_next_request_state_file) );
!bootstrap_mode.(policy_server.enterprise_edition)::
# We need a script to call that should return the API response
"_get_cmdb_data_bin" string => "$(sys.workdir)/httpd/htdocs/scripts/get_cmdb.php";
"_get_cmdb_data_cmd" string => "/var/cfengine/httpd/php/bin/php $(_get_cmdb_data_bin) $(_cmdb_previous_next_request_from)";
# We call the script and we pass it the timestamp from the prior call
"_get_cmdb_data_response"
string => execresult( $(_get_cmdb_data_cmd), useshell ),
if => fileexists( $(_get_cmdb_data_bin) );
"_get_cmdb_data_response_d"
data => parsejson('$(_get_cmdb_data_response)'),
if => validjson( '$(_get_cmdb_data_response)' );
# So that we can write a JSON file for each host we get the indicies of data in the response
"_i" slist => getindices( "_get_cmdb_data_response_d[data]");
# We need to store the timestamp from the most recent change so that we can use that as a starting point for future requests.
"_next_request_from"
string => "$(_get_cmdb_data_response_d[meta][cmdb_epoch])";
files:
# "$(_get_cmdb_data_cmd)" perms => m( 700 );
@if minimum_version(3.18)
!bootstrap_mode.(policy_server.enterprise_edition)::
# This functionality is only present on 3.18.0+ Enterprise hubs, and this
# promise uses the /content/ attribute which was first introduced in
# 3.16.0.
# If the next request state file doesn't exist, we seed one with 0, the
# lowest epoch value possible. because we populate variables from this
# file content.
"$(_cmdb_next_request_state_file)"
content => "0$(const.n)",
handle => "cmdb_data_change_next_seed",
if => and( not(fileexists("$(_cmdb_next_request_state_file)" )),
isvariable( "_cmdb_next_request_state_file" ));
@endif
# Write out the data for each host that had a data change
"$(sys.workdir)/cmdb/$(_i)/host_specific.json"
create => "true", # CFE-2329, ENT-4792
template_data => mergedata("_get_cmdb_data_response_d[data][$(_i)]" ), # mergedata() is necessary in order to pick out a substructure, parsejson() is insufficient because expanding a key results in iteration of /values/ under that key
template_method => "inline_mustache",
edit_template_string => string_mustache( "", "_get_cmdb_data_response_d[data][$(_i)]" ),
if => isgreaterthan( $(_next_request_from), $(_cmdb_previous_next_request_from) );
@if minimum_version(3.18)
# This functionality is only present on 3.18.0+ Enterprise hubs, and this
# promise uses the /content/ attribute which was first introduced in
# 3.16.0.
# Write out the last data change timestamp so we can use it as a startring point
"$(_cmdb_next_request_state_file)"
handle => "cmdb_data_change_next_update",
content => "$(_next_request_from)$(const.n)",
unless => strcmp( $(_next_request_from), $(_cmdb_previous_next_request_from) );
@endif
reports:
DEBUG|DEBUG_cfe_internal_update_cmdb_data_distribution::
"'$(_get_cmdb_data_cmd)' response indicates '$(sys.workdir)/cmdb/$(_i)/host_specific.json' needs refreshed"
if => and( isvariable( "_i" ),
isgreaterthan( $(_next_request_from), $(_cmdb_previous_next_request_from) ));
}
cfe_internal_update_cmdb_data_consumption
Prototype: cfe_internal_update_cmdb_data_consumption
Description: Ensure data to load is up to date
Implementation:
bundle agent cfe_internal_update_cmdb_data_consumption
{
files:
"$(sys.workdir)/data/."
create => "true",
comment => "If a host is to load data from the CMDB, it needs to have a directory where said data is cached.";
"$(sys.workdir)/data/." -> { "ENT-6788", "ENT-8847" }
depth_search => u_recurse( inf ),
file_select => u_all,
copy_from => u_cmdb_data,
comment => "So that hosts have access to the most recent CMDB data, we make sure that it's up to date.",
action => u_immediate;
}
modules_presence
Prototype: modules_presence
Implementation:
#@ ```
{
dirlinks => "delete";
rmdirs => "true";
}