Create Ansible local facts from variables - automation

I am trying to create local facts from variables.
My fact is:
datadog_http_checks:
- name : {{ env }} ResourceManager
url : http://{{ inventory_hostname }}:
threshold : 5
window : 5
timeout : 10
My task is:
- include_vars: clouderamanager.yml
- lineinfile: dest=/etc/ansible/facts.d/datadog_http_checks.fact line={{ datadog_http_checks }} create=yes
Which doesn't create the local facts, it fails with following error
TASK [hadoop : lineinfile] *****************************************************
fatal: [hmn001.dev.abc.cc]: FAILED! => {"changed": false, "failed": true, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\r\n File \"/home/xyz/.ansible/tmp/ansible-tmp-1460581285.88-252089462921262/lineinfile\", line 2540, in <module>\r\n main()\r\n File \"/home/abc/.ansible/tmp/ansible-tmp-1460581285.88-252089462921262/lineinfile\", line 371, in main\r\n ins_aft, ins_bef, create, backup, backrefs)\r\n File \"/home/abc/.ansible/tmp/ansible-tmp-1460581285.88-252089462921262/lineinfile\", line 266, in present\r\n lines.append(line + os.linesep)\r\nTypeError: can only concatenate list (not \"str\") to list\r\n", "msg": "MODULE FAILURE", "parsed": false}

Lineinfile does exactly what it says it does: it changes a single line in a file.
If you want to create a local fact that looks like:
datadog_http_checks:
- name : {{ env }} ResourceManager
url : http://{{ inventory_hostname }}:
threshold : 5
window : 5
timeout : 10
Then you need to create a file that looks like this:
[datadog_http_checks]
name={{ env }} ResourceManager
url=http://{{ inventory_hostname }}:
threshold=5
window=5
timeout=10
You can do this with the template module if, like in your example, you have variables in it that you want to dynamically build.
In this exact scenario though I'm confused as to what the benefit of this is rather than having a variable set up as you have in your question and using that rather than templating out a local fact file and then re-reading it later.

Related

Ansible Jinja Template Failing on Cisco.ios.ios_config playbook

