Forge Home

classifier

A Rule based Node Classifier using Hiera

90,047 downloads

10,658 latest version

2.7 quality score

We run a couple of automated
scans to help you access a
module's quality. Each module is
given a score based on how well
the author has formatted their
code and documentation and
modules are also checked for
malware using VirusTotal.

Please note, the information below
is for guidance only and neither of
these methods should be considered
an endorsement by Puppet.

Version information

  • 0.1.1 (latest)
  • 0.1.0
  • 0.0.11
  • 0.0.10
  • 0.0.9
  • 0.0.8
  • 0.0.7
  • 0.0.6
  • 0.0.5
  • 0.0.4
  • 0.0.3
  • 0.0.2
  • 0.0.1
released Jan 22nd 2020
This version is compatible with:
  • Puppet Enterprise 2023.5.x, 2023.4.x, 2023.3.x, 2023.2.x, 2023.1.x, 2023.0.x, 2021.7.x, 2021.6.x, 2021.5.x, 2021.4.x, 2021.3.x, 2021.2.x, 2021.1.x, 2021.0.x, 2019.8.x, 2019.7.x, 2019.5.x, 2019.4.x, 2019.3.x, 2019.2.x, 2019.1.x, 2019.0.x, 2018.1.x, 2017.3.x
  • Puppet >= 5.0.0

Start using this module

  • r10k or Code Manager
  • Bolt
  • Manual installation
  • Direct download

Add this module to your Puppetfile:

mod 'ripienaar-classifier', '0.1.1'
Learn more about managing modules with a Puppetfile

Add this module to your Bolt project:

bolt module add ripienaar-classifier
Learn more about using this module with an existing project

Manually install this module globally with Puppet module tool:

puppet module install ripienaar-classifier --version 0.1.1

Direct download is not typically how you would use a Puppet module to manage your infrastructure, but you may want to download the module in order to inspect the code.

Download

Documentation

ripienaar/classifier — version 0.1.1 Jan 22nd 2020

What?

An attempt to port and expand https://github.com/binford2k/puppet-classifier to pure Puppet 4 DSL.

This is a node classifier where you can write classification rules that perform boolean matches on fact data and should a rule match some classes will be included. Any number of classification rules can match on a given node and they can all contribute classes.

Different data tiers can contribute rules and it has a way to just add classes statically like the old hiera_include() approach.

NOTE: I'm using semver and as this is still less than 1.0.0 expect breaking changes to happen.

Sample Data

Sticking this in Hiera will create a classification that on RedHat VMs will include the classes centos::vm and on the node node.example.net it will also include ntp in addition.

# common.yaml
classifier::extra_classes:
 - sensu
classifier::rules:
  RedHat VMs:
    match: all
    rules:
      - fact: "%{facts.os.family}"
        operator: ==
        value: RedHat
      - fact: "%{facts.is_virtual}"
        operator: ==
        value: "true"
    data:
      redhat_vm: true
    classes:
      - centos::vm
# node.example.net.yaml
# remove sensu and install nagios instead, also add extra stuff
classifier::extra_classes:
  - --sensu
  - nagios
# clients/acme.yaml
# adjust the RedHat VMs rule to add ntp on node
# these machines but also to remove centos::vm and
# install centos::core instead
classifier::rules:
  RedHat VMs:
    data:
      client_redhat: true
    classes:
      - ntp
      - --centos::vm
      - centos::core

# add acme client team
classifier::extra_classes:
  - acme::sysadmins
node default {
  include classifier
}

The extra_classes parameter lets you specify a array of additional classes without having to construct rules and they are setup with a knockout of -- so that you can remove lower down results.

At present you can't knockout classes included by rules using a knockout prefix on extra_classes, this os something that's planned

You can have many classification rules and which ever match can contribute classes to add

Other classes can access detail about the classification result:

  • $classifier::classification - a array of Hashes with matching Rule names and classes
  • $classifier::classification_classes - just the classes extracted from the classification
  • $classifier::extra_classes - the extra classes resolved from hiera
  • $classifier::classes - the list of classes that will be included
  • $classifier::data[...] - hash of all the data created by the tiers
  • $classifier::enc_used - boolean indicating if the ENC was used
  • $classifier::enc_source - path to the data file that was matched and supplied the environment, undef when not used
  • $classifier::enc_environment - the environment the ENC set, undef when not used

Reference

The full type description of a rule is:

Hash[String,
  Struct[{
    match    => Optional[Enum["all", "any"]],
    rules    => Array[
      Struct[{
        fact     => Optional[Data],
        operator => Enum["==", "=~", ">", " =>", "<", "<=", "in", "has_ip_network", "has_mac", "has_ip_address", "has_ip_netmask"],
        value    => Data,
        invert   => Optional[Boolean]
      }]
    ],
    data     => Optional[Hash[Pattern[/\A[a-z0-9_][a-zA-Z0-9_]*\Z/], Data]],
    classes  => Optional[Array[Pattern[/\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/]]]
  }]
] $rules = {},

A number of custom types are defined for things like the list of valid operators, valid variable and class names, matches, individual rule and the whole classification. Should you wish to build additional classes that consume data from this tool please validate the input using them

  • Classifier::Classification - a single classification made up of rules, classes, data and match type
  • Classifier::Classifications - a collection of classifications
  • Classifier::Matches - the list of valid match types
  • Classifier::Rule - a single rule inside a classification
  • Classifier::Data - valid data items
  • Classifier::Operators - valid operators
  • Classifier::Node - classification result for a node

A few notes:

match

Match can be either any or all and it means that in the case where you have many rules they must either all match a node or at least one.

fact

Use Hiera interprolation to put any fact or trusted data into the rule set. Take note in hiera to interpolate data you have to quote things like this "${facts.thing}" which coherse the data into a string. In the example rule above a boolean fact is cohersed to a string in this manner and so the match value has to be "true" as well.

The fact is optional, since some times like in the case of has_ip_network for example it does not make sense since it checks a range of facts from the node.

operator

Valid operators are "==", "=~", ">", " =>", "<", "<=", "in", "has_ip_network", "has_mac", "has_ip_address", most of these comparisons are done using the versioncmp function so you should probably understand it to really grasp what these will do.

There are a special ones planned like the current has_ip_network, when using that the fact is optional, so something like:

Development Servers:
  rules:
    - operator: has_ip_network
      value: 192.168.88.0
  classes:
    - development

invert

This inverts the match so setting it true just swaps the whole comparison around, so there is no != operator for example, but you can achieve that using the == one and inverting it

data

This is an optional hash of data items kind of like facts, these are accessible in a hash calledcw $classification::data[..] after classification

Setting The Environment?

You can set the environment of a node with the help of a ENC included in bin, see ENC.md

Future Plans?

I want to expand the rules so you can use other functions to do evaluation, things like checking if a node IP belongs to a certain subnet for example, this could be done by adding functions to the classifier and them into the classifier::evaluate_rule function as operators perhaps.

At the moment there is has_ip_network, I am not really sure if this is a good fit so that's experimental while I figure it out. It might be nice to support any function call there not just ones that's hardcoded in classifier::evaluate_rule in order to make the classifier user extendible at their site using any functions they might have. If we only supported a bunch of hard coded ones I can imaginet his becoming a huge nightmare to support in the long term as users might want to add many such matchers. A more extendible approach makes more sense.

But down that road lies basically doing eval() in puppet and this is just a terrible terrible idea so I am not sure what's best. It would be very easy to write a function dispatch for any function that exists, but really it would not be a good idea.

Contact?

R.I.Pienaar / rip@devco.net / http://www.devco.net/ / @ripienaar