Specifying prerequisites for Puppet custom facts

configuration-managementpuppet

I have written a custom Puppet fact that requires the biosdevname tool to be installed. I'm not sure how to set things up correctly such that this tool will be installed before facter tries to instantiate the custom fact.

Facts are loaded early on in the process, so I can't simply put a package { biosdevname: ensure => installed } in the manifest, since by the time Puppet gets this far the custom fact has already failed.

I was curious if I could resolve this through Puppet's run stages. I tried:

stage { pre: before => Stage[main] }
class { biosdevname: stage => pre }

And:

class biosdevname {
  package { biosdevname: ensure => installed }
}

But this doesn't work…Puppet loads facts before entering the pre stage:

info: Loading facts in physical_network_config
./physical_network_config.rb:33: command not found: biosdevname -i eth0
info: Applying configuration version '1320248045'
notice: /Stage[pre]/Biosdevname/Package[biosdevname]/ensure: created

Etc. Is there any way to make this work?

EDIT: I should make it clear that I understand, given a suitable package declaration, that the fact will run correctly on subsequent runs. The difficulty here is that this is part of our initial configuration process. We're running Puppet out of kickstart and want the network configuration to be in place before the first reboot.

It sounds like the only workable solution is to simply run Puppet twice during the initial system configuration, which will ensure that the necessary packages are in place.

Also, for Zoredache:

# This produces a fact called physical_network_config that describes
# the number of NICs available on the motherboard, on PCI bus 1, and on
# PCI bus 2.  The fact value is of the form <x>-<y>-<z>, where <x>
# is the number of embedded interfaces, <y> is the number of interfaces
# on PCI bus 1, and <z> is the number of interfaces on PCI bus 2.

em = 0
pci1 = 0
pci2 = 0

Dir['/sys/class/net/*'].each {
    |file| devname=File.basename(file)
    biosname=%x[biosdevname -i #{devname}]
    case 
    when biosname.match('^pci1')
        pci1 += 1
    when biosname.match('^pci2')
        pci2 += 1
    when biosname.match('^em[0-9]')
        em += 1
    end
}

Facter.add(:physical_network_config) do
    setcode do
        "#{em}-#{pci1}-#{pci2}"
    end
end

Best Answer

As far as I know, you can't. Either let it fail, or detect and fail gracefully. I have a number of plugins that only work on Debian that fail on Red Hat without consequence.

Also, please note that it is IMPOSSIBLE to have a fact evaluated AFTER some configuration went in. The architecture just doesn't support it:

Client                               Server
Compute facts
Ask for catalog passing facts =>     Receive Catalog request
                                     Compute catalog using facts
                              <=     Return Catalog
Based on the dependency tree,
  For each configuration with satisfied dependencies
    Apply configuration
    Mark (or not) dependency as satisfied
Send report, if configured so

So, you see, the configuration is only applied long, long after the facts were processed, and there's no way of going back. What may happen is that the next run will now be able to generate that fact.

See also the trick used by the Common module to handle the lack of lsbrelease on Debian without producing fatal errors.