Managing Network Time Protocol, Part 1 of 4

This is the first in a series of tutorials that will take you through a detailed example of how to manage the Network Time Protocol (NTP) with CFEngine. We will begin with a very basic policy and gradually extend the policy for each tutorial.

In this first tutorial, we will write a simple policy to ensure that the latest version of the NTP service is installed on your system.

Once the NTP service is installed, the second tutorial will explain how to extend the policy to execute the service. In the third and fourth tutorials, we will expand this further to show how you can apply policies to enforce time server configuration via file and template management.

If you’re ready, let’s go ahead and get started!

1. Ensuring the NTP package is installed

You may recall a previous how-to tutorial had provided a sample policy for ensuring that the latest version of software packages are installed on your system. It used the package_latest() bundle, a simple and convenient method from the CFEngine 3.6 standard library. We could use this same bundle to install the NTP package on the system, but in this tutorial, let’s use the package promise to illustrate how this works.

body common control
{
      inputs => { "$(sys.libdir)/stdlib.cf" };
      bundlesequence => { "manage_ntp" };
}

bundle agent manage_ntp
{
   vars:
       "ntp_package_name" string => "ntp";

   packages:
       "$(ntp_package_name)"   -> { "StandardsDoc 3.2.1" } 
       package_policy          => "add",
       package_method          => yum,
       handle                  => "manage_ntp_packages_$(ntp_package_name)",
       classes                 => if_ok("ntp_installed");
}

What does this code do?

Let’s walk through the different sections of the code do see how it works.

body common control

body common control
{
      inputs => { "$(sys.libdir)/stdlib.cf" };
      bundlesequence => { "manage_ntp" };
}

The policy includes a control section that contains general information about what to execute. In this example, we are instructing the agent to execute a bundle called manage_ntp, which contains the actual policy for managing the NTP package install.

bundle agent manage_ntp

You can think of bundles as a collection of desired states. You can have as many bundles as you would like, and also reference them from within themselves. In fact, they are very much like regular function calls in other programming languages. Let’s dive deeper into the code in the manage_ntp bundle.

vars:
    "ntp_package_name" string => "ntp";

A new variable with the name ntp_package_name has been declared and it is assigned a value "ntp". This string variable will be referenced in the other sections of the bundle.

packages:
   "$(ntp_package_name)"   -> { "StandardsDoc 3.2.1" }
   package_policy          => "add",
   package_method          => yum,
   handle                  => "manage_ntp_packages_$(ntp_package_name)",
   classes                 => if_ok("ntp_installed");

packages is a promise type that ensures the presence or absence of a package on a system.

"$(ntp_package_name)"   -> { "StandardsDoc 3.2.1" }

You’ll notice that the ntp_package_name variable is referenced here, which evaluates to ntp as the promiser. You can also associate a stakeholder to this promiser. The stakeholder association is optional, but is particularly useful in when you wish to provide some structure in your policy to tie it to a business rule. In this example, what we are stating is this – “Make sure NTP is installed as it is described in StandardsDoc 3.2.1”.

This promiser has a number of additional attributes defined:

  1. package_policy
    package_policy    => "add",
    

    The package_policy attribute describes what you want to do the package. In this case you want to ensure that it is present on the system. Other valid values of this attribute include delete, update, patch, reinstall, addupdate, and verify. Because of the self-healing capabilities of CFEngine, the agents will continuously check to make sure the package is installed. If it is not installed, CFEngine will try to install it according to its policy.

  2. package_method
    package_method    => yum,
    

    The package_method attribute describes the package manager to be used. This first example assumes a CentOS environment, hence we specify yum as the package manager. If you are using a Debian/Ubuntu environment, you would specify the package manager as apt.

  3. promise handle
    handle  => "manage_ntp_packages_$(ntp_package_name)",
    

    The handle uniquely identifies a promise within a policy. A recommended naming scheme for the handle is bundle_name_promise_type_class_restriction_promiser. It is often used for documentation and compliance purposes. As shown in this example, you can easily substitute values of variables for the handle.

  4. classes
    classes    => if_ok("ntp_installed");
    

    classes provide context which can help drive the logic in your policies. In this example, the class ntp_installed will only be defined if the package promise executed successfully (e.g. if_ok()).

We’ve discussed the example above in great length, but let’s add a small twist here. Let’s improve this policy to handle the NTP package in both CentOS and Debian environments. On Debian environments, the package manager is apt rather than yum. Specifying the package_method as generic instructs CFEngine to choose the default package manager for the operating system environment. The resulting policy would be as follows:

body common control
{
      inputs => { "$(sys.libdir)/stdlib.cf" };
      bundlesequence => { "manage_ntp" };
}

bundle agent manage_ntp
{
   vars:
       "ntp_package_name" string => "ntp";

   packages:
       "$(ntp_package_name)"   -> { "StandardsDoc 3.2.1" }
       package_policy          => "add",
       package_method          => generic,
       handle                  => "manage_ntp_packages_$(ntp_package_name)",
       classes                 => if_ok("ntp_installed");
}

Note: In a previous example, we used a bundle package_latest() to ensure that the latest version of a package is installed. This bundle was introduced in CFEngine 3.6 and it encapsulates the package promise, along with the checking of classes, to simplify policy writing. You may, however, still want to use the package promise in order to provide the most flexibility in your policies.

Try it out for yourself:

  1. Use your favorite text editor to create a new policy file called ensure_ntp_part_1.cf. Since the example above illustrates package management in a Debian or CentOS environment, make sure you are using one of these systems.
    $ nano /tmp/ensure_ntp_part_1.cf
    

    Copy and paste the example code above and save the file.

  2. Validate that your policy has no syntax errors.
    $ cf-promises -f /tmp/ensure_ntp_part_1.cf 
    

    If the code has no syntax error, you shall see no output.

  3. Execute the standalone policy and review the output to ensure that the policy executed successfully. Upon a successful run you should expect to see an output similar to this:
    $ cf-agent -fK /tmp/ensure_ntp_part_1.cf
    2014-08-06T23:17:53+0000     info: /default/manage_ntp/packages: Installing ntp... 
    

Mission accomplished!

Now that we have installed the package, let’s move on to:

where we will extend the policy to ensure that the NTP service is running.






Please help us improve:

7 0

Do you have ideas / feedback to share with us? Send feedback