Complex filtering in Ansible/Jinja2 - ansible

I am pretty new to Jinja2, and I'm wondering how to achieve this.
Say I have the following vars:
---
servers:
192.168.0.1:
names:
- foo.example.com
- foo
exports:
data:
foo1: /disks/foo1
foo2: /disks/foo2
192.168.0.2:
...
I want to create a symlink /data/foo1 to /disks/foo1 and /data/foo2 to /disks/foo2, but only on foo server; on other servers, make symlinks to their respective exports. So I thought file status=link with_items=... would be the correct thing to do. In Python, I can get the array I need using the following logic:
[
{ 'mount': mount, 'export': export }
for ip, server in servers.iteritems()
if ansible_hostname in server['names']
and 'exports' in server
and 'data' in server['exports']
for mount, export in server['exports']['data'].iteritems()'
]
I don't know how to do this in Jinja2. I wanted to do something like
{{ servers | select('ansible_hostname in self.names') | ... }}
but that doesn't work. Would I need to create a plugin for this logic? Or is my approach all wrong and I should rethink the structure of my servers data?

Answer from my comment:
Usually you want to use inventory_hostname variable – it is what you use as host name in inventory.
servers[ansible_hostname] will access servers' key with name of ansible_hostname's value.
Just for curiosity, you can check out this (complex filter chain) and this (runtime object construction).

Related

How to save Ansible ad-hoc result to JSON fast?

For example,
I have 100 IP, I want to collect their hostnames or product type. I know Saltstack has this feature via a simple --output=json # or yaml.
How should I save this result to JSON format even a CSV file fast?
According to Ansible Callback, I can edit ansible.cfg to use a JSON callback. But can I only use specific callback form time to time? I don't want to use specific callback all the time.
If fact_caching is enabled you can according setup_module Examples just run something like
ansible test --ask-pass -m setup --args 'filter=ansible_fqdn'
and find via cat /tmp/ansible/facts_cache/test.example.com a result like
ansible_fqdn: test.example.com
discovered_interpreter_python: /usr/bin/python
depending on your configuration. See Enabling inventory cache plugins for more information.

Why playbook take wrong values for group variables?

I have a problem with groups variables.
Example: I have two inventory groups group_A and group_B, and also have the same name files in group_vars:
inventories/
hosts.inv
[group_A]
server1
server2
[group_B]
server3
server4
group_vars/
group_A - file
var_port: 9001
group_B - file
var_port: 9002
The problem is when i execute:
ansible-playbook playbooks/playbook.yml -i inventories/hosts.inv -l group_B
playbook was executed for proper scope of servers (server3, server4) but it takes variables from group variables file group_A.
expected result: var_port: 9002
in realty : var_port: 9001
ansible 2.4.2.0
BR Oleg
I included ANSIBLE_DEBUG , and what i have found:
2018-05-03 15:23:23,663 p=129458 u=user | 129458 1525353803.66336: Loading data from /ansible/inventories/prod/group_vars/group_B.yml
2018-05-03 15:23:23,663 p=129458 u=user | 129661 1525353803.66060: in run() - task 00505680-eccc-d94e-2b1b-0000000000f4
2018-05-03 15:23:23,664 p=129458 u=user | 129661 1525353803.66458: calling self._execute()
2018-05-03 15:23:23,665 p=129458 u=user | 129458 1525353803.66589: Loading data from /ansible/inventories/prod/group_vars/group_A.yml
on playbook execution ansible scan all files with variables in folder group_vars which have variable "var_port", last will win.....
as you can found in another topic:
Ansible servers/groups in development/production
and from documentation:
http://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable
Note
Within any section, redefining a var will overwrite the previous instance. If multiple groups have the same variable, **the last one loaded wins**. If you define a variable twice in a play’s vars: section, the **2nd one wins**.
For me now NOT clear how to manage configuration files. In this case I must use unique variables names for each group, but it is not possible regarding roles, or should I use include_vars when i call playbook?
Super example how to manage variables files in multistage environment from DigitalOcean
How to Manage Multistage Environments with Ansible
I believe that the problem here, while not explicitly stated in the original question, is that Server{1,2} and Server{3,4} are actually the same servers in 2 different groups at the same level.
I ran into this problem which caused me to do some digging. I don't agree with it, but it is as designed. This was even fixed with full compatibility and the pull request was rejected
Discussion
Pull Request

Get nodes in leaf host group

