Carousel is a lie!

Cfengine 3 Syntax Part II
24th January 2012

Mark Burgess was kind enough to respond to my earlier post about Cfengine syntax:

markburgess_osl: @saintaardvark (soothing) Syntax is definitely an acquired taste (re perl ;)). The list-ref prob can go away soon. Think doc not code 4 cf3

And then, via tweetsification, we were all like:

saintaardvark: .@markburgess_osl Heh, thanks for the reply -- I was going to ask you about this. Fair pt re: syntax being an acquired taste...[1/2]

saintaardvark: .@markburgess_osl ...but any chance the mess of brackets will be reduced? [2/2]

markburgess_osl: @saintaardvark trade one set of () for -> Don't see much point in that. $() has long precedence in sh / make etc. It delimits clearly in txt

saintaardvark: .@markburgess_osl Fair enough, but I'm also thinking of eg "$(services.cfg_file[$(service)])": dollar bracket scopedot square dollar bracket

markburgess_osl: @saintaardvark I agree it's clumsy, but it's also an edge case. You rarely write this if you make good use of patterns. Perl also ugly here.

But this layout came from their own dang documentation! I feel like I'm stuck here:

That last point: what I mean is that

Tags: cfengine.
Pointing LWP::UserAgent (and WWW::Mechanize) at a self-signed certificate
24th January 2012

If you're using LWP::UserAgent -- and if you're using WWW::Mechanize, you're using it -- you can point it at a self-signed SSL certificate like so:

HTTPS_CA_FILE=/path/to/cacert.pem <script file>
Tags: perl, toptip.
Cfengine 3 syntax
23rd January 2012

Cfengine 3 has a lot of things going for it. But its syntax is not one of them.

Consider this situation: you have CentOS machines, SuSE machines and Solaris machines. All of them should run, say, SSH, NTP and Apache why not? The files are slightly different between them, and so is the method of starting/stopping/enabling services, but mostly we're doing the same thing.

I've got a bundle in Cfengine that looks like this:

