Problems monitoring services with array values

Hello,

I’m trying to set up a check using check_http that checks a set of pages on a webserver based on a custom array attached to each host object. While the number of checks expands to match the number of array elements, the check command seems to include all of them (so all checks are the same).

As a result of all array elements being added to the command the check looks like this:

'/usr/lib/nagios/plugins/check_http' '-H' 'sub.example.org' '-I' '172.16.4.65' '-S' '-u' '/GiveMeA404' '-u' '/'

Whereas I’d expect two checks like this:
'/usr/lib/nagios/plugins/check_http' '-H' 'sub.example.org' '-I' '172.16.4.65' '-S' '-u' '/GiveMeA404'
and
'/usr/lib/nagios/plugins/check_http' '-H' 'sub.example.org' '-I' '172.16.4.65' '-S' '-u' '/'

I don’t understand why Icinga is expanding the config to create the correct number of checks, yet the check command isn’t being expanded in the same way. The service template config is below.

Any advice would be gratefully appreciated. I’ve seen the fields interface array example but it stops short of explaining how to use the configuration.

Many thanks in advance


Command

object CheckCommand "HTTP_PageCheck" {
    import "plugin-check-command"
    import "http"

    arguments += {
        "-H" = "$Web_DNS_name$"
        "-S" = {}
        "-u" = {
            required = true
            value = "$Pages_to_check$"
        }
    }
}

That is the command not the service :slight_smile:
We need to see your service apply rule and also an example of your array.

Regards,
Carsten

Hi Carsten, thanks for the swift reply.

Apologies, you’re correct. I’ve edited my original post to correct it.

Service apply rule - zones.d/director-global/service_apply.conf

apply Service "Webpage check for " for (config in host.vars.Pages_to_check) {
    import "webpage-check"

    assign where host.vars.Pages_to_check

    import DirectorOverrideTemplate
}

Example host showing the array - zones.d/master/hosts.conf

object Host "Webserver" {
    import "generic-host"
    import "web-server"

    display_name = "webserver.example.local"
    address = "172.16.4.65"
    groups = [ "Web Servers" ]
    vars.Pages_to_check = [ "/GiveMeA404", "/" ]
    vars.Web_DNS_name = "sub.example.org"
}

Hope that helps,

Jonathan

1 Like

This doesn’t work this way.

Icinga doesn’t expand an array to multiple checks by just giving it an array for a variable.

After your apply for rule is through, you’ll end up with two services where one has /GiveMeA404 the other one / as value of the config variable. You’ll have to use this in your Service definition.

A general tip: Normally you don’t have to change the CheckCommands from the ITL. If you have to do, think twice if you didn’t just go the wrong way. (But if you really have to, your solution of inheriting the command is still the best)

Hello Jonathan,

can you post the full service please.

Edit: Thomas was faster :slight_smile:

Thank you both for your replies. Icinga2 is still quite new to me, so apologies if I ask a lot of (possibly obvious) questions. There’s been a few changes to the environment over lunch, so I’m pasting the up-to-date version.

@twidhalm - if I understand you correctly, the apply rule takes the array defined in the apply for box (so Pages_to_check in the config below) and puts that array into $config$. I should then use $config$ in my arguments list on the command? Icinga does a for each on the elements of the $config$ array. Have I got that correct?

Using $config$ instead gives me

“Error: Non-optional macro ‘config’ used in argument ‘-u’ is missing.”

The command looks like this once I’ve done that:

object CheckCommand "HTTPS_PageCheck" {
    import "plugin-check-command"
    import "http"

    arguments += {
        "-H" = "$Web_DNS_name$"
        "-S" = {}
        "-u" = {
            required = true
            value = "$config$"
        }
    }
}

I only created a new command (rather than using the provided check_http) because I couldn’t see how to add fields / arguments to the check. Suspect I’m missing something so any guidance appreciated.

@anon66228339 - by full service I’m assuming you mean the service with all of its imports? If so:

apply Service "Webpage check for " for (config in host.vars.Pages_to_check) {
    import "https_webpage-check"

    assign where host.vars.Pages_to_check

    import DirectorOverrideTemplate
}

template Service "https_webpage-check" {
    check_command = "HTTPS_PageCheck"
}

object CheckCommand "HTTPS_PageCheck" {
    import "plugin-check-command"
    import "http"

    arguments += {
        "-H" = "$Web_DNS_name$"
        "-S" = {}
        "-u" = {
            required = true
            value = "$Pages_to_check$"
        }
    }
}

Thanks again for your assistance

