SaltStack-blog--360x169

in Development

Saltstack: Creating a State Template using Grains

 

In this quick tutorial, I will show you how to create a Saltstack State template using Grains.  It will require basic Saltstack knowledge, and an understanding of Saltstack States.  To illustrate these concepts, I’ll be configuring the Nagios Remote Plugin Executor (NRPE)  service on a minion.  The point of this tutorial is to show you how to apply these concepts to Saltstack State files of any service you might run on a minion, such as Apache, HAProxy, or Nginx, to name a few.

 

First let’s go over why we would want to create a template in the first place.  When configuring servers, some config files require certain information about the server itself. This could perhaps be an IP Address, or the operating system of the minion, or even how many CPUs a minion has.  The following NRPE config file requires the minion’s server_address.  

 

pid_file=/var/run/nrpe.pid
server_port=5666
# insert IP here
server_address=192.168.2.100 ← (this is the value we are going to templatize)
nrpe_user=nagios
nrpe_group=nagios
allowed_hosts=192.168.2.0/24
dont_blame_nrpe=0
debug=0
command_timeout=60

# System Resources
command[check_users]=/usr/lib64/nagios/plugins/check_users -w 5 -c 10
command[check_load]=/usr/lib64/nagios/plugins/check_load -w 20,15,10 -c 35,30,25
command[check_zombie_procs]=/usr/lib64/nagios/plugins/check_procs -w 10 -c 20 -s Z
command[check_total_procs]=/usr/lib64/nagios/plugins/check_procs -w 400 -c 600

# Nagios
# Disk Partitions
command[check_root]=/usr/lib64/nagios/plugins/check_disk -w 20% -c 10% -p /dev/vda1
command[check_swap]=/usr/lib64/nagios/plugins/check_swap -w 90 -c 85

 

We want a template that will make sure that whenever our State is run against a minion, the NRPE config file will have the correct server_address of the minion.  This is where Grains come into play.  A Grain is a piece of information specific to the minion.  A State is able to query  the Grains interface for any piece of info it wants about the minion.  We will tell our State to build the NRPE config file using these Grains.  Before we get to the State, let’s show off some Grains commands so you can get an idea of the type of info we’re able to query.

 

To get a list of available Grains from your minions:

 

salt '*' grains.ls

 

Output should look similar to this. Results may vary based on specifics of how your Salt minions are setup:

 

minion1:
    - SSDs
    - biosreleasedate
    - biosversion
    - cpu_flags
    - cpu_model
    - cpuarch
    - domain
    - fqdn
    - fqdn_ip4
    - fqdn_ip6
    - gpus
    - host
    - hwaddr_interfaces
    - id
    - init
    - ip4_interfaces
    - ip6_interfaces
    - ip_interfaces
    - ipv4
    - ipv6
    - kernel
    - kernelrelease
    - locale_info
    - localhost
    - lsb_distrib_codename
    - lsb_distrib_description
    - lsb_distrib_id
    - lsb_distrib_release
    - machine_id
    - manufacturer
    - master
    - mdadm
    - mem_total
    - nodename
    - num_cpus
    - num_gpus
    - os
    - os_family
    - osarch
    - oscodename
    - osfinger
    - osfullname
    - osrelease
    - osrelease_info
    - path
    - productname
    - ps
    - pythonexecutable
    - pythonpath
    - pythonversion
    - saltpath
    - saltversion
    - saltversioninfo
    - serialnumber
    - server_id
    - shell
    - virtual
    - zmqversion

 

To get the actual grains data:

 

salt '*' grains.items

 

Output for this command is quite verbose, but I’ll put it here so you can see the kind of info that is available to you:

