ansible: Looping over directory and referencing that file in another playbook - ansible

I am trying to make a playbook that loops over the number of files in a directory and then use those files in another playbook.
My playbook as it is now:
---
- name: Run playbooks for Raji's testing
register: scripts
roles:
- prepare_edge.yml
- prepare_iq.yml
- scriptor.yml
with_fileglob: ~/ansible/test_scripts/*
~
When I run this it doesn't work, I've tried "register: scripts" to make a variable to reference inside scriptor.yml but again the playbook fails. Any advice or help you can provide would be much appreciated.
Thanks!
P.S. I am super new to ansible
here is the scriptor.yml
---
- hosts: all
tasks:
- name: Create directory
command: mkdir /some/path/
- name: If file is a playbook
copy:
src: "{{ scripts }}"
dest: /some/path/
when: "{{ scripts }}" == "*.yml"
- name: if file is a script
shell: . ${{ scripts }}
when: "{{ scripts }}" == "*.sh"
P.S.S prepare_edge.yml and prepare_iq.yml don't reference anything and just need to be called in the loop before scriptor.yml
here is the error:
ERROR! 'register' is not a valid attribute for a Play
The error appears to have been in '/Users/JGrow33/ansible/raji_magic_playbook.yml': line 3, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Run playbooks for Raji's testing
^ here

There error message you're getting is telling you that you can't run register in a Playbook.
You can accomplish what you're looking for by doing something like the following in your scriptor.yml file:
- hosts: all
tasks:
- name: Create directory
command: mkdir /some/path/
- name: If file is a playbook
copy:
src: "{{ item }}"
dest: /some/path/
with_fileglob: ~/ansible/test_scripts/*.yml
- name: if file is a script
shell: . ${{ item }}
with_fileglob: copy:
src: "{{ item }}"
dest: /some/path/
with_fileglobe: ~/ansible/test_scripts/*.sh
References
How can Ansible "register" in a variable the result of including a playbook?

Related

Copy one file between remote hosts with Ansible

I have 2 remote servers (Prod and Demo) and I would like to copy the latest file from a particular folder in Prod to another folder in Demo. Only one file is to be copied.
I can find the latest file in Prod using:
- name: Get files in folder
find:
paths: "/path_in_prod/arch/"
register: found_files
become: true
become_user: root
delegate_to: "{{ prod_server }}"
when: copy_content_from_prod is defined
- name: Get latest file
set_fact:
latest_file: "{{ found_files.files | sort(attribute='mtime', reverse=true) | first }}"
become: true
become_user: root
delegate_to: "{{ prod_server }}"
when: copy_content_from_prod is defined
I can check I have the correct file (debug).
When I try to copy the file with
- name: Fetch the file from prod
fetch: src= {{ latest_file.path }} dest=buffer/ flat=yes
delegate_to: "{{ prod_server }}"
- name: Copy the file to demo
copy: src=buffer/{{ latest_file.path | basename }} dest=/path_in_demo/in
I get a "File not found" error. But if I look for the file it is there (latest_file.path on Prod).
this is the error message
fatal: [demoServerHost -> ProdServerHost ]: FAILED! => {"changed": false, "msg": "file not found: "}
I do not know if I am interpreting the error message correctly but it seems to be looking in Demo in order to copy onto Prod?
In such case the synchronize_module might be the solution.
- name: Synchronize file from PROD to DEMO
synchronize:
src: "/tmp/test.txt"
dest: "/tmp/test.txt"
mode: push
delegate_to: "{{ prod_server }}"
when: "{{ demo_server }}"
which is "copying" a file from the production node to the demo node.
There are also a lot of answers under How to copy files between two nodes using Ansible.
I have faced a similar issue, where the copy task hangs indefinitely. Here is my example which is not site specific (will identify the site and user using the options).
The easiest solution I have found is to scp directly using the shell module:
- name: scp files onto '{{ target_destination }}' looping for each file on '{{ target_source }}'
shell: 'scp {{ hostvars[target_source].ansible_user }}#{{ hostvars[target_source].ansible_host }}:/opt/{{ hostvars[target_source].ansible_user }}/{{ item }} /opt/{{ destuser.stdout }}'
loop: '{{ diffout.stdout_lines }}'
when: diffout.stdout != ""
Some notes:
"target_source" and "target_destination" are defined using the extra-vars option
diffout is an earlier task comparing the folders on "Prod" and "Demo" and shows any new files to copy
this task is run on the "target_destination" (in my case Prod)
hostvars[target_source] will look at the variables for the "target_source" host in the inventory
this serves as a "pull" from Demo to Prod in my case, if your "Demo" doesn't have permissions, then you could delegate the task to "Prod" and rearrange the scp to look for "Demo" vars to push from "Prod"

Ansible Playbook - Synchronize module - Register variable and with_items

I'm trying to write a playbook that will rsync the folders from source to target after a database refresh. Our Peoplesoft HR application also requires a filesystem refresh along with database. I'm new to ansible and not an expert with python. I've written this but my playbook fails if any of the with_items doesn't exist. I'd like to use this playbook for all apps and the folders may differ between apps. How can I skip the folders that doesn't exist in source. I'm passing {{ target }} at command line.
---
- hosts: '<hostname>'
remote_user: <user>
tasks:
- shell: ls -l /opt/custhome/prod/
register: folders
- name: "Copy PROD filesystem to target"
synchronize:
src: "/opt/custhome/prod/{{ item }}"
dest: "/opt/custhome/dev/"
delete: yes
when: "{{ folders == item }}"
with_items:
- 'src/cbl/'
- 'sqr/'
- 'bin/'
- 'NVISION/'
In this case, NVISION doesn't exist in HR app but it does in FIN app. But the playbook is failing coz that folder doesn't exist in source.
You can use find module to find and store paths to source folders and then to iterate over results. Example playbook:
- hosts: '<hostname>'
remote_user: <user>
tasks:
- name: find all directories
find:
file_type: directory
paths: /opt/custhome/prod/
patterns:
- "src"
- "sqr"
- "bin"
register: folders
#debug to understand contents of {{ folders }} variable
# - debug: msg="{{ folders }}"
- name: "Copy PROD filesystem to target"
synchronize:
src: "{{ item.path }}"
dest: "/opt/custhome/dev/"
delete: yes
with_items: "{{ folders.files }}"
You may want to use recurse to descend into subdirectories and use_regex to use the power of python regex instead of shell globbing.

Ansible - Check if multiple directories exist - if so run a script on each directory - How?

Im creating a deployment playbook for our web services. Each web service is in its own directory such as:
/webapps/service-one/
/webapps/service-two/
/webapps/service-three/
I want to check to see if the service directory exists, and if so, I want to run a shell script that stops the service gracefully. Currently, I am able to complete this step by using ignore_errors: yes.
- name: Stop services
with_items: services_to_stop
shell: "/webapps/scripts/stopService.sh {{item}}"
ignore_errors: yes
While this works, the output is very messy if one of the directories doesnt exist or a service is being deployed for the first time. I effectively want to something like one of these:
This:
- name: Stop services
with_items: services_to_stop
shell: "/webapps/scripts/stopService.sh {{item}}"
when: shell: [ -d /webapps/{{item}} ]
or this:
- name: Stop services
with_items: services_to_stop
shell: "/webapps/scripts/stopService.sh {{item}}"
stat:
path: /webapps/{{item}}
register: path
when: path.stat.exists == True
I'd collect facts first and then do only necessary things.
- name: Check existing services
stat:
path: "/tmp/{{ item }}"
with_items: "{{ services_to_stop }}"
register: services_stat
- name: Stop existing services
with_items: "{{ services_stat.results | selectattr('stat.exists') | map(attribute='item') | list }}"
shell: "/webapps/scripts/stopService.sh {{ item }}"
Also note, that bare variables in with_items don't work since Ansible 2.2, so you should template them.
This will let you get a list of existing directory names into the list variable dir_names (use recurse: no to read only the first level under webapps):
---
- hosts: localhost
connection: local
vars:
dir_names: []
tasks:
- find:
paths: "/webapps"
file_type: directory
recurse: no
register: tmp_dirs
- set_fact: dir_names="{{ dir_names+ [item['path']] }}"
no_log: True
with_items:
- "{{ tmp_dirs['files'] }}"
- debug: var=dir_names
You can then use dir_names in your "Stop services" task via a with_items. It looks like you're intending to use only the name of the directory under "webapps" so you probably want to use the | basename jinja2 filter to get that, so something like this:
- name: Stop services
with_items: "{{ dir_names }}"
shell: "/webapps/scripts/stopService.sh {{item | basename }}"

Ansible: Write multiple lines to a file using local_action or another method

I am using the command:
local_action: copy content="The installation failed" dest=~/ansible/ansible_log.txt
However, when I do it again:
local_action: copy content="Contact me for assistance" dest=~/ansible/ansible_log.txt
It overwrites the old text with the new text. What I want to do is append to the file instead of replacing the previous text.
I tried adding in a /n to the end of the original string to no avail.
What about the lineinfile module:
local_action:
module: lineinfile
dest: "~/ansible/ansible_log.txt"
line: "The installation failed"
create: yes
local_action:
module: lineinfile
dest: "~/ansible/ansible_log.txt"
line: "Contact me for assistance"
Tried multiple posts and this is that finally helped what I wanted to do. Collect various facts and finally output it to a local file for review. Posting it out here, hoping to help someone starting on ansbile :)
---
- name: "Collect host OS Version information for the host"
vars:
- output_path: "/tmp"
- filename: "osinfo_{{date}}.csv"
vars_prompt:
- name: input_hostname
prompt: What is the set of hosts you want connect ?
private: no
hosts: "{{ input_hostname }}"
tasks:
- name: CSV Generate output filename
set_fact: date="{{lookup('pipe','date +%Y%m%d_%H%M%S')}}"
run_once: true
- name: CSV - Create file and set the header
local_action: copy content="Hostname,SID,OSVersion,KernelVersion\n" dest="{{output_path}}/{{filename }}"
run_once: true
- name: OS Version info for {{ input_hostname }} hosts
set_fact:
csv_tmp: >
{{ inventory_hostname }},{{SID}},{{ ansible_distribution_version }},{{ ansible_kernel }}
- name: CSV - Write information into .csv file
local_action:
module: lineinfile
dest: "{{output_path}}/{{filename }}"
line: "{{csv_tmp}}"
- name: CSV - Blank lines removal
local_action:
module: lineinfile
dest: "{{output_path}}/{{filename }}"
state: absent
regex: '^\s*$'

Move files on remote system, only if destination doesn't exist, with Ansible

I'm trying to write an Ansible role that moves a number of files on the remote system. I found a Stack Overflow post about how to do this, which essentially says "just use the command module with 'mv'". I have a single task defined with a with_items statement like this where each item in dirs is a dictionary with src and dest keys:
- name: Move directories
command: mv {{ item.src }} {{ item.dest }}
with_items: dirs
This is good and it works, but I run into problems if the destination directory already exists. I don't want to overwrite it, so I thought about trying to stat each dest directory first. I wanted to update the dirs variable with the stat info, but as far as I know, there isn't a good way to set or update variables once they're defined. So I used stat to get the info on each directory and then saved the data with register:
- name: Check if directories already exist
stat: path={{ item.dest }}
with_items: dirs
register: dirs_stat
Is there a way to tie the registered stat info to the mv commands? This would be easy if it were a single directory. The looping is what makes this tricky. Is there a way to do this without unrolling this loop into two tasks per directory?
This is not the simplest solution by any means, but if you wanted to use Ansible and not "unroll":
---
- hosts: all
vars:
dirs:
- src: /home/ubuntu/src/test/src1
dest: /home/ubuntu/src/test/dest1
- src: /home/ubuntu/src/test/src2
dest: /home/ubuntu/src/test/dest2
tasks:
- stat:
path: "{{item.dest}}"
with_items: dirs
register: dirs_stat
- debug:
msg: "should not copy {{ item.0.src }}"
with_together:
- dirs
- dirs_stat.results
when: item.1.stat.exists
Simply adapt the debug task to run the appropriate command task instead and when: to when: not ....
You can use stat keyword in your playbook to check if it exists or not if it doesn't then move.
---
- name: Demo Playbook
hosts: all
become: yes
tasks:
- name: check destination
stat:
path: /path/to/dest
register: p
- name: copy file if not exists
command: mv /path/to/src /path/to/src
when: p.stat.exists == False

Resources