CFEngine: new ifelse() function
  2013-04-16

A multi-level if-else like statement.

Example reproduced below:

body common control

{
bundlesequence => { "example" };
}

###########################################################

bundle agent example

{
  classes:
      "myclass" expression => "any";
      "myclass2" expression => "any";
      "secondpass" expression => "any";
  vars:
      # result: { "1", "single string parameter", "hardclass OK", "bundle class OK", "5 parameters OK" }

      # we need to use the secondpass class because on the first pass,
      # myclass and myclass2 are not defined yet

    secondpass::
      "mylist" slist => {
                          ifelse(1),
                          ifelse("single string parameter"),
                          ifelse("cfengine", "hardclass OK", "hardclass broken"),
                          ifelse("myclass.myclass2", "bundle class OK", "bundle class broken"),
                          ifelse("this is not true", "5 parameters broken",
                                 "this is also not true", "5 parameters broken 2",
                                 "5 parameters OK"),
                        };

  reports:
      "ifelse result list: $(mylist)";
}

Output

Running full policy integrity checks
R: ifelse result list: 1
R: ifelse result list: single string parameter
R: ifelse result list: hardclass OK
R: ifelse result list: bundle class OK
R: ifelse result list: 5 parameters OK

As usual, another example: imagine we have servers over the world, and the only way to get their location is to look at the subnet they belong to, as follows:

  • 192.168.10.0/24: France
  • 192.168.50.0/24: Germany
  • 192.168.100.0/24: Spain
  • 192.168.200.0/24: England

We want to report their country, and report “unknown” when the subnet is not among the ones above.

One way to do this:

body common control
{
  bundlesequence => { "country" };
}

bundle agent country
{
  vars:
    ipv4_192_168_10::
      "country" string  =>  "france",
                policy  =>  "free";

    ipv4_192_168_50::
      "country" string  =>  "germany",
                policy  =>  "free";

    ipv4_192_168_100::
      "country" string  =>  "spain",
                policy  =>  "free";

    ipv4_192_168_200::
      "country" string  =>  "england",
                policy  =>  "free";

    !known_subnet::
      "country" string  =>  "unknown",
                policy  =>  "free";

  classes:
    "known_subnet"    or => {
                              "ipv4_192_168_10",
                              "ipv4_192_168_50",
                              "ipv4_192_168_100",
                              "ipv4_192_168_200"
                            };

  reports:
      "My location is $(country)";
}

Forcing a class, to be in Spain:

cf-agent -KIf country.cf -D ipv4_192_168_100
Running full policy integrity checks
R: My location is spain

This is where ifelse() is handy to handle multiples tests and one default case less verbosely:

body common control
{
    bundlesequence => { "country" };
}

bundle agent country
{
vars:
  "country" string  =>  ifelse("ipv4_192_168_10", "france",
                               "ipv4_192_168_50", "germany",
                               "ipv4_192_168_100", "spain",
                               "ipv4_192_168_200", "england",
                               "unknown");

reports:
  "My location is $(country)";
}

Running it…

$ cf-agent -KIf country_ifelse.cf -D ipv4_192_168_100
Running full policy integrity checks
R: My location is spain

And finally “unknown” country (depending the subnet of your computer, your output may vary):

$ cf-agent -KIf country.cf
Running full policy integrity checks
R: My location is unknown

$ cf-agent -KIf country_ifelse.cf
Running full policy integrity checks
R: My location is unknown

Links to country.cf country_ifelse.cf