How to increment a value on hosts (Ansible) - ansible

I have endless kafka servers to configure, their ids must be different from each other. Configuration must be done through jinja templating.
Their broker.id field should start from 0 to infinity if there are infinite number of servers.
# The id of the broker. This must be set to a unique integer for each broker.
broker.id={{ broker_id }}
Expected on the conf files:
server1
broker.id=0
server2
broker.id=1
serverN
broker.id=N-1
EDIT
main.yml
---
- include: install.yml
tags:
- kafka
- install
- include: config.yml
tags:
- kafka
- config
config.yml
---
- name: server properties
template:
src: server.properties
dest: /opt/kafka/config/server.properties
- name: zookeeper properties
template:
src: zookeeper.properties
dest: /opt/kafka/config/zookeeper.properties
defaults/main.yml
---
#server.properties
broker_id: 0
templates/server.properties
.
.
.
############################# Server Basics #############################
# The id of the broker. This must be set to a unique integer for each broker.
broker.id={{ broker_id }}
############################# Socket Server Settings #############################
.
.
.
Ansible is applying same configuration to multiple servers, as normal behavior. But while applying same configuration broker.id must be unique.
{{ 99999999 | random | to_uuid }}
This is working, still i'm curious if it's possible to assign 0 to broker.id and increment +1 on each server?

Add below
- name: Set Broker ID
set_fact:
broker_id: {{ groups['all'].index(inventory_hostname) }}
followed by other tasks.

May be this helps
- name: Set Broker ID
set_fact:
broker_id: {{ ansible_play_hosts.index(inventory_hostname) }}

I've just added
broker.id={{ groups['kafka'].index(inventory_hostname) | int + 1 }}
to my server.properties.j2 template and it works.

It's possible to create the inventory dynamically in the first play and use it in the second play. For example the playbook below
- hosts: localhost
vars:
no_of_servers: 3
tasks:
- add_host:
name: "srv-{{ item }}"
groups: kafka
id: "{{ my_idx }}"
loop: "{{ range(0, no_of_servers)|list }}"
loop_control:
index_var: my_idx
- hosts: kafka
tasks:
- debug:
msg: "{{ inventory_hostname }} id: {{ id }}"
gives
ok: [srv-0] => {
"msg": "srv-0 id: 0"
}
ok: [srv-1] => {
"msg": "srv-1 id: 1"
}
ok: [srv-2] => {
"msg": "srv-2 id: 2"
}

Try this, I think it will solve
broker.id={{ (inventory_hostname.split('0')[-1] | int) }}

Related

Ansible search sublists for value

