CFengine 3 Tutorial Part 5 Client promises.cf and regular cf-agent operation


Finally, the moment we’ve been waiting for. Lets take a crack at promises.cf and what we have defined.

$ cat -n /var/cfengine/inputs/promises.cf

1 # promises.cf -- clients only
2
3 body common control
4 {
5 bundlesequence => {
6 "update",
7 "garbage_collection",
8 "smf_update",
9 "verify_root",
10 "general_global_configs",
11 };
12
13 inputs => {
14 "update.cf",
15 "cfengine_stdlib.cf",
16 "cf-execd.cf",
17 "smf_update.cf",
18 "verify_root.cf",
19 "general_global_configs.cf",
20 };
21 }
22
23 #######################################################
24
25 bundle common g
26 {
27 # Define some global variables
28 vars:
29 "masterfiles" string => "/var/cfengine/masterfiles";
30 "inputs" string => "/var/cfengine/inputs";
31 "workdir" string => "/var/cfengine";
32 "phost" string => "192.168.1.10";
33
34 # Define global or local zones
35 "solaris_zone_type" expression => usemodule("init_zone-type","");
36 }
37
38 #######################################################
39
40 body agent control
41 {
42 # if default runtime is 5 mins we need this for long jobs
43 ifelapsed => "15";
44
45 # Allow us to use DNS
46 skipidentify => "false";
47 }
48
49 #######################################################
50
51 body monitor control
52 {
53 forgetrate => "0.7";
54 histograms => "true";
55 monitorfacility => "LOG_DAEMON";
56 }
57
58 #######################################################
59
60 body reporter control
61
62 {
63 reports => { "all" };
64 build_directory => "$(sys.workdir)/reports";
65 report_output => "text";
66 time_stamps => "true";
67 }
68
69 #######################################################

Note, the first action cf-agent will execute in “normal operation” is to update itself from the master policy server.

$ find /var/cfengine -name cfengine_stdlib.cf

/var/cfengine/share/doc/cfengine/cfengine_stdlib.cf

And some examples of what these functions can do…

$ egrep 'bundle|body' /var/cfengine/inputs/cfengine_stdlib.cf

bundle edit_line comment_lines_matching(regex,comment)
bundle edit_line uncomment_lines_matching(regex,comment)
bundle edit_line delete_lines_matching(regex)
bundle edit_line append_if_no_line(str)
bundle edit_line append_if_no_lines(list)
bundle edit_line resolvconf(search,list)
bundle edit_line set_variable_values(v)
bundle edit_line append_users_starting(v)
bundle edit_line append_groups_starting(v)
bundle edit_line set_user_field(user,field,val)
bundle edit_line append_user_field(group,field,allusers)
bundle edit_line expand_template(templatefile)
body edit_field quoted_var(newval,method)
body edit_field col(split,col,newval,method)
body replace_with value(x)
body select_region INI_section(x)
body edit_defaults std_defs
body edit_defaults empty
body location start
body replace_with comment(c)
body replace_with uncomment
body action if_elapsed(x)
body action measure_performance(x)
body action warn_only
body action bg(elapsed,expire)
body contain silent
body contain in_dir(s)
body contain silent_in_dir(s)
body contain in_shell
body contain setuid(x)
body contain setuid_sh(x)
body contain jail(owner,root,dir)
body classes if_desired(a)
body classes if_repaired(x)
body classes if_else(yes,no)
body classes if_notkept(x)
body classes if_ok(x)
body copy_from secure_cp(from,server)
body copy_from remote_cp(from,server)
body copy_from local_cp(from)
body copy_from no_backup_cp(from)
body copy_from no_backup_rcp(from,server)
body link_from ln_s(x)
body link_from linkchildren(tofile)
body perms m(mode)
body perms mo(mode,user)
body perms mog(mode,user,group)
body perms og(u,g)
body perms owner(user)
body depth_search recurse(d)
body depth_search recurse_ignore(d,list)
body delete tidy
body rename disable
body rename rotate(level)
body rename to(file)
body file_select name_age(name,age)
body file_select days_old(days)
body file_select size_range(from,to)
body file_select exclude(name)
body file_select plain
body file_select dirs
body file_select ex_list(names)
body changes detect_all_change
body changes detect_content
body package_method zypper
body package_method apt
body package_method yum
body package_method solaris (pkgname, spoolfile, adminfile)
body package_method freebsd
body volume min_free_space(free)
body mount nfs(server,source)
body mount nfs_p(server,source,perm)
body mount unmount
body select_process exclude_procs(x)
body process_count check_range(name,lower,upper)