I have the following playbook, jinja template and yaml data. I am running this against a Cisco 9300 or 3850 switch. I just get the following error. If I run just the commands without the template the playbook runs fine. I have output the template to a yaml file and I don't see any issues with the output. The command in the error below is a valis command.
fatal: [3850-access-42]: FAILED! => {
"changed": false,
"module_stderr": "before: default interface GigabitEthernet2/0/1\r\nbefore: default interface GigabitEthernet2/0/1\r\n ^\r\n% Invalid input detected at '^' marker.\r\n\r\n3850-Access-(config)#",
"module_stdout": "",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error"
}
Playbook
- name: Update edge switch
hosts: all
gather_facts: false
vars:
- template_path: "./templates/interfaces.j2"
tasks:
- name: include variables
include_vars: "./files/vars/jinja-test-data.yml"
- name: update config
cisco.ios.ios_config:
src: "{{ template_path }}"
jinja template
{% for item in ip_interfaces %}
before: default interface {{ item.interface }}
lines:
{% for line in item.int_attributes %}
- {{ line }}
{% endfor %}
parents: interface {{ item.interface }}
match: strict
after:
{% for cmd in item.shut_commands %}
- {{ cmd }}
{% endfor %}
{% endfor %}
yaml data
---
ip_interfaces:
- interface: GigabitEthernet2/0/1
int_attributes:
- switchport access vlan 20
- switchport voice vlan 21
- speed 100
- duplex full
- switchport mode access
- switchport nonegotiate
- no switchport port-security
- spanning-tree portfast
- spanning-tree bpduguard enable
- device-tracking attach-policy IPDT_POLICY
- source template WIRED_DOT1X_CLOSED
shut_commands:
- shutdown
- no shutdown
If I run the playbook without the template using the same data it runs fine.
Working playbook
- name: Update edge switch
hosts: all
gather_facts: false
vars:
- template_path: "./templates/interfaces.j2"
tasks:
- name: include variables
include_vars: "./files/vars/jinja-test-data.yml"
# - name: update config
# cisco.ios.ios_config:
# src: "{{ template_path }}"
- name: update config
cisco.ios.ios_config:
before: default interface TwoGigabitEthernet1/0/1
lines:
- switchport access vlan 10
- switchport voice vlan 11
- speed 100
- duplex full
- switchport mode access
- switchport nonegotiate
- no switchport port-security
- spanning-tree portfast
- spanning-tree bpduguard enable
- device-tracking attach-policy IPDT_POLICY
- source template WIRED_DOT1X_CLOSED
parents: interface TwoGigabitEthernet1/0/1
match: strict
after:
- shutdown
- no shutdown
vars:
ansible_command_timeout: 480
Results from working playbook
TASK [update config]
*************************************************************************************************** task path: redirecting (type: connection) ansible.builtin.network_cli
to ansible.netcommon.network_cli redirecting (type: terminal)
ansible.builtin.ios to cisco.ios.ios redirecting (type: cliconf)
ansible.builtin.ios to cisco.ios.ios redirecting (type: action)
cisco.ios.ios_config to cisco.ios.ios redirecting (type: action)
cisco.ios.ios_config to cisco.ios.ios [WARNING]: To ensure idempotency
and correct diff the input configuration lines should be similar to
how they appear if present in the running configuration on device
changed: [9300-access-240] => {"banners": {}, "changed": true,
"commands": ["default interface TwoGigabitEthernet1/0/1", "interface
TwoGigabitEthernet1/0/1", "switchport voice vlan 11", "speed 100",
"duplex full", "switchport mode access", "switchport nonegotiate", "no
switchport port-security", "spanning-tree portfast", "spanning-tree
bpduguard enable", "device-tracking attach-policy IPDT_POLICY",
"source template WIRED_DOT1X_CLOSED", "shutdown", "no shutdown"],
"updates": ["default interface TwoGigabitEthernet1/0/1", "interface
TwoGigabitEthernet1/0/1", "switchport voice vlan 11", "speed 100",
"duplex full", "switchport mode access", "switchport nonegotiate", "no
switchport port-security", "spanning-tree portfast", "spanning-tree
bpduguard enable", "device-tracking attach-policy IPDT_POLICY",
"source template WIRED_DOT1X_CLOSED", "shutdown", "no shutdown"]}
META: ran handlers META: ran handlers
PLAY RECAP
************************************************************************************************************* hav-lab-9300-access-240 : ok=2 changed=1 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
I figured this out finally. It seems that the ios_config module assumes the lines: command so it is not needed in the template output. All of the lines do not need to be preceeded by the "- " as they would in a normal playbook call directly to the ios_config module. If there is a parent object the subsequent lines are indented by one space. This is plainly shown in the docs which I did not pick up as soon as I wished I had. I hope this helps someone else out or saves them some time. Now I just need to figure out if the before or after command can be used as well. It would be a shame to have to loop through three different templates and hit the interfaces three seperate times.
> # Example ios_template.j2
> # ip access-list extended test
> # permit ip host 192.0.2.1 any log
> # permit ip host 192.0.2.2 any log
> # permit ip host 192.0.2.3 any log
> # permit ip host 192.0.2.4 any log

How to know in which task an ansible play failed when launched using AWX API?

