Salt stack grains data from sqlite3 external pillar - sqlite

I am trying to pull results from a sqlite3 database (set up as an external salt pillar) and use jinja templating to set grains data.
Here is the relevant section of my salt master file:
sqlite3:
database: '/var/lib/salt/stations.db'
timeout: 5.0
ext_pillar:
- sqlite3:
fromdb:
query: 'SELECT * FROM table;'
And here is the relevant part of the init.sls file I am using to create the grains file:
{% set station_id = salt['grains.filter_by']({
{% for row in query_result %}
{% hostname = station_id %}
}, default="UNKNOWN", grain="host") %}
I confirmed that the external pillar produces results by running
salt '*' sqlite3.fetch /var/lib/salt/stations.db 'SELECT * FROM test;'
But I can't figure out how to get results into the jinja file.
I want something like
'SELECT * FROM table WHERE hostname=station_id LIMIT 1;'
and use the result to set the grain environmental variable called 'hostname'.
But am not sure how to get there from here.
Any help is greatly appreciated.

Thanks to the good folks in Saltstack IRC this problem is solved.
Master:
- sqlite3:
station_map:
query: 'SELECT hostname, id
FROM stations
WHERE hostname like ?'
init.sls:
{% set station_id = salt['grains.filter_by']({
{% for row in station_map %}
{{ hostname }} : {{ station_id }}
}, default="UNKNOWN", grain="host") %}

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.

Access listed grain values

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 }}"

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.

For saltstack,how to get nodegroup of the minion in the jinjia template?

I want to get nodegroup of the minion in the jinjia template or pillar.How can I do it?
e.g. /path/jinjia_template_for_nginx.conf
{% if nodegroup == 'web' %}//nodegroup == get the minion's group
param_xxx 1;
{% else %}
param_xxx 2;
{% endif %}
AFAIK there is no way to get the node group of the current minion via a grain or a pillar directly. You can choose to export your master's configuration to you minion using pillar_opts = True in your master configuration, but any salt-call pillar.get master:nodegroups:web will get you an unexpanded list of hosts that is if no use here.
I suggest you create a pillar for this that maches on the group ...
# pillar top.sls
base:
web:
- match: nodegroup
- webserver
Then set a value of your choice ...
# webserver.sls
mygroup: web
And then use it in the template ...
# nginx.conf
{% if salt['pillar.get']('mygroup', 'unknown') == 'web' %}
param_xxx 1;
{% else %}
param_xxx 2;
{% endif %}
I hope this helps.
Looking at the functionality of nodegroups, pillars and compound matchers you could consider configuring only pillar information and either skip nodegroups, or use a compound matcher using pillar data to define your nodegroups.

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