summaryrefslogtreecommitdiffstats
path: root/doc/20-script-debugger.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/20-script-debugger.md')
-rw-r--r--doc/20-script-debugger.md177
1 files changed, 177 insertions, 0 deletions
diff --git a/doc/20-script-debugger.md b/doc/20-script-debugger.md
new file mode 100644
index 0000000..e8ee6db
--- /dev/null
+++ b/doc/20-script-debugger.md
@@ -0,0 +1,177 @@
+# Script Debugger <a id="script-debugger"></a>
+
+You can run the Icinga 2 daemon with the `-X` (`--script-debugger`)
+parameter to enable the script debugger:
+
+```bash
+icinga2 daemon -X
+```
+
+When an exception occurs or the [debugger](17-language-reference.md#breakpoints)
+keyword is encountered in a user script, Icinga 2 launches a console that
+allows the user to debug the script.
+
+You can also attach the script debugger to the [configuration validation](11-cli-commands.md#config-validation):
+
+```bash
+icinga2 daemon -C -X
+```
+
+Here is a list of common errors which can be diagnosed with the script debugger:
+
+* Configuration errors e.g. [apply rules](03-monitoring-basics.md#using-apply)
+* Errors in user-defined [functions](17-language-reference.md#functions)
+
+## Debugging Configuration Errors <a id="script-debugger-config-errors"></a>
+
+The following example illustrates the problem of a service [apply rule](03-monitoring-basics.md#using-apply-for)
+which expects a dictionary value for `config`, but the host custom variable only
+provides a string value:
+
+```
+object Host "script-debugger-host" {
+ check_command = "icinga"
+
+ vars.http_vhosts["example.org"] = "192.168.1.100" // a string value
+}
+
+apply Service for (http_vhost => config in host.vars.http_vhosts) {
+ import "generic-service"
+
+ vars += config // expects a dictionary
+
+ check_command = "http"
+}
+```
+
+The error message on config validation will warn about the wrong value type,
+but does not provide any context which objects are affected.
+
+Enable the script debugger and run the config validation:
+
+```
+# icinga2 daemon -C -X
+
+Breakpoint encountered in /etc/icinga2/conf.d/services.conf: 59:67-65:1
+Exception: Error: Error while evaluating expression: Cannot convert value of type 'String' to an object.
+Location:
+/etc/icinga2/conf.d/services.conf(62): check_command = "http"
+/etc/icinga2/conf.d/services.conf(63):
+/etc/icinga2/conf.d/services.conf(64): vars += config
+ ^^^^^^^^^^^^^^
+/etc/icinga2/conf.d/services.conf(65): }
+/etc/icinga2/conf.d/services.conf(66):
+You can inspect expressions (such as variables) by entering them at the prompt.
+To leave the debugger and continue the program use "$continue".
+<1> =>
+```
+
+You can print the variables `vars` and `config` to get an idea about
+their values:
+
+```
+<1> => vars
+null
+<2> => config
+"192.168.1.100"
+<3> =>
+```
+
+The `vars` attribute has to be a dictionary. Trying to set this attribute to a string caused
+the error in our configuration example.
+
+In order to determine the name of the host where the value of the `config` variable came from
+you can inspect attributes of the service object:
+
+```
+<3> => host_name
+"script-debugger-host-01"
+<4> => name
+"http"
+```
+
+Additionally you can view the service object attributes by printing the value of `this`.
+
+## Using Breakpoints <a id="script-debugger-breakpoints"></a>
+
+In order to halt execution in a script you can use the `debugger` keyword:
+
+```
+object Host "script-debugger-host-02" {
+ check_command = "dummy"
+ check_interval = 5s
+
+ vars.dummy_text = {{
+ var text = "Hello from " + macro("$name$")
+ debugger
+ return text
+ }}
+}
+```
+
+Icinga 2 will spawn a debugger console every time the function is executed:
+
+```
+# icinga2 daemon -X
+...
+Breakpoint encountered in /etc/icinga2/tests/script-debugger.conf: 7:5-7:12
+You can inspect expressions (such as variables) by entering them at the prompt.
+To leave the debugger and continue the program use "$continue".
+<1> => text
+"Hello from script-debugger-host-02"
+<2> => $continue
+```
+
+## Debugging API Filters <a id="script-debugger-api-filters"></a>
+
+Queries against the [Icinga 2 REST API](12-icinga2-api.md#icinga2-api) can use
+filters, just like available in `assign where` expressions. If these filters cause
+an internal error, they return an empty result to the caller.
+
+In order to analyse these server-side errors, you can use the script debugger.
+
+The following example tries filter for all host objects where the custom variable
+`os` is set. There are various possibilities to check that, one of them would be
+`host.vars.os != ""`. Another idea is to use the [contains](18-library-reference.md#dictionary-contains) method on the custom
+attribute dictionary like this: `host.vars.contains("os")`.
+
+```bash
+curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' \
+ -X POST 'https://localhost:5665/v1/objects/services' \
+ -d '{ "filter": "host.vars.contains(\"os\")", "attrs": [ "__name" ], "joins": [ "host.name", "host.vars" ], "pretty": true }'
+```
+
+This will fail on all hosts which don't have any custom variable specified.
+
+```
+# icinga2 daemon -X
+
+Breakpoint encountered.
+Exception: Error: Argument is not a callable object.
+Location: in <API query>: 1:0-1:23
+You can inspect expressions (such as variables) by entering them at the prompt.
+To leave the debugger and continue the program use "$continue".
+
+<1> => this.host
+
+...
+
+ vars = null
+
+<2> => $continue
+```
+
+By definition, a type method can only be invoked on an actual object.
+
+In order to stay safe, add more checks to the API filter:
+
+- `host.vars && host.vars.contains("os")` or
+- `host.vars && typeof(host.vars) == Dictionary && host.vars.contains("os")`
+
+Example:
+
+```bash
+curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' \
+ -X POST 'https://localhost:5665/v1/objects/services' \
+ -d '{ "filter": "host.vars && typeof(host.vars) == Dictionary && host.vars.contains(\"os\")", "attrs": [ "__name" ], "joins": [ "host.name", "host.vars" ], "pretty": true }'
+```