Troubleshooting Hiera from the CLI

Sometimes it’s very difficult to see how hiera works and what values it might return. To help with troubleshooting, you can use the hiera cli to do lookups yourself. You obviously need to do this on a puppet master, or a node that’s configured to have all the hiera data in the same place the masters do. Here’s the hiera.yaml configuration:

# managed by puppet
---
:backends:
  - yaml
:logger: console
:hierarchy:
  - "clientcert/%{clientcert}"
  - "puppet_role/%{puppet_role}"
  - global

:yaml:
   :datadir: /etc/puppet/environments/%{environment}/hiera

Tier 1: global

Let’s look up the ntp::servers key from the global tier. One thing to note here is that the datadir includes the variable environment, so we will need to provide that tuple to the cli, like so:

[rnelson0@puppet ~]$ hiera ntp::servers environment=production
["0.pool.ntp.org", "2.centos.pool.ntp.org", "1.rhel.pool.ntp.org"]

It is important to note that the variables MUST match how they are referred to in hiera.yaml as there is no normalization. Thus, environment=production and ::environment=product are entirely different. Ensure you match the usage in your configuration.

Tier 2: puppet_role

Next, let’s look at the classes key, which exists at the puppet_role tier. Using the same command with a different key won’t work:

[rnelson0@puppet ~]$ hiera classes environment=production
nil

Let’s turn on debug to see why:

[rnelson0@puppet ~]$ hiera classes environment=production --debug
DEBUG: Tue Nov 17 01:42:27 +0000 2015: Hiera YAML backend starting
DEBUG: Tue Nov 17 01:42:27 +0000 2015: Looking up classes in YAML backend
DEBUG: Tue Nov 17 01:42:27 +0000 2015: Looking for data source global
nil

It didn’t even look at the puppet_role tier! This is by design, because we did not provide that value. Let’s try it while specifying a puppet_role:

[rnelson0@puppet ~]$ hiera classes environment=production puppet_role=mysql
role::mysql_server

Tier 3: clientcert

Now let’s take a look at the mysql::override_options hash:

[rnelson0@puppet ~]$ hiera mysql::server::override_options environment=production puppet_role=mysql
nil

Dang, that’s apparently not found at that tier, it’s at the clientcert tier! We’ll have to add that to the arguments:

[rnelson0@puppet ~]$ hiera mysql::server::override_options environment=production puppet_role=mysql clientcert=mysql.nelson.va
{"mysqld"=>
  {"log-bin"=>"/data/mysql/replog/mysql-bin.log",
   "relay-log"=>"/data/mysql/replog/slave-relay.log",
   "server-id"=>1,
   "bind-address"=>"0.0.0.0",
   "log_bin_index"=>"/data/mysql/replog/mysql-bin.log.index",
   "datadir"=>"/data/mysql",
   "auto-increment-increment"=>2,
   "auto-increment-offset"=>1,
   "socket"=>"/data/mysql/mysql.sock",
   "relay-log-index"=>"/data/mysql/replog/slave-relay-log.index"},
 "mysqld_safe"=>{"socket"=>"/data/mysql/mysql.sock"},
 "client"=>{"socket"=>"/data/mysql/mysql.sock"}}

Simplified arguments : Providing facts to hiera

That command line is getting pretty long, though. Let’s find another way to provide the variables and values that the command expects. A quick look at the possible arguments is promising:

[rnelson0@puppet ~]$ hiera --help
Usage: hiera [options] key [default value] [variable='text'...]

The default value will be used if no value is found for the key. Scope variables
will be interpolated into %{variable} placeholders in the hierarchy and in
returned values.

    -V, --version                    Version information
    -d, --debug                      Show debugging information
    -a, --array                      Return all values as an array
    -h, --hash                       Return all values as a hash
    -c, --config CONFIG              Configuration file
    -j, --json SCOPE                 JSON format file to load scope from
    -y, --yaml SCOPE                 YAML format file to load scope from
    -m, --mcollective IDENTITY       Use facts from a node (via mcollective) as scope
    -i, --inventory_service IDENTITY Use facts from a node (via Puppet's inventory service) as scope

We can load scope from a file! We need to create one first, though. Thankfully, facter has what we need:

[rnelson0@mysql ~]$ facter --help
facter(8) -- Gather system information
======

SYNOPSIS
--------

Collect and display facts about the system.

USAGE
-----

    facter [-h|--help] [-t|--timing] [-d|--debug] [-p|--puppet] [-v|--version]
      [-y|--yaml] [-j|--json] [--plaintext] [--external-dir DIR] [--no-external-dir]
      [fact] [fact] [...]

Run facter with the puppet and yaml|json flags and save it in a file:

[rnelson0@mysql ~]$ sudo facter -py > mysql.yaml
[rnelson0@mysql ~]$ grep role mysql.yaml
  puppet_role: mysql

Transfer the file to your puppet master. Before you use it, add any variables you need, like environment or clientcert, that aren’t part of the facter output but are important to your hierarchy:

[rnelson0@puppet ~]$ scp mysql:mysql.yaml ./

This server is intended for use by the Nelson family. All other use is unauthorized.

rnelson0@mysql's password:
mysql.yaml                                                                  100% 4808     4.7KB/s   00:00
[rnelson0@puppet ~]$ cat >>mysql.yaml
  environment: production
  clientcert: mysql.nelson.va

Remember that not all variables in your hierarchy, like clientcert, are considered facts. In this case, the configuration directive clientcert remains the same over time whereas the similar fact fqdn can vary. Though we generated the list using facter, we can add whatever we want to it.

You can now make a much simpler call to hiera that only varies by the key you’re looking up:

[rnelson0@puppet ~]$ hiera -y mysql.yaml ntp::servers
["0.pool.ntp.org", "2.centos.pool.ntp.org", "1.rhel.pool.ntp.org"]
[rnelson0@puppet ~]$ hiera -y mysql.yaml classes
role::mysql_server
[rnelson0@puppet ~]$ hiera -y mysql.yaml mysql::server::override_options
{"client"=>{"socket"=>"/data/mysql/mysql.sock"},
 "mysqld"=>
  {"bind-address"=>"0.0.0.0",
   "log_bin_index"=>"/data/mysql/replog/mysql-bin.log.index",
   "relay-log"=>"/data/mysql/replog/slave-relay.log",
   "server-id"=>1,
   "datadir"=>"/data/mysql",
   "auto-increment-increment"=>2,
   "socket"=>"/data/mysql/mysql.sock",
   "log-bin"=>"/data/mysql/replog/mysql-bin.log",
   "relay-log-index"=>"/data/mysql/replog/slave-relay-log.index",
   "auto-increment-offset"=>1},
 "mysqld_safe"=>{"socket"=>"/data/mysql/mysql.sock"}}

Summary

You now have a good idea of how to use hiera at the cli and how to do repeated lookups without excessive typing and with the actual facts from a node. Enjoy!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s