minion1:
    ----------
    SSDs:
    biosreleasedate:
        01/01/2011
    biosversion:
        Bochs
    cpu_flags:
        - fpu
        - vme
        - de
        - pse
        - tsc
        - msr
        - pae
        - mce
        - cx8
        - apic
        - sep
        - mtrr
        - pge
        - mca
        - cmov
        - pat
        - pse36
        - clflush
        - mmx
        - fxsr
        - sse
        - sse2
        - ss
        - syscall
        - nx
        - pdpe1gb
        - rdtscp
        - lm
        - constant_tsc
        - arch_perfmon
        - rep_good
        - nopl
        - eagerfpu
        - pni
        - pclmulqdq
        - vmx
        - ssse3
        - cx16
        - pcid
        - sse4_1
        - sse4_2
        - x2apic
        - popcnt
        - tsc_deadline_timer
        - aes
        - xsave
        - avx
        - f16c
        - rdrand
        - hypervisor
        - lahf_lm
        - xsaveopt
        - vnmi
        - ept
        - fsgsbase
        - tsc_adjust
        - smep
        - erms
    cpu_model:
        Intel(R) Xeon(R) CPU E5-2630L v2 @ 2.40GHz
    cpuarch:
        x86_64
    domain:
    fqdn:
        minion1
    fqdn_ip4:
    fqdn_ip6:
    gpus:
        |_
          ----------
          model:
              GD 5446
          vendor:
              unknown
    host:
        minion1
    hwaddr_interfaces:
        ----------
        eth0:
            04:01:95:1f:16:01
        lo:
            00:00:00:00:00:00
    id:
        minion1
    init:
        upstart
    ip4_interfaces:
        ----------
        eth0:
            - 192.168.2.100
        lo:
            - 127.0.0.1
    ip6_interfaces:
        ----------
        eth0:
            - fe80::601:95ff:fe1f:1601
        lo:
            - ::1
    ip_interfaces:
        ----------
        eth0:
            - 192.168.2.100
            - fe80::601:95ff:fe1f:1601
        lo:
            - 127.0.0.1
            - ::1
    ipv4:
        - 192.168.2.100
        - 127.0.0.1
    ipv6:
        - ::1
        - fe80::601:95ff:fe1f:1601
        - fe80::601:95ff:fe1f:1602
    kernel:
        Linux
    kernelrelease:
        3.13.0-71-generic
    locale_info:
        ----------
        defaultencoding:
            UTF-8
        defaultlanguage:
            en_US
        detectedencoding:
            UTF-8
    localhost:
        minion1
    lsb_distrib_codename:
        trusty
    lsb_distrib_description:
        Ubuntu 14.04.3 LTS
    lsb_distrib_id:
        Ubuntu
    lsb_distrib_release:
        14.04
    machine_id:
        fdde6ec2231ff9e1ace717e4534ff7c5
    manufacturer:
        Bochs
    master:
        salt
    mdadm:
    mem_total:
        2001
    nodename:
        minion1
    num_cpus:
        2
    num_gpus:
        1
    os:
        Ubuntu
    os_family:
        Debian
    osarch:
        amd64
    oscodename:
        trusty
    osfinger:
        Ubuntu-14.04
    osfullname:
        Ubuntu
    osrelease:
        14.04
    osrelease_info:
        - 14
        - 4
    path:
        /sbin:/usr/sbin:/bin:/usr/bin
    productname:
        Bochs
    ps:
        ps -efHww
    pythonexecutable:
        /usr/bin/python
    pythonpath:
        - /usr/bin
        - /usr/lib/python2.7
        - /usr/lib/python2.7/plat-x86_64-linux-gnu
        - /usr/lib/python2.7/lib-tk
        - /usr/lib/python2.7/lib-old
        - /usr/lib/python2.7/lib-dynload
        - /usr/local/lib/python2.7/dist-packages
        - /usr/lib/python2.7/dist-packages
    pythonversion:
        - 2
        - 7
        - 6
        - final
        - 0
    saltpath:
        /usr/lib/python2.7/dist-packages/salt
    saltversion:
        2015.5.3
    saltversioninfo:
        - 2015
        - 5
        - 3
        - 0
    serialnumber:
        Not Specified
    server_id:
        844019353
    shell:
        /bin/bash
    virtual:
        kvm
    zmqversion:
        4.0.4

 

