How to create puppet external structured facts from script, such as python or bash?

494 Views Asked by At

I have seen the documentation, especially External Facts and Custom Facts.

I have the following yum_repos.rb:

require 'facter'
Facter.add('yum_repos') do
    setcode '/usr/bin/python3 /opt/puppetlabs/puppet/cache/lib/facter/yum_repos.py'
end

I have the following yum_repos.py:

#!/usr/bin/python3
import configparser
import os
import json

result_dict={}
yum_repos_dir = '/etc/yum.repos.d'
for yum_repo_file in os.listdir(yum_repos_dir):
    if not yum_repo_file.endswith(".repo"):
        continue
    yum_repo_name=yum_repo_file.replace(".repo","")
    config = configparser.ConfigParser()
    config.read(os.path.join(yum_repos_dir, yum_repo_file))
    result_dict[yum_repo_name] = {}
    for section in config.sections():
        result_dict[yum_repo_name][section] = dict(config[section])
print(json.dumps(result_dict))

When I check facter, it all runs, but the yum_repos fact is a string. Why isn't it structured? I found somewhere on the internet, saying (for old versions of puppet) that stringify_facts must be set to false, so I tried that, but the behavior did not change. I believe that the default was changed in 4.0 to not stringify.

I am using puppet 6.24.0

I try to access the facts in a puppet class like this:

if $facts['yum_repos']['redhat']...['enabled'] != 1 {
    ...
}

And when I run puppet agent, I get the error message:

Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: A substring operation does not accept a String as a character index. Expected an Integer
1

There are 1 best solutions below

3
Edward Ned Harvey On

I never figured out why puppet is stringifying the fact, but I was able to workaround by explicitly calling parsejson(), like so:

if parsejson($facts['yum_repos'])['redhat']...['enabled'] != "1" {
    ...
}