'for' loop in a SaltStack SLS file - salt-stack

I have a pillar file containing data:
zones:
['us-east-1a','us-east-1b']
Now I want to apply a loop in one of the sls files. This is what I am trying:
{% for zone in salt['pillar.get']('zones') %}<br>
- {{zone}}<br>
{ %endfor %}
But it is throwing an error:
Bad Request: Value (['us-west-1a', 'us-west-1b'])
Can you please help me with that?

Use:
zones:
- 'us-east-1a'
- 'us-east-1b'

Related

How to match a pattern and replace all files in a directory using salt states?

Salt stack state to recursively loop over a directory
Added single and multiple quotes, that didn't help. I don't wish to specify each file for a file replace. I want to loop all files under dir for replace.
{% for file in "/path/{{ to }}/dir" %}
{{ file }}:
file.replace:
- name: {{ file }}
- pattern: /from/
- repl: /{{ to }}/
- backup: False
{% endfor %}
I tried single quotes and multiple quotes, but see the error:
":"ERROR: Minions returned with non-zero exit code
d01xyz011:
Data failed to compile:
----------
Rendering SLS 'base:projects.xyz.p-ser' failed: Jinja syntax error: unexpected '/'; line 140
Can we specify a directory in FOR Loop where we have multiple files to be replaced? Is there additional code that I am missing in the above FOR Loop? Please advise.
You can specify a files list and then do a for loop on it, like
{% set file_list = ['foo', 'bar', 'baz'] %}
{% for f in file_list %}
...
{% endfor %}
But I don't think {% for file in "/path/{{ to }}/dir" %} will do what you think it does: it won't create a list of files in the directory, you will need to create this list elsewhere or in another way and then give it to your for loop in Jinja.

How to compare version strings in salt sls files

Does saltstack have an equivalent to puppets versioncmp() function? Or alternatively, is there a way to get the distutils.version or packaging.version.parse methods (as mentioned on Compare version strings in Python) available in a jinja+yaml rendered sls file?
you can use the module pkg.version_cmp:
# salt-call pkg.version_cmp '1.0.2' '1.1.1'
local:
-1
# salt-call pkg.version_cmp '0.2.4.1-0ubuntu1' '0.2.4-0ubuntu1'
local:
1
Inside jinja you can use it in a way similar to:
{% if salt['pkg.version_cmp']('1.1.0','1.0.5') > 0 %}
....
{% endif %}

Salt: Install multiple packages that have been *listed in a file*

Imagine if you had a file like a standard Python requirements.txt but instead of listing Python packages it listed apt-get-able Ubuntu packages. In its simplest form it would just be a list of package names with newlines delimiters:
# apt-requirements.txt
git
python3.5
python3.5-dev
libssl-dev
Now what if you wanted to install these packages with a salt state by looking at the file at runtime? Here's one way I can imagine doing it:
apt-requirements.txt_installed:
pkg.installed:
- pkgs:
{% for line in salt['cmd.run']('cat ' + my_file).splitlines() %}
- {{ line.strip() }}
{% endfor %}
This seems terrible, though. In addition to being ugly, the file has to be present at render time, which is a serious nuisance.
Does anyone have a better recipe?
You can achieve your goal by using cp.get_file_str.
As you can see in the following example am trying to install two packages saved on a file located on the minion
# yum-requirements.txt
mlocate
screen
The state file will be as the following:
# packages.sls
{% set packages_to_be_installed = salt.cp.get_file_str('/home/yum-requirements.txt').splitlines() %}
install_packages:
pkg.latest:
- pkgs: {{ packages_to_be_installed }}
The result:
minion01:
----------
ID: install_packages
Function: pkg.latest
Result: True
Comment: All packages are up-to-date (mlocate, screen).
Started: 02:09:27.490644
Duration: 4036.966 ms
Changes:
Summary for minion01
------------
Succeeded: 1
Failed: 0
------------
Total states run: 1
Total run time: 4.037 s
Note: I assume that your requirements file doesn't have any commented lines.

