I foolishly assumed that Ansible already had modules for scheduling and clearing Icinga 2 downtimes, but a reasonably extensive search turned up empty. The only available Icinga modules are for adding or removing hosts and enabling/disabling features. Am I missing something? Before I start hacking, has anyone done any work in this area?
Hi
The ansible modules are a WIP and we are adding functionality as we go along.
There are 2 ansible icinga2 repositories, one with the ability to add hosts and configure checks,
the other is a massive re-write and designed to help build a full redundant and distributed Icinga2 deployment with features management.
Sadly work on the new module has slowed down due to people IRL commitments and we are always happy to accept PR’s from the community to enhance the existing functionality for both playbooks.
If you want to contribute, please go over the issues listing in each repository to see what we are aiming to add, or if you have any functionality that is not covered in any of the listed issues, we will gladly check any PR that answers that feature.
Hi Magnus, I had the exact same issue. I ended up writing a role using the URI module and triggering downtimes via Icinga2 API. It looks like this:
indent preformatted text by 4 spaces
- name: set Host downtime
delegate_to: localhost
uri:
url: "{{ icinga_host }}:{{ icinga_api_port }}{{ icinga_dt_url }}type={{ type }}&{{ type|lower }}={{ item }}.{{ ansible_domain }}"
user: "{{ icinga_api_user }}"
password: "{{ icinga_api_pass }}"
validate_certs: false
method: POST
body_format: json
headers:
Accept: "application/json"
status_code: 200
body: '{ "author": "{{ author }}", "comment": "{{ comment }}",
"notify": true, "pretty": true, "start_time": {{ ansible_date_time.epoch }},
"end_time": "{{ ansible_date_time.epoch | int + duration | int }}", "duration": {{ duration }}, "fixed": false }'
with_items: "{{ ansible_hostname }}"
when: type == 'Host'
Just as an example for host downtimes…hope this helps… Best Steve
Forgot:
Example Run
ansible-playbook icinga_downtime.yml -l HOST --ask-vault-pass --extra-vars '{dt_task: "add or remove", type: "Service or Host", (service: ["SERVICENAME"],) comment: "your comment", duration: "your duration", author: "author"}'
Hi Steve,
great example. Could you please format the code samples as pre-formatted text? It would increase the readability
Kind regards,
Bernd
Those are Ansible roles for setting up Icinga itself. I don’t see how they would be useful for manipulating downtimes from non-Icinga playbooks.
Thanks for the example! I had the uri module as a backup plan and I’m glad it’s actually doable, but it sure doesn’t look pretty.
Instead of hard-coding a serialized JSON object perhaps one could construct a Jinja2 dict and pass it through the to_json
filter:
body: '{{ {"author": author, "comment": comment, ...} | to_json }}'
Hi Bernd, sorry I’m new here. I really tried using the CTRL+SHIFT+C solution from this editor here, but it is still not working. Is there any trick? Thanks
Hi Steve,
no problem I realized right now it isn’t sufficient to just indent the text (or use the Ctrl+Shift+C key). You have to insert a blank line before:
- name: set Host downtime
delegate_to: localhost
...
vs. not having a blank line:
- name: set Host downtime
delegate_to: localhost
…
Kind regards,
Bernd
hi.
I tried a simplified playbook to test downtime from ansible:
---
- name: set Host downtime
hosts: localhost
tasks:
- name: set Host downtime to API endpoint
delegate_to: localhost
uri:
url: https://icingahostname.domain.int:5665/v1/actions/schedule-downtime
user: 'superapiuser'
password: 'password'
validate_certs: false
method: POST
body: '{ "type": "Host", "filter": "host.name==\"downtimehostname.domain.int\"", "start_time": 1727276800, "end_time": 1727277400, "duration": 1000, "author": "icingaaaaa", "comment": "IPv4 network maintenance", "pretty": true, "all_services": true }'
body_format: json
headers:
Content-Type: application/json
register: result
but I obtain:
“msg”: “Status code was 400 and not [200]: HTTP Error 400: Bad Request”,
“redirected”: false,
“server”: “Icinga/r2.14.2-1”,
“status”: 400,
“url”: “https:/icingahostname.domain.int:5665/v1/actions/schedule-downtime”
Looking on Icinga log I see:
information/HttpServerConnection: Request POST /v1/actions/schedule-downtime (from [::ffff:]:47912), user: , agent: ansible-httpget, status: Bad Request) took 0ms.
If I use the same body using curl, it works.
What is the problem in your opinion?
Thanks a lot
Mario
Looking at your playbook, there are two things I would change:
- Add
force_basic_auth: true
asuser
andpassword
alone may not result in the header to be set, unless the server responds with a 401 first. - The
header.Content-Type
looks wrong. You should send anAccept
header.headers: Accept: application/json
Applying those changes (and altering the credentials), I just ran your playbook against my local Icinga 2 and received a 404 instead of a 400, which is to be expected there is no such object on my instance.
Thanks a lot!
It works!
Mario
This is my final playbook.
I added “Item” so I can iterate with inventory hostname of the group “windows”
---
- name: set Host downtime
hosts: localhost
tasks:
- name: set Host downtime to API endpoint
delegate_to: localhost
uri:
url: https://icingahostname.domain.int:5665/v1/actions/schedule-downtime
force_basic_auth: true
user: 'superapiuser'
password: 'password'
validate_certs: false
method: POST
body: '{ "type": "Host", "filter": "host.name==\"{{ item }}\"", "start_time": {{ ansible_date_time.epoch }}, "end_time": "{{ ansible_date_time.epoch | int + 7200 | int }}", "duration": 7200, "author": "icingaaaa", "comment": "Update", "pretty": true, "all_services": true }'
body_format: json
headers:
Content-Type: application/json
headers:
Accept: application/json
with_items:
- "{{ groups['windows'] }}"
register: result
I call it with:
ansible-playbook -i inventory.yaml
where inventory.yaml is:
[windows]
hostname1.domain.int
hostname2.domain.int
If you want to do something to Icinga2 with Ansible, it’s always worth a look at the LFOPS of the @linuxfabrik.