Functions on macros

I need to pass an array to a powershell check. powershell is mess as this is the recommended implementation:

object CheckCommand "powershell_check_services" {
  command = [ "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe" ]
  arguments = {
    "-command" = {
    value = "$ps_command$"
    order = -1
    }
    "-services" = {
    value = "$services$"
    repeat_key = false
    required = true
    }
    ";exit" = {
    value = "$$LastExitCode"
    }
  }
}

(not kidding)

  • check_services.ps1 expects an array (powershell style: “-services foo, bar, batz”)
  • You can not do “-service foo -service batz” with powershell.

I worked around this by assigning vars.services like this:

vars.services = ["MSExchangeADTopology", "MSExchangeDagMgmt"].join(", ")

Basically as a string - of course this breaks every functionality of having arrays like adding elements, etc.

To proper solve this i would have to call a function on a macro like

$services$.join(", ")

which does not work. Anybody has a good idea for this?

Hello,
To me it’s the right way to go, just let your service definition build up your needs, i tend to prefer to let CheckCommands being simple and easy to read and have much more flexibles services definitions by abusing macros/functions/DSL to put all the smartness in one place :wink:

If you however need more flexibility to process your service array, you can go for something like this :

vars.services = globals.myfunction(["MSExchangeADTopology", "MSExchangeDagMgmt"])

globals.myfunction= function(arr) {
 [do whatever preprocessing you want to arr]
 return arr.join(", ")
}

I guess you could also do something like this (didn’t test) :
vars.services = ["MSExchangeADTopology", "MSExchangeDagMgmt"]

"-services" = {
    value = globals.myfunction($services$)
    repeat_key = false
    required = true
    }

This can come handy :
https://icinga.com/docs/icinga2/latest/doc/08-advanced-topics/#register-and-use-global-functions

The main problem is that i have to store the services as string at all. It would we be nice if you could just convert them when actually needed as a string.

Your 2nd example would have worked for me but sadly is not allowed as well.

Jul 29 12:16:06 srv-icinga2 safe-reload[19615]: [2020-07-29 12:16:06 +0200] information/cli: Loading configuration file(s).
Jul 29 12:16:06 srv-icinga2 safe-reload[19615]: [2020-07-29 12:16:06 +0200] critical/config: Error: syntax error, unexpected $undefined, expecting ')'
Jul 29 12:16:06 srv-icinga2 safe-reload[19615]: Location: in /etc/icinga2/zones.d/global-templates/commands.conf: 81:26-81:26
Jul 29 12:16:06 srv-icinga2 safe-reload[19615]: /etc/icinga2/zones.d/global-templates/commands.conf(79):     }
Jul 29 12:16:06 srv-icinga2 safe-reload[19615]: /etc/icinga2/zones.d/global-templates/commands.conf(80):     "-services" = {
Jul 29 12:16:06 srv-icinga2 safe-reload[19615]: /etc/icinga2/zones.d/global-templates/commands.conf(81):     value = globals.join($services$)

It’s unexpected, let’s try something else :

"-services" = {
value = {{ return service.vars.services.join(", ") }}
repeat_key = false
required = true
}

This sadly fails on agent side:
Icinga2 reloads but this is the check output:

Error: Argument is not a callable object.
Location: in /etc/icinga2/zones.d/global-templates/commands.conf: 82:23-82:54
/etc/icinga2/zones.d/global-templates/commands.conf(80):     "-services" = {
/etc/icinga2/zones.d/global-templates/commands.conf(81):     #value = "$services$"
/etc/icinga2/zones.d/global-templates/commands.conf(82):     value = {{ return service.vars.services.join(", ") }}
                                                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/etc/icinga2/zones.d/global-templates/commands.conf(83):     repeat_key = false
/etc/icinga2/zones.d/global-templates/commands.conf(84):     required = true

:frowning:

It’s weird, i have a similar configuration in production (for 2.10.5), but for a linux satellite.

I’m using something like this an arguments dict which is running fine :

"-D" = {
    required = true
    value = {{ Json.encode(host.vars.data["XXX"]["XXX"] + service.vars.XXX) }}
}

Given your CheckCommand configuration, it seems you are using a windows icinga agent, we may have a problem if functionnality is different, i’d prefer to ping icinga team to confirm it.

Maybe there is a real issue here @theFeu ?

Hello @kochd!

Have you tried to convert array -> string like this:

==> prefix1/etc/icinga2/zones.d/master/commands.conf <==
object CheckCommand "lolcat" {
	command = [ "whatever" ]
	arguments = {
		"it" = "$takes$"
	}
	vars.takes = {{ macro("$takes_arr$").join(", ") }}
}

==> prefix1/etc/icinga2/zones.d/master/hosts.conf <==
object Host "master" {
	check_command = "lolcat"
	vars.takes_arr = [ 1, 2, 3 ]
}
[2020-07-30 15:33:01 +0200] warning/PluginCheckTask: Check command for object 'master' (PID: 29866, arguments: 'whatever' 'it' '1, 2, 3') terminated with exit code 128, output: execvpe(whatever) failed: No such file or directory