Salt Grains are really just descriptions of various pieces of the operating systems' information on the minion.
As SaltStack documentation states, they basically provide you with "grains of information". This allows you to grab information about the minion such as CPU information, underlying hardware (provided to you by dmidecode), network interfaces, and operating system revisions.
Each minion comes with pre-defined grains that are generated upon minion
setup. You can assign custom grains and keep them in /etc/salt/grains
or
your minion config file. There are also existing auto_sync
reactors that
you can use to sync custom grains from a pillar (key-value store) to
minions upon authentication or minion startup.
Besides using minion ID for minion targeting (which in itself isn't a bad idea in secure environments), you can use these grains to target hosts in your salt CLI commands, states, and pillars. This empowers the SaltStack engine to create configuration templates for various environments on your network and to only affect those that you want. It's also great if you're shoehorning SaltStack into an existing, highly segmented environment.
You can view existing grains for a minion with the following command:
$ salt 'webserver01' grains.items
$ salt 'webserver01' grains.get ipv4
With that in mind, you can specify a grain and its value and execute an execution module against a set of minions with that matching grain.
Here we will install a package called nmap-ncat
on all systems that are
from the Red Hat family.
$ salt -G 'os_family:RedHat' pkg.install nmap-ncat
With -P
flag, you can use PCRE (perl-compatible regex) on grains as
well. This is described further in SaltStack's Compound Matchers
documentation
(a highly recommended read in itself):
$ salt -P 'os_family:(RedHat|Debian)' pkg.install htop
You can utilize grains in pillars and states using the following Jinja templating syntax. Jinja is a templating engine that is default in Salt and various web frameworks (such as Flask). It uses python-like syntax but is a bit more limited in terms of conditional tests that it can perform (unless you write your own filters).
{% if grains['os_family'] == 'RedHat' %}
- key: value
{% endif %}
{% if salt['grains.get']('ipv4').startswith('RedHat') %}
- key: value
{% endif %}
Grains are parsed into salt as a dictionary of values. So in python-speak, a grain will look like this:
grain = {'os_family': 'RedHat'}
Multiple grain values will be stored as a list inside of a dict. In this case, it'll look like this:
grain = {'ipv4': ['192.168.1.10', '192.168.1.11', '192.168.1.12']}
So now comes a question, what if you want to execute some state against
a minion which may have a list (of IP addresses) for its ipv4
grain?
Checking the equality with ==
(equal) sign will not work since the
grain contains a list. Jinja renderer will start throwing errors. You
can't use .startswith()
or .endswith()
string methods either.
Well, you could check if a particular string is in a grain:
{% if '192.168.1' in salt['grains.get']('ipv4') %}
- key: value
{% endif %}
This should parse through the entire lists in ipv4
grain. You could
also match on the first items in the ipv4
grain if you choose so by
adding [0]
at the end of the salt['grains.get']()
method:
{% if '192.168.1' in salt['grains.get']('ipv4')[0] %}
- key: value
{% endif %}
Or you could loop through each item in the ipv4
grain. Beware that
this might cause problems since state/pillar IDs must be unique. You
might need to affix a suffix at the end of the key to keep it unique:
{% for ip_addr in salt['grains.get']('ipv4') %}
- key_{{ ip_addr }}: value
{% endfor %}
Iterating through lists of items in singular Salt grains does pose a challenge and each use case may have different requirements. Due to limits in Jinja, you may have to come up with a hackish solution that is hopefully pythonic and doesn't introduce race conditions when deploying states/pillars.
As you can see, Salt grains are fun! They're not just boring static blobs of information. They can really bring your SaltStack environment alive and you should feel encouraged to use them wherever you can.
Comments
comments powered by Disqus