A webhook triggers an AWX job and I want to run the deployment on a certain host depending on the service, since they run on different servers. I need to know which server uses that service to set is as a var so it can be used as a host in the following play.
My variable inside vars.yaml looks like this:
staging_hosts:
server1: ['service1', 'service2', 'service3']
server2: ['service4', 'service5', 'service6']
server3: ['service7', 'service8', 'service9']
Playbook:
- name: write deployment hosts
hosts: localhost
vars:
deployment_hosts: absent
vars_files:
- ./group_vars/vars.yaml
tasks:
- set_fact:
modified_repos: (small regex filter to find modified repository)
- set_fact:
deployment_hosts: "{{ item }}"
when: '{{ modified_repos }} in {{ item }}'
with_list:
- "{{ staging_hosts }}"
- name: connect to Cluster
hosts: "{{ hostvars['localhost']['deployment_hosts'] }}"
What can I do against this warning and error?
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: {{ modified_repos }} in {{ item }}
fatal: [localhost]: FAILED! => {"msg": "The conditional check '{{ modified_repos }} in {{ item }}' failed. True {% else %} False {% endif %}): unhashable type: 'list'
Oh I forgot to mention. It is important, that deployment_hosts could also contain two hosts if modified repos include for example service1 and service4.
Q: "deployment_hosts could also contain two hosts if modified repos include for example service1 and service4."
A: Use intersect filter. For example, the playbook
- hosts: localhost
vars:
staging_hosts:
server1: ['service1', 'service2', 'service3']
server2: ['service4', 'service5', 'service6']
server3: ['service7', 'service8', 'service9']
modified_repos: ['service1', 'service4']
tasks:
- set_fact:
deployment_hosts: "{{ deployment_hosts|default([]) + [item.key] }}"
loop: "{{ staging_hosts|dict2items }}"
when: modified_repos|intersect(item.value)|length > 0
- debug:
var: deployment_hosts
gives
deployment_hosts:
- server1
- server2

Ansible loop list in dictionary and retain the key

I'm trying to create a series of firewalld rules using a variable imported from a yaml file. The yaml file creates a dictionary of service names and the associated ports are a list within each item. A segment of the yaml looks like this:
---
myservice:
description: My service desc
ports:
- 1234/tcp
- 1235/tcp
another:
description: Another service
ports:
- 2222/tcp
- 3333/tcp
The Ansible role I have so far is:
- name: Read services from file
include_vars:
file: "services.yml"
name: services
- name: Create firewalld services
command: >
firewall-cmd --permanent
--new-service={{ item.key }}
--set-description="{{ item.value.description }}"
register: addserv
failed_when: addserv.rc != 26 and addserv.rc != 0
changed_when: not "NAME_CONFLICT" in addserv.stderr
with_dict: "{{ services }}"
- name: Add ports to firewalld service
command: >
firewall-cmd --permanent
--service={{ item.key }} --add-port={{ item.value.ports }}
register: addport
changed_when: not "ALREADY_ENABLED" in addport.stderr
The first segment to create the firewalld service works fine but I'm stumped on the second part with how to loop over the list of ports while retaining the dictionary key. I've tried using subelements to extract the ports list and that works but I can't figure out how to retain the service name from the key.
Use subelements. For example
- debug:
msg: "{{ item.0.key }} - {{ item.0.value.description }} - {{ item.1 }}"
with_subelements:
- "{{ services|dict2items }}"
- value.ports
gives
"msg": "myservice - My service desc - 1234/tcp"
"msg": "myservice - My service desc - 1235/tcp"
"msg": "another - Another service - 2222/tcp"
"msg": "another - Another service - 3333/tcp"

Ansible - Filter host groups by prefix

I'm trying to fetch the names of host groups in Ansible that have a specific prefix. Right now, I'm trying to delegate a template task to servers under host groups with the prefix "config_".
I'm using json_query which uses JMESPath expressions. The query however is incorrect. Can anyone guess what I'm missing?
- name: Create configsvr config file
template: src=mongod.conf.j2 dest={{ mongod.conf.path }} owner=mongod group=mongod mode=0600
delegate_to: "{{ groups|json_query([?starts_with(#, `config_`)]) }}"
Error msg:
FAILED! => {"failed": true, "msg": "template error while templating string: unexpected char u'?' at 22. String: {{ groups|json_query([?starts_with(#, `config_m`)]) }}"}
You should simply use built-in patterns to select your target hosts.
---
- hosts: conf_*
tasks:
- name: Create configsvr config file
template:
src: mongod.conf.j2
dest: "{{ mongod.conf.path }}"
owner: mongod
group: mongod
mode: 0600
You can improve your inventory by using groups of groups, like so:
[conf:children]
conf_a
conf_b
conf_c
[conf_a]
srv1
[conf_b]
srv2
[conf_c]
srv3
And then target conf group in your playbook:
---
- hosts: conf
tasks:
- name: Create configsvr config file
template:
src: mongod.conf.j2
dest: "{{ mongod.conf.path }}"
owner: mongod
group: mongod
mode: 0600

How set ansible function to when modeule?

I want install one of Filebit prospectors only if service redis is running on the host. For that i create default list of prospectors ( redis, aerospike, postgress ) with {{ item.id }}. But right now i want put some expression to "when:" which will install prospector for redis only if it running - how can i do it ?
- name: Configure Filebeat prospectors
template: src=filebeat_conf.yml.j2 dest=/etc/filebeat/conf.d/{{ item.id }}.yml
notify: restart filebeat
with_items: prospectors
when: { " service: " }
Split your task in two: collect facts, act depending on facts.
In your case, you can list installed services first, then configure only existing ones.
For example:
- hosts: test-host
vars:
services:
- name: apache2
id: 1
- name: nginx
id: 2
- name: openvpn
id: 3
tasks:
- name: get list of services
shell: "service --status-all 2>&1 | awk {'print $4'}"
args:
warn: false
register: services_list
- name: process only existing services
debug: msg="service {{ item.name }} with id={{ item.id }} exists"
with_items: "{{ services }}"
when: item.name in services_list.stdout_lines

Ansible recursive checks in playbooks

We need to go through this structure
Zone spec
https://gist.github.com/git001/9230f041aaa34d22ec82eb17d444550c
I was able to run the following snipplet but now I'm stucked at the error checking.
playbook
--
- hosts: all
gather_facts: no
vars_files:
- "../doc/application-zone-spec.yml"
roles:
- { role: ingress_add, customers: "{{ application_zone_spec }}" }
role
- name: check if router exists
shell: "oc get dc -n default {{ customers.zone_name }}-{{ item.type }}"
with_items: "{{ customers.ingress }}"
ignore_errors: True
register: check_router
- name: Print ingress hostnames
debug: var=check_router
- name: create new router
shell: "echo 'I will create a router'"
with_items: "{{ customers.ingress }}"
when: check_router.rc == 1
Output of a ansible run
https://gist.github.com/git001/dab97d7d12a53edfcf2a69647ad543b7
The problem is that I need to go through the ingress items and I need to map the error of the differnt types from the "check_router" register.
It would be nice to make something like.
Pseudo code.
Iterate through the "customers.ingress"
check in "check_router" if the rc is ! 0
execute command.
We use.
ansible-playbook --version
ansible-playbook 2.1.0.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You can replace the second loop with:
- name: create new router
shell: "echo 'I will create a router with type {{ item.item }}'"
with_items: "{{ check_router.results }}"
when: item.rc == 1
This will iterate over every step of check_route loop and you can access original items via item.item.

Resources