Is there any way around the "no dots in hostnames" rule in CFEngine?

111 views Asked by At

With our cfengine setup I keep wanting to define classes consisting of groups of hosts, and we have lots of hosts with 4-part hostnames in which the short name is non-unique. I've repeatedly read docs that say not to use dots in hostnames, but it seems like sometimes in certain cases, it works to use xxx_yyy_domain_com, for instance:

in a roles file we define an array with a bunch of machines to be nameservers:

bundle agent tfn_roles {

vars:

# DNS servers
"dns_servers" slist => {
    "hetzner8",     # ns-frk
    "ubiquity1",    # ns-lax
    "ns_ubi3_domain_org",
    "vps001_dfw_domain_org",   # ns-dfw
    "tagadab2",     # ns-lcy
    "atlantic1",    # ns-mco
};

and then in a bind9.cf promise file:

bundle agent service_bind9 {

meta:
    "tags" slist => { "autorun" };

classes:
    "dns_servers" or => { "@(tfn_roles.dns_servers)" };

and a bunch of promises for that class seem to correctly get executed for those 2 hosts that have FQDNs with underscores.

however we have another class that is defined by only a string, in that same roles file:

# Server running the daily tasks - should only be one.
"daily_tasks_server" string => "vps007_dfw_domain_org";

and then in a daily tasks promise file:

bundle agent service_daily_tasks {

meta:
    "tags" slist => { "autorun" };

classes:
    "daily_task_server" expression => "$(tfn_roles.daily_tasks_server)";

And this doesn't seem to work.

Can someone explain why, and a way around it? do i have to say instead:

"daily_task_server" or => "$(tfn_roles.daily_tasks_server)"; 

and if so why? am i misunderstanding some fundamental cfengine syntax rule??

UPDATE: no, making the change above still doesn't work.

(btw I have already read Host group on CFEngine - please don't tell me i have to read Mr. Zamboni's book. Although I'd love to at some point, when i have time...)

1

There are 1 answers

4
Wildcard On

The error would be in your use of the "expression" attribute (also the "or" attribute) of your classes promises. You're using "$(tfn_roles.daily_tasks_server)", which evaluates to "vps007_dfw_domain_org" in your example. Then CFEngine parses that as a class expression, sees that there is no class set with the name vps007_dfw_domain_org and so evaluates the whole class expression as false. Since the expression attribute of the classes promise evaluates to false ("!any" if you look under the hood), the class "daily_task_server" is not set.

I'm not quite understanding what you're expecting to do with the "daily_task_server" class, to be honest. Classes are booleans, and you seem to be trying to stuff a string into a class. Perhaps you should be using a vars promise? But without seeing what you're ultimately trying to accomplish it's hard for me to be sure.

http://www.cfenginetutorial.org/ has recently come online; you may find it helpful in clearing up CFEngine syntax and definitions of terms. (Full disclosure: I work closely with the author. ;)


EDIT: Based on your comment that you want one value for one specific server and a different (default) value for all other servers, you would want code something like the following:

bundle agent whatever {
  vars:
    any::
      "myvar"
        string => "Default value to apply to all servers";

      "myvar"
        string => "Value only to apply to host001.mydomain.com",
        ifvarclass => strcmp( "$(sys.fqhost)", "host001.mydomain.com" );
}

It doesn't matter what type of promise you are using. I haven't used packages promises very much, but something like the following could be done, again without using classes, just ifvarclass attribute:

bundle agent handle_packages {
  packages:
      "apache"
        policy => "present",
        package_module => "yum",
        version => "2.2.22",
        ifvarclass => strcmp( "$(sys.fqhost)", "host001.mydomain.com" ),
        comment => "Only install this on host001";

      "apache"
        policy => "absent",
        package_module => "yum",
        ifvarclass => not( strcmp( "$(sys.fqhost)", "host001.mydomain.com" )),
        comment => "...Remove it everywhere else.";
}

I haven't used packages promises much as I'm still on 3.6.6 but the ifvarclass attribute can be used in any promise type.