]> granicus.if.org Git - icinga2/commitdiff
Documentation: Add function usage examples to advanced topics
authorMichael Friedrich <michael.friedrich@netways.de>
Tue, 16 Jun 2015 17:04:40 +0000 (19:04 +0200)
committerMichael Friedrich <michael.friedrich@netways.de>
Tue, 16 Jun 2015 17:08:59 +0000 (19:08 +0200)
Includes typeof() examples, referenced to command variable scopes
and examples from community support.

fixes #9297
fixes #9310
fixes #9311

doc/5-advanced-topics.md

index 34effc4e0352db97e0c435bfe769454716b32bdb..4fe71a8259615a7ad8d72f5e42a2725370693aef 100644 (file)
@@ -208,6 +208,166 @@ Use the `period` attribute to assign time periods to
       period = "workhours"
     }
 
+## <a id="use-functions-object-config"></a> Use Functions in Object Configuration
+
+There is a limited scope where functions can be used as object attributes such as:
+
+* As value for [Custom Attributes](3-monitoring-basics.md#custom-attributes-functions)
+* Returning boolean expressions for [set_if](5-advanced-topics.md#use-functions-command-arguments-setif) inside command arguments
+* Returning a [command](5-advanced-topics.md#use-functions-command-attribute) array inside command objects
+
+The other way around you can create objects dynamically using your own global functions.
+
+> **Note**
+>
+> Functions called inside command objects share the same global scope as runtime macros.
+> Therefore you can access host custom attributes like `host.vars.os`, or any other
+> object attribute from inside the function definition used for [set_if](5-advanced-topics.md#use-functions-command-arguments-setif) or [command](5-advanced-topics.md#use-functions-command-attribute).
+
+Tips when implementing functions:
+
+* Use [log()](20-library-reference.md#global-functions) to dump variables. You can see the output
+inside the `icinga2.log` file depending in your log severity
+* Use the `icinga2 console` to test basic functionality (e.g. iterating over a dictionary)
+* Build them step-by-step. You can always refactor your code later on.
+
+### <a id="use-functions-command-arguments-setif"></a> Use Functions in Command Arguments set_if
+
+The `set_if` attribute inside the command arguments definition in the
+[CheckCommand object definition](6-object-types.md#objecttype-checkcommand) is primarly used to
+evaluate whether the command parameter should be set or not.
+
+By default you can evaluate runtime macros for their existance, and if the result is not an empty
+string the command parameter is passed. This becomes fairly complicated when want to evaluate
+multiple conditions and attributes.
+
+The following example was found on the community support channels. The user had defined a host
+dictionary named `compellent` with the key `disks`. This was then used inside service apply for rules.
+
+    object Host "dict-host" {
+      check_command = "check_compellent"
+      vars.compellent["disks"] = {
+        file = "/var/lib/check_compellent/san_disks.0.json",
+        checks = ["disks"]
+      }
+    }
+
+The more significant problem was to only add the command parameter `--disk` to the plugin call
+when the dictionary `compellent` contains the key `disks`, and omit it if not found.
+
+By defining `set_if` as [abbreviated lambda function](19-language-reference.md#nullary-lambdas)
+and evaluating the host custom attribute `compellent` containing the `disks` this problem was
+solved like this:
+
+    object CheckCommand "check_compellent" {
+      import "plugin-check-command"
+      command   = [ "/usr/bin/check_compellent" ]
+      arguments   = {
+        "--disks"  = {
+          set_if = {{
+            var host_vars = host.vars
+            log(host_vars)
+            var compel = host_vars.compellent
+            log(compel)
+            compel.contains("disks")
+          }}
+        }
+      }
+    }
+
+This implementation uses the dictionary type method [contains](20-library-reference.md#dictionary-contains)
+and will fail if `host.vars.compellent` is not of the type `Dictionary`.
+Therefore you can extend the checks using the [typeof](19-language-reference.md#types) function.
+
+You can test the types using the `icinga2 console`:
+
+    # icinga2 console
+    Icinga (version: v2.3.0-193-g3eb55ad)
+    <1> => srv_vars.compellent["check_a"] = { file="outfile_a.json", checks = [ "disks", "fans" ] }
+    null
+    <2> => srv_vars.compellent["check_b"] = { file="outfile_b.json", checks = [ "power", "voltages" ] }
+    null
+    <3> => typeof(srv_vars.compellent)
+    type 'Dictionary'
+    <4> =>
+
+The more programmatic approach for `set_if` could look like this:
+
+        "--disks" = {
+          set_if = {{
+            var srv_vars = service.vars
+            if(len(srv_vars) > 0) {
+              if (typeof(srv_vars.compellent) == Dictionary) {
+                return srv_vars.compellent.contains("disks")
+              } else {
+                log(LogInformationen, "checkcommand set_if", "custom attribute compellent_checks is not a dictionary, ignoring it.")
+                return false
+              }
+            } else {
+              log(LogWarning, "checkcommand set_if", "empty custom attributes")
+              return false
+            }
+          }}
+        }
+
+
+### <a id="use-functions-command-attribute"></a> Use Functions as Command Attribute
+
+This comes in handy for [NotificationCommands](6-object-types.md#objecttype-notificationcommand)
+or [EventCommands](6-object-types.md#objecttype-eventcommand) which does not require
+a returned checkresult including state/output.
+
+The following example was taken from the community support channels. The requirement was to
+specify a custom attribute inside the notification apply rule and decide which notification
+script to call based on that.
+
+    object User "short-dummy" {
+    }
+    
+    object UserGroup "short-dummy-group" {
+      assign where user.name == "short-dummy"
+    }
+    
+    apply Notification "mail-admins-short" to Host {
+       import "mail-host-notification"
+       command = "mail-host-notification-test"
+       user_groups = [ "short-dummy-group" ]
+       vars.short = true
+       assign where host.vars.notification.mail
+    }
+
+The solution is fairly simple: The `command` attribute is implemented as function returning
+an array required by the caller Icinga 2.
+The local variable `mailscript` sets the default value for the notification scrip location.
+If the notification custom attribute `short` is set, it will override the local variable `mailscript`
+with a new value.
+The `mailscript` variable is then used to compute the final notification command array being
+returned.
+
+You can omit the `log()` calls, they only help debugging.
+
+    object NotificationCommand "mail-host-notification-test" {
+      import "plugin-notification-command"
+      command = {{
+        log("command as function")
+        var mailscript = "mail-host-notification-long.sh"
+        if (notification.vars.short) {
+           mailscript = "mail-host-notification-short.sh"
+        }
+        log("Running command")
+        log(mailscript)
+    
+        var cmd = [ SysconfDir + "/icinga2/scripts/" + mailscript ]
+        log(LogCritical, "me", cmd)
+        return cmd
+      }}
+    
+      env = {
+      }
+    }
+
+
+
 ## <a id="access-object-attributes-at-runtime"></a> Access Object Attributes at Runtime
 
 The [Object Accessor Functions](20-library-reference.md#object-accessor-functions)