SaltStack Grains provide static information about minions, making them invaluable for creating dynamic and adaptable configurations. This data, collected directly from the minion, can include details about the operating system, network interfaces, CPU architecture, memory, and much more. Leveraging Grains allows administrators to tailor configurations based on the specific characteristics of each target system without manual intervention.
Consider a scenario where Apache web servers need to be configured to listen only on their primary local IP address. Since IP addresses vary across servers, hardcoding them in configuration files is impractical. Grains offer a clean solution by providing the minion's network details, which can then be injected into configuration templates.
Inspecting Minion Grains
To undertsand the available data, you can list all grains reported by a minion using the grains.items module:
salt 'minion-01' grains.items
This command returns a comprehensive dictionary of all identified grains. For example, to retrieve specific information like total memory or the operating system:
salt 'minion-01' grains.item mem_total os
Output might look like:
minion-01:
----------
mem_total:
7812
os:
CentOS
Targeting Minions Using Grains
Grains are also powerful for targeting. You can selectively execute commands or apply states to minions that match specific grain criteria. For instance, to check the uptime of all CentOS machines:
salt -G 'os:CentOS' cmd.run 'uptime'
Accessing Network Interface IPs
A common requirement is to retrieve the IP address assigned to a minion. The fqdn_ip4 grain provides a list of IPv4 addresses associated with the fully qualified domain name. This is useful for binding services to a specific local IP.
salt '*' grains.item fqdn_ip4
Example output:
server-a:
----------
fqdn_ip4:
- 192.168.1.101
server-b:
----------
fqdn_ip4:
- 192.168.1.102
Implementing Dynamic Apache Binding with Grains
Let's create a Salt state to deploy and configure Apache, making it listen on the minion's primary IPv4 address retrieved via Grains. This involves using a Jinja templated configuration file.
First, define your Salt states in an SLS file (e.g., /srv/salt/apache/init.sls):
# /srv/salt/apache/init.sls
apache_installation:
pkg.installed:
- pkgs:
- httpd
- php
- php-mysql
main_apache_config:
file.managed:
- name: /etc/httpd/conf/httpd.conf
- source: salt://apache/files/httpd.conf.jinja
- user: root
- group: root
- mode: '0644'
- template: jinja
- defaults:
HTTP_PORT: 80
BIND_IP: {{ grains['fqdn_ip4'][0] }}
- require:
- pkg: apache_installation
apache_auth_tools:
pkg.installed:
- name: httpd-tools
apache_authentication_file:
cmd.run:
- name: htpasswd -bc /etc/httpd/conf/htpasswd_file webadmin securepassword
- unless: test -f /etc/httpd/conf/htpasswd_file
- require:
- pkg: apache_auth_tools
extra_apache_configs:
file.recurse:
- name: /etc/httpd/conf.d
- source: salt://apache/files/conf.d
- require:
- pkg: apache_installation
php_configuration_file:
file.managed:
- name: /etc/php.ini
- source: salt://apache/files/php.ini
- user: root
- group: root
- mode: '0644'
- require:
- pkg: apache_installation
apache_service_management:
service.running:
- name: httpd
- enable: True
- reload: True
- watch:
- file: main_apache_config
- file: extra_apache_configs
- file: php_configuration_file
Next, create the Jinja template for httpd.conf (e.g., /srv/salt/apache/files/httpd.conf.jinja), where the BIND_IP and HTTP_PORT variables will be dynamically inserted:
# Snippet from httpd.conf.jinja
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
Listen {{ BIND_IP }}:{{ HTTP_PORT }}
# Other Apache configurations...
Ensure you also have the placeholder files for php.ini and conf.d directory within files if they are managed by the state, e.g., /srv/salt/apache/files/php.ini and /srv/salt/apache/files/conf.d/some_config.conf.
Applying the State
To deploy this configuration to all minions, you can use state.apply. If this state is included in your top.sls, you would use state.highstate.
salt '*' state.apply apache
This command will ensure Apache is installed, configured to listen on the minion's specific IP address, and the service is running and properly managed.