I am trying to using Salt state files to configure network devices. I will briefly describe my current setup:
I have pillar ntp.sls file saved as /etc/salt/pillar/ntp.sls and it looks like this:
ntp.servers:
- 11.1.1.1
- 2.2.2.2
Then I have Jinja template saved as /etc/salt/states/ntp/templates/ntp.jinja and looks like this:
{%- for server in servers %}
ntp {{ server }}
{%- endfor %}
Finally I have state file saved as /etc/salt/states/ntp/init.sls as this:
ntp_example:
netconfig.managed:
- template_name: salt://ntp/templates/ntp.jinja
- debug: true
- servers: {{ salt.pillar.get('ntp.servers') }}
I am getting the following error while trying to run the command: sudo salt sw state.sls ntp, where sw is the proxy minion, so here is the error:
sw:
Data failed to compile:
ID ntp.servers in SLS ntp is not a dictionary
Command to get data from pillar is working, command: sudo salt sw pillar.get ntp.servers
Output:
sw:
- 11.1.1.1
- 2.2.2.2
Any suggetions what could be wrong and how to fix it?
Thanks
I think you should declare in /etc/salt/pillar/ntp.sls something like:
ntp-servers:
- 11.1.1.1
- 2.2.2.2
and than load these values with:
- servers: {{ salt.pillar.get('ntp-servers') }}
The . is a directory separator in SaltStack.
Related
I hope someone can help me 🙂.
I have stored ssh keys in vault as a secret. With saltstack config I am reading the secret with a pillar. Like this
docker:
{% set sshkey = salt['vault'].read_secret('super/secret/sshinfo', 'slackbot') %}
sshkey: |
{{ sshkey | indent(4) }}
And init.sls file looks like this:
{% set docker_config = pillar['docker'] %}
git-key:
file.managed:
- name: /root/.ssh/slack-bot
- contents: {{ docker_config.sshkey }}
- mode: 400
- user: root
Which works great and I get the ssh key by typing salt-call pillar.items
However, when I run salt-call state.apply I get the following error message
local:
Data failed to compile:
----------
Rendering SLS 'base:docker' failed: could not find expected ':'
Which is driving me nuts!
I read Mulitline string on Github that in order to write mulitline with salt you need to have | indent(4) (as an example).
Does someone have any ideas? Would be much appreciated 🙂
So, after a while I tried to change indent(4) to indent(5), but it didn't gave up the error. So I also tried to change content to be
- contents: |
{{ docker_config.sshkey | indent(5) }}
Which fixed the problem and is now working.
Hope anyone who has similar problems find this useful.
Cheers!
Is there a way to log a custom debug message in saltstack out of an .sls or a .jinja file? i.e. something like:
{% salt.log_message("Entering...") %}
This was added as a feature in 2017.7.0:
Yes, in Salt, one is able to debug a complex Jinja template using the
logs. For example, making the call:
{%- do salt.log.error('testing jinja logging') -%}
Will insert the following message in the minion logs:
2017-02-01 01:24:40,728 [salt.module.logmod][ERROR ][3779] testing jinja logging
Add a state using test.nop and add things you want to inspect as arguments to it.
The use
salt-call -l debug state.apply yourslsfile test=True
or
salt-call --output=yaml state.show_sls yourslsfile
to check the result.
For example:
debug.sls
test:
test.nop:
- user: {{ grains.username }}
- nested:
foo: bar
Here is the result of state.show_sls
local:
test:
test:
- user: ian
- nested:
foo: bar
- nop
- order: 10000
__sls__: !!python/unicode dotfiles
__env__: base
It is better to setup a standalong environment to test states, like this
I want to install this file via salt-stack.
# /etc/logrotate.d/foo
/home/foo/log/foo.log {
compress
# ...
postrotate
systemctl restart foo.service
endscript
}
Unfortunately there are some old machines which don't have systemd yet.
For those machines I need this postrotate script:
/etc/init.d/foo restart
How to get this done in salt?
I guess I need something like this:
postrotate
{% if ??? %}
/etc/init.d/foo restart
{% else %}
systemctl restart foo.service
{% endif %}
endscript
But how to implement ??? ?
We can discover this by taking advantage of the service module, which is a virtual module that is ultimately implemented by the specific module appropriate for the machine.
From the command line we can discover the specific module being used with test.provider. Here is an example:
$ sudo salt 'some.*' test.provider service
some.debian.8.machine:
systemd
some.debian.7.machine:
debian_service
some.redhat.5.machine:
rh_service
To discover this in a template we can use:
{{ salt["test.provider"]("service") }}
So, you could use something like:
postrotate
{% if salt["test.provider"]("service") != "systemd" %}
/etc/init.d/foo restart
{% else %}
systemctl restart foo.service
{% endif %}
endscript
NOTE:
The possible return value of test.provider will vary across platform. From the source, these appear to be the currently available providers:
$ cd salt/modules && grep -l "__virtualname__ = 'service'" *.py
debian_service.py
freebsdservice.py
gentoo_service.py
launchctl.py
netbsdservice.py
openbsdrcctl.py
openbsdservice.py
rest_service.py
rh_service.py
smf.py
systemd.py
upstart.py
win_service.py
I'd just call out directly to Salt's service module which will do the right thing based on the OS.
postrotate
salt-call service.restart foo
endscript
A more "salty" way of doing this would be something like this:
my_file:
file.managed:
- source: salt://logrotate.d/foo
- name: /etc/logrotate.d/foo
- watch_in:
- service: my_foo_service
my_foo_service:
service.running:
- name: foo
This will lay down the file for you and then restart the foo service if any changes are made.
Suppose I have different credentials in two different environments, but that's the only thing that differs between them, and I don't want to make extra pillar files for a single item.
Suppose I attack the problem like this:
{%- set deployment = grains.get('deployment') %}
{%- load_yaml as credentials %}
prod: prodpassword
test: testpassword
dev: devpassword
{%- endload %}
some_app:
user: someuser
password: {{ credentials[deployment] }}
...more configuration here...
This works as expected. But can a minion in test theoretically get the password for prod? That depends on whether the dict lookup happens before or after data is sent to the client, I think, which in turn depends on when the jinja is rendered. Does the master render it first and then send the resulting data, or does the minion receive the pillar file as-is, then render it itself?
Pillar data is always rendered on the master, never the minion. The master does have access to the minion's grains, however, which is why your example works.
Given a Pillar SLS file with the following contents:
test: {{ grains['id'] }}
The following pillar data will result:
# salt testminion pillar.item test
testminion:
----------
test:
testminion
Source: I'm a SaltStack core developer.
Is salt caching /etc/hosts?
I'm in a situation where I change /etc/hosts such that the FQDN points to the external IP address instead of 127.0.0.1
The problem is that in the first run, the fqdn_ipv4 stays 127.0.0.1 and I need to rerun salt '*' state.highstate to get the right values. This leads to problems like this, which cost me a lot of time.
Is salt rendering everything before execution (or caches DNS)? How do I address this problem?
The state file looks like this:
127.0.0.1:
host.absent:
- name: {{ nodename }}
- ip: 127.0.0.1
127.0.1.1:
host.absent:
- name: {{ nodename }}
- ip: 127.0.1.1
{% for minion, items in salt['mine.get']('environment:' + environment, 'grains.item', expr_form='grain')|dictsort %}
{{ minion }}:
host.present:
- ip: {{ items['ip_addr'] }}
- names:
- {{ minion }}
- {{ minion.split('.')[0] }}
{% endfor %}
And the code that uses the IP looks like this:
{% set ipv4 = salt['config.get']('fqdn_ip4') -%}
# IP Address that Agent should listen on
listening_ip={{ ipv4[0] }}
Salt is caching the values of grains. Therfore the salt['config.get']('fqdn_ip4') will retrieve the value from the beginning of the script.
Use the following in your state file to refresh the grain information:
refreshgrains:
module.run:
- name: saltutil.sync_grains
Salt will render the state before executing it, so you might not be able to use any new grain information inside the state file itself.
But you will be able to use the new grain values in Jinja templates for files. I assume the second code snippet is from a template that is used by Salt's file.managed, so you should be safe here.