Now , let’s take a look at the specific Grain we will use for this template:

 

salt '*' grains.item ip4_interfaces

 

The output for this will look something like:

 

minion1:
    ----------
    ip4_interfaces:
        ----------
        eth0:
            - 192.168.2.100
        lo:
            - 127.0.0.1

 

Here’s what our state file will look like:

 

nrpe:
  pkg:
   - installed
  service:
    - running
    - watch:
      - file: nrpe
  file:
    - managed
    - user: root
    - group: root
    - mode: 600
    - template: jinja
    - name: /etc/nagios/nrpe.cfg
    - source: salt://nrpe/nrpe.cfg
nagios-plugins-nrpe:
  pkg:
   - installed

 

The above State file describes how NRPE will be installed and configured by Saltstack, but the magic is in the NRPE config file itself. Here’s what the nrpe.cfg file will look like to Saltstack:

 

pid_file=/var/run/nrpe.pid
server_port=5666
# insert IP here
server_address={{ grains['ip4_interfaces'].eth0[0] }}
nrpe_user=nagios
nrpe_group=nagios
allowed_hosts=192.168.2.0/24
dont_blame_nrpe=0
debug=0
command_timeout=60

# System Resources
command[check_users]=/usr/lib64/nagios/plugins/check_users -w 5 -c 10
command[check_load]=/usr/lib64/nagios/plugins/check_load -w 20,15,10 -c 35,30,25
command[check_zombie_procs]=/usr/lib64/nagios/plugins/check_procs -w 10 -c 20 -s Z
command[check_total_procs]=/usr/lib64/nagios/plugins/check_procs -w 400 -c 600


# Disk Partitions
command[check_root]=/usr/lib64/nagios/plugins/check_disk -w 20% -c 10% -p /dev/vda1
command[check_swap]=/usr/lib64/nagios/plugins/check_swap -w 90 -c 85

 

It’s within this config that you’ll notice we’ve put some Jinja code that contains a Grains item query. :

 

server_address={{ grains['ip4_interfaces'].eth0[0] }}

 

Now, when we run our state:

 

salt '*' state.sls nrpe

 

Saltstack will create the nrpe.cfg file on our minion, that looks like this:

 

pid_file=/var/run/nrpe.pid
server_port=5666
# insert IP here
server_address=192.168.2.100
nrpe_user=nagios
nrpe_group=nagios
allowed_hosts=192.168.2.0/24
dont_blame_nrpe=0
debug=0
command_timeout=60

# System Resources
command[check_users]=/usr/lib64/nagios/plugins/check_users -w 5 -c 10
command[check_load]=/usr/lib64/nagios/plugins/check_load -w 20,15,10 -c 35,30,25
command[check_zombie_procs]=/usr/lib64/nagios/plugins/check_procs -w 10 -c 20 -s Z
command[check_total_procs]=/usr/lib64/nagios/plugins/check_procs -w 400 -c 600

# Disk Partitions
command[check_root]=/usr/lib64/nagios/plugins/check_disk -w 20% -c 10% -p /dev/vda1
command[check_swap]=/usr/lib64/nagios/plugins/check_swap -w 90 -c 85

 

And that’s it. Using this technique we can keep one config file per service, and use Grains to modify values that require minion info. We can easily apply this Saltstack State template concept to just about any service we configure with SaltStack. As you build your Saltstack State collection, keep this idea in mind for greater control of your infrastructure. Stay tuned for a future post covering how to create custom Grains for your minions.

Thanks for reading!

  • Pramod Singh

    Nicely written… Thanks.

  • william_sv

    Nice example! But, be careful.

    Today I ran into a problem using a very similar pattern. My network interface name (eth0) was not available on a new ec2 instance. It had been replaced with ens3 or something similar. Having only one ip on the server I solved this by writing a small module that returns the ip based off of the ipv4 grain (removing the localhost first)

    Hope this info saves someone a few headaches