I intend to launch ansible jobs on AWX using AWX api and get a call back from the ansible playbook to be informed about the result of the play.
To do so I'm using the /api/v2/job_templates/<job-template-id>/launch/ with some extra_vars in the body to pass parameters to my play.
{
"extra_vars": {
"target": "w.x.y.z", (put here a real IP)
"directory_name_1": "dir1",
"directory_name_2": "dir2",
"file_name": "file1" (or "subdir/file1" to make it fails)
}
}
I've also configured a webhook notification in the job-template with the default customization: {{ job_metadata }}
I've put here the play I'm using which is super simple, it creates 2 directories and one file in the first directory.
- hosts: "{{ target }}"
name: fbplay
tasks:
- name: Create dummy directory 1
file:
path: "{{directory_name_1}}"
state: directory
- name: Create dummy directory 2
file:
path: "{{directory_name_2}}"
state: directory
- name: Create dummy file in directory
file:
path: "{{directory_name_1}}/{{file_name}}"
state: touch
mode: u=rw,g=r,o=r
All of this work great and in case of error 4 tasks will be executed on the target machine:
TASK [Gathering Facts] *********************************************************
TASK [Create dummy directory 1] ************************************************
TASK [Create dummy directory 2] ************************************************
TASK [Create dummy file in directory] ******************************************
...but here is my question with regard to error handling: How can I indicate in the call back which task failed in case of error ?
In fact I can know if the play failed or not getting, in case of success:
"hosts": {
"w.x.y.z": {
"failed": false,
"changed": 1,
"dark": 0,
"failures": 0,
"ok": 4,
"processed": 1,
"skipped": 0,
"rescued": 0,
"ignored": 0
}
}
and in case of failure:
"hosts": {
"w.x.y.z": {
"failed": true,
"changed": 0,
"dark": 0,
"failures": 1,
"ok": 3,
"processed": 1,
"skipped": 0,
"rescued": 0,
"ignored": 0
}
}
But I cannot get the exact task that failed (in this case the last one by passing a filename containing a sub-directory that does not exist for example).
I'm a newbie on AWX & ansible and I'm fighting with what I thought would be a relatively simple point... so any hints or ideas is welcome.
Thx beforehand.
in case it helps someone, I actually confirm that ARA does what I was looking for above which is to display if using its web client (or provide through an API) exactly which task failed in each playbook you ran.
ARA does an excellent job for analyzing awx-runs, but it has to be set up on the AWX-server (the host running the AWX instance), if I understand correctly.
A workaroud would be to use the awx commandline interface ( see https://docs.ansible.com/ansible-tower/latest/html/towercli/index.html and * ):
watch --color 'awx --conf.host https://awx.site --conf.username my.user --conf.password "my_super_secret" -k -f human job_events list --job 3232445 --event "runner_on_failed" --filter "stdout"'
One could also sroll the API for different "?event=runner_on_failed", like:
https://your.awx.site/api/v2/jobs/3232445/job_events/?event=runner_on_failed
Or other "filters" like:
awx --conf.host https://awx.site --conf.username my.user --conf.password "my_supersecret" -k -f human job_events list --job 3232445 --event "runner_on_unreachable" --filter "*" >/tmp/tmp
https://docs.ansible.com/ansible-tower/latest/html/towercli/output.html#human-readable-tabular-formatting
https://docs.ansible.com/ansible-tower/latest/html/towercli/reference.html#awx-job-events-list
* (not to confuse with awx-cli/tower-cli - I think they are different)

Ansible: Lookup variables dynamically in v2.3

I have a set of variables and a task as follows. My intent is to dynamically do a healthcheck based on the URL the user chose.
vars:
current_hostname: "{{ ansible_hostname }}"
hc_url1: "https://blah1.com/healthcheck"
hc_url2: "https://blah2.com/healthcheck"
tasks:
- name: Notification Msg For Healthcheck
shell: "echo 'Performing healthcheck at the URL {{ lookup('vars', component) }} on host {{ current_hostname }}'"
Run playbook in Ansible 2.3
ansible-playbook ansible_playbook.yml -i inventory -k -v --extra-vars "component=hc_url1"
Error
fatal: [hostname]: FAILED! => {"failed": true, "msg": "lookup plugin (vars) not found"}
I know this happens because lookup plugin "var" was introduced in Ansible v2.5. Is there a way to do this in Ansible 2.3? I want get the value of {{ component }}, and then the value of {{ hc_url1 }}
PS - upgrading to 2.5 is not an option because of org restrictions
Alternatively, maybe you can do this using a dictionary.
For example,
vars:
current_hostname: "{{ ansible_hostname }}"
urls:
hc_url1: "https://blah1.com/healthcheck"
hc_url2: "https://blah2.com/healthcheck"
tasks:
- name: Notification Msg For Healthcheck
shell: "echo 'Performing healthcheck at the URL {{ urls[component] }} on host {{ current_hostname }}'"
That way, the user provided value of component will just be looked up as a key in the dictionary.

ansible delegate_to variable

Hi I have below dynamic/static host group entry which has information about datacenter and environment
tag_Cluster_gateway_use1_qa_gw_proxy
I need to use above group entry for delegate_to with two variables passed inline like below
- name: Copy Nginx Template
template:
src: nginx.conf.ctmpl.j2
dest: "/var/consul/template/web.conf.ctmpl"
delegate_to: "{{ item }}"
with_items: "groups.tag_Cluster_gateway_{{ datacenter }}_{{ stage }}_gw_proxy"
But when when result was
failed: [172.16.16.136] (item=groups.tag_Cluster_gateway_use1_qa_gw_proxy) => {"item": "groups.tag_Cluster_gateway_use1_qa_gw_proxy", "msg": "Failed to connect to the host via ssh.", "unreachable": true}
fatal: [172.16.16.136]: UNREACHABLE! => {"changed": false, "msg": "All items completed", "results": [{"_ansible_item_result": true, "item": "groups.tag_Cluster_gateway_use1_qa_gw_proxy", "msg": "Failed to connect to the host via ssh.", "unreachable": true}]}
Which I assume Ansible is taking groups.tag_Cluster_gateway_use1_qa_gw_proxy as a host entry rather group entry.
Also tried with
delegate_to: "{{ item }}"
with_items: "{{ groups['tag_Cluster_gateway_{{ datacenter }}_{{ stage }}_gw_proxy'] }}"
And result was
[DEPRECATION WARNING]: Skipping task due to undefined Error, in the future this will be a fatal error.: 'dict object' has no attribute
'tag_Cluster_gateway_{{ datacenter }}_{{ stage }}_gw_proxy'.
This feature will be removed in a future release. Deprecation warnings can
be disabled by setting deprecation_warnings=False in ansible.cfg.
fatal: [172.16.16.136]: FAILED! => {"failed": true, "msg": "'item' is undefined"}
But if I statically mention like groups.tag_Cluster_gateway_use1_qa_gw_proxy it works fine.
Hope I've provided enough details for the issue. Please help me on how to have variables passed with deletage_to for host group
Your question was pretty long ago, so I assume it is not a problem anymore. If it still is, try the following:
delegate_to: "{{ item }}"
with_items: "groups['tag_Cluster_gateway_{{ datacenter }}_{{ stage }}_gw_proxy']"
The {{ }} brackets around the whole statement lets ansible search for the whole "tag_..._proxy" statement as variable. Ansible still seems to try to replace {{ item }} with the whole statement, which is why the error message states "'item' is undefined"

How to add more than one value to a variable in ansible playbook?

I have defined a value to variable in my playbook as :
war_name: abc
and i m calling this war_name in the roles as :
- name: Download war file
get_url:
url=http://url/directory/packages/tomcat/{{ war_name }}.war
Now the problem is I have to assign 2 values to variable war_name in playbook
like
war_name: abc,xyz
How can i do this in my playbook?
If used with_items as :
- name: Download war file
get_url:
url=http://url/directory/packages/tomcat/{{ item }}.war
with_items:
- abc
- xyz
when: "'server' in app_name"
It results in an error as :
TASK [tomcat : Download war file]
********************************************** failed: [10.x.x.x] (item=abc) => {"failed": true, "item": "abc", "msg": "missing required
arguments: dest"} failed: [10.x.x.x] (item=xyz) => {"failed": true,
"item": "xyz", "msg": "missing required arguments: dest"}
You are missing the dest argument that is required for get_url
Here is the modified version of your task:
- name: Download war file
get_url:
url: "http://url/directory/packages/tomcat/{{ item }}.war"
dest: "/path-where-you-want-to-place/{{ item }}"
with_items:
- abc
- xyz
when: "'server' in app_name"
Hope that help you
what you can do is something like this
war_name:
war1: one
war2: two
This is basically a dictionary. Though i am not sure what actually you wanna do here? I guess you want to place 2 wars at a same location with different names, if that is the case you can actually use with_items