I'm trying to get all then nodes under a host group in order to fill a cluster variable in a template. My ansible hosts is something like this
[des_cns]
10.0.0.1
10.0.0.2
[des_rtc]
10.0.0.11
10.0.0.12
[des_ogl]
10.0.0.21
10.0.0.22
[des:children]
des_cns
des_rtc
des_ogl
[clustered:children]
des
When launching my playbook for 10.0.0.1, I can get with groups['des_cns'] all the nodes in this group (this is what i want) but seems like I'm unable to do so with variables. Whith group_names i get all groups in which this IP matches, but i only want the leaf group. I have managed to use a _ in all leaf groups for filtering purposes.
The closer i can get is (im sure im messing too much with this line):
{{groups[group_names|list|join(' ')|regex_findall('[a-z]*_[a_z]*')|to_json]}}
But that returns an error saying dict object has no attribute '[\"des_cns\"]'...
Thank you all for your help and sorry for my bad English.
Use select filter and search test:
{{ groups[ group_names | select('search','_') | first ] }}
Inner expression: selects only groups with _ character from group_names and take only first item out of it.

How can I structure my playbook to make more sense?

Currently, I have the following playbook:
- hosts: "{{env}}_{{product}}"
name: "tools"
sudo: yes
vars_prompt:
product: "Which product would you like to deploy [all|proxy|query|rest]?"
env: "Which environment should we deploy to [dev|qa|test|prod]?"
roles:
- { role: proxy, when: "product == 'all' or product == 'proxy'" }
- { role: query, when: "product == 'all' or product == 'query'" }
- { role: rest, when: "product == 'all' or product == 'rest'" }
All of my groups are stored in a single ./inventory/all file, e.g.,:
# -----------------------------------------------------------------------------------
# --------------
# ### DEV ###
# --------------
# -----------------------------------------------------------------------------------
[dev_all:children]
dev_redis
dev_query
dev_rest
dev_proxy
[dev_redis]
.hosts
[dev_query]
.hosts
[dev_rest]
.hosts
[dev_proxy:children]
dev_proxy-dc1
dev_proxy-dc2
dev_proxy-dc3
[dev_proxy-dc1]
.hosts
[dev_proxy-dc2]
.hosts
[dev_proxy-dc3]
.hosts
# -----------------------------------------------------------------------------------
# --------------
# ### PROD ###
# --------------
# -----------------------------------------------------------------------------------
[prod_all:children]
prod_redis
prod_query
prod_rest
prod_proxy
[prod_redis]
.hosts
[prod_query]
.hosts
[prod_rest]
.hosts
[prod_proxy:children]
prod_proxy-dc1
prod_proxy-dc2
prod_proxy-dc3
[prod_proxy-dc1]
.hosts
[prod_proxy-dc2]
.hosts
[prod_proxy-dc3]
.hosts
I can't help but feel like I'm making this overly complex though. I'm trying to avoid requiring people to pass in tags or inventory files. But now I'm not sure what the best way to go about allowing deploys for things like redis hosts, which aren't really what we would consider a "product", but still requires it's own group since it's hosted on it's own set of hosts. I could just add it to the current list, [all|proxy|query|rest|redis], but... it seems like there should be a way to specify redis and another product, but at the same time not requiring both... I don't know how though.
I wanted to have something where you could say
"I want to deploy proxy to dev, and let's update redis while we're at it"
-- is this possible with my current setup?
This doesn't feel like the best way to handle this.
Instead, I'd structure my playbooks individually to target specific roles and I'd equally set my inventories to only cover a certain environment rather than everything.
If you want to make it so that people don't have to pass in the inventory file or the specific playbook then I'd wrap the ansible-playbook command(s) in some form of wrapper script.
This allows people to use your playbooks a lot more granularly and flexible as you need it but still offer the same ability to offer presets through your wrapper script.

How to remove a string in limits.conf with ansible pam_limits module?

I'm configuring /etc/security/limits.conf with Ansible' new module pam_limits.
What I've succeeded at:
Setting values for specific domain and type in the default limits.conf. (A new string is appended to the end of the file).
Changing values (the string gets rewritten).
The problem is when I want to completely remove the setting. E.g. I don't want to save core dumps anymore. How should I use pam_limits to remove the string completely?
I've managed to develop the following workaround, but I don't consider it good. It doesn't remove the string but rather sets the limit to 0, which may be not the same.
roles/myrole/tasks/main.yaml
...
- name: enable core dumps for myservice
pam_limits: domain='*' limit_type='-' limit_item=core value="{{ 'unlimited' if myrole_save_core_dumps else 0 }}"
...
group_vars/myhosts.yaml:
myrole_save_core_dumps: true
myservice.yaml
hosts: myhosts
become: yes
roles:
- myrole
I believe this would be an feature which is currently not implemented. But there is a feature request on github for this feature.

Resources