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
[email protected]
https://cfengine.org/mailman/listinfo/help-cfengine