]> granicus.if.org Git - icinga2/blob - doc/08-advanced-topics.md
Merge pull request #5668 from Icinga/feature/docs-enhance-monitoring-basics
[icinga2] / doc / 08-advanced-topics.md
1 # Advanced Topics <a id="advanced-topics"></a>
2
3 This chapter covers a number of advanced topics. If you're new to Icinga, you
4 can safely skip over things you're not interested in.
5
6 ## Downtimes <a id="downtimes"></a>
7
8 Downtimes can be scheduled for planned server maintenance or
9 any other targeted service outage you are aware of in advance.
10
11 Downtimes will suppress any notifications, and may trigger other
12 downtimes too. If the downtime was set by accident, or the duration
13 exceeds the maintenance, you can manually cancel the downtime.
14 Planned downtimes will also be taken into account for SLA reporting
15 tools calculating the SLAs based on the state and downtime history.
16
17 Multiple downtimes for a single object may overlap. This is useful
18 when you want to extend your maintenance window taking longer than expected.
19 If there are multiple downtimes triggered for one object, the overall downtime depth
20 will be greater than `1`.
21
22
23 If the downtime was scheduled after the problem changed to a critical hard
24 state triggering a problem notification, and the service recovers during
25 the downtime window, the recovery notification won't be suppressed.
26
27 ### Fixed and Flexible Downtimes <a id="fixed-flexible-downtimes"></a>
28
29 A `fixed` downtime will be activated at the defined start time, and
30 removed at the end time. During this time window the service state
31 will change to `NOT-OK` and then actually trigger the downtime.
32 Notifications are suppressed and the downtime depth is incremented.
33
34 Common scenarios are a planned distribution upgrade on your linux
35 servers, or database updates in your warehouse. The customer knows
36 about a fixed downtime window between 23:00 and 24:00. After 24:00
37 all problems should be alerted again. Solution is simple -
38 schedule a `fixed` downtime starting at 23:00 and ending at 24:00.
39
40 Unlike a `fixed` downtime, a `flexible` downtime will be triggered
41 by the state change in the time span defined by start and end time,
42 and then last for the specified duration in minutes.
43
44 Imagine the following scenario: Your service is frequently polled
45 by users trying to grab free deleted domains for immediate registration.
46 Between 07:30 and 08:00 the impact will hit for 15 minutes and generate
47 a network outage visible to the monitoring. The service is still alive,
48 but answering too slow to Icinga 2 service checks.
49 For that reason, you may want to schedule a downtime between 07:30 and
50 08:00 with a duration of 15 minutes. The downtime will then last from
51 its trigger time until the duration is over. After that, the downtime
52 is removed (may happen before or after the actual end time!).
53
54 ### Scheduling a downtime <a id="scheduling-downtime"></a>
55
56 You can schedule a downtime either by using the Icinga 2 API action
57 [schedule-downtime](12-icinga2-api.md#icinga2-api-actions-schedule-downtime) or
58 by sending an [external command](14-features.md#external-commands).
59
60
61 #### Fixed Downtime <a id="fixed-downtime"></a>
62
63 If the host/service changes into a NOT-OK state between the start and
64 end time window, the downtime will be marked as `in effect` and
65 increases the downtime depth counter.
66
67 ```
68    |       |         |
69 start      |        end
70        trigger time
71 ```
72
73 #### Flexible Downtime <a id="flexible-downtime"></a>
74
75 A flexible downtime defines a time window where the downtime may be
76 triggered from a host/service NOT-OK state change. It will then last
77 until the specified time duration is reached. That way it can happen
78 that the downtime end time is already gone, but the downtime ends
79 at `trigger time + duration`.
80
81
82 ```
83    |       |         |
84 start      |        end               actual end time
85            |--------------duration--------|
86        trigger time
87 ```
88
89
90 ### Triggered Downtimes <a id="triggered-downtimes"></a>
91
92 This is optional when scheduling a downtime. If there is already a downtime
93 scheduled for a future maintenance, the current downtime can be triggered by
94 that downtime. This renders useful if you have scheduled a host downtime and
95 are now scheduling a child host's downtime getting triggered by the parent
96 downtime on `NOT-OK` state change.
97
98 ### Recurring Downtimes <a id="recurring-downtimes"></a>
99
100 [ScheduledDowntime objects](09-object-types.md#objecttype-scheduleddowntime) can be used to set up
101 recurring downtimes for services.
102
103 Example:
104
105     apply ScheduledDowntime "backup-downtime" to Service {
106       author = "icingaadmin"
107       comment = "Scheduled downtime for backup"
108
109       ranges = {
110         monday = "02:00-03:00"
111         tuesday = "02:00-03:00"
112         wednesday = "02:00-03:00"
113         thursday = "02:00-03:00"
114         friday = "02:00-03:00"
115         saturday = "02:00-03:00"
116         sunday = "02:00-03:00"
117       }
118
119       assign where "backup" in service.groups
120     }
121
122
123 ## Comments <a id="comments-intro"></a>
124
125 Comments can be added at runtime and are persistent over restarts. You can
126 add useful information for others on repeating incidents (for example
127 "last time syslog at 100% cpu on 17.10.2013 due to stale nfs mount") which
128 is primarily accessible using web interfaces.
129
130 You can add a comment either by using the Icinga 2 API action
131 [add-comment](12-icinga2-api.md#icinga2-api-actions-add-comment) or
132 by sending an [external command](14-features.md#external-commands).
133
134 ## Acknowledgements <a id="acknowledgements"></a>
135
136 If a problem persists and notifications have been sent, you can
137 acknowledge the problem. That way other users will get
138 a notification that you're aware of the issue and probably are
139 already working on a fix.
140
141 Note: Acknowledgements also add a new [comment](08-advanced-topics.md#comments-intro)
142 which contains the author and text fields.
143
144 You can send an acknowledgement either by using the Icinga 2 API action
145 [acknowledge-problem](12-icinga2-api.md#icinga2-api-actions-acknowledge-problem) or
146 by sending an [external command](14-features.md#external-commands).
147
148
149 ### Sticky Acknowledgements <a id="sticky-acknowledgements"></a>
150
151 The acknowledgement is removed if a state change occurs or if the host/service
152 recovers (OK/Up state).
153
154 If you acknowlege a problem once you've received a `Critical` notification,
155 the acknowledgement will be removed if there is a state transition to `Warning`.
156 ```
157 OK -> WARNING -> CRITICAL -> WARNING -> OK
158 ```
159
160 If you prefer to keep the acknowledgement until the problem is resolved (`OK`
161 recovery) you need to enable the `sticky` parameter.
162
163
164 ### Expiring Acknowledgements <a id="expiring-acknowledgements"></a>
165
166 Once a problem is acknowledged it may disappear from your `handled problems`
167 dashboard and no-one ever looks at it again since it will suppress
168 notifications too.
169
170 This `fire-and-forget` action is quite common. If you're sure that a
171 current problem should be resolved in the future at a defined time,
172 you can define an expiration time when acknowledging the problem.
173
174 Icinga 2 will clear the acknowledgement when expired and start to
175 re-notify, if the problem persists.
176
177
178 ## Time Periods <a id="timeperiods"></a>
179
180 [Time Periods](09-object-types.md#objecttype-timeperiod) define
181 time ranges in Icinga where event actions are triggered, for
182 example whether a service check is executed or not within
183 the `check_period` attribute. Or a notification should be sent to
184 users or not, filtered by the `period` and `notification_period`
185 configuration attributes for `Notification` and `User` objects.
186
187 > **Note**
188 >
189 > If you are familiar with Icinga 1.x, these time period definitions
190 > are called `legacy timeperiods` in Icinga 2.
191 >
192 > An Icinga 2 legacy timeperiod requires the `ITL` provided template
193 >`legacy-timeperiod`.
194
195 The `TimePeriod` attribute `ranges` may contain multiple directives,
196 including weekdays, days of the month, and calendar dates.
197 These types may overlap/override other types in your ranges dictionary.
198
199 The descending order of precedence is as follows:
200
201 * Calendar date (2008-01-01)
202 * Specific month date (January 1st)
203 * Generic month date (Day 15)
204 * Offset weekday of specific month (2nd Tuesday in December)
205 * Offset weekday (3rd Monday)
206 * Normal weekday (Tuesday)
207
208 If you don't set any `check_period` or `notification_period` attribute
209 on your configuration objects, Icinga 2 assumes `24x7` as time period
210 as shown below.
211
212     object TimePeriod "24x7" {
213       import "legacy-timeperiod"
214
215       display_name = "Icinga 2 24x7 TimePeriod"
216       ranges = {
217         "monday"    = "00:00-24:00"
218         "tuesday"   = "00:00-24:00"
219         "wednesday" = "00:00-24:00"
220         "thursday"  = "00:00-24:00"
221         "friday"    = "00:00-24:00"
222         "saturday"  = "00:00-24:00"
223         "sunday"    = "00:00-24:00"
224       }
225     }
226
227 If your operation staff should only be notified during workhours,
228 create a new timeperiod named `workhours` defining a work day from
229 09:00 to 17:00.
230
231     object TimePeriod "workhours" {
232       import "legacy-timeperiod"
233
234       display_name = "Icinga 2 8x5 TimePeriod"
235       ranges = {
236         "monday"    = "09:00-17:00"
237         "tuesday"   = "09:00-17:00"
238         "wednesday" = "09:00-17:00"
239         "thursday"  = "09:00-17:00"
240         "friday"    = "09:00-17:00"
241       }
242     }
243
244 Furthermore if you wish to specify a notification period across midnight,
245 you can define it the following way:
246
247     object Timeperiod "across-midnight" {
248       import "legacy-timeperiod"
249
250       display_name = "Nightly Notification"
251       ranges = {
252         "saturday" = "22:00-24:00"
253         "sunday" = "00:00-03:00"
254       }
255     }
256
257 Below you can see another example for configuring timeperiods across several
258 days, weeks or months. This can be useful when taking components offline
259 for a distinct period of time.
260
261     object Timeperiod "standby" {
262       import "legacy-timeperiod"
263
264       display_name = "Standby"
265       ranges = {
266         "2016-09-30 - 2016-10-30" = "00:00-24:00"
267       }
268     }
269
270 Please note that the spaces before and after the dash are mandatory.
271
272 Once your time period is configured you can Use the `period` attribute
273 to assign time periods to `Notification` and `Dependency` objects:
274
275     object Notification "mail" {
276       import "generic-notification"
277
278       host_name = "localhost"
279
280       command = "mail-notification"
281       users = [ "icingaadmin" ]
282       period = "workhours"
283     }
284
285 ### Time Periods Inclusion and Exclusion <a id="timeperiods-includes-excludes"></a>
286
287 Sometimes it is necessary to exclude certain time ranges from
288 your default time period definitions, for example, if you don't
289 want to send out any notification during the holiday season,
290 or if you only want to allow small time windows for executed checks.
291
292 The [TimePeriod object](09-object-types.md#objecttype-timeperiod)
293 provides the `includes` and `excludes` attributes to solve this issue.
294 `prefer_includes` defines whether included or excluded time periods are
295 preferred.
296
297 The following example defines a time period called `holidays` where
298 notifications should be supressed:
299
300     object TimePeriod "holidays" {
301       import "legacy-timeperiod"
302     
303       ranges = {
304         "january 1" = "00:00-24:00"                 //new year's day
305         "july 4" = "00:00-24:00"                    //independence day
306         "december 25" = "00:00-24:00"               //christmas
307         "december 31" = "18:00-24:00"               //new year's eve (6pm+)
308         "2017-04-16" = "00:00-24:00"                //easter 2017
309         "monday -1 may" = "00:00-24:00"             //memorial day (last monday in may)
310         "monday 1 september" = "00:00-24:00"        //labor day (1st monday in september)
311         "thursday 4 november" = "00:00-24:00"       //thanksgiving (4th thursday in november)
312       }
313     }
314
315 In addition to that the time period `weekends` defines an additional
316 time window which should be excluded from notifications:
317
318     object TimePeriod "weekends-excluded" {
319       import "legacy-timeperiod"
320     
321       ranges = {
322         "saturday"  = "00:00-09:00,18:00-24:00"
323         "sunday"    = "00:00-09:00,18:00-24:00"
324       }
325     }
326
327 The time period `prod-notification` defines the default time ranges
328 and adds the excluded time period names as an array.
329
330     object TimePeriod "prod-notification" {
331       import "legacy-timeperiod"
332     
333       excludes = [ "holidays", "weekends-excluded" ]
334     
335       ranges = {
336         "monday"    = "00:00-24:00"
337         "tuesday"   = "00:00-24:00"
338         "wednesday" = "00:00-24:00"
339         "thursday"  = "00:00-24:00"
340         "friday"    = "00:00-24:00"
341         "saturday"  = "00:00-24:00"
342         "sunday"    = "00:00-24:00"
343       }
344     }
345
346 ## External Check Results <a id="external-check-results"></a>
347
348 Hosts or services which do not actively execute a check plugin to receive
349 the state and output are called "passive checks" or "external check results".
350 In this scenario an external client or script is sending in check results.
351
352 You can feed check results into Icinga 2 with the following transport methods:
353
354 * [process-check-result action](12-icinga2-api.md#icinga2-api-actions-process-check-result) available with the [REST API](12-icinga2-api.md#icinga2-api) (remote and local)
355 * External command sent via command pipe (local only)
356
357 Each time a new check result is received, the next expected check time
358 is updated. This means that if there are no check result received from
359 the external source, Icinga 2 will execute [freshness checks](08-advanced-topics.md#check-result-freshness).
360
361 > **Note**
362 >
363 > The REST API action allows to specify the `check_source` attribute
364 > which helps identifying the external sender. This is also visible
365 > in Icinga Web 2 and the REST API queries.
366
367 ## Check Result Freshness <a id="check-result-freshness"></a>
368
369 In Icinga 2 active check freshness is enabled by default. It is determined by the
370 `check_interval` attribute and no incoming check results in that period of time.
371
372 The threshold is calculated based on the last check execution time for actively executed checks:
373
374     (last check execution time + check interval) > current time
375
376 If this host/service receives check results from an [external source](08-advanced-topics.md#external-check-results),
377 the threshold is based on the last time a check result was received:
378
379     (last check result time + check interval) > current time
380
381 If the freshness checks fail, Icinga 2 will execute the defined check command.
382
383 Best practice is to define a [dummy](10-icinga-template-library.md#plugin-check-command-dummy) `check_command` which gets
384 executed when freshness checks fail.
385
386 ```
387 apply Service "external-check" {
388   check_command = "dummy"
389   check_interval = 1m
390
391   /* Set the state to UNKNOWN (3) if freshness checks fail. */
392   vars.dummy_state = 3
393
394   /* Use a runtime function to retrieve the last check time and more details. */
395   vars.dummy_text = {{
396     var service = get_service(macro("$host.name$"), macro("$service.name$"))
397     var lastCheck = DateTime(service.last_check).to_string()
398
399     return "No check results received. Last result time: " + lastCheck
400   }}
401
402   assign where "external" in host.vars.services
403 }
404 ```
405
406 References: [get_service](18-library-reference.md#objref-get_service), [nacro](18-library-reference.md#scoped-functions-macro), [DateTime](18-library-reference.md#datetime-type).
407
408 Example output in Icinga Web 2:
409
410 ![Icinga 2 Freshness Checks](images/advanced-topics/icinga2_external_checks_freshness_icingaweb2.png)
411
412
413 ## Check Flapping <a id="check-flapping"></a>
414
415 Icinga 2 supports optional detection of hosts and services that are "flapping".
416
417 Flapping occurs when a service or host changes state too frequently, resulting
418 in a storm of problem and recovery notifications. Flapping can be the source of
419 configuration problems (i.e. thresholds set too low), troublesome services,
420 or real network problems.
421
422 Flapping detection can be enabled or disabled using the `enable_flapping` attribute.
423 The `flapping_threshold` attributes allows to specify the percentage of state changes
424 when a [host](09-object-types.md#objecttype-host) or [service](objecttype-service) is considered to flap.
425
426 Note: There are known issues with flapping detection. Please refrain from enabling
427 flapping until [#4982](https://github.com/Icinga/icinga2/issues/4982) is fixed.
428
429 ## Volatile Services <a id="volatile-services"></a>
430
431 By default all services remain in a non-volatile state. When a problem
432 occurs, the `SOFT` state applies and once `max_check_attempts` attribute
433 is reached with the check counter, a `HARD` state transition happens.
434 Notifications are only triggered by `HARD` state changes and are then
435 re-sent defined by the `interval` attribute.
436
437 It may be reasonable to have a volatile service which stays in a `HARD`
438 state type if the service stays in a `NOT-OK` state. That way each
439 service recheck will automatically trigger a notification unless the
440 service is acknowledged or in a scheduled downtime.
441
442 ## Monitoring Icinga 2 <a id="monitoring-icinga"></a>
443
444 Why should you do that? Icinga and its components run like any other
445 service application on your server. There are predictable issues
446 such as "disk space is running low" and your monitoring suffers from just
447 that.
448
449 You would also like to ensure that features and backends are running
450 and storing required data. Be it the database backend where Icinga Web 2
451 presents fancy dashboards, forwarded metrics to Graphite or InfluxDB or
452 the entire distributed setup.
453
454 This list isn't complete but should help with your own setup.
455 Windows client specific checks are highlighted.
456
457 Type            | Description                   | Plugins and CheckCommands
458 ----------------|-------------------------------|-----------------------------------------------------
459 System          | Filesystem                    | [disk](10-icinga-template-library.md#plugin-check-command-disk), [disk-windows](10-icinga-template-library.md#windows-plugins) (Windows Client)
460 System          | Memory, Swap                  | [mem](10-icinga-template-library.md#plugin-contrib-command-mem), [swap](10-icinga-template-library.md#plugin-check-command-swap), [memory](10-icinga-template-library.md#windows-plugins) (Windows Client)
461 System          | Hardware                      | [hpasm](10-icinga-template-library.md#plugin-contrib-command-hpasm), [ipmi-sensor](10-icinga-template-library.md#plugin-contrib-command-ipmi-sensor)
462 System          | Virtualization                | [VMware](10-icinga-template-library.md#plugin-contrib-vmware), [esxi_hardware](10-icinga-template-library.md#plugin-contrib-command-esxi-hardware)
463 System          | Processes                     | [procs](10-icinga-template-library.md#plugin-check-command-processes), [service-windows](10-icinga-template-library.md#windows-plugins) (Windows Client)
464 System          | System Activity Reports       | [check_sar_perf](https://github.com/dnsmichi/icinga-plugins/blob/master/scripts/check_sar_perf.py)
465 System          | I/O                           | [iostat](10-icinga-template-library.md#plugin-contrib-command-iostat)
466 System          | Network interfaces            | [nwc_health](10-icinga-template-library.md#plugin-contrib-command-nwc_health), [interfaces](10-icinga-template-library.md#plugin-contrib-command-interfaces)
467 System          | Users                         | [users](10-icinga-template-library.md#plugin-check-command-users), [users-windows](10-icinga-template-library.md#windows-plugins) (Windows Client)
468 System          | Logs                          | Forward them to [Elastic Stack](14-features.md#elastic-stack-integration) or [Graylog](14-features.md#graylog-integration) and add your own alerts.
469 System          | NTP                           | [ntp_time](10-icinga-template-library.md#plugin-check-command-ntp-time)
470 System          | Updates                       | [apt](10-icinga-template-library.md#plugin-check-command-apt), [yum](10-icinga-template-library.md#plugin-contrib-command-yum)
471 Icinga          | Status & Stats                | [icinga](10-icinga-template-library.md#itl-icinga) (more below)
472 Icinga          | Cluster & Clients             | [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks)
473 Database        | MySQL                         | [mysql_health](10-icinga-template-library.md#plugin-contrib-command-mysql_health)
474 Database        | PostgreSQL                    | [postgres](10-icinga-template-library.md#plugin-contrib-command-postgres)
475 Database        | Housekeeping                  | Check the database size and growth and analyse metrics to examine trends.
476 Database        | DB IDO                        | [ido](10-icinga-template-library.md#itl-icinga-ido) (more below)
477 Webserver       | Apache2, Nginx, etc.          | [http](10-icinga-template-library.md#plugin-check-command-http), [apache_status](10-icinga-template-library.md#plugin-contrib-command-apache_status), [nginx_status](10-icinga-template-library.md#plugin-contrib-command-nginx_status)
478 Webserver       | Certificates                  | [http](10-icinga-template-library.md#plugin-check-command-http)
479 Webserver       | Authorization                 | [http](10-icinga-template-library.md#plugin-check-command-http)
480 Notifications   | Mail (queue)                  | [smtp](10-icinga-template-library.md#plugin-check-command-smtp), [mailq](10-icinga-template-library.md#plugin-check-command-mailq)
481 Notifications   | SMS (GSM modem)               | [check_sms3_status](https://exchange.icinga.com/netways/check_sms3status)
482 Notifications   | Messengers, Cloud services    | XMPP, Twitter, IRC, Telegram, PagerDuty, VictorOps, etc.
483 Metrics         | PNP, RRDTool                  | [check_pnp_rrds](https://github.com/lingej/pnp4nagios/tree/master/scripts) checks for stale RRD files.
484 Metrics         | Graphite                      | [graphite](10-icinga-template-library.md#plugin-contrib-command-graphite)
485 Metrics         | InfluxDB                      | [check_influxdb](https://exchange.icinga.com/Mikanoshi/InfluxDB+data+monitoring+plugin)
486 Metrics         | Elastic Stack                 | [elasticsearch](10-icinga-template-library.md#plugin-contrib-command-elasticsearch), [Elastic Stack integration](14-features.md#elastic-stack-integration)
487 Metrics         | Graylog                       | [Graylog integration](14-features.md#graylog-integration)
488
489
490 The [icinga](10-icinga-template-library.md#itl-icinga) CheckCommand provides metrics for the runtime stats of
491 Icinga 2. You can forward them to your preferred graphing solution.
492 If you require more metrics you can also query the [REST API](12-icinga2-api.md#icinga2-api) and write
493 your own custom check plugin. Or you keep using the built-in [object accessor functions](08-advanced-topics.md#access-object-attributes-at-runtime)
494 to calculate stats in-memory.
495
496 There is a built-in [ido](10-icinga-template-library.md#itl-icinga-ido) check available for DB IDO MySQL/PostgreSQL
497 which provides additional metrics for the IDO database.
498
499 ```
500 apply Service "ido-mysql" {
501   check_command = "ido"
502
503   vars.ido_type = "IdoMysqlConnection"
504   vars.ido_name = "ido-mysql" //the name defined in /etc/icinga2/features-enabled/ido-mysql.conf
505
506   assign where match("master*.localdomain", host.name)
507 }
508 ```
509
510 More specific database queries can be found in the [DB IDO](14-features.md#db-ido) chapter.
511
512 Distributed setups should include specific [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks).
513 You might also want to add additional checks for SSL certificate expiration.
514
515
516 ## Advanced Configuration Hints <a id="advanced-configuration-hints"></a>
517
518 ### Advanced Use of Apply Rules <a id="advanced-use-of-apply-rules"></a>
519
520 [Apply rules](03-monitoring-basics.md#using-apply) can be used to create a rule set which is
521 entirely based on host objects and their attributes.
522 In addition to that [apply for and custom attribute override](03-monitoring-basics.md#using-apply-for)
523 extend the possibilities.
524
525 The following example defines a dictionary on the host object which contains
526 configuration attributes for multiple web servers. This then used to add three checks:
527
528 * A `ping4` check using the local IP `address` of the web server.
529 * A `tcp` check querying the TCP port where the HTTP service is running on.
530 * If the `url` key is defined, the third apply for rule will create service objects using the `http` CheckCommand.
531 In addition to that you can optionally define the `ssl` attribute which enables HTTPS checks.
532
533 Host definition:
534
535     object Host "webserver01" {
536       import "generic-host"
537       address = "192.168.56.200"
538       vars.os = "Linux"
539
540       vars.webserver = {
541         instance["status"] = {
542           address = "192.168.56.201"
543           port = "80"
544           url = "/status"
545         }
546         instance["tomcat"] = {
547           address = "192.168.56.202"
548           port = "8080"
549         }
550         instance["icingaweb2"] = {
551           address = "192.168.56.210"
552           port = "443"
553           url = "/icingaweb2"
554           ssl = true
555         }
556       }
557     }
558
559 Service apply for definitions:
560
561     apply Service "webserver_ping" for (instance => config in host.vars.webserver.instance) {
562       display_name = "webserver_" + instance
563       check_command = "ping4"
564
565       vars.ping_address = config.address
566
567       assign where host.vars.webserver.instance
568     }
569
570     apply Service "webserver_port" for (instance => config in host.vars.webserver.instance) {
571       display_name = "webserver_" + instance + "_" + config.port
572       check_command = "tcp"
573
574       vars.tcp_address = config.address
575       vars.tcp_port = config.port
576
577       assign where host.vars.webserver.instance
578     }
579
580     apply Service "webserver_url" for (instance => config in host.vars.webserver.instance) {
581       display_name = "webserver_" + instance + "_" + config.url
582       check_command = "http"
583
584       vars.http_address = config.address
585       vars.http_port = config.port
586       vars.http_uri = config.url
587
588       if (config.ssl) {
589         vars.http_ssl = config.ssl
590       }
591
592       assign where config.url != ""
593     }
594
595 The variables defined in the host dictionary are not using the typical custom attribute
596 prefix recommended for CheckCommand parameters. Instead they are re-used for multiple
597 service checks in this example.
598 In addition to defining check parameters this way, you can also enrich the `display_name`
599 attribute with more details. This will be shown in in Icinga Web 2 for example.
600
601 ### Use Functions in Object Configuration <a id="use-functions-object-config"></a>
602
603 There is a limited scope where functions can be used as object attributes such as:
604
605 * As value for [Custom Attributes](03-monitoring-basics.md#custom-attributes-functions)
606 * Returning boolean expressions for [set_if](08-advanced-topics.md#use-functions-command-arguments-setif) inside command arguments
607 * Returning a [command](08-advanced-topics.md#use-functions-command-attribute) array inside command objects
608
609 The other way around you can create objects dynamically using your own global functions.
610
611 > **Note**
612 >
613 > Functions called inside command objects share the same global scope as runtime macros.
614 > Therefore you can access host custom attributes like `host.vars.os`, or any other
615 > object attribute from inside the function definition used for [set_if](08-advanced-topics.md#use-functions-command-arguments-setif) or [command](08-advanced-topics.md#use-functions-command-attribute).
616
617 Tips when implementing functions:
618
619 * Use [log()](18-library-reference.md#global-functions-log) to dump variables. You can see the output
620 inside the `icinga2.log` file depending in your log severity
621 * Use the `icinga2 console` to test basic functionality (e.g. iterating over a dictionary)
622 * Build them step-by-step. You can always refactor your code later on.
623
624 #### Use Functions in Command Arguments set_if <a id="use-functions-command-arguments-setif"></a>
625
626 The `set_if` attribute inside the command arguments definition in the
627 [CheckCommand object definition](09-object-types.md#objecttype-checkcommand) is primarily used to
628 evaluate whether the command parameter should be set or not.
629
630 By default you can evaluate runtime macros for their existence. If the result is not an empty
631 string, the command parameter is passed. This becomes fairly complicated when want to evaluate
632 multiple conditions and attributes.
633
634 The following example was found on the community support channels. The user had defined a host
635 dictionary named `compellent` with the key `disks`. This was then used inside service apply for rules.
636
637     object Host "dict-host" {
638       check_command = "check_compellent"
639       vars.compellent["disks"] = {
640         file = "/var/lib/check_compellent/san_disks.0.json",
641         checks = ["disks"]
642       }
643     }
644
645 The more significant problem was to only add the command parameter `--disk` to the plugin call
646 when the dictionary `compellent` contains the key `disks`, and omit it if not found.
647
648 By defining `set_if` as [abbreviated lambda function](17-language-reference.md#nullary-lambdas)
649 and evaluating the host custom attribute `compellent` containing the `disks` this problem was
650 solved like this:
651
652     object CheckCommand "check_compellent" {
653       command   = [ "/usr/bin/check_compellent" ]
654       arguments   = {
655         "--disks"  = {
656           set_if = {{
657             var host_vars = host.vars
658             log(host_vars)
659             var compel = host_vars.compellent
660             log(compel)
661             compel.contains("disks")
662           }}
663         }
664       }
665     }
666
667 This implementation uses the dictionary type method [contains](18-library-reference.md#dictionary-contains)
668 and will fail if `host.vars.compellent` is not of the type `Dictionary`.
669 Therefore you can extend the checks using the [typeof](17-language-reference.md#types) function.
670
671 You can test the types using the `icinga2 console`:
672
673     # icinga2 console
674     Icinga (version: v2.3.0-193-g3eb55ad)
675     <1> => srv_vars.compellent["check_a"] = { file="outfile_a.json", checks = [ "disks", "fans" ] }
676     null
677     <2> => srv_vars.compellent["check_b"] = { file="outfile_b.json", checks = [ "power", "voltages" ] }
678     null
679     <3> => typeof(srv_vars.compellent)
680     type 'Dictionary'
681     <4> =>
682
683 The more programmatic approach for `set_if` could look like this:
684
685         "--disks" = {
686           set_if = {{
687             var srv_vars = service.vars
688             if(len(srv_vars) > 0) {
689               if (typeof(srv_vars.compellent) == Dictionary) {
690                 return srv_vars.compellent.contains("disks")
691               } else {
692                 log(LogInformationen, "checkcommand set_if", "custom attribute compellent_checks is not a dictionary, ignoring it.")
693                 return false
694               }
695             } else {
696               log(LogWarning, "checkcommand set_if", "empty custom attributes")
697               return false
698             }
699           }}
700         }
701
702
703 #### Use Functions as Command Attribute <a id="use-functions-command-attribute"></a>
704
705 This comes in handy for [NotificationCommands](09-object-types.md#objecttype-notificationcommand)
706 or [EventCommands](09-object-types.md#objecttype-eventcommand) which does not require
707 a returned checkresult including state/output.
708
709 The following example was taken from the community support channels. The requirement was to
710 specify a custom attribute inside the notification apply rule and decide which notification
711 script to call based on that.
712
713     object User "short-dummy" {
714     }
715     
716     object UserGroup "short-dummy-group" {
717       assign where user.name == "short-dummy"
718     }
719     
720     apply Notification "mail-admins-short" to Host {
721        import "mail-host-notification"
722        command = "mail-host-notification-test"
723        user_groups = [ "short-dummy-group" ]
724        vars.short = true
725        assign where host.vars.notification.mail
726     }
727
728 The solution is fairly simple: The `command` attribute is implemented as function returning
729 an array required by the caller Icinga 2.
730 The local variable `mailscript` sets the default value for the notification scrip location.
731 If the notification custom attribute `short` is set, it will override the local variable `mailscript`
732 with a new value.
733 The `mailscript` variable is then used to compute the final notification command array being
734 returned.
735
736 You can omit the `log()` calls, they only help debugging.
737
738     object NotificationCommand "mail-host-notification-test" {
739       command = {{
740         log("command as function")
741         var mailscript = "mail-host-notification-long.sh"
742         if (notification.vars.short) {
743            mailscript = "mail-host-notification-short.sh"
744         }
745         log("Running command")
746         log(mailscript)
747     
748         var cmd = [ SysconfDir + "/icinga2/scripts/" + mailscript ]
749         log(LogCritical, "me", cmd)
750         return cmd
751       }}
752     
753       env = {
754       }
755     }
756
757 #### Use Custom Functions as Attribute <a id="custom-functions-as-attribute"></a>
758
759 To use custom functions as attributes, the function must be defined in a
760 slightly unexpected way. The following example shows how to assign values
761 depending on group membership. All hosts in the `slow-lan` host group use 300
762 as value for `ping_wrta`, all other hosts use 100.
763
764     globals.group_specific_value = function(group, group_value, non_group_value) {
765         return function() use (group, group_value, non_group_value) {
766             if (group in host.groups) {
767                 return group_value
768             } else {
769                 return non_group_value
770             }
771         }
772     }
773     
774     apply Service "ping4" {
775         import "generic-service"
776         check_command = "ping4"
777     
778         vars.ping_wrta = group_specific_value("slow-lan", 300, 100)
779         vars.ping_crta = group_specific_value("slow-lan", 500, 200)
780     
781         assign where true
782     }
783
784 #### Use Functions in Assign Where Expressions <a id="use-functions-assign-where"></a>
785
786 If a simple expression for matching a name or checking if an item
787 exists in an array or dictionary does not fit, you should consider
788 writing your own global [functions](17-language-reference.md#functions).
789 You can call them inside `assign where` and `ignore where` expressions
790 for [apply rules](03-monitoring-basics.md#using-apply-expressions) or
791 [group assignments](03-monitoring-basics.md#group-assign-intro) just like
792 any other global functions for example [match](18-library-reference.md#global-functions-match).
793
794 The following example requires the host `myprinter` being added
795 to the host group `printers-lexmark` but only if the host uses
796 a template matching the name `lexmark*`.
797
798     template Host "lexmark-printer-host" {
799       vars.printer_type = "Lexmark"
800     }
801
802     object Host "myprinter" {
803       import "generic-host"
804       import "lexmark-printer-host"
805
806       address = "192.168.1.1"
807     }
808
809     /* register a global function for the assign where call */
810     globals.check_host_templates = function(host, search) {
811       /* iterate over all host templates and check if the search matches */
812       for (tmpl in host.templates) {
813         if (match(search, tmpl)) {
814           return true
815         }
816       }
817
818       /* nothing matched */
819       return false
820     }
821
822     object HostGroup "printers-lexmark" {
823       display_name = "Lexmark Printers"
824       /* call the global function and pass the arguments */
825       assign where check_host_templates(host, "lexmark*")
826     }
827
828
829 Take a different more complex example: All hosts with the
830 custom attribute `vars_app` as nested dictionary should be
831 added to the host group `ABAP-app-server`. But only if the
832 `app_type` for all entries is set to `ABAP`.
833
834 It could read as wildcard match for nested dictionaries:
835
836     where host.vars.vars_app["*"].app_type == "ABAP"
837
838 The solution for this problem is to register a global
839 function which checks the `app_type` for all hosts
840 with the `vars_app` dictionary.
841
842     object Host "appserver01" {
843       check_command = "dummy"
844       vars.vars_app["ABC"] = { app_type = "ABAP" }
845     }
846     object Host "appserver02" {
847       check_command = "dummy"
848       vars.vars_app["DEF"] = { app_type = "ABAP" }
849     }
850
851     globals.check_app_type = function(host, type) {
852       /* ensure that other hosts without the custom attribute do not match */
853       if (typeof(host.vars.vars_app) != Dictionary) {
854         return false
855       }
856
857       /* iterate over the vars_app dictionary */
858       for (key => val in host.vars.vars_app) {
859         /* if the value is a dictionary and if contains the app_type being the requested type */
860         if (typeof(val) == Dictionary && val.app_type == type) {
861           return true
862         }
863       }
864
865       /* nothing matched */
866       return false
867     }
868
869     object HostGroup "ABAP-app-server" {
870       assign where check_app_type(host, "ABAP")
871     }
872
873 ### Access Object Attributes at Runtime <a id="access-object-attributes-at-runtime"></a>
874
875 The [Object Accessor Functions](18-library-reference.md#object-accessor-functions)
876 can be used to retrieve references to other objects by name.
877
878 This allows you to access configuration and runtime object attributes. A detailed
879 list can be found [here](09-object-types.md#object-types).
880
881 #### Access Object Attributes at Runtime: Cluster Check <a id="access-object-attributes-at-runtime-cluster-check"></a>
882
883 This is a simple cluster example for accessing two host object states and calculating a virtual
884 cluster state and output:
885
886 ```
887 object Host "cluster-host-01" {
888   check_command = "dummy"
889   vars.dummy_state = 2
890   vars.dummy_text = "This host is down."
891 }
892
893 object Host "cluster-host-02" {
894   check_command = "dummy"
895   vars.dummy_state = 0
896   vars.dummy_text = "This host is up."
897 }
898
899 object Host "cluster" {
900   check_command = "dummy"
901   vars.cluster_nodes = [ "cluster-host-01", "cluster-host-02" ]
902
903   vars.dummy_state = {{
904     var up_count = 0
905     var down_count = 0
906     var cluster_nodes = macro("$cluster_nodes$")
907
908     for (node in cluster_nodes) {
909       if (get_host(node).state > 0) {
910         down_count += 1
911       } else {
912         up_count += 1
913       }
914     }
915
916     if (up_count >= down_count) {
917       return 0 //same up as down -> UP
918     } else {
919       return 2 //something is broken
920     }
921   }}
922
923   vars.dummy_text = {{
924     var output = "Cluster hosts:\n"
925     var cluster_nodes = macro("$cluster_nodes$")
926
927     for (node in cluster_nodes) {
928       output += node + ": " + get_host(node).last_check_result.output + "\n"
929     }
930
931     return output
932   }}
933 }
934 ```
935
936 #### Time Dependent Thresholds <a id="access-object-attributes-at-runtime-time-dependent-thresholds"></a>
937
938 The following example sets time dependent thresholds for the load check based on the current
939 time of the day compared to the defined time period.
940
941 ```
942 object TimePeriod "backup" {
943   import "legacy-timeperiod"
944
945   ranges = {
946     monday = "02:00-03:00"
947     tuesday = "02:00-03:00"
948     wednesday = "02:00-03:00"
949     thursday = "02:00-03:00"
950     friday = "02:00-03:00"
951     saturday = "02:00-03:00"
952     sunday = "02:00-03:00"
953   }
954 }
955
956 object Host "webserver-with-backup" {
957   check_command = "hostalive"
958   address = "127.0.0.1"
959 }
960
961 object Service "webserver-backup-load" {
962   check_command = "load"
963   host_name = "webserver-with-backup"
964
965   vars.load_wload1 = {{
966     if (get_time_period("backup").is_inside) {
967       return 20
968     } else {
969       return 5
970     }
971   }}
972   vars.load_cload1 = {{
973     if (get_time_period("backup").is_inside) {
974       return 40
975     } else {
976       return 10
977     }
978   }}
979 }
980 ```
981
982
983 ## Advanced Value Types <a id="advanced-value-types"></a>
984
985 In addition to the default value types Icinga 2 also uses a few other types
986 to represent its internal state. The following types are exposed via the [API](12-icinga2-api.md#icinga2-api).
987
988 ### CheckResult <a id="advanced-value-types-checkresult"></a>
989
990   Name                      | Type                  | Description
991   --------------------------|-----------------------|----------------------------------
992   exit\_status              | Number                | The exit status returned by the check execution.
993   output                    | String                | The check output.
994   performance\_data         | Array                 | Array of [performance data values](08-advanced-topics.md#advanced-value-types-perfdatavalue).
995   check\_source             | String                | Name of the node executing the check.
996   state                     | Number                | The current state (0 = OK, 1 = WARNING, 2 = CRITICAL, 3 = UNKNOWN).
997   command                   | Value                 | Array of command with shell-escaped arguments or command line string.
998   execution\_start          | Timestamp             | Check execution start time (as a UNIX timestamp).
999   execution\_end            | Timestamp             | Check execution end time (as a UNIX timestamp).
1000   schedule\_start           | Timestamp             | Scheduled check execution start time (as a UNIX timestamp).
1001   schedule\_end             | Timestamp             | Scheduled check execution end time (as a UNIX timestamp).
1002   active                    | Boolean               | Whether the result is from an active or passive check.
1003   vars\_before              | Dictionary            | Internal attribute used for calculations.
1004   vars\_after               | Dictionary            | Internal attribute used for calculations.
1005
1006 ### PerfdataValue <a id="advanced-value-types-perfdatavalue"></a>
1007
1008 Icinga 2 parses performance data strings returned by check plugins and makes the information available to external interfaces (e.g. [GraphiteWriter](09-object-types.md#objecttype-graphitewriter) or the [Icinga 2 API](12-icinga2-api.md#icinga2-api)).
1009
1010   Name                      | Type                  | Description
1011   --------------------------|-----------------------|----------------------------------
1012   label                     | String                | Performance data label.
1013   value                     | Number                | Normalized performance data value without unit.
1014   counter                   | Boolean               | Enabled if the original value contains `c` as unit. Defaults to `false`.
1015   unit                      | String                | Unit of measurement (`seconds`, `bytes`. `percent`) according to the [plugin API](05-service-monitoring.md#service-monitoring-plugin-api).
1016   crit                      | Value                 | Critical threshold value.
1017   warn                      | Value                 | Warning threshold value.
1018   min                       | Value                 | Minimum value returned by the check.
1019   max                       | Value                 | Maximum value returned by the check.
1020
1021