Take the 2-minute tour ×
Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

I don't think there is a right way to do the following (yet) in Puppet, but this strikes me as desirable.

I want my classes to be able to influence the content of a templated file from another class so that we can both avoid duplicating information, and put the info where it belongs.

For instance we have an "iptables" class, and various service classes, such as "postfix", "webappfoo" etc.

class webappfoo {
     $myfwrules +> [ "-A INPUT -p tcp --state NEW -m tcp --dport 80 -j ACCEPT" ]
     include apache
}

node webappserver {
     include webappfoo
     include iptables
}

But this does not work; the $myfwrules array only contains that line within webappfoo.

Note that a class can read into another one; for instance here iptables could just read $iptables::myfwrules; but I don't want iptables to have to know about webappfoo.

share|improve this question

4 Answers 4

This is a scoping issue. From the language tutorial:

Classes, components, and nodes introduce a new scope. Puppet is currently dynamically scoped, which means that scope hierarchies are created based on where the code is evaluated instead of where the code is defined.

So $myfwrules is scoped into the class it's defined in. In your case you're scoping it into class webappfoo, where it has no knowledge of any previously defined $myfwrules. You can bodge around this but a better approach might be to use definitions. Something like this (untested, YMMV, etc):

class iptables {
  define rule($rule) {
    # $title is the 'name' of the resource, like ntpd in service { "ntpd": }
    exec { "rule-$title":
      command => "/sbin/iptables $rule"
    }
  }
}

class webappfoo {
  include iptables
  iptables::rule { "webappfoo":
    rule => "-A INPUT -p tcp --state NEW -m tcp --dport 80 -j ACCEPT"
  }
}

class postfix {
  include iptables
  iptables::rule { "postfix":
    rule => "-A INPUT -p tcp --state NEW -m tcp --dport 25 -j ACCEPT"
  }
}

node foo {
  include webappfoo
  include postfix
}

This way you have a reusable way to add rules into your classes without needing to define variables and worry about scope. You'll end up with a bunch of Exec resources (Exec["rule-webappfoo"], Exec["rule-postfix"]) representing node foo's ruleset.

Also see the ready-baked iptables modules.

Edit: this is just an example to demonstrate how definitions might be used. It's not meant to be problem-free solution. For a start, there are issues around the order in which rules might be applied (could use before/after, perhaps), and the efficiency of calling /sbin/iptables every time.

share|improve this answer
    
Yeah but ... iptables was an example. There are other cases I'm interested in. And anyway, I don't want Puppet to run /sbin/iptables -- it's not idempotent first of all --, I want it to generate /etc/sysconfig/iptables, so that I may choose to start it, or ont. –  niXar Aug 21 '09 at 22:48
    
My answer was an example too :~) You can create files by having the define generate fragments which are assembled by another exec process. This is a bit ugly but about the only way at the moment; your array-appending dream just can't be done because the way scoping works. –  markdrayton Aug 22 '09 at 5:23
    
is it still the case in puppet 2.6.x ? Anybody has some notices about a road-map for a better alternative to the client side file concatenation ? –  AlberT Mar 24 '11 at 16:00

I decided to use templates and multiple concatenation to build iptables rules. e.g.

define iptables::rules {
  file { "$local_rules_file":
    ensure  => "file",
    content => template($iptables_start, "iptables/iptables.$name.erb", $iptables_stop)
  }
}

It's not pretty, and it requires a dedicated iptables.hostname.erb file for each computer. But it's stupid simple.

Also read up on Puppet's store configs and multiple file concatenation.

share|improve this answer
    
Yup, stored config is actually the way to go, it's just that the description in the doc was so unclear that I never understood how it worked. –  niXar Sep 9 '09 at 9:13

I haven't touched puppet for some time, so my syntax may be a bit rusty. Should "+>" be "+="? Anyway, have you tried this?

class webappfoo {
    include apache
}
class webappfoo::myfwrules {
    $webappfoo_myfwrules = [ "-A INPUT -p tcp --state NEW -m tcp --dport 80 -j ACCEPT" ]
}
node webappserver {
    include iptables

    $webapplist = ["webappfoo", "webappbar" ]
    $webappserver_myfwrules => $iptables::iptables_myfwrules

    $webappserver_myfwrules += $webapplist ? {
         webappfoo => $webappfoo::myfwrules::webappfoo_myfwrules,
         webappbar => $webappbar::myfwrules::webappfoo_myfwrules,
    }
}
share|improve this answer

This iptables module seems to be the only way to do what I want; it's basically a script which takes files in an iptables.d/ directory and builds /etc/sysconfig/iptables from it. This is probably what I'm going to use; but I feel that this kind of pattern should be possible in Puppet itself.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.