In my playbook I have several shell task per playbook, like ten or more. I want to use creates shell arg to avoid executing them over and over.
Currently I have this:
- name: Download sonar-runner
get_url:
url: http://repo1.maven.org/maven2/org/codehaus/sonar/runner/sonar- runner-dist/2.4/sonar-runner-dist-2.4.zip
dest: /tmp
mode: 0755
- name: Unarchive
unarchive:
src: /tmp/sonar-runner-dist-2.4.zip
dest: /opt/tools/sonar-runner-2.4
- name: Sym link
shell: ln -s sonar-runner-2.4 sonar-runner
args:
creates: ~/.ansible/sonar-runner.task/step.3
- name: Configure profile
shell: |
echo 'export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/' > /etc/profile.d/maven.sh
echo 'export M2_HOME=/opt/maven/apache-maven-3.5.3' >> /etc/profile.d/maven.sh
echo 'export PATH=${M2_HOME}/bin:${PATH}' >> /etc/profile.d/maven.sh
args:
creates: ~/.ansible/sonar-runner.task/step.4
Is there any way to achieve this using variables. I'm thinking something like this:
- name: Sym link
shell: ln -s sonar-runner-2.4 sonar-runner
args:
creates: ~/.ansible/{{playbook_name}}/{{task_index}}
- name: Configure profile
shell: |
echo 'export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/' > /etc/profile.d/maven.sh
echo 'export M2_HOME=/opt/maven/apache-maven-3.5.3' >> /etc/profile.d/maven.sh
echo 'export PATH=${M2_HOME}/bin:${PATH}' >> /etc/profile.d/maven.sh
args:
creates: ~/.ansible/{{playbook_name}}/{{task_index}}
Is there any way to do this? Am I missing something? Or isn't that the way Ansible works?
Is there any way to do this? Am I missing something? Or isn't that the way Ansible works?
Yes, yes, and no :-)
Is there any way to do this?
The shell: and command: support inline creates= declarations, so you can keep all of that together in one block (and thus it is a candidate for being a variable, or a yaml anchor):
- shell: |
creates=/etc/profile.d/maven.sh
echo 'hello' > /etc/profile.d/maven.sh
- command: |
creates=/etc/profile.d/maven.sh
cp /something /etc/profile.d/maven.sh
However, I just told you that for your information, and for the circumstances where these next set of steps won't work because ...
Am I missing something?
You want to use the built-in idempotency whenever possible, to get you out of the business of having to do manual "has this task run" bookkeeping. Thus:
- file:
src: sonar-runner-2.4
dest: sonar-runner
state: link
- copy:
dest: /etc/profile.d/maven.sh
content: |
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
export M2_HOME=/opt/maven/apache-maven-3.5.3
export PATH=${M2_HOME}/bin:${PATH}
I'm genuinely surprised ansible didn't whine when you tried to use ln manually, as it knows about common shell commands and will nudge you to switch to the built-in module file:
Well, finally I've managed to solve this using a mix of vars, facts and roles.
testfacts.yml playbook
---
- hosts: all
vars:
current_role: 'testfacts'
roles:
- testfacts
roles/testfacts/tasks/main.yml
---
- name: Import task1
import_tasks: task1.yml
- name: Import task2
import_tasks: task2.yml
roles/testfacts/tasks/task1.yml
---
- name: Prepare task
include_role:
name: common
tasks_from: set_facts
vars:
current_task: 'task1'
- name: Test facts in task step 1
shell: "echo foo > /home/awx/outp"
args:
creates: "{{ creates_dir }}/test-fact-tasks-step-1"
# this never gets executed
- name: Test facts in task 2 step 2
shell: "echo bar > /home/awx/outp"
args:
creates: "{{ creates_dir }}/test-fact-tasks-step-1"
roles/common/tasks/set_facts.yml
---
- name: set_facts
set_fact:
creates_dir: "{{ bookeeping_dir }}/roles.d/{{ current_role }}/tasks.d/{{ current_task }}/steps.d"
inventories/prod/group_vars/group_vars.yml
---
bookeeping_dir: /home/awx/.ansible/ansible_bookeeping
Related
I recently learned that it's not possible to use the wildcard on Ansible. So I would like to know how could I run the next command with a Role.
cat /sys/class/net/*/address | while read mac
I tried to do it so, but it doesn't work.
- name: Cat address file
command: 'cat /sys/class/net/lo/address'
register: my_items
- name: Cat address file 2
command: 'cat /sys/class/net/enp2s0/address'
register: my_items
- name: Cat address file 3
command: 'cat /sys/class/net/wlo1/address'
register: my_items
- name: Read address file
command: read
with_items: my_items.stout_lines
I don't know whats your error but I used this and it works for me:
---
- hosts: dev
become: yes
tasks:
- name: get mac
shell: cat /sys/class/net/*/address
register: items
- name: debug
debug:
msg: "{{items.stdout_lines}}"
Result:
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.
In Ansible, I have written an Yaml playbook that takes list of host name and the executes command for each host. I have registered a variable for these task and at the end of executing a task I append output of each command to a single file.
But every time I try to append to my output file, only the last record is getting persisted.
---
- hosts: list_of_hosts
become_user: some user
vars:
output: []
tasks:
- name: some name
command: some command
register: output
failed_when: "'FAILED' in output"
- debug: msg="{{output | to_nice_json}}"
- local_action: copy content='{{output | to_nice_json}}' dest="/path/to/my/local/file"
I even tried to append using lineinfile using insertafter parameter yet was not successful.
Anything that I am missing?
You can try something like this:
- name: dummy
hosts: myhosts
serial: 1
tasks:
- name: create file
file:
dest: /tmp/foo
state: touch
delegate_to: localhost
- name: run cmd
shell: echo "{{ inventory_hostname }}"
register: op
- name: append
lineinfile:
dest: /tmp/foo
line: "{{ op }}"
insertafter: EOF
delegate_to: localhost
I have used serial: 1 as I am not sure if lineinfile tasks running in parallel will garble the output file.
Ansible doc recommend use copy:
- name: get jstack
shell: "/usr/lib/jvm/java/bin/jstack -l {{PID_JAVA_APP}}"
args:
executable: /bin/bash
register: jstackOut
- name: write jstack
copy:
content: "{{jstackOut.stdout}}"
dest: "tmp/jstack.txt"
If you want write local file, add this:
delegate_to: localhost
Why to complicate things ?
I did this like that and it worked:
ansible-playbook your_playbook.yml >> /file/you/want/to/redirect/output.txt
you can also try some parsing with grep or some other stuff with tee -a.
I always wonder what is the good way to replace the following shell tasks using the "ansible way" (with get_url, etc.):
- name: Install oh-my-zsh
shell: wget -qO - https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | bash -
or
- name: Install nodesource repo
shell: curl -sL https://deb.nodesource.com/setup_5.x | bash -
This worked for me:
- name: Download zsh installer
get_url:
url: https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh dest=/tmp/zsh-installer.sh
- name: Execute the zsh-installer.sh
shell: /tmp/zsh-installer.sh
- name: Remove the zsh-installer.sh
file:
path: /tmp/zsh-installer.sh
state: absent
#RaviTezu solution doesn't work because the file/script that you wish to execute must be on the machine where you execute your play/role.
As per the documentation here
The local script at path will be transferred to the remote node and then executed.
So one way to do it is by downloading the file locally and using a task like below:
- name: execute the script.sh
script: /local/path/to/script.sh
Or you can do this:
- name: download setup_5.x file to tmp dir
get_url:
url: https://deb.nodesource.com/setup_5.x
dest: /tmp/
mode: 0755
- name: execute setup_5.x script
shell: setup_5.x
args:
chdir: /tmp/
I would go for the first method if you are uploading your own script, the second method is more useful in your case because the script might gets updated in time so you are sure each time you execute it it uses the latest script.
For me, the following statement worked:
- name: "Execute Script"
shell: curl -sL https://rpm.nodesource.com/setup_6.x | bash -
Consider using the get_url or uri module rather than running curl.
For example:
- name: Download setup_8.x script
get_url: url=https://deb.nodesource.com/setup_8.x dest=/opt mode=755
- name: Setup Node.js
command: /opt/setup_8.x
- name: Install Node.js (JavaScript run-time environment)
apt: name=nodejs state=present
This playbook is what I've come up with. So far, it's as close to an idiomatic solution as I have come, and seems to be idempotent.
It will check for the existence of a command (in this case, starship) and if/when the test fails it will download the script.
---
- name: Check for Starship command
command: command -v starship >/dev/null 2>&1
register: installed
no_log: true
ignore_errors: yes
- name: Download Starship installer
get_url:
url: https://starship.rs/install.sh
dest: /tmp/starship-installer.sh
mode: 'u+rwx'
when: installed.rc != 0
register: download
- name: Run the install script
shell: /tmp/starship-installer.sh
when: download.changed
- name: Remove the starship-installer.sh
file:
path: /tmp/starship-installer.sh
state: absent
May be this basic example can help you to start:
---
- name: Installing Zsh and git
apt: pkg=zsh,git state=latest
register: installation
- name: Backing up existing ~/.zshrc
shell: if [ -f ~/.zshrc ]; then mv ~/.zshrc{,.orig}; fi
when: installation|success
sudo: no
- name: Cloning oh-my-zsh
git:
repo=https://github.com/robbyrussell/oh-my-zsh
dest=~/.oh-my-zsh
when: installation|success
register: cloning
sudo: no
- name: Creating new ~/.zshrc
copy:
src=~/.oh-my-zsh/templates/zshrc.zsh-template
dest=~/.zshrc
when: cloning|success
sudo: no
Note the: "force=yes", which will always download the script, overriding the old one.
Also note the "changed_when", which you can refine per your case.
- name: 'Download {{ helm.install_script_url }}'
environment:
http_proxy: '{{proxy_env.http_proxy | default ("") }}'
https_proxy: '{{proxy_env.https_proxy | default ("") }}'
no_proxy: '{{proxy_env.no_proxy | default ("") }}'
get_url: url={{ helm.install_script_url | default ("") }} dest=/tmp/helm_install_script force=yes mode="0755"
when: helm.install_script_url is defined
tags:
- helm_x
- name: Run {{ helm.install_script_url }}
environment:
http_proxy: '{{proxy_env.http_proxy | default ("") }}'
https_proxy: '{{proxy_env.https_proxy | default ("") }}'
no_proxy: '{{proxy_env.no_proxy | default ("") }}'
command: "/tmp/helm_install_script"
register: command_result
changed_when: "'is up-to-date' not in command_result.stdout"
when: helm.install_script_url is defined
args:
chdir: /tmp/
tags:
- helm_x
Following is my yamal file,
---
- hosts: qa-workstations
tasks:
- name: update java version
shell: echo "asdfasdf" > /tmp/abc
shell: echo "asdf" >> /tmp/abc
If I execute ansible using below command,
ansible-playbook test.yml -k
It executes only 1st shell. How to solve this issue?
If you want a task execute many commands, you can use with_items loop:
Example:
tasks:
- name: test
shell: "{{ item }}"
with_items:
- echo Ansible
- df -h
But if you have many commands, you should use script module. script module copies your shell script to remote machine and executes it.
You've actually only defined a single task here. The second shell line simply overrides the first. The proper way to write this is:
---
- hosts: qa-workstations
tasks:
- name: create /tmp/abc
shell: echo "asdfasdf" > /tmp/abc
- name: Append to /tmp/abc
shell: echo "asdf" >> /tmp/abc