Hi,

command fields

You might want to check this howto, specifically the first screenshot where you navigate into the external imported CheckCommand and pick the Fields tab on the right.

apply for

In terms of the other question, the apply for rule generated passes the config loop variable to the service apply rule.

In its context, it is missing to set the custom variable Pages_to_check expected from your CheckCommand object below.

In the DSL this would look like this

apply Service "Webpage check for " for (config in host.vars.Pages_to_check) {
    import "https_webpage-check"

    vars.Pages_to_check = config //added, config is the loop value from host.vars.Pages_to_check

    assign where host.vars.Pages_to_check

    import DirectorOverrideTemplate
}

Now your task is to figure out how this can be done in the interface, can you share a screenshot of what you’ve done thus far?

Cheers,
Michael

1 Like

Hello,

I’ve been away a few days. I’ll have a look now and report back shortly.

Thanks

Hello,

OK, think I’m getting myself very confused here. Thank you all for your guidance so far and @dnsmichi for the link to that howto - that’s explained adding fields to checks nicely. I think I’ll need to specify my own command though as I need to say “this argument is populated with this field”.

To use the custom array on the host I’ve been following this tutorial. I’ve just tried to start from scratch but have got myself into a right mess. Screenshots etc. below.

Command
I’ve got a command, HTTP_Page_Check, based on HTTP, which I apply arguments to for -H (hostname) and -u (URL to check, relative to -H):

Fields are specified (I’m not sure why I’m seeing them twice, I don’t recall seeing that before):

Arguments are then mapped:

That renders like this:

object CheckCommand "HTTP_PageCheck" {
    import "plugin-check-command"
    import "http"

    arguments += {
        "-H" = "$Web_DNS_name$"
        "-u" = {
            required = true
            value = "$config$"
        }
    }
}

Service Template

The service template renders like so:

template Service "http-webpage-check" {
    check_command = "HTTP_PageCheck"
}

Apply rule

Generating:

apply Service "HTTP webpage check for " for (config in host.vars.HTTP_Pages_To_Check) {
    import "http-webpage-check"

    assign where host.vars.HTTP_Pages_To_Check

    import DirectorOverrideTemplate
}

The above configuration gives an error o the service (which seems to be correctly applied to various hosts):

Error: Non-optional macro ‘config’ used in argument ‘-u’ is missing.

I don’t understand why $config$ is missing. What am I doing wrong please?

Thanks for your help!

Hi,

back from vacation :sunglasses:

$config$ is a special macro introduced by the Director when using apply rules with for loops. I don’t know much about its specific rendering, but reading the tutorial you’ve linked, your generated apply rule is missing a field.

apply Service "Check SSL certificate for " for (config in host.vars.ssl_domains) {
    import "Check SSL certificates"

    assign where host.vars.ssl_domains
    vars.config = config //this one

    import DirectorOverrideTemplate
}

Meaning to say, the apply rule is missing the custom property config sourcing from your http pages to check.

Though, the host object holds that array, right? Maybe you can add the current config object for Webserver?

Besides, which Director version are you using now?

Cheers,
Michael

Hello,

Sorry, lots of things happening at work at the moment so I’ve not had opportunity to look into this further. Hopefully next week! Hope you had a good vacation.

Current Director version is 1.6.2 which is the latest release.

Yes, the host holds the HTTP pages array. I haven’t seen an existing object for Webserver though?

Jonathan

Hi,

re-iterating the CheckCommand attribute config for the -u parameter - I would change that, so that it looks like this:

object CheckCommand "HTTP_PageCheck" {
    import "plugin-check-command"
    import "http"

    arguments += {
        "-H" = "$http_web_dns_name$"
        "-u" = {
            required = true
            value = "$http_page$"
        }
    }
}

This requires the two fields http_web_dns_name and http_page to be defined as data field for this newly created CheckCommand. These fields should then be populated by service sets.

The apply for rule resulting from this should look like this:

apply Service "Webpage check for " for (config in host.vars.Pages_to_check) {
    import "https_webpage-check"

   //custom variables as data field attributes
   vars.http_web_dns_name = host.name //or something else
   vars.http_page = config //holds a single value, 1. iteration "/GiveMeA404" 2. iteration "/"

    assign where host.vars.Pages_to_check

    import DirectorOverrideTemplate
}

I’m not sure though how to configure that in the Director.

Cheers,
Michael

Apologies, not had time to look into this further. It may be it simply can’t be done in Director.

Sadly it’s not looking like I’ll have time to look into this any time soon :cry:.