When “bulk” is just too much…

Managing software packages with cfengine is, normally, no problem: you have "packages" promises, and you have pre-canned bodies for a number of package managers, plus the ubiquitous "generic" that should help you on almost all systems where cfengine can run. However, I hit one of those corner cases where you have to build your own package_method body — although stealing almost everything from one in the standard library 😉 …You'll find the code of the whole bundle at the end of this post. For now, suffice it to say that problems arose when I used a package promise to verify if three packages were installed or not, and define classes accordingly. Here is the original promise:

      "$(puppetpkgs)"
	  package_method => apt,
	  package_policy => "verify",
	  classes        => if_else(
				     "$(pkgclass[$(puppetpkgs)])_installed",
				     "$(pkgclass[$(puppetpkgs)])_not_installed"
				   ) ;

puppetpkgs and pkgclass[] are defined as follows (in a "vars" promise, of course):

      "puppetpkgs" slist  => {
			       "puppet",
			       "puppetmaster",
			       "puppet-common"
			     } ;
      "pkgclass[$(puppetpkgs)]" string => canonify("$(puppetpkgs)") ;

and if_else is a "classes" body in the standard library: if a promise is kept/repaired, it will define the class given as the first argument, otherwise it will define the second. For example, in the case of the puppet-common package, it will define puppet_common_installed if the package is installed, and puppet_common_not_installed otherwise.

In the first tests I did on a Debian Linux "Squeeze", everything worked as expected, with a class defined for each package. When, recently, I tested this bundle on a "Lenny", only the class related to puppet-common was properly defined, with the classes for the other two packages missing.

At first, and after some runs of cf-agent with the "-v" option on, I thought the culprit was aptitude. So, for starters, I borrowed the body for the apt package method, renamed it aptget, and narrowed it down to use apt-get and dpkg. That didn't help, but I could at least understand what was going wrong. For some reason, cfengine was mistaken by the output of aptitude/dpkg: it was trying to verify all three packages in one fell swoop, but it set results for last package in the list only. Uhm…

Then I got back to the body source again, and I noticed this line:

package_changes => "bulk";

That reminded me something… uhm… package changes… bulk? Let's see the reference:

‘package_changes’
Type: (menu option)

Allowed input range:
individual
bulk

Synopsis: Menu option – whether to group packages into a single aggregate command

Notes:

This indicate whether the package manager is capable of handling package operations in bulk, i.e. with by given multiple arguments. If this is set to ‘bulk’ then multiple arguments will be passed to the package commands. If set to ‘individual’ packages will be handled one by one. This might add a significant overhead to the operations, and also affect the ability of the operating system's package manager to handle dependencies.

Yes, that definitely rang a bell, a good one. So I changed my aptget package_method and set package_changes to "individual", and that worked! The three package were now verified one by one, and one class for each of them was correctly set.

Now, you may ask yourself: what has cfengine to do with puppet? Well, disable it! What else??? 🙂

You can find the code of the bundle here below; the code of the aptget body is not included since it's quite easy to make it by yourself: get the apt body from the standard library, remove all the aptitude-related section, and set package_changes to individual — and that's done. I'll leave it to you as an exercise :p

Enjoy, and I'll see you next post… erm… time!

bundle agent puppet
{
  vars:
      "puppetlock" string => "/var/lib/puppet/state/puppetdlock" ;
      "puppetcron" string => "/var/spool/cron/crontabs/root" ;
      "puppetpkgs" slist  => {
			       "puppet",
			       "puppetmaster",
			       "puppet-common"
			     } ;

      "pkgclass[$(puppetpkgs)]" string => canonify("$(puppetpkgs)") ;

  files:
    puppet_installed.!kill_puppet::
      "$(puppetlock)"
	  create  => "yes",
	  comment => "Disable puppet, so that it doesn't step on cfengine's feet" ;

      "$(puppetcron)"
          edit_line  => disable_puppetd_cronjob,
	  comment    => "prevent cron from trying to keep puppet alive" ;

    kill_puppet::
      "$(puppetcron)"
	  edit_line => remove_puppet_cronjobs,
	  comment   => "cleanup puppet stuff from root's crontab" ;
      

  packages:
      "$(puppetpkgs)"
	  package_method => aptget,
	  package_policy => "verify",
	  classes        => if_else(
				     "$(pkgclass[$(puppetpkgs)])_installed",
				     "$(pkgclass[$(puppetpkgs)])_not_installed"
				   ) ;

    kill_puppet::
      "$(puppetpkgs)"
	  package_method => aptget,
	  package_policy => "delete",
	  ifvarclass     => "$(pkgclass[$(puppetpkgs)])_installed",
	  classes        => if_else(
				     "$(pkgclass[$(puppetpkgs)])_killed",
				     "$(pkgclass[$(puppetpkgs)])_not_killed"
				   ) ;
      

  reports:
    inform_mode|verbose_mode::
      "$(puppetpkgs) is installed"
	  ifvarclass => "$(pkgclass[$(puppetpkgs)])_installed" ;

      "$(puppetpkgs) is NOT installed"
	  ifvarclass => "$(pkgclass[$(puppetpkgs)])_not_installed" ;

      "$(puppetpkgs) killed"
	  ifvarclass => "$(pkgclass[$(puppetpkgs)])_killed" ;

      "$(puppetpkgs) NOT killed"
	  ifvarclass => "$(pkgclass[$(puppetpkgs)])_not_killed" ;
}


bundle edit_line disable_puppetd_cronjob
{
  delete_lines:
      "# Puppet Name: cron_puppetds*" ;
      ".+s+/etc/init.d/puppets+starts+.+" ;
}


bundle edit_line remove_puppet_cronjobs
{
  delete_lines:
      "# HEADER: .*" ;
      "# Puppet Name: .*" ;
      ".+s+/etc/init.d/puppet.*" ;
}
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.