I am writing a sls template in saltstack and tried to correctly write a cronjob entry but i am not able to do so cause compiler always cries about syntax problem cause it seems the escaping is not working as i thought.
i need the escaping backslashes inside the cron command too cause otherwise cronjob does not use the format charackters correctly.
this is the code in my template
{{instance}} cron-entry-for-backup-daily:
cron.present:
- user: root
- name: "{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master }} -x {{ instance + '.domain.name' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' + '`date' + '\"' + '+\%Y_\%m_\%d-\%H_\%M' + '\"' + '`' + '_daily.zip'}}"
- minute: '*'
- hour: '22'
- daymonth: '*'
- month: '*'
- dayweek: '1-6'
- identifier: {{ instance + '_dailybackup' }}
- comment: 'Installed via Salt'
the cronjob entry should look like this
* 22 * * 1-6 /opt/online/online_tools/db-tools.py -b 63000 -s xyz123 -x sub.domain.name backup -d dbname -f /opt/onlinebackupdir/dbname/dbname-odoo_db-`date "+\%Y_\%m_\%d-\%H_\%M"`_daily.zip
If i execute this on command line it work if i execute it via cron it works ....
Error message:
i cannot compile my code from the sls file expected , but
found '' in "", line 124, column 154:
... ckupdir/dbname/dbname-odoo_db-date"+\%Y_\%m_\%d-\%H_\%M"_daily.zip"
i already tried some ways but i think i am unable to solve it at the moment to much brain dead already :-( maybe someone knows a solution i also checked the jinja docs for escaping aso ... but i am helpless at the moment
tries:
'`date +\%Y_\%m_\%d-\%H_\%M`' --> error
'`date "+\%Y_\%m_\%d-\%H_\%M"`' --> error
'`date \"+\%Y_\%m_\%d-\%H_\%M\"`' --> error
'`date '+'\%Y_\%m_\%d-\%H_\%M`' --> error
'`date '+\%Y_\%m_\%d-\%H_\%M'`' --> error
'`date \"+"\%Y_\%m_\%d-\%H_\%M"\"'`' --> error
'`date' + '"' + '+\%Y_\%m_\%d-\%H_\%M' + '"' + '`' --> error
'`date' + '"' + '+\%Y_\%m_\%d-\%H_\%M' + '"' + '`' --> error
'`date +%Y_%m_%d-%H%M`' --> compiled and worked in command line but not inside crontab % needs to be escaped
in the meantime i tried this too
"{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master_pw }} -x {{ instance + '.domainname.net' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' }} + {% raw %} `date "+\%Y_\%m_\%d-\%H_\%M"` {% endraw %} + {{'_daily.zip'}}"
"{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master_pw }} -x {{ instance + '.domainname.net' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' + {% raw %} `date "+\%Y_\%m_\%d-\%H_\%M"` {% endraw %} + '_daily.zip'}}"
This does not work either
OK i solved the Problem with this solution, maybe this is not the best way but it solves my problem....
If someone reads this i would be interested in why {% raw %} .... {% endraw % }
does not work
SOLUTION:
"{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master_pw }} -x {{ instance + '.domainname.net' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' + 'date \\\"+\\\\%Y_\\\\%m_\\\\%d-\\\\%H_\\\\%M\\\"' + '_weekly.zip'}}"
now the line in the crontab looks exactly like i need it:
.......`date "+\%Y_\%m_\%d-\%H_\%M"`..... .zip
RESULT in my BACKUP Folder: instance-odoo_db-2016_10_19-22_00_daily.zip
Related
Newbie ansible user here.
I'm trying to print output of ps into csv file but somehow its printing on next column rather than next row.
This is my playbook xml:
- name: Write running process into a csv file
hosts: servers
gather_facts: yes
vars:
output_path: "./reports/"
filename: "process_{{ date }}.csv"
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
lineinfile:
dest: "{{ output_path }}/{{ filename }}"
line:
PID,Started,CPU,Memory,User,Process
create: yes
state: present
delegate_to: localhost
- name: CSV - Get ps cpu
ansible.builtin.shell:
ps -e -o %p, -o lstart -o ,%C, -o %mem -o user, -o %c --no-header
register: ps
- name: CSV - Write into csv file
lineinfile:
insertafter: EOF
dest: "{{ output_path }}/{{ filename }}"
line: "{inventory_hostname}},{{ps.stdout_lines}}"
loop: "{{ ps.stdout_lines }}"
delegate_to: localhost
- name: CSV - Blank lines removal
lineinfile:
path: "./{{ output_path }}/{{ filename }}"
state: absent
regex: '^\s*$'
delegate_to: localhost
current output is like
Example of desired output :
... but somehow its printing on next column rather than next row ... current output is like ...
This is because within your task "CSV - Write into CSV file" you are printing out all stdout_lines for each iteration steps instead of one, the line for the iteration step only.
To print out line by line one could use an approach like
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: CSV - Get ps cpu
shell:
cmd: ps -e -o %p, -o lstart -o ,%C, -o %mem -o user, -o %c --no-header
register: ps
- name: Show 'stdout_lines' one line per iteration for CSV
debug:
msg: "{{ inventory_hostname }}, {{ ps.stdout_lines[item | int] }}"
loop: "{{ range(0, ps.stdout_lines | length) | list }}"
I want to use hostnames of a group in ansible inventory in a template bash script file.
This is my inventory:
[group_srv]
srv1.co.company
srv2.co.company
srv3.co.company
This is that I wrote in script.sh.j2
for i in '{{ groups['group_srv'][0:3].split('.')[0] | join(' ') }}' ; do
echo $i
done
This is my desired output, but I cannot use split() and join() together.
for i in 'srv1' 'srv2' 'srv3'; do
echo $i
done
Any help is appreciated.
Ansible provides a magic variable containing the short name for each server in your inventory (i.e. the first string before the first dot): inventory_hostname_short
You can access all servers vars in an other magic var: hostvars
You can use the map filter to:
extract values for a list of keys inside a dict (e.g. the relevant hosts for your group in hostvars)
select a single key in a list of dictionaries (e.g. the short inventory name in each extracted server vars).
Those two operations can actually be done at once at extraction time.
Putting it all together:
for i in '{{ groups['group_srv'] | map('extract', hostvars) | map(attribute='inventory_hostname_short') | join("' '") }}' ; do
echo $i
done
Which can be shortened as
for i in '{{ groups['group_srv'] | map('extract', hostvars, 'inventory_hostname_short') | join("' '") }}' ; do
echo $i
done
The declaration below does the job
short_0_3: "{{ groups.group_srv[0:3]|
map('split', '.')|
map('first')|
join(' ') }}"
The next option is the extraction of the variable inventory_hostname_short
short_0_3: "{{ groups.group_srv[0:3]|
map('extract', hostvars, 'inventory_hostname_short')|
join(' ') }}"
Both options give
short_0_3: srv1 srv2 srv3
Example of a complete playbook for testing
- hosts: all
gather_facts: false
vars:
short_0_3: "{{ groups.group_srv[0:3]|
map('split', '.')|
map('first')|
join(' ') }}"
tasks:
- block:
- shell: "for i in {{ short_0_3 }}; do echo $i; done"
register: out
- debug:
var: out.stdout_lines
delegate_to: localhost
run_once: true
shell> cat hosts
[group_srv]
srv1.co.company
srv2.co.company
srv3.co.company
srv4.co.company
srv5.co.company
In Jinja2 loop you can use filters.
{% if not loop.last %} {% endif %} adds a white space after every element except the last one.
for i in {% for host in groups['group_srv'] %}'{{ host.split('.')[0] }}'{% if not loop.last %} {% endif %}{% endfor %} ; do
echo $i
done
Result:
for i in 'test-001' 'test-002' ; do
echo $i
done
I'm trying to create a github job that transforms a list of changed files to list of their directories in json format, but completely stuck with the
error shown on screenshot (line 8: Packages/test: Is a directory). Echo command at the end shows empty string.
Action step code here:
- name: Get folders
id: get-folders
run: |
echo "Changed packages: ${{ steps.filter.outputs.changed_files }}"
folders=()
for package in ${{ steps.filter.outputs.changed_files }};
do
folder="$(dirname ${package})"
folders+=("${folder}")
done
result=$("${folders[#]}" | jq -R . | jq -s)
#result=$(jq --compact-output --null-input '$ARGS.positional' --args -- "${folders[#]}")
echo '::set-output name=FOLDERS::${result}'
- run: echo "${{ steps.get-folders.outputs.FOLDERS }}"
YOUR_BASH_ARRAY=( a b c );
jsonString="$(jq --compact-output --null-input '$ARGS.positional' --args -- "${YOUR_BASH_ARRAY[#]}")"
Result: jsonString==["a","b","c"]
Credit to pmf for calling out the answer provided by the ops' question.
I was trying to write a state to set patching status, the if statement based on a result from a cmd.run, I checked the result that it should be 'No', meaning no available patches, but my state return said otherwise.
I checked:-
# salt 'minion1' cmd.run "yum --security check-update | grep 'for security'| awk '{print \$1}'"
minion1:
No
#
My state file:-
{% set minion = salt['cmd.run']("yum --security check-update | grep 'for security'| awk '{print \$1}'") %}
{% if minion|float == 'No' %}
no_patch:
cmd.run:
- name: echo "no update"
{% else %}
patch_pls:
cmd.run:
- name: echo "Patch please"
{% endif %}
my run result:-
minion1:
----------
ID: patch_pls
Function: cmd.run
Name: echo "Patch please"
Result: True
Comment: Command "echo "Patch please"" run
Started: 12:22:10.894779
Duration: 10.047 ms
Changes:
----------
pid:
19397
retcode:
0
stderr:
stdout:
Patch please
Summary for minion1
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 10.047 ms
Been spending hours trying to figure it out but just feeling defeated and frustrated right now.
You're my last hope. Thanks in advance.
How can I append multiple templates to a single file depending on a variable.
I have 3 variables vara, varb, varc. And their true/false determine whether I can use template1, template2, template3.
Their can be multiple permutations of these three files.
So what should be best strategy to append these templates in a single file.
Eg: if vara is true, varb is false, varc is true.
Then output file should contain template1 and template3.
If you are putting all of this in a single output file, you should only use one template, but put your if elsif logic in the template itself.
{% if vara == true %}
text1
{% elif varb == true %}
text2
{% elif varc == true %}
text3
{% endif %}
Q: "Eg: if vara is true, varb is false, varc is true. Then output file should contain template1 and template3."
A: It's possible to use lookup and template plugin. For example
shell> cat test.j2
{% if vara %}{{ lookup('template', 'template1.j2') }}{% endif %}
{% if varb %}{{ lookup('template', 'template2.j2') }}{% endif %}
{% if varc %}{{ lookup('template', 'template3.j2') }}{% endif %}
shell> cat template1.j2
vara: {{ vara }}
shell> cat template2.j2
varb: {{ varb }}
shell> cat template3.j2
varc: {{ varc }}
- hosts: localhost
vars:
vara: true
varb: false
varc: true
tasks:
- template:
src: test.j2
dest: test.txt
give
shell> cat test.txt
vara: True
varc: True
This would cause each template to be copied if its corresponding variable is true without the need of extra Jinja2 templating
- name: copy template
template:
src: "{{ item.name }}"
dest: /my/dest/dir/
when: item.state ## this is a simplified version of "when item.state == True"
loop:
- vara_template: "{ name: 'template1', state: vara }"
- varb_template: "{ name: 'template2', state: varb }"
- varc_template: "{ name: 'template3', state: varc }"