Exposing facter facts via mcollective YAML plugin

At my current client, we use MCollective to support the deployment of code, configuration and test data amongst a large number of potential nodes. In order to ensure that we target the correct machines to run these tasks, we rely on the following:

  • A set of values in the /etc/mcollective/facts.yaml file that are application and node specific (i.e. deployment environment)
  • An additional set of custom facts that are deployed into the /var/lib/puppet/facts directory
  • The default set of facts made available by facter

To expose these to our mcollective server, we started off using the FactsFacter plugin along with a custom fact that read the contents of the /etc/mcollective/fact.yaml file. This has worked but we have noticed that the time taken to instantiate the facts on a give node can be quite lengthy. This can impact our configuration deployment (which happens via an mcollective puppet agent) because the time required to get the facts established (as well as other issues) causes the agent call to timeout.

In order to improve the speed of facts collection by mcollective, we decided to return to using the YAML plugin – we just had to find a way to expose all of the facts that we relied upon previously into a YAML file and then make that YAML file available alongside the /etc/mcollective/facts.yaml that already existed.

Our solution is a cron job that reads all the facts from facter as well as our custom facts and writes them into a secondary yaml file in the /etc/mcollective directory.

Here is the script (facter_to_yaml.rb) that generates the yaml files on each of the nodes:

#!/usr/bin/ruby
require 'facter'
require 'yaml'
rejected_facts = ["sshdsakey", "sshrsakey"]
custom_facts_location = "/var/lib/puppet/facts"
outputfile = "/etc/mcollective/facter_generated.yaml"

Facter.search(custom_facts_location)
facts = Facter.to_hash.reject { |k,v| rejected_facts.include? k }
File.open(outputfile, "w") { |fh| fh.write(facts.to_yaml) }

We then deploy this script and use it in a cron job configured via puppet:

  file { "/usr/local/bin/facter_to_yaml.rb":
    source  => "puppet://puppet/modules/mcollective/usr/local/bin/facter_to_yaml.rb",
    owner   => root,
    group   => root,
    mode    => 0700,
  }

  cron { "factertoyaml":
    command => "/usr/local/bin/facter_to_yaml.rb",
    user    => root,
    minute  => [13, 43],
    require => File["/usr/local/bin/facter_to_yaml.rb"],
  }

Finally, we configure our mcollective server.cfg to use the newly generated file (snippet only below):

  # facts
  factsource = yaml
  plugin.yaml = /etc/mcollective/facter_generated.yaml:/etc/mcollective/facts.yaml

ISSUES

  • Something to note about the order to the YAML files listed in the plugin.yaml config option – the order matters. The second yaml file values take precedence over the first – therefore, if you are overriding any of the default facts (or any of your custom facts) in the facts.yaml file, it must be second. Not an issue in our case but something to keep in mind.
  • The other issue with this approach is if we create a new custom fact/update an existing one OR if something on a node changes that would affect one of the default fact values, it will not get updated for our mcollective configuration until the next time the cron job runs. This could cause us issues in the future and it is likely that we will also create an mcollective agent that can call the ‘facter_to_yaml.rb’ script outside of the regular cron times to provide us with the option of calling it on an as-needed basis.

ALTERNATIVES
Alternatives to our approach – if you are simply looking to expose certain facter facts to mcollective then you should consider the approach detailed on the mcollective-plugin wiki: FactsFacterYAML