would not work as expected.
As a matter of fact I need to do exactly that: set a CheckCommand parameter only if the relevant variables exist.
Also, in order to not have to write the same statements all over again, I tried using a function, like this:
function all_exist(l) {
# Check if all elements provided in list l not empty
for (i in l) {
if (len(i)==0) {
return false
}
}
return true
}
object CheckCommand "SNMP_PROCESS" {
.....
"--protocols" = {
# set_if = {{ all_exist["$snmpv3_authprot$", "$snmpv3_privprot$"] }}
value = "$snmpv3_authprot$,$snmpv3_privprot$"
}
.....
But it’s not accepting that. (“Error: Argument is not a callable object.”)
In case this has been solved, how do I do this correctly nowadays?
Thanks,
Marki
You’re missing the macro resolving here, actually you’re just passing strings with dollars around. macro() is what you’re looking for.
Not much to change in your algorithm, already very good. I’ve only added more telling variable names, I think, short characters are barely readable. The macro() call is new with the extra variable.
The function needs to be registered in the global namespace, otherwise you cannot call it from set_if.
globals.all_exist = function (args) {
# Check if all elements provided in list l not empty
for (unresolvedArg in args) {
var resolvedArg = macro(unresolvedArg)
if (len(resolvedArg)==0) {
return false
}
}
return true
}
The call to set_if also needs to be adjusted a bit with () brackets to pass the function argument.
However, I was restructuring our config to use more of this feature, and I am now getting:
Location: in /etc/icinga2/zones.d/global-templates/commands.conf: 518:23-518:42
/etc/icinga2/zones.d/global-templates/commands.conf(516): # Check if all elements provided in list not empty
/etc/icinga2/zones.d/global-templates/commands.conf(517): for (unresolvedArg in args) {
/etc/icinga2/zones.d/global-templates/commands.conf(518): var resolvedArg = macro(unresolvedArg)
^^^^^^^^^^^^^^^^^^^^
/etc/icinga2/zones.d/global-templates/commands.conf(519):
/etc/icinga2/zones.d/global-templates/commands.conf(520): if (len(resolvedArg)==0) {
(0) Executing check for object 'host!HTTP_Web'
The debugger gives me this:
<4> => args
[ "$postdata$" ]
If I use set_if = {{ len(macro("$postdata$"))>0 }} instead of calling the function, it seems to work fine (at least there are no errors).
The service definition is not worth mentioning. It currently does not contain “vars.postdata”, which is why we have the “set_if” check in the command in the first place.
globals.all_exist = function (args) {
# Check if all elements provided in list not empty
for (unresolvedArg in args) {
var resolvedArg = macro(unresolvedArg)
if (len(resolvedArg)==0) {
return false
}
}
return true
}
It’s as if the “macro” function didn’t exist in that scope.
As a matter of fact I have tried with a vanilla Icinga setup and it does the same. Feel free to reproduce by adding the following into /etc/icinga2/conf.d/xyz.conf of a vanilla install.
globals.all_exist = function (args) {
# Check if all elements provided in list not empty
for (unresolvedArg in args) {
var resolvedArg = macro(unresolvedArg)
if (len(resolvedArg)==0) {
return false
}
}
return true
}
object CheckCommand "COM_HTTP" {
import "plugin-check-command"
command = [ PluginDir + "/check_http" ]
arguments = {
"-H" = {
value = "$address$"
required = true
}
"-u" = {
value = "$url$"
set_if = {{ all_exist(["$url$"]) }}
}
"-s" = {
value = "$string$"
set_if = "$string$"
}
"-S" = {
set_if = "$use_ssl$"
}
"-p" = {
value = "$port$"
set_if = "$port$"
}
"-P" = {
value = "$login$"
set_if = {{ all_exist(["$login$"]) }}
}
"-t" = {
value = "$timer$"
set_if = "$timer$"
}
"-f" = "follow"
}
if (vars.use_ssl) {
if (vars.port) {
arguments += {
"-p" = {
value = "$port$"
}
}
} else {
arguments += {
"-p" = {
value = "443"
}
}
}
}
}
object Host "test" {
import "generic-host"
address = "1.2.3.4"
vars.service.check_http = {
"http" = {
}
}
}
apply Service "HTTP" for (var k => var v in host.vars.service.check_http) {
import "generic-service"
check_command = "COM_HTTP"
display_name = "HTTP_"+k
assign where host.vars.service.check_http
}
Maybe I was wrong with the assumption in the global function scope then, I did not test it. Better modify the algorithm like this:
globals.all_exist = function (args) {
# Check if all elements provided in list not empty
for (arg in args) {
if (len(arg)==0) {
return false
}
}
return true
}
Related question: https://icinga.com/docs/icinga2/latest/doc/03-monitoring-basics/ explains the following: “Whenever a host/service object sets the http_sni custom variable to true, the parameter is added to the command line.” It uses the following example:
None of them, it is a way of how the scope works. macro() only works when called inside a command, where the resolve lists are populated. This isn’t the case anywhere else inside the DSL scope. I had forgotten about this while putting that into the function itself.
Very good question.
The example with http and the boolean value just does a lookup on the objects, first it looks on the service, then host, then checkcommand. The latter may have set a default value. One of them should evaluate into a value.
If no value is specified, it still resolve to boolean false. That’s all just fine and common best practice what you should use.
The problem with the PostgreSQL command is that
host
unixsocket
are two different configuration methods for the plugin. Either you pass the -H parameter and tell the plugin to connect to an IP address, or you set the unixsocket variable to avoid setting the -H parameter, and just use this parameter.
Setting both collides and renders the plugin in failure. In order to keep just one CheckCommand, and not the two modes in 2 different CheckCommand objects, this is enabled like this.
Whenever postgres_host is used, postgres_unixsocket must be false.
If postgres_unixsocket is true, -H is not added to the command line.
So with the power of the DSL and runtime lambda functions for set_if, we can still solve this scenario. Pretty much advanced and therefore hidden in the template library. No-one expects you to fully understand it, just use it
Forget about it First year computer science class fail My question was so stupid… It’s obviously about the expressionmacro("$postgres_unixsocket$") == false evaluating to true, meaning set_if is true if the macro’s not set. So the intention is to have the exact inverse case of what you normally have (we normally set the parameter if the value exists). My bad, I should have looked more carefully at the very special check in question.
Is there any chance that macros will be eliminated in the long run and you will just be able to use service.vars.bla etc as usual instead of macro("$bla$") ?
Hmmm there’s currently no plans to remove the runtime macros, as they allow specific different value overrides e.g. when the attribute is not define on the service, it will be picked from the host automatically.
Also, users are aware of it and not many are using advanced techniques like you. If that’s subject to change, maybe in future real major versions, 3 or 4 or whatever version schema we will be using.
In terms of the boolean question, ask yourself why it uses a runtime lambda function and no direct access