Il 05/12/2011 16:33, Marco Marongiu ha scritto: > Let's see what I can turn up with with your suggestions.
OK, I seem to have made it. It was hard, damn how it was. We should really meditate on this. Having a common block of reusable code should be far easier than this... Here's a summary of how I solved the problem. The goals: - load a policy file called ntp.cf, always, and a location specific one, say ntp_x.cf for location x; - in ntp_x.cf, define an array which will hold values depending on the machine being a unicast client, or a server, or whatever else it could be; - call a method "ntp", defined in ntp.cf, which should use the information in ntpconf to build ntp.conf The first problem was how to pass an associative array of settings to a parametric bundle. toddnni suggested: ntp("@(bundle_where_var_is_defined.ntpconf)") or ntp("@(this.ntpconf)") which didn't work. davidlee suggested: usebundle => action_user_promise("caller_N.user_a"); which worked. What I didn't (read: I don't) like in it was that it's way too similar to symbolic references in Perl[*]. At this point, the hurdle was: how to dereference this symbolic reference correctly. After some trial and error I realized I could dereference single values in the associative array, e.g.: vars: # dereferencing a scalar value: "tstring" string => "$($(atest)[string])" ; # dereferencing a list value: "tlist" slist => { "@($(atest)[slist])" } ; but I couldn't dereference the whole associative array itself. There doesn't even exist an explicit data type for associative arrays. OK, have to live with it... Now I had a way to pass values around with an associative arrays. I couldn't pass classes unfortunately, and I didn't feel like defining global classes, that's just looking for trouble[**]. But why did I need to pass classes, in the first place? Well, the plan was to define in each location-specific ntp_x.cf file which machine was a server or a client (is_client and is_server classes) and which machine would use unicast or multicast (is_ucast and is_mcast). These classes would then be combined in other non-location-specific ones, hence in ntp.cf, like in: classes: any:: "ucastserver" and => { "is_ucast", "is_server" } ; "ucastclient" and => { "is_ucast", "is_client" } ; "mcastclient" and => { "is_mcast", "is_client" } ; "mcastserver" and => { "is_mcast", "is_server" } ; # These classes will help us when editing the file, to decide # what we should put into that "has_keys" expression => "is_mcast" ; "has_serverlist" or => { "ucastclient", "is_server" } ; "has_peerlist" expression => "is_server" ; For this to work, I needed a way to "propagate" the local, location-specific class definitions to the called bundle. This is how I did it. In the location-specific file I define: vars: is_client:: "ntpconf[role]" string => "client" ; is_ucast:: "ntpconf[casting]" string => "ucast" ; and then in ntp.cf I use this: vars: any:: "role" string => "$($(ntpconf)[role])" ; "casting" string => "$($(ntpconf)[casting])" ; classes: any:: "is_$(role)" expression => "any" ; "is_$(casting)" expression => "any" ; The pieces were finally in place. But something still didn't work as expected. It took a number of tests to understand that the join() function had problems to dereference, e.g., this.serverlist correctly. I changed all "this." with "ntp.", and that finally did the trick. My first location-specific policy file is 28 lines long (but it's a really minimal one), and the shared one is 200, much smaller and manageable than the previous version (~800 lines). There is some more work to do on both of them, but I am attaching them here for your review, and hoping they are useful to someone. Ciao -- bronto [*] Don't get me wrong: I like Perl a lot. It's just that I don't like symrefs in Perl. [**] Even if I chose very specific class names, like is_ntp_client or uses_ntp_unicast, I would still fear that, one day or another, that will backfire -- e.g.: someone else writes another class, doesn't know about my classes and defines them again... ooops! Mess ahead!
bundle agent ntp_int_vboxvm { classes: "is_client" expression => "any" ; "is_server" not => "is_client" ; "is_ucast" expression => "any" ; "is_mcast" not => "is_ucast" ; vars: is_client:: "ntpconf[serverlist]" slist => { "time1.example.com", "time2.example.com", "time3.example.com", "time4.example.com" } ; "ntpconf[role]" string => "client" ; is_ucast:: "ntpconf[casting]" string => "ucast" ; methods: "ntp_int_vboxvm" usebundle => ntp("ntp_int_vboxvm.ntpconf") ; }
######################################################################## # Bundles ######################################################################## bundle agent ntp(ntpconf) { files: any:: "/etc/ntp.conf" comment => "Create ntp.conf from a template", perms => mog("0644","root","root"), create => "true", classes => restart_ntpd, edit_line => expand_template("$(ntp.templates)/ntp.conf.txt"), edit_defaults => empty ; "/var/lib/ntp/." comment => "Create a directory for drift file and other stuff", perms => mog("0755","ntp","ntp"), create => "true" ; has_keys:: "/etc/ntp/." comment => "Create a directory for ntp keys", perms => mog("0500","root","root"), create => "true" ; processes: any:: "ntpd" comment => "This should ensure we run only one ntpd process", classes => one_ntpd_running, process_select => select_ntpd, process_count => exactly_one_ntpd ; kill_all_ntpd:: "ntpd" comment => "This should kill any runaway ntpd", process_select => select_ntpd, signals => { "term", "kill" }, restart_class => "start_ntpd" ; commands: start_ntpd:: "/etc/init.d/ntp" comment => "Will try to start ntpd using its init script", args => "start" ; restart_ntpd:: "/etc/init.d/ntp" comment => "Will try to restart ntpd using its init script", args => "restart" ; packages: "$(packages)" comment => "Install packages required to implement NTP", package_policy => "add", package_method => aptget; classes: any:: "is_$(role)" expression => "any" ; "is_$(casting)" expression => "any" ; "ucastserver" and => { "is_ucast", "is_server" } ; "ucastclient" and => { "is_ucast", "is_client" } ; "mcastclient" and => { "is_mcast", "is_client" } ; "mcastserver" and => { "is_mcast", "is_server" } ; # These classes will help us when editing the file, to decide # what we should put into that "has_keys" expression => "is_mcast" ; "has_serverlist" or => { "ucastclient", "is_server" } ; "has_peerlist" expression => "is_server" ; vars: # define values and defaults for some variables any:: "packages" slist => { "ntp", "ntpdate" } ; "templates" string => "$(global.templates)/ntp" ; "baseoptions" string => "default kod notrap nomodify nopeer noquery" ; "mcastaddr" string => "224.0.1.1" ; "role" string => "$($(ntpconf)[role])" ; "casting" string => "$($(ntpconf)[casting])" ; #################################################################### # String definitions is_ucast:: "cast" string => "unicast" ; is_mcast:: "cast" string => "multicast" ; has_keys:: "keyfile" string => "keys /etc/ntp/ntp.keys" ; "trustedkeyslist" slist => { "@($(ntpconf)[keys])" } ; "trustedkeys" string => concat( "trustedkey ", join(" ","ntp.trustedkeyslist") ) ; !has_keys:: "keyfile" string => "# No keyfile in this configuration" ; "trustedkeys" string => "# No trusted keys in this configuration" ; has_xen_workaround:: "xen_workaround" string => "disable kernel" ; !has_xen_workaround:: "xen_workaround" string => "# No xen workaround needed" ; has_serverlist:: "serverlist" slist => { "@($(ntpconf)[serverlist])" } ; "servers" string => concat( "server ", join(" iburst$(const.n)server ","ntp.serverlist"), " iburst$(const.n)" ), policy => "free" ; !has_serverlist:: "servers" string => "# No unicast servers configured", policy => "free" ; has_peerlist:: "peerlist" slist => { "@($(ntpconf)[peerlist])" } ; "peers" string => concat( "peer ", join("$(const.n)peer ","ntp.peerlist") ) ; !has_peerlist:: "peers" string => "# No peers configured" ; mcastserver:: "broadcast" string => concat( "broadcast $(mcastaddr) key ", join(" ","ntp.trustedkeyslist"), " ttl 7" ) ; !mcastserver:: "broadcast" string => "# We don't broadcast" ; mcastclient:: "multicastclient" string => "multicastclient $(mcastaddr)" ; "extraoptions" string => "notrust" ; !mcastclient:: "multicastclient" string => "# We are not multicastclients" ; "extraoptions" string => "" ; reports: kill_all_ntpd:: "Either none or too many ntpd processes found, repairing" ; start_ntpd:: "Attempted to start ntpd (no process found)" ; restart_ntpd:: "Attempted to REstart ntpd (configuration file changed)" ; one_ntpd_running:: "One ntpd running" ; } body perms ntp_accessible_file { mode => "644" ; owners => { "root" } ; groups => { "root" } ; } body classes restart_ntpd { promise_repaired => { "restart_ntpd" } ; } body classes one_ntpd_running { promise_kept => { "one_ntpd_running" } ; } body process_count exactly_one_ntpd { match_range => irange("1","1") ; out_of_range_define => { "kill_all_ntpd" } ; } body process_select select_ntpd { command => "^\S*\bntpd\b.*$" ; process_result => "command" ; }
_______________________________________________ Help-cfengine mailing list Help-cfengine@cfengine.org https://cfengine.org/mailman/listinfo/help-cfengine