How can I .. without smart variables?

Preparing for the 1.24 to 2.x upgrade I noticed smart-variables have been removed.
Extremely bummed to learn that, as it was the only somewhat nice way I could think of to get a variable that indicates which class is assigned to a host.

We use roles and profiles and lookup data in a specific layer/file based on what role has been assigned to a host.

So one of the layers in hiera is:

- name: "Puppet role"
  path: "roles/%{::puppet_role}.yaml"

Then for each role I add a smart parameter to the class that sets $role_xyz = true.
And in site.pp set the $puppet_role variable based which role_xyx is defined/true.

if $role_xxx { puppet_role = "xxx" }
elsif $role_yyy { puppet_role = "yyy" }
elsif $role_zzz etc etc

The hole thing is basically a workaround for not having the (list of) class(es) assigned to a node in a variable we can use in hiera(.yaml)… but at least it worked.

How do I do this without smart variables?

Could of course just manually add a puppet_role parameter to each and every host and set it to the role assigned. But that that get’s old fast and is error prone.
Config groups maybe? Havent really looked into it but would suffer from the same issue of having to set both.
We already use hostgroups for other purposes, with an N:N relationship between hostgroups & roles.
Thought of doing it via a fact, pull the role from the classes file. But first time around it won’t be set yet, so some manifest will get data, possibly do things, that is/are wrong on on hosts with a certain role.

Anyone have another clever idea?

Note, the roles layer in hiera is not used to provide data for the role class itself, it’s more for other classes/profiles who’s data (values) depend on what role we assign. For example, say we have some base profile that does all the generic setup, including setting some sysctl settings. If hosts with a certain role need different values for some sysctl the roles/xyz.yaml provides a place to override the value for say profile::base::sysctl_settings otherwise set in some lower layer.

Regards,
Mark.

Are you assigning the classes to the nodes in foreman?
If so then foreman passes puppet the list of assigned classes and smart parameter values in the ENC data at the top scope. You can pull that data in heira with the scope function (or interpolation) or it will be available at the top level at the time site.pp gets instantiated.

Based on what you’ve said your node might have a section in the enc data like so?

classes:
  roles::xxx:
    role_xxx: true

so in hiera you could reference that with

%{roles::xxx::role_xxx}.yaml

obviously that will just render out as true.yaml so you’d probably want to refactor your smart parameter so it had the role name as the value instead.

1 Like

Additionally if you are up for a complete redesign you should investigate CSR attributes. There is a specific attribute that can be embedded in the puppet cert for the role. These get translated into trusted facts which you could use in hiera. That way on the first run the role information will already be there. This is what we do at our facility.

1 Like

Yes.

Yes and no. Gave it a try and created a role & setup smart class parameter in foreman:

classes:
  role::xxx:
    self: World

In both site.pp and the role I do a “notify {"Hello $role::xxx:self from ...":}”
Which results in:

Notice: Hello , from site.pp
Notice: Hello World, from class.

So it gets passed to the class as expected. But that’s it, it’s not available anywhere else.

Thanks.

Ah yeah I forgot that puppet splits out the special “classes” hash from the ENC and applies it at the node scope. So this is an ordering thing.
The scope hierarchy goes like this ENC > site.pp > node. Hiera is applied at the node scope but has access to the top scope and data from the ENC classes hash overrides data from Hiera.

Try this for example in site.pp you should get the current host group for the node just to illustrate the main ENC data is there at site.pp time.

notify {"Hello $hostgroup from ...":}

also note, as a test, if you include another class in the site.pp you’ll be able to get the param value from the class. It’ll have to be a class that’s not included for the node via foreman though otherwise you’ll get a duplicate declaration error.

include role::yyy
notify {"Hello $role::yyy:self from ...":}

So the reason you can’t get the variable is because the class hasn’t been included yet at the time site.pp is parsed.

The smart params you set will be available when hiera is parsed though so you might be able to do away with the logic in site.pp and let heira select the right yaml file to include from variables set in the classes.

The only issue will be replacing what you’re doing in site.pp which is collapsing many different name-spaced variables to a single top level variable you can reference as part of the hierarchy.

We include a set of global classes from foreman so that every node has a common place we can reference. See here for a write up Info to move foreman to hiera - want to build a guide - #4 by Matt_Cahill

It’s slightly changed since that was written because smart variable went away so we now use a smart class parameter in our hiera hierarchy like this (which is why i’m confident about the fact your vars will be there in hiera)

  - name: "Foreman Hostgroup specific data"
    mapped_paths: ["foreman_enc::params::global::parent_hostgroup_paths", hostgroup_path, "21_hostgroups/%{hostgroup_path}.yaml"]

So every hostgroup gets it’s own hiera data and we also provision nodes with trusted facts via CSR attributes so you can bootstrap a node with a role

- name: "Role specific data"
    path: "20_roles/%{trusted.extensions.pp_department}/%{trusted.extensions.pp_team}/%{trusted.extensions.pp_environment}/%{trusted.extensions.pp_role}.yaml"

If i think of anything i’ll let you know but probably you’ll want to experiment with mapped_paths and the classes hash in hiera. You may not even need the parameter any more and you’ll be able to hit a yaml file just by the class name being included from foreman.

Ah unfortunately the ENC classes hash gets completely stripped out just leaving the resulting included classes and their smart parameters and the rest of the ENC data from the top scope. You might have to do something similar to us where you invent a utility class that you include via foreman everywhere, with the sole purpose of having the role name in a common name space for all nodes.

Hey… I remember reading that when researching a better way to do the hierarchical hostgroup (and location) lookup. What I ended up doing was definitely based on and inspired by your post. Thanks. :slight_smile:

Yeah. Figuring that out and realizing I had to find a way to get it - or at least something - in the parameters section is what lead me down the path to smart variables.

Is there a chance a feature request to include the classes assigned in foreman in the parameters section might be accepted? Something like:

parameters:
  foreman_classes:
  - role::xxx
  - ...

Alternatively, could something like that (injecting extra parameters) be done with a plugin?
I googled around a bit, but it’s pretty far from my area of expertise, and I have no idea where to start tbh.