salt attribute(key/value) replacement based on particular stanza

Using salt i want to find the attribute(key) and replace it with value based on specific stanza. The attribute(key) is present in multiple times in a file under different stanzas. I want to find my attribute under specific stanza and replace with value.
Example:
output.kafka:
# Boolean flag to enable or disable the output module.
enabled:
I need to find enabled: under output.kafka: and replace it with value. The enabled: attribute present multiple times in my file.
Thanks
Bala.
Salt has a few commands like file.line, file.replace and file.blockreplace that can modify an existing file, but I highly recommend managing the whole file using file.managed. It makes for a less brittle experience.
Here's an example based off your question:
Pillar top file:
cat /srv/pillar/top.sls
base:
'*':
- common
'minion01':
- minion01kafkasettings
Set our pillar data:
cat /srv/pillar/minion01kafkasettings.sls
kafka_output: True
Here's our filebeat template:
cat /srv/salt/filebeat.tmpl
output.kafka:
# Boolean flag to enable or disable the output module.
enabled: {{ pillar.get('kafka_output', True) }}
Here's the filebeat Salt sls file:
cat /srv/salt/filebeat.sls
the_filebeat_file:
file.managed:
- name: /etc/filebeat/filebeat.yml
- template: jinja
- user: root
- group: root
Then we can run the following:
Refresh our pillar data
salt 'minion01' saltutil.refresh_pillar
Then apply the sls file:
salt 'minion01' state.sls filebeat
I have another theory using file.seralize that might work but not in its current state, Maybe Dave could help.
{% set json_data = salt.cp.get_file_str('/etc/filebeat/filebeat.yml') | load_yaml %}
{% do json_data.update({'enabled': pillar.get('kafka_output', True) }) %}
update_config:
file.serialize:
- name: /etc/filebeat/filebeat.yml
- user: root
- group: root
- mode: 644
- formatter: yaml
- dataset: |
{{ json_data | yaml(False)| indent(8) }}
This state should load the whole configuration file then you can modify any of its values based on your pillar setting using the do statement in your case it could be
{% do json_data.update({'enabled': pillar.get('kafka_output', True) }) %}
The config file is populated but not as exepcted as the result will be as following:
'enabled: true
status: active
'
Note there are quotes and the yaml is not intended correctly, is there another way to make it work ? I will update this answer if I found any new results

SaltStack - How can I use grain values in pillar.get statement?

I've got a few configuration values in my application that are tied to ip, mac addr, server name, etc and am trying to come up with a modular state that will match on the correct value from my pillar with a pillar.get. I'm not finding any info on how to use grain values in pillar.get.
pillar example:
account:
qa:
server1:
appUsername: 'user2'
appPassword: 'password2'
server2:
appUsername: 'user2'
appPassword: 'password2'
prod:
server3:
appUsername: 'user3'
appPassword: 'password3'
server4:
appUsername: 'user4'
appPassword: 'password4'
Lines from my template:
keyUser={{ salt['pillar.get']('account:grains['env']:grains['id']:appUsername', 'default_user') }}
keyPass={{ salt['pillar.get']('account:grains['env']:grains['id']:appPassword', 'default_pass') }}
This just seems so natural, but whatever I try errors out, or will escape actual grain lookup and give me the defaults. I also can't find anything on google. Anybody have a solution? Should I dynamically set the appUsername and appPassword values on the pillar instead? I like the layout in the pillar as is, because it's a great easy to read lookup table, without a ton of conditional jinja.
First you can't just embed grains['env'] into the pillar lookup string- you'll need to concatenate. Second, your Jinja assignment looks wrong. Try this:
{% set keyUser = pillar.get('account:'~grains['env']~':'~grains['id']~':appUsername', 'default_user') %}
~ is the concatenate operator in Jinja.
Also, salt['pillar.get']('blah') is the same as pillar.get('blah').
However! It's difficult to be sure without the actual error and/or the full template.

Resources