Reach multi-dimensional array defined in Host object from CheckCommand

Hi!

I’m trying to redefine the default “disk” CheckCommand to be more dynamic. My goal is to create a new “my_check_disk” command which checks all of the disks on the host with a given warning and critical level, but warning and critical levels could be redefined to some mountpoints. Here’s and example:

I want to check all of my disks and send a warning when the free space is equals or less than 20% and a critical when less than 10% except for /tmp (w: 60% c: 50%) and /var (w: 50% c: 30%). If I call the plugin manually this way it works:
check_disk -w 20% -c 10% -r "/.*" -w 60% -c 50% -r "/tmp" -w 50% -c 30% -r "/var"

So, I defined a multi-dimensional array in the Host object:

object Host "my-host" {
  address = "192.168.56.111"
  address6 = "2a00:1450:4001:815::2003"
  check_command = "hostalive"

  vars.disk_wfree = "20%"
  vars.disk_cfree = "10%"

  vars.disk_limits = [
    ["/tmp","60%","50%"],
    ["/var","50%","30%"]
  ]

}

Then created the CheckCommand based on the original. If I would have 3 single-dimensional array with warnings, criticals and mountpoints separated I could pass it to the plugin easily I think. So I should iterate trough my multidimensional array from the Host to get arrays with warning, critical and mountpoint triplets which can be stored in a local array defined in CheckCommand. My problem is that I can’t reach the multi-dimensional array in Host. How can I do this? I tried this (and some other)way but the variable is not defined:

object CheckCommand "my_disk_check" {
  command = [ PluginDir + "/check_disk" ]

  arguments = {
    ...
  }

  vars.my_warnings = []
  vars.my_criticals = []
  vars.my_mountpoints = []

#
#  host.vars.disk_limits doesn't work :(
#

  for (var array in host.vars.disk_limits) {
	  log("Add to mountpoint array: " + array[0])
      vars.my_warnings += array[0]
	  log("Add to warnings array: " + array[1])
      vars.my_criticals += array[1]
	  log("Add to critical array: " + array[2])
      vars.my_mountpoints += array[1]
  }

}

Thanks!

Hi,

the CheckCommand object is the wrong scope for accessing the virtual host variable - that’s only available in apply rules when e.g. creating service objects.

So you can pre-generate the required arguments array in the service apply rule …

apply Service "disk" {

  var limits = host.vars.disk_limits

  assign where ...
}

Or you follow the approach within the CheckCommand. Therefore you’ll need to learn about argument values, and lambda functions evaluated at plugin execution time.

https://icinga.com/docs/icinga2/latest/doc/03-monitoring-basics/#command-arguments
https://icinga.com/docs/icinga2/latest/doc/08-advanced-topics/#use-functions-in-object-configuration
https://icinga.com/docs/icinga2/latest/doc/18-library-reference/#macro

object CheckCommand "my-disk" {
  
  arguments = {
    "-w" = { 
      value = "$disk_wfree$"
      order = -10
    }
    "-c" = { 
      value = "$disk_cfree$"
      order = -9
    }
   "-diskarguments" = {
     skip_key = true
     repeat_key = false
     // runtime lambda function
     value = {{
       // return an array like [ "-r", "/.*" "-w", "60%", "-c", "50%", "-r", "/tmp", "-w", "50%", "-c", "30%" ]

       var disks = macro($disk_limits$) //either on the host, or service
       var args = []

       for (d in disks) {
         //3 or 1 entry is allowed
         if (len(d) == 1) {
          args.add("-r")
          args.add(d[0])
         } else if (len(d) == 3) {
           args += [ "-r", d[0], "-w", d[1], "-c", d[2] ] //can be made line by line, but I am lazy today
         }
       }

      return args
     }}
   }
  }
  
}

A better approach would be to use key-value pairs in the disk_limits array to avoid off-by-one errors with array access.

  vars.disk_limits = {
    "/tmp" = [ "60%","50%"]
    "/var" = [ "50%","30%"]
  }
    value = {{
       var disks = macro($disk_limits$) //either on the host, or service
       var args = []

       for (d => limits in disks) {
         args += [ d ]

         if (len(limits) == 2) {
           args += [ -w, limits[0] ]
           args += [ -c, limits[1] ]
         }
       }
    }}

There’s multiple ways to solve this. Always think of the least error-prone for your users when setting the host custom variable. You might also want to put the warning and critical thresholds into actual key value pairs to allow the user to immediately see what’s the intention of this value.

Play around with it until you’re satisfied. I haven’t tested the above, it may contain fixable errors.

Cheers,
Michael

Thanks for your reply, it works great. I defined the dictionary in the Host object and pass it to the service.