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

Reply via email to