bundle common services {
  vars:
    redhat|centos::
      "cfg_file_prefix"     string => "centos/5";

      "cfg_file[httpd]"     string => "/etc/httpd/conf/httpd.conf";
      "daemon[httpd]"       string => "httpd";
      "start[httpd]"        string => "/sbin/service httpd start";
      "enable[httpd]"       string => "/sbin/chkconfig httpd on";

      "cfg_file[ssh]"       string => "/etc/ssh/sshd_config";
      "daemon[ssh]"         string => "sshd";
      "start[ssh]"          string => "/sbin/service sshd restart";
      "enable[ssh]"         string => "/sbin/chkconfig sshd on";

...and so on. We're basically setting up four hashes -- daemon, start, enable and cfg -- and populating them with the appropriate

entries for Red Hat/Centos ssh and Apache configs; you can imagine slightly different entries for Solaris and SuSE. The cfg_file_prefix allows me to put CentOS' config files in a separate directory from other OS.

Then there's this bundle:

bundle agent fix_service(service) {
  files:
    "$(services.cfg_file[$(service)])"
      copy_from     => secure_cp("$(g.masterfiles)/$(services.cfg_file_prefix)/$(services.cfg_file[$(service)])", "$(g.masterserver)"),
      classes       => if_repaired("$(service)_restart"),
      comment       => "Copy a stock configuration file template from repository";

  processes:
    "$(services.daemon[$(service)])"
      comment       => "Check that the server process is running, and start if necessary",
      restart_class => canonify("$(service)_restart"),
      ifvarclass    => canonify("$(services.daemon[$(service)])");

  commands:
    "$(services.start[$(service)])"
      comment       => "Method for starting this service",
      ifvarclass    => canonify("$(service)_restart");

    "$(services.enable[$(service)])"
      comment       => "Method for enabling this service",
      ifvarclass    => canonify("$(service)_restart");
}

This bundle takes a service name as an argument, and assigns it to the local variable "service". It copies the OS-and-service-appropriate config file into place if it needs to, and enables/starts the service if it needs to. How does it know if it needs to? By setting the class "$(service)_restart" if the service isn't running, or if the config file had to be copied.

So far, so good. Well, except for the mess of brackets. All those hashes are in the services bundle, so you need to be explicit about the scope. (There are provisions for global variables, but I've kept my use of 'em to a minimum.) And so what in Perl would be, say:

$services->start{$service}

becomes

"$(services.start[$(service)])"

Square brackets for the hash, round brackets for the string (and to indicate that you're using a variable -- IOW, it's "$(variable)", not "$variable" like you're used to), and dots to indicate scope ("services.start" == the start variable in the services bundle).

It's...well, it's an ugly mess o' brackets. But I can deal with that. And this arrangement/pattern, which came from the Cfengine documentation itself, has been pretty helpful to me for dealing with single config file services.

But what about the case where a service has more than one config file? Like autofs: you gotta copy around a map file but in SuSE you also need /etc/sysconfig/autofs to set the LDAP variables.

Again, in Perl this would be an anonymous array on top of a hash -- something like:

$services->cfg_file{"autofs"}[0] = "/etc/auto.master
$services->cfg_file{"autofs"}[1] = "/etc/sysconfig/aufofs"

and you'd walk it like so:

foreach my $i in ($services->cfg_file{"autofs"}) { # something with $i }

or even:

while ($services->cfg_file{"autofs"}) { # something with $_ }

(I think...I'm embarrassed sometimes at how rusty my Perl is.)

In Cfengine, you pile an anonymous array on top of a has like so:

  "cfg_file[autofs]" slist => { "/etc/auto.master", "/etc/sysconfig/autofs" };

An slist is a list of strings. All right, fine; different layout, same idea, stick it in the services bundle and away we go. But: remote scalars can be referenced; remote lists cannot without gymnastics. From the docs:

During list expansion, only local lists can be expanded, thus global list references have to be mapped into a local context if you want to use them for iteration. Instead of doing this in some arbitrary way, with possibility of name collisions, cfengine asks you to make this explicit. There are two possible approaches.

The first of those two approaches is, I think, passing the list as a parameter, whereupon it just works? maybe? (It's a not-so-minor nitpick that there are lots of examples in the Cf3 handbook that are not explained and don't make much sense. They apparently work, but how is not at all clear, or discernible.) I think it's meant to be like Perl's let's-flatten-everything-into-a-list approach to passing variables.

The second is to just go ahead and redeclare the remote slist (array) as a local one that's set to the remote value. Again, from the docs:

bundle common va {
  vars:
   "tmpdirs"  slist => { "/tmp", "/var/tmp", "/usr/tmp"  };
}

bundle agent hardening {
  classes:
    "ok" expression => "any";

  vars:
   "other"    slist => { "/tmp", "/var/tmp" };
   "x"        slist => { @(va.tmpdirs) };

  reports:
    ok::
      "Do $(x)";
      "Other: $(other)";
}

which makes this prelude to all of that handwaving even more irritating:

Instead of doing this in some arbitrary way, with possibility of name collisions...

...

...I mean...

...I mean, what is the point of requiring explicit paths to variables in other scopes if you're just going to insert random speedbumps to assauge needless worries about name collisions? What the hell is with this let's-redeclare-it-AGAIN approach?

The rage, it fills me.

Did you just tell me to go fuck myself?

Tags: cfengine, didyoujusttellmetogofuckmyself.
PPD changes in Oneiric
20th January 2012

In Cfengine3, I had been setting up printers for people using lpadmin commands. Among other things, it used a particular PPD file for the local HP printer. It turns out that in Oneiric, those files are no longer present, or even available; judging by what I found on my laptop, the PPD file is (I think) generated automagically by /usr/share/cups/ppd-updaters/hplip-cups.

It's possible that I could figure this out for my new workstation. But right now, I don't think I can be bothered. I'm going to just set this up by hand, and hope that either I'll get a print server or I'll figure it out.

Tags: cfengine, ubuntu.
Well, that didn't take long
19th January 2012

Megaupload is back.

Tags: politics.
Cfengine 3 and SELinux
19th January 2012
Tags: cfengine, selinux.
Introducing Thornhill
19th January 2012

At $WORK I'm moving a web server to a new machine. Natch, I'm getting the new one ready, testing as I go, and when things are good I'll change DNS to point to the new machine. I found myself testing a lot of Apache Alias directives -- we've accumulated rather a lot -- my usual routine:

...was getting damned tiresome. Perl to the rescue! WWW::Mechanize, Test::More and Apache::Admin::Config are damned useful, and when the authors weren't looking I bent them to my will.

So: thornhill, a Small but Useful(tm) script to check URLs mentioned in Apache config files. Here's what it does:

We'll call this release 0.1 ("works for me!"). You can download it here; be sure to note the bugs and assumptions.

Share and enjoy!

Tags: software.
Cfengine 3 error: Redefinition of body "control" for "common" is a broken promise, near token '{'
19th January 2012

I tripped across this error today with Cfengine 3:

cf3:./inputs/promises.cf:1,22: Redefinition of body "control" for "common" is a broken promise, near token '{'

The weird thing was this was a stripped down promises.cf, and I could not figure out why it was complaining about redefinitions. I finally found the error:

body common control {
bundlesequence => { "test" };
inputs => { "promises.cf", "cfengine_stdlib.cf" };
}

Yep, including the promises.cf file itself in the inputs section borked everything; removing it fixed things right away.

Tags: cfengine.
Blacked out for SOPA
18th January 2012

This site has gone dark today in protest of the U.S. Stop Online Piracy Act (SOPA) and PROTECT-IP Act (PIPA). The U.S. Congress is about to censor the Internet, even though the vast majority of Americans are opposed. We need to kill these bills to protect our rights to free speech, privacy, and prosperity. Learn more at AmericanCensorship.org!

Tags: politics.
HP printing won't work with SNMP public community disabled
18th January 2012

I think I just tripped across this bug, though on Ubuntu rather than Red Hat: disabling the public SNMP community on an HP printer means that CUPS will no longer print to it. The CUPS error_log shows this:

prnt/backend/hp.c 745: ERROR: open device failed stat=12: hp:/net/HP_Color_LaserJet_4700?ip=1.2.3.4

Re-enabling the public community got CUPS working again, but made Baby Tom Limoncelli cry.

No tags

RSS feed

Created by Chronicle v4.4