0

I am at wits end.

My scenario is that I am using python requests to interact with Icinga2 API and I am trying to schedule a downtime. So I know how that is supposed to works and it works most of time. But unfortunately I am totally out of luck when the Icinga2 service I try to set a downtime to has name with a backslash in it.

My test environment:

  • Icinga2 2.9.0
  • Python 3.6.8 / Python 3.8.11
  • requests 2.27.0

Prerequisite: Create a host in Icinga. Create a service in Icinga with a "\"-character.

Python Code for Reproduction:

import requests
session = requests.Session()
session.hooks = {
             "response": lambda r, *args, **kwargs: r.raise_for_status()
         }

session.headers.update(
             {
                 "Accept": "application/json",
                 "Content-Type": "application/json",
                 "cache-control": "no-cache",
             }
         )

session.auth = requests.auth.HTTPBasicAuth("user","password")
url = "https://icinga2-server.com:5665/v1/actions/schedule-downtime"

body = {
   'comment': 'Downtime',
    'author': ('icingaadmin',),
    'start_time': 1605196800.0,
    'filter': 'host.name==\"HOSTNAME\" && service.name==\"SERVICE\\NAME\"',
    'end_time': 1605286800.0,
    'fixed': True,
    'type': 'Service'}

session.post(url, json=body, verify=False)

Result:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/root/.pyenv/versions/3.8.11/lib/python3.8/site-packages/requests/sessions.py", line 590, in post
    return self.request('POST', url, data=data, json=json, **kwargs)
  File "/root/.pyenv/versions/3.8.11/lib/python3.8/site-packages/requests/sessions.py", line 542, in request
    resp = self.send(prep, **send_kwargs)
  File "/root/.pyenv/versions/3.8.11/lib/python3.8/site-packages/requests/sessions.py", line 662, in send
    r = dispatch_hook('response', hooks, r, **kwargs)
  File "/root/.pyenv/versions/3.8.11/lib/python3.8/site-packages/requests/hooks.py", line 31, in dispatch_hook
    _hook_data = hook(hook_data, **kwargs)
  File "<stdin>", line 2, in <lambda>
  File "/root/.pyenv/versions/3.8.11/lib/python3.8/site-packages/requests/models.py", line 953, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://icinga2-server.com:5665/v1/actions/schedule-downtime

I know very well that this error message indicates that Icinga2 could not find/match a service. But executing the command via curl is clearly working for me and I get a proper scheduled downtime!

CURL Request:

curl -k -s -u user:password -H 'Accept: application/json'  -X POST 'https://icinga2-server.com:5665/v1/actions/schedule-downtime'  -d '{"comment": "Downtime", "author": ["icingaadmin"], "start_time": 1605196800.0, "filter": "host.name==\"MSSQLSERVER\" && service.name==\"MSSQLSERVER\\\\INSTANCE2\"", "end_time": 1605286800.0, "fixed": true, "type": "Service"}'

CURL Answer (SUCCESS):

{"results":[{"code":200.0,"legacy_id":8.0,"name":"MSSQLSERVER!MSSQLSERVER\\INSTANCE2!137c9ef9-3150-4e57-ba0b-a22ddc6611d4","status":"Successfully scheduled downtime 'MSSQLSERVER!MSSQLSERVER\\INSTANCE2!137c9ef9-3150-4e57-ba0b-a22ddc6611d4' for object 'MSSQLSERVER!MSSQLSERVER\\INSTANCE2'."}]}

Alternate approaches that did not help:

session.post(url, data=json.dumps(body), verify=False)

string_body = json.dumps(body)
session.post(url, data=string_body, verify=False)

phospi
  • 3
  • 4
  • Are you sure the problem didn't occur when you *created* the service due to a missed backslash? You may have created a service with a newline instead of a backslash. Does Icinga have an API you can use to retrieve the actual service names? – Panagiotis Kanavos Jul 23 '21 at 15:34
  • Of course, I created the service (outside of API) and read the service via API. Service is shown with a "\". – phospi Jul 26 '21 at 08:23

2 Answers2

0

You can try adding an r to the front of the string after the filter key. Also, I think this closed parenthesis is unnecessary.

Python treats backslashes in strings as special characters, adding an 'r' before the string make it not treat them as special characters, and instead as backslashes 'filter': r'host.name=="HOSTNAME" && service.name=="SERVICE\NAME"'),

https://www.journaldev.com/23598/python-raw-string

body1 = {
   'comment': 'Downtime',
    'author': ('icingaadmin',),
    'start_time': 1605196800.0,
    'filter': 'host.name==\"HOSTNAME\" && service.name==\"SERVICE\\NAME\"',
    'end_time': 1605286800.0,
    'fixed': True,
    'type': 'Service'}

body2 = {
   'comment': 'Downtime',
    'author': ('icingaadmin',),
    'start_time': 1605196800.0,
    'filter': r'host.name==\"HOSTNAME\" && service.name==\"SERVICE\\NAME\"',
    'end_time': 1605286800.0,
    'fixed': True,
    'type': 'Service'}

body1==body2 -> False

body1['filter'] = 'host.name=="HOSTNAME" && service.name=="SERVICE\\NAME"'

body2['filter'] = 'host.name==\\"HOSTNAME\\" && service.name==\\"SERVICE\\\\NAME\\"'
Leo Wotzak
  • 196
  • 7
  • Same error. Take your proposal into the body (body1) and compare it to my body (body2). In python cli: body1 == body2 >>> True – phospi Jul 26 '21 at 08:20
  • Really? when i do a direct comparison in my CLI its saying they are not equal – Leo Wotzak Jul 26 '21 at 16:32
0

Thank you very much for your support. I figured it out. I was able to successfully set a downtime with the following command to which Leo Wotzak beat me to.

body1 = {
   'comment': 'Downtime',
    'author': ('icingaadmin',),
    'start_time': 1605196800.0,
    'filter': 'host.name==\"HOSTNAME\" && service.name==\"SERVICE\\\\NAME\"',
    'end_time': 1605286800.0,
    'fixed': True,
    'type': 'Service'}

I don't know why I didn't run a test with 4 backslashes earlier.

And to add some background to the situation. The initial problem was, that I was reading the output from available icinga services which gives me:

>>>> resp.json()['results'][1]['name']
'MSSQLSERVER!SERVICE\\NAME'

If I put that answer into the body it will fail because json converter does not convert 2 backslashes to 4 backslashes. Question is, what is the best way to tell json that 2 backslashes need to be 4 backslashes? My idea is to work with encoding:

svc_name = resp.json()['results'][1]['name'].encode("unicode-escape").decode("utf-8")

I don't know if that's the best way but it works.

phospi
  • 3
  • 4