Embed dictionary data in YAML file into a state file - salt-stack

I'm looking to embed dictionary data in a YAML file into a state file as i have access to execute only state files.
Below is the dictionary data in YAML file.
---
var1:
values:
val1: "string1"
val2: "string2"
Below is the state file in which i have written YAML dictionary data as below.
{%- load_yaml as var %}
var1:
values:
val1: "string1"
val2: "string2"
{%- endload %}
I'm not sure if this is right, can anyone help me out.

I got the answer, yaml file can be mentioned in state file as below.
{% load_yaml as values %}
var1: 'string1'
var2: 'string2'
{% endload %}

I think you are looking for import_yaml functionality.
YAML file var.yml with some variables:
---
var1:
values:
val1: "string1"
val2: "string2"
An example.sls state file:
{% import_yaml 'path/to/var.yml' as var_data %}
show-imported-variables:
module.run:
- name: test.echo
- text: {{ var_data['var1']['values'] }}
Now that the YAML file has been imported as var_data we can access the values within the dictionary.
When the state file is run:
----------
ret:
----------
val1:
string1
val2:
string2

Related

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

SaltStack extending another pillar

I'm using SaltStack, and I'm trying to re-use the values of a nested dictionary from one Pillar config in another one. Here's a simple example of what I'm trying to do:
Say I have pillar/app/common.sls which has the following items:
app:
lookup:
custom1: 'change the default'
custom2: 'change the default'
service1:
value1: 'foo'
value2: 'bar'
list1:
- apple
- banana
value3: 'xen'
What I'm aiming for is to have a new service (and the YAML key would be service2) on the same machine, but set up in such a way so that I'm not writing values twice (keep it DRY!). I would also like to override the value of one of the parameters. Essentially, I'm trying to extend one pillar into another.
The end result would be:
app:
lookup:
custom1: 'change the default'
custom2: 'change the default'
service1:
value1: 'foo'
value2: 'bar'
list1:
- apple
- banana
value3: 'xen'
service2:
value1: 'foo'
value2: 'bar'
list1:
- apple
- banana
value3: 'future'
I've tried the following with a pillar/app/someserver.sls:
{% import_yaml "app/common.sls" as common %}
app:
service2:
{{ common.app.service1 }}
value3: 'future'
What I've found is {{ common.app.service1 }} successfully renders, but it doesn't like the additional value3: 'future', which is my attempt to override value3 which comes from common.sls.
I'm using Salt 2016.11.4 on Ubuntu Xenial 16.04.2. Any ideas would be helpful. Thank you!
Since your common.app.service1 variable is nothing more than a common Python dict, you should be able to modify it using update. After that, you can use Jinja's yaml filter to render it into the new pillar:
{% import_yaml "app/common.sls" as common %}
{% set service2 = common.app.service1.copy() %}
{% do service2.update({value3: 'future'}) %}
app:
service2: {{ service2 | yaml }}

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.

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

Resources