Update 2015-10-07: This plugin has evolved – please check the latest README for up to date details.
Hello all ! I wrote a plugin for Nagios that will parse JSON from an HTTP response. If that sounds interesting to you, feel free to check out my check_http_json repo on Github. The plugin has been tested with Ruby 1.8.7 and 1.9.3. Pull requests welcome !
Usage: ./check_http_json.rb -u -e -w -c -h, --help Help info. -v, --verbose Additional human output. -u, --uri URI Target URI. Incompatible with -f. --user USERNAME HTTP basic authentication username. --pass PASSWORD HTTP basic authentication password. -f, --file PATH Target file. Incompatible with -u. -e, --element ELEMENT Desired element (ex. foo=>bar=>ish is foo.bar.ish). -E, --element_regex REGEX Desired element expressed as regular expression. -d, --delimiter CHARACTER Element delimiter (default is period). -w, --warn VALUE Warning threshold (integer). -c, --crit VALUE Critical threshold (integer). -r, --result STRING Expected string result. No need for -w or -c. -R, --result_regex REGEX Expected string result expressed as regular expression. No need for -w or -c. -W, --result_warn STRING Warning if element is [string]. -C is required. -C, --result_crit STRING Critical if element is [string]. -W is required. -t, --timeout SECONDS Wait before HTTP timeout.
The --warn
and --crit
arguments conform to the Nagios threshold format guidelines.
If a simple result of either string or regular expression (-r
or -R
) is specified :
- A match is OK and anything else is CRIT.
- The warn / crit thresholds will be ignored.
If the warn and crit results (-W
and -C
) are specified :
- A match is WARN or CRIT and anything else is OK.
- The warn / crit thresholds will be ignored.
Note that (-r
or -R
) and (-W
and -C
) are mutually exclusive.
Note also that the response must be pure JSON. Bad things happen if this isn’t the case.
How you choose to implement the plugin is, of course, up to you. Here’s one suggestion:
# check json from http define command{ command_name check_http_json-string command_line /etc/nagios3/plugins/check_http_json.rb -u 'http://$HOSTNAME$:$ARG1$/$ARG2$' -e '$ARG3$' -r '$ARG4$' } define command{ command_name check_http_json-int command_line /etc/nagios3/plugins/check_http_json.rb -u 'http://$HOSTNAME$:$ARG1$/$ARG2$' -e '$ARG3$' -w '$ARG4$' -c '$ARG5$' } # make use of http json check define service{ service_description elasticsearch-cluster-status check_command check_http_json-string!9200!_cluster/health!status!green } define service{ service_description elasticsearch-cluster-nodes check_command check_http_json-int!9200!_cluster/health!number_of_nodes!4:!3: }
Dear Dan, may I ask you how to address the entry DOB in the following JSON response using your plugin? Thanks for help and sorry about pointing to such an old post 😉 Regards, Reinhard
{
“responseHeader”:{
“status”:0,
“QTime”:1,
“params”:{
“indent”:”true”,
“q”:”firstName:MyFirstName ANDnlastName:MyLastName ANDncity:MyCity”,
“wt”:”json”}},
“response”:{“numFound”:1,”start”:0,”docs”:[
{
“id”:”37858200″,
“Sex”:”M”,
“firstName”:”MyFirstName”,
“lastName”:”MyLastName”,
“DOB”:”12345678″,
“zipCode”:”1234″,
“city”:”MyCity”,
“streetName”:”MyStreet”,
“streetNumber”:”1″,
“Status”:”U”,
“Wikex_Pers”:”12″,
“Wikex_PAC”:”12″,
“Wikex_Raster”:”12″,
“_version_”:12345678}]
}}
LikeLike
Hello,
You probably tried specifying the element as
response.docs.DOB
and the plugin couldn’t find it, right? 🙂 Pro tip: you can use theelement_regex
feature to help you track down cases like this. Consider:$ ./check_http_json.rb -f ./reinhard.json -E DOB -r 12345678
OK: response.docs.0.DOB is 12345678
As you can see, the dotted path contains a zero, which solves your problem but raises a new question: why is there a zero at all? If you look at the JSON again, you can see that the data structure described by
response.docs
is an array, which adds a layer of abstraction that needs to be labelled. In Ruby, arrays are referenced positionally (starting with zero), which is exactly where the label comes from.Hope that helps!
LikeLike
You saved my day – it works perfect now! Many thanks for sharing your developed plugin and the detailed explanation, I’ve learned something new 😉
Best regards, Reinhard
LikeLiked by 1 person
Hi! Can you explain with examples, how works the regex argument? I want to evaluate a json key that is similar to another key into the json response. Then, the -E argument give me both of them. How can it match the exact key word using the -E argument? For example, I need to get a json element from “foo” key, but the plugin is giving me the “foo_bar” element because it is placed first in the json order.
Sorry for my English 😦
Artur
LikeLike
Hello! You can see an example above in these very comments. Note that
-E
is meant to find things which are inexact, so the behaviour is harder to predict. If you already know the exact item (and your comment suggests that this is the case), then you’re better off just specifying that exact item directly to-e
.LikeLike
Hello! Thanks a lot for your reply!
I did it. I used the following command:
./check_http_json.rb -u ‘http://localhost/info.json’ -E ‘\bfoo\b’ -r ‘A’
RESULT: OK: First ‘\bfoo\b’ (regex) does match A
But I wonder now if the plugin can apply for json list objects. I see that the result only was applied for the first element in the json list, and skipped the next one.
LikeLike
Try out this option:
LikeLike
Hi! Thanks a lot! I tried out this option and it worked.
I have another issue:
Using this command: ./check_http_json.rb -u ‘http://localhost/info.json’ -E ‘\bfoo\b’ -r ‘A’ –element_regex_global from command line works fine. But when I setting up the icinga2 files, the \b (look at -E option above) symbols are parsed as literal and not as escape symbol (\b wrapping a string is for matching exact text in Ruby, according about I was googled). If I do not use \b, then the plugin match first another json field key like ‘foor_bar’, and I need to skip this.
I’ve never programming in ruby, so I wonder if there is any trick to do that the plugin reads these escape symbols properly from the icinga2 config files .
Here the files:
commands.conf:
object CheckCommand “check-json-response” {
command = [ PluginDir + “/check_http_json.rb” ]
arguments = {
“-u” = “$url$”
“-E” = “$json_field_name$”
“-r” = “$expected_value$”
“–element_regex_global” = {
set_if = true
}
}
}
services.conf:
apply Service “check-json-response-service” {
import “generic-service”
check_command = “check-json-response”
check_interval = 5
max_check_attempts = 5
retry_interval = 2
vars.url = “http://localhost/info.json”
vars.json_field_name = “\bfoo\b”
vars.expected_value = “A”
assign where host.name == NodeName
}
LikeLike
If you’re trying to find an exact string, then why are you using regex in the first place? Just use
-e
to specify exactly what you’re looking for. It’s hard for me to guess what you want without seeing the JSON in any case.LikeLike
Because when I use ‘-e’, it matches another json field which its name start with the same string. For instance, I need to match the json field “city”, but the scrips is giving me the field “cityInitial”. Is it clear?
If we have this json file:
[{“country”: “Argentina”, “cityInitial”: “CH”, “city”: “Chacabuco”}, … and some others list items …
]
How do you doing to get the exact json field “city” for checking its value?
Thanks in advance for your support!
LikeLike
-e
should only match the exact string. If it’s matching something else, then I guess that’s a bug, but one that I’m not able to reproduce. If you can supply the source json – or a sanitised snippet thereof – that you’re querying, it would help me to debug.LikeLike
Hi! I’ve not seen this comment. For example, use the following json file:
[{“country”: “Argentina”, “cityInitial”: “BS”, “city”: “Buenos Aires”}, {“country”: “Japan”, “cityInitial”: “TK”, “city”: “Tokyo”}, {“country”: “Australia”, “cityInitial”: “SY”, “city”: “Sydney”}]
and I would like to check every ‘city’ element for matching ‘Buenos Aires’ as result.
LikeLike
Oh, I understand the problem now:
-e
matches the exact field, but only the first response, whereas-E
matches all responses, but not the exact field. It’s not really a bug but the implementation isn’t obvious.Recall that
-E
is parsed as a regular expression, so in this case becausecityInitial
appears first, and because-E city
is parsed as-E /.*city.*/
, the script correctly matches oncityInitial
instead ofcity
. The solution is to provide a more restrictive matching pattern – in your case, it’s as simple as indicating the end of the string (which is done with a dollar sign):-E city$
This explains the behaviour you’re seeing, but unfortunately it won’t actually solve for your use case. Consider:
Combining
-E
,--element_regex_global
, and-r
means that all of the results must match the given result string (which isn’t the case for your data set). It sounds like what you want is a way to see if[{"city":"Buenos Aires"}]
exists anywhere in the result set, which this script is not currently designed to do. That said, if your data set is static or append-only (i.e. the positions don’t change), you can always pick your item position specifically. Consider:Hope that helps (or at least explains things).
LikeLike
Hi again! How can we check the json list length using “-E” plugin option?
Warm regards!
Artur.
LikeLike