Cfengine is built on promises. Promises were chosen as the model for Cfengine's configuration language, because they represent an expression of intention.
If you are using custom scripts to manage your systems, you are using recipes. Take a look at any cookbook and you will see that all recipes look the same: take flour, eggs, butter, sugar ... and you know nothing because you can make a hundred things from these steps. If you don't make clear your intention, it is very hard to know what the recipe is supposed to be: is it a cake, a waffle, a pastry?
The same is true in system administration. Recipes are not merely scripts, they encapsulate knowledge and experience. Their value is in communicating desired outcomes or states.
This library of standard components is like a cookbook that tells you only how to make basics well. It gives these basic skills names and therefore gives you a common vocabulary – you will put together these basics in creative ways to build your systems.
Please contribute to this guide by helping to develop a repertoire of basic skills and names. This collection should be comprehensive but parsimonious. Basics are only basics if they are few and carefully thought out. This is a work in progress and your experience is welcome. This library will be moderated by Cfengine, and contributions and discussions ca n be made to the help-cfengine@cfengine.org mailing list. |
body acl access_generic(acl) # default/inherited ACLs are left unchanged, # applicable for both files and directories on all platforms { acl_method => "overwrite"; aces => { "@(acl)" }; windows:: acl_type => "ntfs"; !windows:: acl_type => "posix"; }
body acl ntfs(acl) { acl_type => "ntfs"; acl_method => "overwrite"; aces => { "@(acl)" }; }
body acl strict # NOTE: May need to take ownership of file/dir # to be sure no-one else is allowed access { acl_method => "overwrite"; windows:: aces => { "user:Administrator:rwx" }; !windows:: aces => { "user:root:rwx" }; }
body action bg(elapsed,expire) { ifelapsed => "$(elapsed)"; expireafter => "$(expire)"; background => "true"; }
body action if_elapsed(x) { ifelapsed => "$(x)"; expireafter => "$(x)"; }
body action ifwin_bg { windows:: background => "true"; }
body action immediate { ifelapsed => "0"; }
body action log_repaired(log,message) { log_string => "$(sys.date), $(message)"; log_repaired => "$(log)"; }
body action log_verbose { log_level => "verbose"; }
body action measure_performance(x) { measurement_class => "Detect changes in $(this.promiser)"; ifelapsed => "$(x)"; expireafter => "$(x)"; }
body action policy(p) { action_policy => "$(p)"; } # Log a message to log=[/file|stdout]
body action sample_rate(x) { ifelapsed => "$(x)"; expireafter => "10"; }
body action warn_only { action_policy => "warn"; ifelapsed => "60"; }
body changes detect_all_change # This is fierce, and will cost disk cycles { hash => "best"; report_changes => "all"; update_hashes => "yes"; }
body changes detect_content # This is a cheaper alternative { hash => "md5"; report_changes => "content"; update_hashes => "yes"; }
body changes diff # Generates diff report (Nova and above) { hash => "sha256"; report_changes => "content"; report_diffs => "true"; update_hashes => "yes"; }
body changes diff_noupdate { hash => "sha256"; report_changes => "content"; report_diffs => "true"; update_hashes => "no"; }
body changes noupdate # Use on (small) files that should never change { hash => "sha256"; report_changes => "content"; update_hashes => "no"; }
body classes always(x) # Define a class no matter what the outcome of the promise is { promise_repaired => { "$(x)" }; promise_kept => { "$(x)" }; repair_failed => { "$(x)" }; repair_denied => { "$(x)" }; repair_timeout => { "$(x)" }; } # agent bundles
body classes cf2_if_else(yes,no) # meant to match cf2 semantics { promise_repaired => { "$(yes)" }; repair_failed => { "$(no)" }; repair_denied => { "$(no)" }; repair_timeout => { "$(no)" }; }
body classes cmd_repair(code,cl) { repaired_returncodes => { "$(code)" }; promise_repaired => { "$(cl)" }; }
body classes enumerate(x) # # This is used by commercial editions to count # instances of jobs in a cluster # { promise_repaired => { "mXC_$(x)" }; promise_kept => { "mXC_$(x)" }; persist_time => "15"; }
body classes if_else(yes,no) { promise_kept => { "$(yes)" }; promise_repaired => { "$(yes)" }; repair_failed => { "$(no)" }; repair_denied => { "$(no)" }; repair_timeout => { "$(no)" }; }
body classes if_notkept(x) { repair_failed => { "$(x)" }; repair_denied => { "$(x)" }; repair_timeout => { "$(x)" }; }
body classes if_ok(x) { promise_repaired => { "$(x)" }; promise_kept => { "$(x)" }; }
body classes if_repaired(x) { promise_repaired => { "$(x)" }; }
body classes state_repaired(x) { promise_repaired => { "$(x)" }; persist_time => "10"; }
body contain in_dir(s) { chdir => "$(s)"; }
body contain in_dir_shell(s) { chdir => "$(s)"; useshell => "true"; }
body contain in_shell { useshell => "true"; }
body contain in_shell_and_silent { useshell => "true"; no_output => "true"; }
body contain in_shell_bg { useshell => "true"; background => "true"; }
body contain jail(owner,root,dir) { exec_owner => "$(owner)"; useshell => "true"; chdir => "$(dir)"; chroot => "$(root)"; }
body contain setuid(x) { exec_owner => "$(x)"; useshell => "false"; }
body contain setuid_sh(x) { exec_owner => "$(x)"; useshell => "true"; }
body contain setuidgid_sh(owner,group) { exec_owner => "$(owner)"; exec_group => "$(group)"; useshell => "true"; }
body contain silent { no_output => "true"; }
body contain silent_in_dir(s) { chdir => "$(s)"; no_output => "true"; }
body copy_from backup_local_cp(from) # Local copy, keeping a backup of old versions { source => "$(from)"; copy_backup => "timestamp"; } # Copy only if the file does not already exist, i.e. seed the placement
body copy_from local_cp(from) { source => "$(from)"; }
body copy_from local_dcp(from) { source => "$(from)"; compare => "digest"; }
body copy_from no_backup_cp(from) { source => "$(from)"; copy_backup => "false"; }
body copy_from no_backup_dcp(from) { source => "$(from)"; copy_backup => "false"; compare => "digest"; }
body copy_from no_backup_rcp(from,server) { servers => { "$(server)" }; source => "$(from)"; compare => "mtime"; copy_backup => "false"; }
body copy_from perms_cp(from) { source => "$(from)"; preserve => "true"; }
body copy_from remote_cp(from,server) { servers => { "$(server)" }; source => "$(from)"; compare => "mtime"; }
body copy_from remote_dcp(from,server) { servers => { "$(server)" }; source => "$(from)"; compare => "digest"; }
body copy_from secure_cp(from,server) { source => "$(from)"; servers => { "$(server)" }; compare => "digest"; encrypt => "true"; verify => "true"; }
body copy_from seed_cp(from) { source => "$(from)"; compare => "exists"; }
body copy_from sync_cp(from,server) { servers => { "$(server)" }; source => "$(from)"; purge => "true"; preserve => "true"; type_check => "false"; }
body delete tidy { dirlinks => "delete"; rmdirs => "true"; }
body depth_search include_base { include_basedir => "true"; }
body depth_search recurse(d) { depth => "$(d)"; xdev => "true"; }
body depth_search recurse_ignore(d,list) { depth => "$(d)"; exclude_dirs => { @(list) }; }
body edit_defaults empty { empty_file_before_editing => "true"; edit_backup => "false"; max_file_size => "300000"; }
body edit_defaults no_backup { edit_backup => "false"; }
body edit_defaults std_defs { empty_file_before_editing => "false"; edit_backup => "false"; max_file_size => "300000"; }
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"; }
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"; }
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"; }
body file_select by_name(names) { leaf_name => { @(names)}; file_result => "leaf_name"; }
body file_select days_old(days) { mtime => irange(0,ago(0,0,"$(days)",0,0,0)); file_result => "mtime"; }
body file_select dirs { file_types => { "dir" }; file_result => "file_types"; }
body file_select ex_list(names) { leaf_name => { @(names)}; file_result => "!leaf_name"; }
body file_select exclude(name) { leaf_name => { "$(name)"}; file_result => "!leaf_name"; }
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"; }
body file_select plain { file_types => { "plain" }; file_result => "file_types"; }
body file_select size_range(from,to) { search_size => irange("$(from)","$(to)"); file_result => "size"; }
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 ln_s(x) { link_type => "symlink"; source => "$(x)"; when_no_source => "force"; }
body location after(str) { before_after => "after"; select_line_matching => "$(str)"; }
body location start { before_after => "before"; }
body match_value scan_changing_file(line) { select_line_matching => "$(line)"; track_growing_file => "false"; }
body match_value scan_log(line) { select_line_matching => "$(line)"; track_growing_file => "true"; }
body mount nfs(server,source) { mount_type => "nfs"; mount_source => "$(source)"; mount_server => "$(server)"; edit_fstab => "true"; }
body mount nfs_p(server,source,perm) { mount_type => "nfs"; mount_source => "$(source)"; mount_server => "$(server)"; mount_options => {"$(perm)"}; edit_fstab => "true"; }
body mount unmount { mount_type => "nfs"; edit_fstab => "true"; unmount => "true"; }
body package_method apt { package_changes => "bulk"; package_list_command => "/usr/bin/dpkg -l"; package_list_name_regex => "ii\s+([^\s]+).*"; package_list_version_regex => "ii\s+[^\s]+\s+([^\s]+).*"; package_installed_regex => ".*"; # all reported are installed package_name_convention => "$(name)"; # set it to "0" to avoid caching of list during upgrade package_list_update_ifelapsed => "240"; have_aptitude:: package_add_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/aptitude -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --assume-yes install"; package_list_update_command => "/usr/bin/aptitude update"; package_delete_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/aptitude -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --assume-yes -q remove"; package_update_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/aptitude -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --assume-yes install"; package_verify_command => "/usr/bin/aptitude show"; package_noverify_regex => "(State: not installed|E: Unable to locate package .*)"; !have_aptitude:: package_add_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/apt-get -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --yes install"; package_list_update_command => "/usr/bin/apt-get update"; package_delete_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/apt-get -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --yes -q remove"; package_update_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/apt-get -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --yes install"; package_verify_command => "/usr/bin/dpkg -s"; package_noverify_returncode => "1"; }
body package_method dpkg_version(repo) { package_changes => "individual"; package_list_command => "/usr/bin/dpkg -l"; # set it to "0" to avoid caching of list during upgrade package_list_update_ifelapsed => "240"; package_list_name_regex => "ii\s+([^\s]+).*"; package_list_version_regex => "ii\s+[^\s]+\s+([^\s]+).*"; package_installed_regex => ".*"; # all reported are installed package_file_repositories => { "$(repo)" }; 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 --install"; package_delete_command => "/usr/bin/dpkg --purge"; package_update_command => "/usr/bin/dpkg --install"; }
body package_method freebsd { package_changes => "individual"; # Could use rpm for this package_list_command => "/usr/sbin/pkg_info"; # Remember to escape special characters like | package_list_name_regex => "([^\s]+)-.*"; package_list_version_regex => "[^\s]+-([^\s]+).*"; package_name_regex => "([^\s]+)-.*"; package_version_regex => "[^\s]+-([^\s]+).*"; package_installed_regex => ".*"; package_name_convention => "$(name)-$(version)"; package_add_command => "/usr/sbin/pkg_add -r"; package_delete_command => "/usr/sbin/pkg_delete"; } # Single bundle for all the similar managers simplifies promises
body package_method generic { SuSE:: package_changes => "bulk"; package_list_command => "/usr/bin/zypper packages"; package_patch_list_command => "/usr/bin/zypper patches"; package_installed_regex => "i.*"; package_list_name_regex => "[^|]+\|[^|]+\|\s+([^\s]+).*"; package_list_version_regex => "[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*"; package_list_arch_regex => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*"; 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 => "/usr/bin/zypper --non-interactive install"; package_delete_command => "/usr/bin/zypper --non-interactive remove --force-resolution"; package_update_command => "/usr/bin/zypper --non-interactive update"; package_patch_command => "/usr/bin/zypper --non-interactive patch$"; # $ means no args package_verify_command => "/usr/bin/zypper --non-interactive verify$"; redhat:: package_changes => "bulk"; package_list_command => "/bin/rpm -qa --qf '%{name} %{version}-%{release} %{arch}\n'"; package_patch_list_command => "/usr/bin/yum check-update"; package_list_name_regex => "^(\S+?)\s\S+?\s\S+$"; package_list_version_regex => "^\S+?\s(\S+?)\s\S+$"; package_list_arch_regex => "^\S+?\s\S+?\s(\S+)$"; package_installed_regex => ".*"; package_name_convention => "$(name)"; package_list_update_ifelapsed => "0"; # sometimes, caching is pretty disturbing package_patch_installed_regex => "^\s.*"; package_patch_name_regex => "([^.]+).*"; package_patch_version_regex => "[^\s]\s+([^\s]+).*"; package_patch_arch_regex => "[^.]+\.([^\s]+).*"; package_add_command => "/usr/bin/yum -y install"; package_update_command => "/usr/bin/yum -y update"; package_delete_command => "/bin/rpm -e --nodeps --allmatches"; package_verify_command => "/bin/rpm -V"; # package_changes => "bulk"; # package_list_command => "/usr/bin/yum list installed"; # package_patch_list_command => "/usr/bin/yum check-update"; # package_list_name_regex => "([^.]+).*"; # package_list_version_regex => "[^\s]\s+([^\s]+).*"; # package_list_arch_regex => "[^.]+\.([^\s]+).*"; # package_installed_regex => ".*(installed|\s+@).*"; # package_name_convention => "$(name).$(arch)"; # package_list_update_ifelapsed => "240"; # package_patch_installed_regex => "^\s.*"; # package_patch_name_regex => "([^.]+).*"; # package_patch_version_regex => "[^\s]\s+([^\s]+).*"; # package_patch_arch_regex => "[^.]+\.([^\s]+).*"; # package_add_command => "/usr/bin/yum -y install"; # package_delete_command => "/bin/rpm -e --nodeps"; # package_verify_command => "/bin/rpm -V"; debian:: package_changes => "bulk"; package_list_command => "/usr/bin/dpkg -l"; package_list_name_regex => "ii\s+([^\s]+).*"; package_list_version_regex => "ii\s+[^\s]+\s+([^\s]+).*"; package_installed_regex => ".*"; # all reported are installed package_name_convention => "$(name)"; package_list_update_ifelapsed => "240"; # 4 hours debian.have_aptitude:: package_add_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/aptitude -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --assume-yes install"; package_list_update_command => "/usr/bin/aptitude update"; package_delete_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/aptitude -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --assume-yes remove"; package_update_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/aptitude -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --assume-yes install"; package_verify_command => "/usr/bin/aptitude show"; package_noverify_regex => "(State: not installed|E: Unable to locate package .*)"; debian.!have_aptitude:: package_add_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/apt-get -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --yes install"; package_list_update_command => "/usr/bin/apt-get update"; package_delete_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/apt-get -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --yes remove"; package_update_command => "/usr/bin/env DEBIAN_FRONTEND=noninteractive LC_ALL=C /usr/bin/apt-get -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --yes install"; package_verify_command => "/usr/bin/dpkg -s"; package_noverify_returncode => "1"; freebsd:: package_changes => "individual"; package_list_command => "/usr/sbin/pkg_info"; package_list_name_regex => "([^\s]+)-.*"; package_list_version_regex => "[^\s]+-([^\s]+).*"; package_name_regex => "([^\s]+)-.*"; package_version_regex => "[^\s]+-([^\s]+).*"; package_installed_regex => ".*"; package_name_convention => "$(name)-$(version)"; package_add_command => "/usr/sbin/pkg_add -r"; package_delete_command => "/usr/sbin/pkg_delete"; }
body package_method msi_explicit(repo) # use software name as promiser, e.g. "7-Zip", and explicitly # specify any package_version and package_arch { 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"; }
body package_method msi_implicit(repo) # Use 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 { 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"; }
body package_method rpm_version(repo) { package_changes => "individual"; package_list_command => "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\""; # set it to "0" to avoid caching of list during upgrade package_list_update_ifelapsed => "240"; 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_update_command => "/bin/rpm -Uvh "; package_delete_command => "/bin/rpm -e --nodeps"; package_noverify_regex => ".*[^\s].*"; }
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_list_name_regex => "\s*PKGINST:\s+([^\s]+).*"; package_list_version_regex => "\s*VERSION:\s+([^\s]+).*"; package_list_arch_regex => "\s*ARCH:\s+([^\s]+)"; package_installed_regex => "\s*STATUS:\s*(completely|partially)\s+installed.*"; package_name_convention => "$(name)"; package_add_command => "/usr/sbin/pkgadd -n -a /tmp/$(adminfile) -d /tmp/$(spoolfile)"; package_delete_command => "/usr/sbin/pkgrm -n -a /tmp/$(adminfile)"; } # # The following bundle is part of a package setup for solaris, see unit examples #
body package_method yum { package_changes => "bulk"; package_list_command => "/usr/bin/yum list installed"; package_patch_list_command => "/usr/bin/yum check-update"; # Remember to escape special characters like | package_list_name_regex => "([^.]+).*"; package_list_version_regex => "[^\s]\s+([^\s]+).*"; package_list_arch_regex => "[^.]+\.([^\s]+).*"; package_installed_regex => ".*(installed|\s+@).*"; package_name_convention => "$(name).$(arch)"; # set it to "0" to avoid caching of list during upgrade package_list_update_ifelapsed => "240"; package_patch_installed_regex => "^\s.*"; package_patch_name_regex => "([^.]+).*"; package_patch_version_regex => "[^\s]\s+([^\s]+).*"; package_patch_arch_regex => "[^.]+\.([^\s]+).*"; package_add_command => "/usr/bin/yum -y install"; package_update_command => "/usr/bin/yum -y update"; package_delete_command => "/bin/rpm -e --nodeps"; package_verify_command => "/bin/rpm -V"; }
body package_method yum_rpm # Contributed by Trond Hasle Amundsen # More efficient package method for RedHat - uses rpm to list instead of yum # Notes: # - using $(name).$(arch) instead of $(name) for package_name_convention # causes uninstallation to fail. # - using allmatches to remove for all architectures # { package_changes => "bulk"; package_list_command => "/bin/rpm -qa --qf '%{name} %{version}-%{release} %{arch}\n'"; package_patch_list_command => "/usr/bin/yum check-update"; package_list_name_regex => "^(\S+?)\s\S+?\s\S+$"; package_list_version_regex => "^\S+?\s(\S+?)\s\S+$"; package_list_arch_regex => "^\S+?\s\S+?\s(\S+)$"; package_installed_regex => ".*"; package_name_convention => "$(name)"; package_patch_installed_regex => "^\s.*"; package_patch_name_regex => "([^.]+).*"; package_patch_version_regex => "[^\s]\s+([^\s]+).*"; package_patch_arch_regex => "[^.]+\.([^\s]+).*"; package_add_command => "/usr/bin/yum -y install"; package_update_command => "/usr/bin/yum -y update"; package_delete_command => "/bin/rpm -e --nodeps --allmatches"; package_verify_command => "/bin/rpm -V"; } # The solaris package system is poorly designed, with too many different # names to track. See the example in tests/units/unit_package_solaris.cf # to see how to use this
body package_method zypper { package_changes => "bulk"; package_list_command => "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\""; # set it to "0" to avoid caching of list during upgrade package_list_update_ifelapsed => "240"; package_patch_list_command => "/usr/bin/zypper patches"; package_installed_regex => "i.*"; package_list_name_regex => "[^|]+\|[^|]+\|\s+([^\s]+).*"; package_list_version_regex => "[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*"; package_list_arch_regex => "[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s+([^\s]+).*"; 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 => "/usr/bin/zypper --non-interactive install"; package_delete_command => "/usr/bin/zypper --non-interactive remove --force-resolution"; package_update_command => "/usr/bin/zypper --non-interactive update"; package_patch_command => "/usr/bin/zypper --non-interactive patch$"; # $ means no args package_verify_command => "/usr/bin/zypper --non-interactive verify$"; }
body perms m(mode) { mode => "$(mode)"; }
body perms mo(mode,user) { owners => { "$(user)" }; mode => "$(mode)"; }
body perms mog(mode,user,group) { owners => { "$(user)" }; groups => { "$(group)" }; mode => "$(mode)"; }
body perms og(u,g) { owners => { "$(u)" }; groups => { "$(g)" }; }
body perms owner(user) { owners => { "$(user)" }; }
body process_count any_count(cl) { match_range => "0,0"; out_of_range_define => { "$(cl)" }; }
body process_count check_range(name,lower,upper) { match_range => irange("$(lower)","$(upper)"); out_of_range_define => { "$(name)_out_of_range" }; }
body process_select days_older_than(d) { stime_range => irange(ago(0,0,"$(d)",0,0,0),now); process_result => "stime"; }
body process_select exclude_procs(x) { command => "$(x)"; process_result => "!command"; }
body rename disable { disable => "true"; }
body rename rotate(level) { rotate => "$(level)"; }
body rename to(file) { newname => "$(file)"; }
body replace_with comment(c) { replace_value => "$(c) $(match.1)"; occurrences => "all"; }
body replace_with uncomment { replace_value => "$(match.1)"; occurrences => "all"; }
body replace_with value(x) { replace_value => "$(x)"; occurrences => "all"; }
body select_region INI_section(x) { select_start => "\[$(x)\]\s*"; select_end => "\[.*\]\s*"; }
body service_method bootstart { service_autostart_policy => "boot_time"; service_dependence_chain => "start_parent_services"; windows:: service_type => "windows"; }
body service_method force_deps { service_dependence_chain => "all_related"; windows:: service_type => "windows"; }
body volume min_free_space(free) { check_foreign => "false"; freespace => "$(free)"; sensible_size => "10000"; sensible_count => "2"; }
bundle agent cronjob(commands,user,hours,mins) # For adding lines to crontab for a user # methods: # "cron" usebundle => cronjob("/bin/ls","mark","*","5,10"); { vars: SuSE:: "crontab" string => "/var/spool/cron/tabs"; redhat|fedora:: "crontab" string => "/var/spool/cron"; !(SuSE|redhat|fedora):: "crontab" string => "/var/spool/cron/crontabs"; files: !windows:: "$(crontab)/$(user)" comment => "A user's regular batch jobs are added to this file", create => "true", edit_line => append_if_no_line("$(mins) $(hours) * * * $(commands)"), perms => mo("644","$(user)"), classes => if_repaired("changed_crontab"); processes: changed_crontab:: "cron" comment => "Most crons need to be huped after file changes", signals => { "hup" }; }
bundle edit_line append_groups_starting(v) # For adding groups to /etc/group, needs # an array v[groupname] string => "line..." { vars: "index" slist => getindices("$(v)"); classes: "add_$(index)" not => groupexists("$(index)"); insert_lines: "$($(v)[$(index)])", comment => "Append users into a group file format", ifvarclass => "add_$(index)"; }
bundle edit_line append_if_no_line(str) { insert_lines: "$(str)" comment => "Append a line to the file if it doesn't already exist"; }
bundle edit_line append_if_no_lines(list) { insert_lines: "$(list)" comment => "Append lines to the file if they don't already exist"; }
bundle edit_line append_to_line_end(start,end) # # Lines starting with "$(start)" and not ending with "$(end)" # will get appended 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 resume=/dev/sda9 vga=791" # # WARNING: Be careful not to have multiple promises matching the same line, # which would result in the line growing indefinetively. { field_edits: "\s*$(start)\s.*" edit_field => line("(^|\s)$(start)\s*", "2", "$(end)","append"); }
bundle edit_line append_user_field(group,field,allusers) # For adding users to to a file like /etc/group # at field position "field", comma separated subfields { vars: "val" slist => { @(allusers) }; field_edits: "$(group):.*" comment => "Append users into a password file format", edit_field => col(":","$(field)","$(val)","alphanum"); }
bundle edit_line append_users_starting(v) # For adding to /etc/passwd or etc/shadow, needs # an array v[username] string => "line..." { vars: "index" slist => getindices("$(v)"); classes: "add_$(index)" not => userexists("$(index)"); insert_lines: "$($(v)[$(index)])", comment => "Append users into a password file format", ifvarclass => "add_$(index)"; }
bundle edit_line comment_lines_containing(regex,comment) # Comment lines of a file containing a regex { replace_patterns: "^(.*$(regex).*)$" replace_with => comment("$(comment)"), comment => "Comment out lines in a file"; }
bundle edit_line comment_lines_matching(regex,comment) # Comment lines of a file matching a regex { replace_patterns: "^($(regex))$" replace_with => comment("$(comment)"), comment => "Search and replace string"; }
bundle edit_line create_solaris_admin_file { insert_lines: "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"; }
bundle edit_line delete_lines_matching(regex) { delete_lines: "$(regex)" comment => "Delete lines matching regular expressions"; }
bundle edit_line expand_template(templatefile) # Read in the named text file and expand $(var) # inside the file { insert_lines: "$(templatefile)" insert_type => "file", comment => "Expand variables in the template file", expand_scalars => "true"; }
bundle edit_line insert_file(templatefile) { insert_lines: "$(templatefile)" comment => "Insert the template file into the file being edited", insert_type => "file"; }
bundle edit_line replace_line_end(start,end) # # Lines starting with "$(start)" will get 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" { field_edits: "\s*$(start)\s.*" edit_field => line("(^|\s)$(start)\s*", "2", "$(end)","set"); }
bundle edit_line replace_or_add(pattern,line) # Replace a pattern in a file with a single line. # If the pattern is not found, add the line to the file. # The pattern must match the whole line (it is automatically # anchored to the start and end of the line) to avoid # ambiguity. { vars: "cline" string => canonify("$(line)"); replace_patterns: "^(?!$(line))$(pattern)$" replace_with => value("$(line)"), classes => always("replace_done_$(cline)"); insert_lines: "$(line)" ifvarclass => "replace_done_$(cline)"; }
bundle edit_line resolvconf(search,list) # search is the search domains with space # list is an slist of nameserver addresses { 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"; }
bundle edit_line set_config_values(v) # 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. # The argument is the fully-qualified name of an associative array containing v[LHS]="rhs" { vars: "index" slist => getindices("$(v)"); # 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+.*)$" replace_with => value("$(index) $($(v)[$(index)])"), classes => always("replace_attempted_$(cindex[$(index)])"); insert_lines: "$(index) $($(v)[$(index)])" ifvarclass => "replace_attempted_$(cindex[$(index)])"; }
bundle edit_line set_user_field(user,field,val) # Set the value of field number "field" in # a :-field formatted file like /etc/passwd { field_edits: "$(user):.*" comment => "Edit a user attribute in the password file", edit_field => col(":","$(field)","$(val)","set"); }
bundle edit_line set_variable_values(v) # Sets the RHS of variables in the file of the form # LHS = RHS # Adds a new line if no LHS exists, repairs RHS values if one does exist # # To use: # 1) Define an array, where the keys are the LHS and the values are the RHS # "stuff[lhs-1]" string => "rhs1"; # "stuff[lhs-2]" string => "rhs2"; # 2) The parameter passed to the edit_line promise is the fully qualified # name of the array (i.e., "bundlename.stuff") WITHOUT any "$" or "@" { vars: "index" slist => getindices("$(v)"); # Be careful if the index string contains funny chars "cindex[$(index)]" string => canonify("$(index)"); field_edits: # match a line starting like the key = something "\s*$(index)\s*=.*" edit_field => col("=","2","$($(v)[$(index)])","set"), classes => if_ok("$(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"; }
bundle edit_line set_variable_values2(file,v) # # Another implementation of set_variable_values. # The input and output should be exactly the same (except the file name), # but in some Cfengine versions there are bugs, # so this bundle can be used as a workaround. # { vars: "index" slist => getindices("$(v)"); # Be careful if the index string contains funny chars "cindex[$(index)]" string => canonify("$(index)"); "fieldc_$(cindex[$(index)])" int => getfields("$(index).*","$(file)","=","FIELD_$(cindex[$(index)])"); classes: "$(cindex[$(index)])_in_file" expression => strcmp("$(index)=$($(v)[$(index)])", "$(FIELD_$(index)[1])=$(FIELD_$(index)[2])"); delete_lines: # delete any lhs when no match "$(index)=.*", ifvarclass => "!$(cindex[$(index)])_in_file"; insert_lines: "$(index)=$($(v)[$(index)])", comment => "Insert a variable definition", ifvarclass => "!$(cindex[$(index)])_in_file"; }
bundle edit_line uncomment_lines_containing(regex,comment) # Uncomment lines of a file where the regex matches # the text after the comment string { replace_patterns: "^$(comment)\s?(.*$(regex).*)$" replace_with => uncomment, comment => "Uncomment a line containing a fragment"; }
bundle edit_line uncomment_lines_matching(regex,comment) # Uncomment lines of a file where the regex matches # the text after the comment string { replace_patterns: "^$(comment)\s?($(regex))$" replace_with => uncomment, comment => "Uncomment lines matching a regular expression"; }
bundle edit_line warn_lines_matching(regex) { delete_lines: "$(regex)" comment => "Warn about lines in a file", action => warn_only; }
Table of Contents