I have 2 roles both with a list of tasks.
However, SOME (not all) of the tasks in role A are almost identical to the tasks in role B
Example role A task:
- name: Ensure bible server is running
command: npm run forever
args:
chdir: ~/bible-server
when: "foreverlist.stdout.find('bibleServer.js') == -1"
Example role B task:
- name: Ensure certs server is running
command: npm run forever
args:
chdir: ~/certs-server
when: "foreverlist.stdout.find('certsServer.js') == -1"
Is it possible to parametise a task such that I can declare a task like I would declare a function and pass in arguments to it?
Yes, in Ansible this is what the inventory is for. Specify the configuration as variables in the inventory, If both roles are on the same host, you could use a dictionary. Then iterate through the dictionary to repeat the task on each configuration.
In the inventory:
servers:
- path: bible-server
script: bibleServer.js
- path: cert-server
script: certServer.js
Then in the task:
- name: Ensure Servers are running
command: npm run forever
args:
chdir: "~/{{ item.path }}"
when: "foreverlist.stdout.find('{{ item.script }}') == -1"
with_items: "{{ servers }}"
That's the high level overview. I would highly recommend reading up on the inventory because it's use is a core principle of Ansible. Also read up on loops.
Related
I have been desperately trying selectively include tasks from a collection. According to the documentation at https://docs.ansible.com/ansible/latest/modules/include_tasks_module.html#include-tasks-module, tags can be passed along when including tasks, in two possible ways:
Using the free-form
- name: <whatever>
include_tasks: tasks.yml
args:
apply:
tags:
- t1
As a parameter
- name: <whatever>
include_tasks:
file: tasks.yml
apply:
tags:
- t1
Based on the above, I prepared a small proof of concept, as presented below. The idea is to have a (play)book book.yml use tags to pick specific tasks from a catalogue tasks.yml.
Unfortunately, invoking
ansible-playbook book.yml
will execute all the tasks every time tasks.yml is included, regardless of specifying the tags.
I have tried using Python 2.7 and Ansible 2.7, 2.8 and 2.9 on Mac OSX (Mojave).
What am I doing wrong?
Thank you in advance.
Code
File book.yml
---
- hosts: localhost
vars:
_task_file: tasks.yml
tasks:
- name: include_tasks "{{ _task_file }}" (free-form) with tag(s) pwd
include_tasks: "{{ _task_file }}"
args:
apply:
tags:
- pwd
tags:
- pwd
- name: include_tasks "{{ _task_file }}" with tag(s) dummy
include_tasks:
file: "{{ _task_file }}"
apply:
tags:
- dummy
tags:
- dummy
...
File tasks.yml
---
- name: dummy
command: echo -n
tags: dummy
- name: printenv
command: bash -c "printenv | sort | grep -i ansible"
register: _printenv
tags: printenv
- debug: var=_printenv.stdout_lines
tags: debug
- name: pwd
command: pwd
register: _pwd
tags: pwd
- debug: var=_pwd.stdout_lines
tags: debug
...
Q: "The idea is to ... use tags to pick specific tasks from a catalog tasks.yml."
A: It's not possible to use tags "inside" a playbook this way. apply: tags will add the tags to the tasks not select them. The tags can select the tasks from "outside" of the playbook only:
on the command line. Options: --tags, --skip-tags
in the configuration section tags. Keys: run, skip
in the environment. Variables: ANSIBLE_RUN_TAGS, ANSIBLE_SKIP_TAGS
See:
TAGS_RUN
TAGS_SKIP
The next option to consolidate tags is the concept of Ansible runner's project.
I have a playbook with this structure:
---
- hosts: foo-servers
roles:
- foo_setup
become: yes
tags: tweaks
- hosts: bar-servers
roles:
- bar_setup
become: yes
tags: tweaks
[a few more server groups with a similar pattern]
I have a somewhat similar feature to deploy in all servers but each server group has it's own small differences, so I need to keep separate roles for each group.
I also want to run just a select group of tasks from each role, the ones tagged 'tweaks', in all hosts.
And all hosts should be run with raised privileges, but that is not true for all playbooks, so I want that setting to apply just to this playbook (no global vars).
I would like to move all the repeated parameters - become: yes and tags: tweaks outside of host: plays where they will be indicated to apply to all roles bellow. Something to the effect of
--
- all_hosts_this_playbook:
become: yes
tags: tweaks
- hosts: foo-servers
roles:
- foo_setup
- hosts: bar-servers
roles:
- bar_setup
I suppose this is possible in the command line. Like ansible-playbook setup_tweaks.yml --tags "tweak" --become? But is there a playbook equivalent? I'd rather have these in the file than in the command line, where I often forget to add stuff.
And looping doesn't work...
ERROR! 'loop' is not a valid attribute for a Play
- name: Make tweaks in many servers
become: yes
tags: tweaks
hosts: "{{ item.host }}"
roles:
- "{{ item.role }}"
loop:
- { host: 'foo-servers', role: 'foo_setup' }
- { host: 'bar-servers', role: 'bar_setup' }
I also want to add post_tasks: to be run in all servers (maybe they also need to be tagged?):
post_tasks_all_hosts:
- name: Upgrade system
apt:
autoremove: yes
autoclean: yes
update_cache: yes
upgrade: dist
tags: tweaks
- name: Reboot
shell: sleep 2 && reboot
async: 3
poll: 0
tags: tweaks
Is it possible to define playbook-wide pre_tasks or post_tasks?
Here Ansible: How to declare global variable within playbook? it is indicated that one 'cannot define a variable accessible on a playbook level', but in my case it's not variables - it's task parameters and post_tasks:.
Maybe the parameters and the 'pre/post tasks' are different problems with different solutions, but I decided to ask in the same place because they both fall on the same category of parameters that I'd like to set for the whole playbook, outside of host: plays.
Q: "I suppose this is possible in the command line. Like ansible-playbook setup_tweaks.yml --tags "tweak" --become? But is there a playbook equivalent?"
A: No. There is no such playbook equivalent.
Isn't this is a misunderstanding of the command-line option --tags
only run plays and tasks tagged with these values
versus
tag inheritance ?
Adding tags: to a play, or to statically imported tasks and roles, adds those tags to all of the contained tasks...When you apply tags: attributes to structures other than tasks, Ansible processes the tag attribute to apply ONLY to the tasks they contain. Applying tags anywhere other than tasks is just a convenience so you don’t have to tag tasks individually.
Details
In the play below tag "tweaks" is added to all of the contained tasks
- hosts: foo-servers
roles:
- foo_setup
tags: tweaks
The command below selects only tasks tagged "tweaks"
ansible-playbook setup_tweaks.yml --tags "tweak"
Q: "Is it possible to define playbook-wide pre_tasks or post_tasks?"
A: No. It's not possible. The scope of pre/post_tasks is the play.
I have an ansible playbook that has a task to output the list of installed Jenkins plugins for each servers.
here is the host file:
[masters]
server1
server2
server3
server4
server5
server6
Here is the task that prints out the list of plugins installed on each of the jenkins servers:
- name: Obtaining a list of Jenkins Plugins
jenkins_script:
script: 'println(Jenkins.instance.pluginManager.plugins)'
url: "http://{{ inventory_hostname }}.usa.com:8080/"
user: 'admin'
password: 'password'
What I want to do next is do a comparison with all of the installed plugins across all of the servers -- to ensure that all of the servers are running the same plugins.
I don't necessarily want to force an update -- could break things -- just inform the user that they are running a different version of the plug in that the rest of the servers.
I am fairly new to ansible, will gladly accept any suggestions on how to accomplish this.
This is a bit ugly, but should work:
- hosts: master
tasks:
- jenkins_script:
script: 'println(Jenkins.instance.pluginManager.plugins)'
url: "http://{{ inventory_hostname }}.usa.com:8080/"
user: 'admin'
password: 'password'
register: call_result
- copy:
content: '{{ call_result.output }}'
dest: '/tmp/{{ inventory_hostname }}'
delegate_to: 127.0.0.1
- shell: 'diff /tmp/{{groups.master[0]}} /tmp/{{ inventory_hostname }}'
delegate_to: 127.0.0.1
register: diff_result
failed_when: false
- debug:
var: diff_result.stdout_lines
when: diff_result.stdout_lines | length != 0
This will save the result of the jenkins_script module onto the calling host (where you are running ansible-playbook), into /tmp/{{hostname}}. Afterwards it will run a normal diff against the first server's result and each of the others', and then print out if there are any differences.
It's a bit ugly, as it:
Uses /tmp on the calling host to store some temporary data
Does not clean up after itself
Uses the diff shell commands for something that might be doable with some clever use of jinja
Ansible 2.3 will have the tempfile module, that you might use to clean up /tmp
There are 3 hosts in my play.
[machines]
MachineA
MachineB
MachineC
MongoDB runs on these servers. And one of these servers can be a MasterDB of Mongo.
So, each of these machines can be a 'Master'. This is determined by setting the fact if the machine is master, in this example only MachineA is targeted:
- name: check if master
shell: 'shell command to check if master'
set_fact: MasterHost="machineA"
when: 'shell command to check if master'.stdout == "true"
This is also done for MachineB and MachineC.
Mission to achieve: To run commands only on on the Master machine, which has the fact "MasterHost".
I tried the delegate_to module, but delegate_to also uses the two other machines:
- name: some task
copy: src=/tmp/test.txt dest=/tmp/test.txt
delegate_to: "{{ MasterHost }}"
I want to target the master it in my playbook and run only commands on the master, not in the shell via the --limit option.
Assuming the command run to check whether the host is the master or not is not costly, you can go without setting a specific fact:
- name: check if master
shell: 'shell command to check if master'
register: master_check
- name: some task
copy: src=/tmp/test.txt dest=/tmp/test.txt
when: master_check.stdout == "true"
Run the play on all hosts and only the one that is the master will run some task.
Eventually, this was my answer. Sorry for the first post, still learning how to make a good post. Hihi
- name: Check which host is master
shell: mongo --quiet --eval 'db.isMaster().ismaster'
register: mongoMaster
- name: Set fact for mongoMasterr
set_fact: MongoMasterHost="{{ item }}"
with_items: "{{ groups['HOSTS'] }}"
when: mongoMaster.stdout == "true"
- name: Copy local backup.tgz to master /var/lib/mongodb/backup
copy: src=/tmp/backup.tgz dest=/var/lib/backup/backup.tgz
when: mongoMaster.stdout == "true"
Thanks for helping and pointing me toward the right direction.
Error MsgI am trying to execute a ansible playbook using roles. I have some variables, which I defined in main.yaml of vars section. I am copying these variables (main.yaml) from another remote machine.
My question is, my playbook throws an error for the first time even though it copies the main.yaml file in my vars section. When I run for second time, it executes playbook well. I am understanding, for the first time though the file is copied it doesn't read it as it was not present before the playbook was running. Is there a way I can run it successfully without an error for the first time.
Image roles will give clear idea about roles and sub files. Roles
site.yaml
---
- name: Testing the Mini project
hosts: all
roles:
- test
tasks/main.yaml
---
- name: Converting Mysql to CSV file
command: mysqldump -u root -padmin -T/tmp charan test --fields-terminated-by=,
when: inventory_hostname == "ravi"
- name: Converting CSV file to yaml format
shell: python /tmp/test.py > /tmp/main.yaml
when: inventory_hostname == "ravi"
- name:Copying yaml file from remote node to vars
shell: sshpass -p admin scp -r root#192.168.56.101:/tmp/main.yaml /etc/ansible/Test/roles/vars/main.yaml
when: inventory_hostname == "charan"
- name:Install Application as per the table
apt: name={{ item.Application }} state=present
when: inventory_hostname == {{ item.Username }}
with_items: user_app
/vars/main.yaml This will be copied from remote machine.
---
user_app:
- {Username: '"ravi"' , Application: curl}
- {Username: '"charan"' , Application: git}
Take a look at the include_vars task. It may do what you need. It looks like you need to be explicitly including /vars/main.yaml in a task before your apt task where you reference the variables.