By importing this library, a lot of the complexity of lower-level policy writing is taken care of for you. You can also view a web-based version of what these policies contain here.

Looking back at promises.cf, on lines 34-35 we define a custom class using a module. Remember from before, when cf-agent executes in verbose mode, we can see which classes that were discovered. Using basic shell scripts, we can extend this to define whatever classes we want to. Lets take a look at the module we’ve defined.

$ cat /var/cfengine/modules/init_zone-type

#!/bin/bash

PATH=/usr/bin:/usr/sbin

TYPE=`zoneadm list -cp | cut -d: -f2`

if [ "$TYPE" == 'global' ]
then
echo '+global_zone'
else
echo '+local_zone'
fi

So this is super easy. We can execute whatever shell commands we want and use basic logic to determine if we’ve matched that class. If so, echo “+[class_name]” and cf-agent will pick it up. So, if you wanted to create a class for machines with Python installed, specific running processes, etc… The possibilities here are endless. Use modules to define classes of systems. Don’t modify system state – just collect data and make decisions about what type of classes a machine should belong to. Use CFEngine policies to execute changes.

Finally, the last interesting part about promises.cf here is on line 16. We define a separate file for cf-execd. Using Neil Watson’s example tutorial, he split up configuration settings for the separate daemons into their own file. It makes sense, so I followed his example here. Remember, cf-execd actually drives the execution of cf-agent, so lets take a look at this file.

$ cat -n /var/cfengine/inputs/cf-execd.cf

1 body executor control
2 {
3 # Splaytime is a critical varible that determines the "back off" time in minutes for cf-agent
4 # to check in with cf-serverd. Setting this to a higher value like "10" will allow thousands
5 # of clients to pull updates from a single policy server without hammering the network too bad all
6 # at once.
7 splaytime => "10";
8 mailto => "fatkitty@sinatra.com";
9 mailfrom => "root@$(sys.host)";
10 smtpserver => "localhost";
11 schedule => { "Min00_Min10", "Min15", "Min20", "Min25", "Hr07.Min30", "Min35", "Min40", "Min45", "Min50", "Min55" };
12 executorfacility => "LOG_DAEMON";
13
14 # This is the command that actually drives cf-execd to execute cf-agent on the schedule above.
15 exec_command => "${sys.workdir}/bin/cf-agent -f failsafe.cf && ${sys.workdir}/bin/cf-agent";
16 }
17 ##########################################
18 bundle agent garbage_collection
19 {
20 files:
21
22 "$(sys.workdir)/outputs"
23
24 delete => tidy,
25 file_select => days_old("3"),
26 depth_search => recurse("inf");
27 }

That’s it in a nutshell! We’ve traced through normal client operations and how to construct general CFengine policies. To continue to add functionality into what cf-agent will execute upon, just include more *.cf files on the import and bundlesequence statemetns!

CFengine is an extremely powerful tool for controlling the configuration of hundreds / thousands / tens of thousands of machines. Its used at enterprises like Facebook to drive system management. ALWAYS test changes in a dev/test environment BEFORE pushing new policies into production. With a powerful tool, you can damage machines easily!

This article was posted by Mike on 2010-07-02 16:36:00 -0400 EDT