Access listed grain values - salt-stack

I'm trying to access listed grain values from state file, need help on this.
State file is as below
{% set list = grains['selinux'] %}
echo {{ list }}:
cmd.run
But when i run the state file got the error.
# salt '*' state.sls list_grains
client1:
Data failed to compile:
----------
Rendering SLS 'base:list_grains' failed: mapping values are not allowed in this context
ERROR: Minions returned with non-zero exit code
[root#server ~]# vim /srv/salt/list_grains.sls
grain values accessed are as below
# salt '*' grains.item selinux
client1:
----------
selinux:
----------
enabled:
True
enforced:
Permissive

The selinux grain is a dictionary/map like:
selinux:
enabled: True
enforced: Permissive
So in a state ID you cannot have dictionary/map. You can pick the required dictionary key like list.enabled or list.enforced.
For example, the below state ID will output Permissive:
{% set list = grains['selinux'] %}
echo {{ list.enforced }}:
cmd.run
If you want to get the complete dict as output, you can use a module like test.echo:
{% set list = grains['selinux'] %}
show-selinux-grains:
module.run:
- name: test.echo
- text: "{{ list }}"

Related

No Matching SLS found for 'mods.sls (result - No Matching sls found for 'mods' in env 'base')

Context: I am noob to salt stack open and working on evaluating as an option for state management. I am getting my hands on it. Currently I am running into not been able to run my mods.sls file.
Environment: Three AWS linux systems (t2) One master and two nodes/minions.
/srv/salt/apache/ contains init.sls, map.sls, mods.sls, and welcome.sls.
# mods.sls
{% for conf in ['status', 'info'] %}
mod_{{ conf }}:
file.managed:
- name: /etc/apache2/conf-available/mod_{{ conf }}.conf
- contents: |
<Location "/{{ conf }}">
SetHandler server-{{ conf }}
</Location>
{% if salt.grains.get('os_family') == 'Debian' %}
cmd.run:
- name: a2enmod {{ conf }} && a2enconf mod_{{ conf }}
- creates: /etc/apache2/conf-enabled/mod_{{ conf }}.conf
{% endif %}
{% endfor %}
# salt '*' state.show_sls mods`
node2:
- No matching sls found for 'mods' in env 'base'
node:
- No matching sls found for 'mods' in env 'base'
ERROR: Minions returned with non-zero exit code
Any advice from the community would be appreciated or anything I can do to make the question easier to understand.
The sls path is apache.mods, not mods.

If statements with nested grain returns nothing

I am trying to write an if statement based on a nested grain. I have tried this statement in multiple different ways:
System Services Needed:
module.run:
- name: service.systemctl_reload
- onchanges:
- file: /lib/systemd/system/salt-minion.service
{% if salt['grains.get']('Project:DeviceTypeID') == '2' %}
- file: /etc/rc.local
- file: /opt/interfaces_init.sh
{% endif %}
Returns:
Rendering SLS 'Development:System' failed: Jinja variable 'dict object' has no attribute 'Project:DeviceTypeID'
System Services Needed:
module.run:
- name: service.systemctl_reload
- onchanges:
- file: /lib/systemd/system/salt-minion.service
{% if grains['Project']['DeviceTypeID'] == '2' %}
- file: /etc/rc.local
- file: /opt/interfaces_init.sh
{% endif %}
System Services Needed:
module.run:
- name: service.systemctl_reload
- onchanges:
- file: /lib/systemd/system/salt-minion.service
{% if grains['Project:DeviceTypeID'] == '2' %}
- file: /etc/rc.local
- file: /opt/interfaces_init.sh
{% endif %}
As you can tell from the example their are multiple device type IDs. In this example DeviceTypeID = 2 I need to worry about rc.local and a shell script. I can not seem to get this work for the life of me. I know the grain exists as I can run the following:
sudo salt 'Dev-Box' grains.get Project
and I will get:
Dev-Box:
DeviceTypeID:
1
IsActive:
True
SoftwareEnvironmentName:
Production
SoftwareVersion:
Foo
This is either a bug or I am missing something (significantly more likely I am missing something). Any help would be much appreciated.
Edit 1:
Added ['grains.get']('Project:DeviceTypeID') example
in salt grains.get return a dictionary in the following format:
{'minion-id': value}
I believe if you change your code into something like bellow, it should works.
{% if salt['grains.get']('Project:DeviceTypeID')[minion-id] == '2' %}
If you can't do:
salt 'Dev-Box' grains.get 'Project:DeviceTypeID'
Then you don't actually have the proper grain set.
Try the following:
salt 'Dev-Box' grains.setval Project '{"DeviceTypeID": 2, "IsActive": True, "SoftwareEnvironmentName": "Production", "SoftwareVersion": "Foo"}'
Then the following state:
Do the {{ salt['grains.get']('Project:DeviceTypeID') }} things:
test.succeed_with_changes:
- some: thing
You should get:
ID: Do the 2 things
Function: test.succeed_with_changes
Result: True
Comment: Success!
Started: 17:10:42.739240
Duration: 0.491 ms
Changes:
----------
testing:
----------
new:
Something pretended to change
old:
Unchanged
Given what you wrote elsewhere
salt Dev-Box grains.setval BETTI "{'DeviceTypeID': 2, 'IsActive': True SoftwareEnvironmentName': 'Production', 'SoftwareVersion': 'Foo'}"
Your problem is that you have ' and " confused.
Wrapping the value with " makes it a string. Wrapping it with ' and providing valid JSON makes it a dictionary value.

Salt State Rendering: Can I See the Output of the Jinja Phase?

Question:
Is there any way to see the output of the Jinja phase, before attempting to parse as YAML?
Background:
I was trying to debug a Salt problem where I was getting this error:
local:
Data failed to compile:
----------
Rendering SLS ':test.sls' failed: mapping values are not allowed in this context
Not very helpful: No line number? What's a 'mapping value'? etc.
The problem boiled down to something like this:
{%- for x in [1, 2] -%}
Test {{ x }}:
cmd.run:
- name: echo Test {{ x }}
{%- endfor -%}
A seasoned Salt person will recognize that I've messed up the whitespace so that the Jinja was producing the output below:
Test 1:
cmd.run:
- name: echo Test 1 Test 2:
cmd.run:
- name: echo Test 2
However, this was frustrating to find in a complex set of states with no information on where the problem was occurring and no clear description of what the problem even was.
In the process of debugging this I learned that you can get the YAML tree using slsutil.renderer, like this:
% salt-call --local slsutil.renderer `pwd`/test.sls 'jinja'
local:
----------
Test 1:
----------
cmd.run:
|_
----------
name:
echo Test 1
Test 2:
----------
cmd.run:
|_
----------
name:
echo Test 2
But this of course requires that the YAML is valid. So how can I get Salt to output the templates as in my third snippet above, AFTER the Jinja has been evaluated but BEFORE Salt tries to parse it as YAML?
Oh wow, as I revisit this I've learned something new. I believe the answer is cp.get_template.
Test file:
% cat test.sls
{%- for x in [1, 2] -%}
Test {{ x }}:
cmd.run:
- name: echo Test {{ x }}
{%- endfor -%}
Now cp.get_template renders the Jinja and shows the raw output:
% salt-call --local cp.get_template `pwd`/test.sls /dev/stdout
Test 1:
cmd.run:
- name: echo Test 1Test 2:
cmd.run:
- name: echo Test 2
local:
/dev/stdout

How to get element from return statement with sls file

I have the following line in an .sls file, but it always seems to succeed whatever server I put in there:
{% if salt['network.connect']('server.network.com', 80).result == True -%}
When I run salt-call --local network.connect server.network.com 80 I get the following output:
local:
----------
comment:
Unable to connect to server.network.com on tcp port 80
result:
False
The result is structured.
How can I get to result in the sls state above?
There's no need for the == True.
I tested these both:
{% if salt['network.connect']('server.network.com', 80).result %}
as well as the inverse:
{% if not salt['network.connect']('server.network.com', 80).result %}

Run salt states twice with different pillar data?

I have set up pillar data for websites, e.g. web_root, virtualhost and mysql:
web_root:
config_file: salt://some/path.conf
key: some data
directory_name: directoryA
virtualhost:
config_file: salt://some/path.conf
name: websiteA
mysql:
database:
- websiteA_db
These map to states for web_root, virtualhost and mysql (using formula).
I'd like to use have a minion run these states multiple times, using separate pillar data, e.g.
include:
- apache
- php
{% for instance in [instanceA, instanceB] -%}
{% load pillar data /pillar/{{ instance }} -%}
- web_root #run the state
- virtualhost #run the state
- mysql #run the state
{% endfor -%}
Is this possible? I know I can set up pillar data like so:
web_root:
instanceA:
config_file: salt://some/pathA.conf
key: some data
directory_name: directoryA
instanceB:
config_file: salt://some/pathB.conf
key: some data
directory_name: directoryB
virtualhost:
instanceA:
config_file: salt://some/pathA.conf
name: websiteA
instanceB:
config_file: salt://some/pathB.conf
name: websiteB
mysql:
database:
- websiteA_db
- websiteB_db
But it means I have to add loops to each state file, making it less readable as well as use different syntax, e.g. for mysql which is a formula with set syntax requirements.
You'll want to do something like this:
Pillar Data
web_root:
instances:
A:
- name: A
- key: key_A_data
B:
- name: B
- key: key_B_data
State file
{% set names = salt['pillar.get']('web_root:instances') %}
apache:
pkg.installed: []
{% for name in names %}
instance{{ name }}:
- config_file: salt://some/path{{ name }}.conf
- key: {{ key }}
- directory_name: directory{{ name }}
{% endfor %}
Then just do the same thing for the rest of your objects. This way you don't have to change your state file when you add objects to the pillar.

Resources