mirror of
				https://github.com/Icinga/icinga2.git
				synced 2025-10-31 19:24:25 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			924 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			924 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # <a id="advanced-topics"></a> Advanced Topics
 | |
| 
 | |
| This chapter covers a number of advanced topics. If you're new to Icinga, you
 | |
| can safely skip over things you're not interested in.
 | |
| 
 | |
| ## <a id="downtimes"></a> Downtimes
 | |
| 
 | |
| Downtimes can be scheduled for planned server maintenance or
 | |
| any other targeted service outage you are aware of in advance.
 | |
| 
 | |
| Downtimes will suppress any notifications, and may trigger other
 | |
| downtimes too. If the downtime was set by accident, or the duration
 | |
| exceeds the maintenance, you can manually cancel the downtime.
 | |
| Planned downtimes will also be taken into account for SLA reporting
 | |
| tools calculating the SLAs based on the state and downtime history.
 | |
| 
 | |
| Multiple downtimes for a single object may overlap. This is useful
 | |
| when you want to extend your maintenance window taking longer than expected.
 | |
| If there are multiple downtimes triggered for one object, the overall downtime depth
 | |
| will be greater than `1`.
 | |
| 
 | |
| 
 | |
| If the downtime was scheduled after the problem changed to a critical hard
 | |
| state triggering a problem notification, and the service recovers during
 | |
| the downtime window, the recovery notification won't be suppressed.
 | |
| 
 | |
| ### <a id="fixed-flexible-downtimes"></a> Fixed and Flexible Downtimes
 | |
| 
 | |
| A `fixed` downtime will be activated at the defined start time, and
 | |
| removed at the end time. During this time window the service state
 | |
| will change to `NOT-OK` and then actually trigger the downtime.
 | |
| Notifications are suppressed and the downtime depth is incremented.
 | |
| 
 | |
| Common scenarios are a planned distribution upgrade on your linux
 | |
| servers, or database updates in your warehouse. The customer knows
 | |
| about a fixed downtime window between 23:00 and 24:00. After 24:00
 | |
| all problems should be alerted again. Solution is simple -
 | |
| schedule a `fixed` downtime starting at 23:00 and ending at 24:00.
 | |
| 
 | |
| Unlike a `fixed` downtime, a `flexible` downtime will be triggered
 | |
| by the state change in the time span defined by start and end time,
 | |
| and then last for the specified duration in minutes.
 | |
| 
 | |
| Imagine the following scenario: Your service is frequently polled
 | |
| by users trying to grab free deleted domains for immediate registration.
 | |
| Between 07:30 and 08:00 the impact will hit for 15 minutes and generate
 | |
| a network outage visible to the monitoring. The service is still alive,
 | |
| but answering too slow to Icinga 2 service checks.
 | |
| For that reason, you may want to schedule a downtime between 07:30 and
 | |
| 08:00 with a duration of 15 minutes. The downtime will then last from
 | |
| its trigger time until the duration is over. After that, the downtime
 | |
| is removed (may happen before or after the actual end time!).
 | |
| 
 | |
| ### <a id="scheduling-downtime"></a> Scheduling a downtime
 | |
| 
 | |
| You can schedule a downtime either by using the Icinga 2 API action
 | |
| [schedule-downtime](12-icinga2-api.md#icinga2-api-actions-schedule-downtime) or
 | |
| by sending an [external command](14-features.md#external-commands).
 | |
| 
 | |
| 
 | |
| #### <a id="fixed-downtime"></a> Fixed Downtime
 | |
| 
 | |
| If the host/service changes into a NOT-OK state between the start and
 | |
| end time window, the downtime will be marked as `in effect` and
 | |
| increases the downtime depth counter.
 | |
| 
 | |
| ```
 | |
|    |       |         |
 | |
| start      |        end
 | |
|        trigger time
 | |
| ```
 | |
| 
 | |
| #### <a id="flexible-downtime"></a> Flexible Downtime
 | |
| 
 | |
| A flexible downtime defines a time window where the downtime may be
 | |
| triggered from a host/service NOT-OK state change. It will then last
 | |
| until the specified time duration is reached. That way it can happen
 | |
| that the downtime end time is already gone, but the downtime ends
 | |
| at `trigger time + duration`.
 | |
| 
 | |
| 
 | |
| ```
 | |
|    |       |         |
 | |
| start      |        end               actual end time
 | |
|            |--------------duration--------|
 | |
|        trigger time
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### <a id="triggered-downtimes"></a> Triggered Downtimes
 | |
| 
 | |
| This is optional when scheduling a downtime. If there is already a downtime
 | |
| scheduled for a future maintenance, the current downtime can be triggered by
 | |
| that downtime. This renders useful if you have scheduled a host downtime and
 | |
| are now scheduling a child host's downtime getting triggered by the parent
 | |
| downtime on `NOT-OK` state change.
 | |
| 
 | |
| ### <a id="recurring-downtimes"></a> Recurring Downtimes
 | |
| 
 | |
| [ScheduledDowntime objects](9-object-types.md#objecttype-scheduleddowntime) can be used to set up
 | |
| recurring downtimes for services.
 | |
| 
 | |
| Example:
 | |
| 
 | |
|     apply ScheduledDowntime "backup-downtime" to Service {
 | |
|       author = "icingaadmin"
 | |
|       comment = "Scheduled downtime for backup"
 | |
| 
 | |
|       ranges = {
 | |
|         monday = "02:00-03:00"
 | |
|         tuesday = "02:00-03:00"
 | |
|         wednesday = "02:00-03:00"
 | |
|         thursday = "02:00-03:00"
 | |
|         friday = "02:00-03:00"
 | |
|         saturday = "02:00-03:00"
 | |
|         sunday = "02:00-03:00"
 | |
|       }
 | |
| 
 | |
|       assign where "backup" in service.groups
 | |
|     }
 | |
| 
 | |
| 
 | |
| ## <a id="comments-intro"></a> Comments
 | |
| 
 | |
| Comments can be added at runtime and are persistent over restarts. You can
 | |
| add useful information for others on repeating incidents (for example
 | |
| "last time syslog at 100% cpu on 17.10.2013 due to stale nfs mount") which
 | |
| is primarily accessible using web interfaces.
 | |
| 
 | |
| You can add a comment either by using the Icinga 2 API action
 | |
| [add-comment](12-icinga2-api.md#icinga2-api-actions-add-comment) or
 | |
| by sending an [external command](14-features.md#external-commands).
 | |
| 
 | |
| ## <a id="acknowledgements"></a> Acknowledgements
 | |
| 
 | |
| If a problem persists and notifications have been sent, you can
 | |
| acknowledge the problem. That way other users will get
 | |
| a notification that you're aware of the issue and probably are
 | |
| already working on a fix.
 | |
| 
 | |
| Note: Acknowledgements also add a new [comment](8-advanced-topics.md#comments-intro)
 | |
| which contains the author and text fields.
 | |
| 
 | |
| You can send an acknowledgement either by using the Icinga 2 API action
 | |
| [acknowledge-problem](12-icinga2-api.md#icinga2-api-actions-acknowledge-problem) or
 | |
| by sending an [external command](14-features.md#external-commands).
 | |
| 
 | |
| 
 | |
| ### <a id="sticky-acknowledgements"></a> Sticky Acknowledgements
 | |
| 
 | |
| The acknowledgement is removed if a state change occurs or if the host/service
 | |
| recovers (OK/Up state).
 | |
| 
 | |
| If you acknowlege a problem once you've received a `Critical` notification,
 | |
| the acknowledgement will be removed if there is a state transition to `Warning`.
 | |
| ```
 | |
| OK -> WARNING -> CRITICAL -> WARNING -> OK
 | |
| ```
 | |
| 
 | |
| If you prefer to keep the acknowledgement until the problem is resolved (`OK`
 | |
| recovery) you need to enable the `sticky` parameter.
 | |
| 
 | |
| 
 | |
| ### <a id="expiring-acknowledgements"></a> Expiring Acknowledgements
 | |
| 
 | |
| Once a problem is acknowledged it may disappear from your `handled problems`
 | |
| dashboard and no-one ever looks at it again since it will suppress
 | |
| notifications too.
 | |
| 
 | |
| This `fire-and-forget` action is quite common. If you're sure that a
 | |
| current problem should be resolved in the future at a defined time,
 | |
| you can define an expiration time when acknowledging the problem.
 | |
| 
 | |
| Icinga 2 will clear the acknowledgement when expired and start to
 | |
| re-notify, if the problem persists.
 | |
| 
 | |
| 
 | |
| ## <a id="timeperiods"></a> Time Periods
 | |
| 
 | |
| [Time Periods](9-object-types.md#objecttype-timeperiod) define
 | |
| time ranges in Icinga where event actions are triggered, for
 | |
| example whether a service check is executed or not within
 | |
| the `check_period` attribute. Or a notification should be sent to
 | |
| users or not, filtered by the `period` and `notification_period`
 | |
| configuration attributes for `Notification` and `User` objects.
 | |
| 
 | |
| > **Note**
 | |
| >
 | |
| > If you are familiar with Icinga 1.x, these time period definitions
 | |
| > are called `legacy timeperiods` in Icinga 2.
 | |
| >
 | |
| > An Icinga 2 legacy timeperiod requires the `ITL` provided template
 | |
| >`legacy-timeperiod`.
 | |
| 
 | |
| The `TimePeriod` attribute `ranges` may contain multiple directives,
 | |
| including weekdays, days of the month, and calendar dates.
 | |
| These types may overlap/override other types in your ranges dictionary.
 | |
| 
 | |
| The descending order of precedence is as follows:
 | |
| 
 | |
| * Calendar date (2008-01-01)
 | |
| * Specific month date (January 1st)
 | |
| * Generic month date (Day 15)
 | |
| * Offset weekday of specific month (2nd Tuesday in December)
 | |
| * Offset weekday (3rd Monday)
 | |
| * Normal weekday (Tuesday)
 | |
| 
 | |
| If you don't set any `check_period` or `notification_period` attribute
 | |
| on your configuration objects, Icinga 2 assumes `24x7` as time period
 | |
| as shown below.
 | |
| 
 | |
|     object TimePeriod "24x7" {
 | |
|       import "legacy-timeperiod"
 | |
| 
 | |
|       display_name = "Icinga 2 24x7 TimePeriod"
 | |
|       ranges = {
 | |
|         "monday"    = "00:00-24:00"
 | |
|         "tuesday"   = "00:00-24:00"
 | |
|         "wednesday" = "00:00-24:00"
 | |
|         "thursday"  = "00:00-24:00"
 | |
|         "friday"    = "00:00-24:00"
 | |
|         "saturday"  = "00:00-24:00"
 | |
|         "sunday"    = "00:00-24:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
| If your operation staff should only be notified during workhours,
 | |
| create a new timeperiod named `workhours` defining a work day from
 | |
| 09:00 to 17:00.
 | |
| 
 | |
|     object TimePeriod "workhours" {
 | |
|       import "legacy-timeperiod"
 | |
| 
 | |
|       display_name = "Icinga 2 8x5 TimePeriod"
 | |
|       ranges = {
 | |
|         "monday"    = "09:00-17:00"
 | |
|         "tuesday"   = "09:00-17:00"
 | |
|         "wednesday" = "09:00-17:00"
 | |
|         "thursday"  = "09:00-17:00"
 | |
|         "friday"    = "09:00-17:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
| Furthermore if you wish to specify a notification period across midnight,
 | |
| you can define it the following way:
 | |
| 
 | |
|     object Timeperiod "across-midnight" {
 | |
|       import "legacy-timeperiod"
 | |
| 
 | |
|       display_name = "Nightly Notification"
 | |
|       ranges = {
 | |
|         "saturday" = "22:00-24:00"
 | |
|         "sunday" = "00:00-03:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
| Below you can see another example for configuring timeperiods across several
 | |
| days, weeks or months. This can be useful when taking components offline
 | |
| for a distinct period of time.
 | |
| 
 | |
|     object Timeperiod "standby" {
 | |
|       import "legacy-timeperiod"
 | |
| 
 | |
|       display_name = "Standby"
 | |
|       ranges = {
 | |
|         "2016-09-30 - 2016-10-30" = "00:00-24:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
| Please note that the spaces before and after the dash are mandatory.
 | |
| 
 | |
| Once your time period is configured you can Use the `period` attribute
 | |
| to assign time periods to `Notification` and `Dependency` objects:
 | |
| 
 | |
|     object Notification "mail" {
 | |
|       import "generic-notification"
 | |
| 
 | |
|       host_name = "localhost"
 | |
| 
 | |
|       command = "mail-notification"
 | |
|       users = [ "icingaadmin" ]
 | |
|       period = "workhours"
 | |
|     }
 | |
| 
 | |
| ### <a id="timeperiods-includes-excludes"></a> Time Periods Inclusion and Exclusion
 | |
| 
 | |
| Sometimes it is necessary to exclude certain time ranges from
 | |
| your default time period definitions, for example, if you don't
 | |
| want to send out any notification during the holiday season,
 | |
| or if you only want to allow small time windows for executed checks.
 | |
| 
 | |
| The [TimePeriod object](9-object-types.md#objecttype-timeperiod)
 | |
| provides the `includes` and `excludes` attributes to solve this issue.
 | |
| `prefer_includes` defines whether included or excluded time periods are
 | |
| preferred.
 | |
| 
 | |
| The following example defines a time period called `holidays` where
 | |
| notifications should be supressed:
 | |
| 
 | |
|     object TimePeriod "holidays" {
 | |
|       import "legacy-timeperiod"
 | |
|     
 | |
|       ranges = {
 | |
|         "january 1" = "00:00-24:00"                 //new year's day
 | |
|         "july 4" = "00:00-24:00"                    //independence day
 | |
|         "december 25" = "00:00-24:00"               //christmas
 | |
|         "december 31" = "18:00-24:00"               //new year's eve (6pm+)
 | |
|         "2017-04-16" = "00:00-24:00"                //easter 2017
 | |
|         "monday -1 may" = "00:00-24:00"             //memorial day (last monday in may)
 | |
|         "monday 1 september" = "00:00-24:00"        //labor day (1st monday in september)
 | |
|         "thursday 4 november" = "00:00-24:00"       //thanksgiving (4th thursday in november)
 | |
|       }
 | |
|     }
 | |
| 
 | |
| In addition to that the time period `weekends` defines an additional
 | |
| time window which should be excluded from notifications:
 | |
| 
 | |
|     object TimePeriod "weekends-excluded" {
 | |
|       import "legacy-timeperiod"
 | |
|     
 | |
|       ranges = {
 | |
|         "saturday"  = "00:00-09:00,18:00-24:00"
 | |
|         "sunday"    = "00:00-09:00,18:00-24:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
| The time period `prod-notification` defines the default time ranges
 | |
| and adds the excluded time period names as an array.
 | |
| 
 | |
|     object TimePeriod "prod-notification" {
 | |
|       import "legacy-timeperiod"
 | |
|     
 | |
|       excludes = [ "holidays", "weekends-excluded" ]
 | |
|     
 | |
|       ranges = {
 | |
|         "monday"    = "00:00-24:00"
 | |
|         "tuesday"   = "00:00-24:00"
 | |
|         "wednesday" = "00:00-24:00"
 | |
|         "thursday"  = "00:00-24:00"
 | |
|         "friday"    = "00:00-24:00"
 | |
|         "saturday"  = "00:00-24:00"
 | |
|         "sunday"    = "00:00-24:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
| ## <a id="check-result-freshness"></a> Check Result Freshness
 | |
| 
 | |
| In Icinga 2 active check freshness is enabled by default. It is determined by the
 | |
| `check_interval` attribute and no incoming check results in that period of time.
 | |
| 
 | |
|     threshold = last check execution time + check interval
 | |
| 
 | |
| Passive check freshness is calculated from the `check_interval` attribute if set.
 | |
| 
 | |
|     threshold = last check result time + check interval
 | |
| 
 | |
| If the freshness checks are invalid, a new check is executed defined by the
 | |
| `check_command` attribute.
 | |
| 
 | |
| 
 | |
| ## <a id="check-flapping"></a> Check Flapping
 | |
| 
 | |
| Icinga 2 supports optional detection of hosts and services that are "flapping".
 | |
| 
 | |
| Flapping occurs when a service or host changes state too frequently, resulting
 | |
| in a storm of problem and recovery notifications. Flapping can be the source of
 | |
| configuration problems (i.e. thresholds set too low), troublesome services,
 | |
| or real network problems.
 | |
| 
 | |
| Flapping detection can be enabled or disabled using the `enable_flapping` attribute.
 | |
| The `flapping_threshold` attributes allows to specify the percentage of state changes
 | |
| when a [host](9-object-types.md#objecttype-host) or [service](objecttype-service) is considered to flap.
 | |
| 
 | |
| Note: There are known issues with flapping detection. Please refrain from enabling
 | |
| flapping until [#4982](https://github.com/Icinga/icinga2/issues/4982) is fixed.
 | |
| 
 | |
| ## <a id="volatile-services"></a> Volatile Services
 | |
| 
 | |
| By default all services remain in a non-volatile state. When a problem
 | |
| occurs, the `SOFT` state applies and once `max_check_attempts` attribute
 | |
| is reached with the check counter, a `HARD` state transition happens.
 | |
| Notifications are only triggered by `HARD` state changes and are then
 | |
| re-sent defined by the `interval` attribute.
 | |
| 
 | |
| It may be reasonable to have a volatile service which stays in a `HARD`
 | |
| state type if the service stays in a `NOT-OK` state. That way each
 | |
| service recheck will automatically trigger a notification unless the
 | |
| service is acknowledged or in a scheduled downtime.
 | |
| 
 | |
| ## <a id="monitoring-icinga"></a> Monitoring Icinga 2
 | |
| 
 | |
| Why should you do that? Icinga and its components run like any other
 | |
| service application on your server. There are predictable issues
 | |
| such as "disk space is running low" and your monitoring suffers from just
 | |
| that.
 | |
| 
 | |
| You would also like to ensure that features and backends are running
 | |
| and storing required data. Be it the database backend where Icinga Web 2
 | |
| presents fancy dashboards, forwarded metrics to Graphite or InfluxDB or
 | |
| the entire distributed setup.
 | |
| 
 | |
| This list isn't complete but should help with your own setup.
 | |
| Windows client specific checks are highlighted.
 | |
| 
 | |
| Type		| Description			| Plugins and CheckCommands
 | |
| ----------------|-------------------------------|-----------------------------------------------------
 | |
| System		| Filesystem			| [disk](10-icinga-template-library.md#plugin-check-command-disk), [disk-windows](10-icinga-template-library.md#windows-plugins) (Windows Client)
 | |
| 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)
 | |
| System		| Hardware			| [hpasm](10-icinga-template-library.md#plugin-contrib-command-hpasm), [ipmi-sensor](10-icinga-template-library.md#plugin-contrib-command-ipmi-sensor)
 | |
| System		| Virtualization		| [VMware](10-icinga-template-library.md#plugin-contrib-vmware), [esxi_hardware](10-icinga-template-library.md#plugin-contrib-command-esxi-hardware)
 | |
| System		| Processes			| [procs](10-icinga-template-library.md#plugin-check-command-processes), [service-windows](10-icinga-template-library.md#windows-plugins) (Windows Client)
 | |
| System		| System Activity Reports	| [check_sar_perf](https://github.com/dnsmichi/icinga-plugins/blob/master/scripts/check_sar_perf.py)
 | |
| System		| I/O				| [iostat](10-icinga-template-library.md#plugin-contrib-command-iostat)
 | |
| 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)
 | |
| System		| Users				| [users](10-icinga-template-library.md#plugin-check-command-users), [users-windows](10-icinga-template-library.md#windows-plugins) (Windows Client)
 | |
| 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.
 | |
| System		| NTP				| [ntp_time](10-icinga-template-library.md#plugin-check-command-ntp-time)
 | |
| System		| Updates			| [apt](10-icinga-template-library.md#plugin-check-command-apt), [yum](10-icinga-template-library.md#plugin-contrib-command-yum)
 | |
| Icinga		| Status & Stats		| [icinga](10-icinga-template-library.md#itl-icinga) (more below)
 | |
| Icinga		| Cluster & Clients		| [health checks](6-distributed-monitoring.md#distributed-monitoring-health-checks)
 | |
| Database	| MySQL				| [mysql_health](10-icinga-template-library.md#plugin-contrib-command-mysql_health)
 | |
| Database	| PostgreSQL			| [postgres](10-icinga-template-library.md#plugin-contrib-command-postgres)
 | |
| Database	| Housekeeping			| Check the database size and growth and analyse metrics to examine trends.
 | |
| Database	| DB IDO			| [ido](10-icinga-template-library.md#itl-icinga-ido) (more below)
 | |
| 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)
 | |
| Webserver	| Certificates			| [http](10-icinga-template-library.md#plugin-check-command-http)
 | |
| Webserver	| Authorization			| [http](10-icinga-template-library.md#plugin-check-command-http)
 | |
| Notifications	| Mail (queue)			| [smtp](10-icinga-template-library.md#plugin-check-command-smtp), [mailq](10-icinga-template-library.md#plugin-check-command-mailq)
 | |
| Notifications	| SMS (GSM modem)		| [check_sms3_status](https://exchange.icinga.com/netways/check_sms3status)
 | |
| Notifications	| Messengers, Cloud services	| XMPP, Twitter, IRC, Telegram, PagerDuty, VictorOps, etc.
 | |
| Metrics		| PNP, RRDTool			| [check_pnp_rrds](https://github.com/lingej/pnp4nagios/tree/master/scripts) checks for stale RRD files.
 | |
| Metrics		| Graphite			| [graphite](10-icinga-template-library.md#plugin-contrib-command-graphite)
 | |
| Metrics		| InfluxDB			| [check_influxdb](https://exchange.icinga.com/Mikanoshi/InfluxDB+data+monitoring+plugin)
 | |
| Metrics		| Elastic Stack			| [elasticsearch](10-icinga-template-library.md#plugin-contrib-command-elasticsearch), [Elastic Stack integration](14-features.md#elastic-stack-integration)
 | |
| Metrics		| Graylog			| [Graylog integration](14-features.md#graylog-integration)
 | |
| 
 | |
| 
 | |
| The [icinga](10-icinga-template-library.md#itl-icinga) CheckCommand provides metrics for the runtime stats of
 | |
| Icinga 2. You can forward them to your preferred graphing solution.
 | |
| If you require more metrics you can also query the [REST API](12-icinga2-api.md#icinga2-api) and write
 | |
| your own custom check plugin. Or you keep using the built-in [object accessor functions](8-advanced-topics.md#access-object-attributes-at-runtime)
 | |
| to calculate stats in-memory.
 | |
| 
 | |
| There is a built-in [ido](10-icinga-template-library.md#itl-icinga-ido) check available for DB IDO MySQL/PostgreSQL
 | |
| which provides additional metrics for the IDO database.
 | |
| 
 | |
| ```
 | |
| apply Service "ido-mysql" {
 | |
|   check_command = "ido"
 | |
| 
 | |
|   vars.ido_type = "IdoMysqlConnection"
 | |
|   vars.ido_name = "ido-mysql" //the name defined in /etc/icinga2/features-enabled/ido-mysql.conf
 | |
| 
 | |
|   assign where match("master*.localdomain", host.name)
 | |
| }
 | |
| ```
 | |
| 
 | |
| More specific database queries can be found in the [DB IDO](14-features.md#db-ido) chapter.
 | |
| 
 | |
| Distributed setups should include specific [health checks](6-distributed-monitoring.md#distributed-monitoring-health-checks).
 | |
| You might also want to add additional checks for SSL certificate expiration.
 | |
| 
 | |
| 
 | |
| ## <a id="advanced-configuration-hints"></a> Advanced Configuration Hints
 | |
| 
 | |
| ### <a id="advanced-use-of-apply-rules"></a> Advanced Use of Apply Rules
 | |
| 
 | |
| [Apply rules](3-monitoring-basics.md#using-apply) can be used to create a rule set which is
 | |
| entirely based on host objects and their attributes.
 | |
| In addition to that [apply for and custom attribute override](3-monitoring-basics.md#using-apply-for)
 | |
| extend the possibilities.
 | |
| 
 | |
| The following example defines a dictionary on the host object which contains
 | |
| configuration attributes for multiple web servers. This then used to add three checks:
 | |
| 
 | |
| * A `ping4` check using the local IP `address` of the web server.
 | |
| * A `tcp` check querying the TCP port where the HTTP service is running on.
 | |
| * If the `url` key is defined, the third apply for rule will create service objects using the `http` CheckCommand.
 | |
| In addition to that you can optionally define the `ssl` attribute which enables HTTPS checks.
 | |
| 
 | |
| Host definition:
 | |
| 
 | |
|     object Host "webserver01" {
 | |
|       import "generic-host"
 | |
|       address = "192.168.56.200"
 | |
|       vars.os = "Linux"
 | |
| 
 | |
|       vars.webserver = {
 | |
|         instance["status"] = {
 | |
|           address = "192.168.56.201"
 | |
|           port = "80"
 | |
|           url = "/status"
 | |
|         }
 | |
|         instance["tomcat"] = {
 | |
|           address = "192.168.56.202"
 | |
|           port = "8080"
 | |
|         }
 | |
|         instance["icingaweb2"] = {
 | |
|           address = "192.168.56.210"
 | |
|           port = "443"
 | |
|           url = "/icingaweb2"
 | |
|           ssl = true
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
| Service apply for definitions:
 | |
| 
 | |
|     apply Service "webserver_ping" for (instance => config in host.vars.webserver.instance) {
 | |
|       display_name = "webserver_" + instance
 | |
|       check_command = "ping4"
 | |
| 
 | |
|       vars.ping_address = config.address
 | |
| 
 | |
|       assign where host.vars.webserver.instance
 | |
|     }
 | |
| 
 | |
|     apply Service "webserver_port" for (instance => config in host.vars.webserver.instance) {
 | |
|       display_name = "webserver_" + instance + "_" + config.port
 | |
|       check_command = "tcp"
 | |
| 
 | |
|       vars.tcp_address = config.address
 | |
|       vars.tcp_port = config.port
 | |
| 
 | |
|       assign where host.vars.webserver.instance
 | |
|     }
 | |
| 
 | |
|     apply Service "webserver_url" for (instance => config in host.vars.webserver.instance) {
 | |
|       display_name = "webserver_" + instance + "_" + config.url
 | |
|       check_command = "http"
 | |
| 
 | |
|       vars.http_address = config.address
 | |
|       vars.http_port = config.port
 | |
|       vars.http_uri = config.url
 | |
| 
 | |
|       if (config.ssl) {
 | |
|         vars.http_ssl = config.ssl
 | |
|       }
 | |
| 
 | |
|       assign where config.url != ""
 | |
|     }
 | |
| 
 | |
| The variables defined in the host dictionary are not using the typical custom attribute
 | |
| prefix recommended for CheckCommand parameters. Instead they are re-used for multiple
 | |
| service checks in this example.
 | |
| In addition to defining check parameters this way, you can also enrich the `display_name`
 | |
| attribute with more details. This will be shown in in Icinga Web 2 for example.
 | |
| 
 | |
| ### <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](8-advanced-topics.md#use-functions-command-arguments-setif) inside command arguments
 | |
| * Returning a [command](8-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](8-advanced-topics.md#use-functions-command-arguments-setif) or [command](8-advanced-topics.md#use-functions-command-attribute).
 | |
| 
 | |
| Tips when implementing functions:
 | |
| 
 | |
| * Use [log()](18-library-reference.md#global-functions-log) 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](9-object-types.md#objecttype-checkcommand) is primarily used to
 | |
| evaluate whether the command parameter should be set or not.
 | |
| 
 | |
| By default you can evaluate runtime macros for their existence. 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](17-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" {
 | |
|       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](18-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](17-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](9-object-types.md#objecttype-notificationcommand)
 | |
| or [EventCommands](9-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" {
 | |
|       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="custom-functions-as-attribute"></a> Use Custom Functions as Attribute
 | |
| 
 | |
| To use custom functions as attributes, the function must be defined in a
 | |
| slightly unexpected way. The following example shows how to assign values
 | |
| depending on group membership. All hosts in the `slow-lan` host group use 300
 | |
| as value for `ping_wrta`, all other hosts use 100.
 | |
| 
 | |
|     globals.group_specific_value = function(group, group_value, non_group_value) {
 | |
|         return function() use (group, group_value, non_group_value) {
 | |
|             if (group in host.groups) {
 | |
|                 return group_value
 | |
|             } else {
 | |
|                 return non_group_value
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     apply Service "ping4" {
 | |
|         import "generic-service"
 | |
|         check_command = "ping4"
 | |
|     
 | |
|         vars.ping_wrta = group_specific_value("slow-lan", 300, 100)
 | |
|         vars.ping_crta = group_specific_value("slow-lan", 500, 200)
 | |
|     
 | |
|         assign where true
 | |
|     }
 | |
| 
 | |
| #### <a id="use-functions-assign-where"></a> Use Functions in Assign Where Expressions
 | |
| 
 | |
| If a simple expression for matching a name or checking if an item
 | |
| exists in an array or dictionary does not fit, you should consider
 | |
| writing your own global [functions](17-language-reference.md#functions).
 | |
| You can call them inside `assign where` and `ignore where` expressions
 | |
| for [apply rules](3-monitoring-basics.md#using-apply-expressions) or
 | |
| [group assignments](3-monitoring-basics.md#group-assign-intro) just like
 | |
| any other global functions for example [match](18-library-reference.md#global-functions-match).
 | |
| 
 | |
| The following example requires the host `myprinter` being added
 | |
| to the host group `printers-lexmark` but only if the host uses
 | |
| a template matching the name `lexmark*`.
 | |
| 
 | |
|     template Host "lexmark-printer-host" {
 | |
|       vars.printer_type = "Lexmark"
 | |
|     }
 | |
| 
 | |
|     object Host "myprinter" {
 | |
|       import "generic-host"
 | |
|       import "lexmark-printer-host"
 | |
| 
 | |
|       address = "192.168.1.1"
 | |
|     }
 | |
| 
 | |
|     /* register a global function for the assign where call */
 | |
|     globals.check_host_templates = function(host, search) {
 | |
|       /* iterate over all host templates and check if the search matches */
 | |
|       for (tmpl in host.templates) {
 | |
|         if (match(search, tmpl)) {
 | |
|           return true
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       /* nothing matched */
 | |
|       return false
 | |
|     }
 | |
| 
 | |
|     object HostGroup "printers-lexmark" {
 | |
|       display_name = "Lexmark Printers"
 | |
|       /* call the global function and pass the arguments */
 | |
|       assign where check_host_templates(host, "lexmark*")
 | |
|     }
 | |
| 
 | |
| 
 | |
| Take a different more complex example: All hosts with the
 | |
| custom attribute `vars_app` as nested dictionary should be
 | |
| added to the host group `ABAP-app-server`. But only if the
 | |
| `app_type` for all entries is set to `ABAP`.
 | |
| 
 | |
| It could read as wildcard match for nested dictionaries:
 | |
| 
 | |
|     where host.vars.vars_app["*"].app_type == "ABAP"
 | |
| 
 | |
| The solution for this problem is to register a global
 | |
| function which checks the `app_type` for all hosts
 | |
| with the `vars_app` dictionary.
 | |
| 
 | |
|     object Host "appserver01" {
 | |
|       check_command = "dummy"
 | |
|       vars.vars_app["ABC"] = { app_type = "ABAP" }
 | |
|     }
 | |
|     object Host "appserver02" {
 | |
|       check_command = "dummy"
 | |
|       vars.vars_app["DEF"] = { app_type = "ABAP" }
 | |
|     }
 | |
| 
 | |
|     globals.check_app_type = function(host, type) {
 | |
|       /* ensure that other hosts without the custom attribute do not match */
 | |
|       if (typeof(host.vars.vars_app) != Dictionary) {
 | |
|         return false
 | |
|       }
 | |
| 
 | |
|       /* iterate over the vars_app dictionary */
 | |
|       for (key => val in host.vars.vars_app) {
 | |
|         /* if the value is a dictionary and if contains the app_type being the requested type */
 | |
|         if (typeof(val) == Dictionary && val.app_type == type) {
 | |
|           return true
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       /* nothing matched */
 | |
|       return false
 | |
|     }
 | |
| 
 | |
|     object HostGroup "ABAP-app-server" {
 | |
|       assign where check_app_type(host, "ABAP")
 | |
|     }
 | |
| 
 | |
| ### <a id="access-object-attributes-at-runtime"></a> Access Object Attributes at Runtime
 | |
| 
 | |
| The [Object Accessor Functions](18-library-reference.md#object-accessor-functions)
 | |
| can be used to retrieve references to other objects by name.
 | |
| 
 | |
| This allows you to access configuration and runtime object attributes. A detailed
 | |
| list can be found [here](9-object-types.md#object-types).
 | |
| 
 | |
| Simple cluster example for accessing two host object states and calculating a virtual
 | |
| cluster state and output:
 | |
| 
 | |
|     object Host "cluster-host-01" {
 | |
|       check_command = "dummy"
 | |
|       vars.dummy_state = 2
 | |
|       vars.dummy_text = "This host is down."
 | |
|     }
 | |
| 
 | |
|     object Host "cluster-host-02" {
 | |
|       check_command = "dummy"
 | |
|       vars.dummy_state = 0
 | |
|       vars.dummy_text = "This host is up."
 | |
|     }
 | |
| 
 | |
|     object Host "cluster" {
 | |
|       check_command = "dummy"
 | |
|       vars.cluster_nodes = [ "cluster-host-01", "cluster-host-02" ]
 | |
| 
 | |
|       vars.dummy_state = {{
 | |
|         var up_count = 0
 | |
|         var down_count = 0
 | |
|         var cluster_nodes = macro("$cluster_nodes$")
 | |
| 
 | |
|         for (node in cluster_nodes) {
 | |
|           if (get_host(node).state > 0) {
 | |
|             down_count += 1
 | |
|           } else {
 | |
|             up_count += 1
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (up_count >= down_count) {
 | |
|           return 0 //same up as down -> UP
 | |
|         } else {
 | |
|           return 2 //something is broken
 | |
|         }
 | |
|       }}
 | |
| 
 | |
|       vars.dummy_text = {{
 | |
|         var output = "Cluster hosts:\n"
 | |
|         var cluster_nodes = macro("$cluster_nodes$")
 | |
| 
 | |
|         for (node in cluster_nodes) {
 | |
|           output += node + ": " + get_host(node).last_check_result.output + "\n"
 | |
|         }
 | |
| 
 | |
|         return output
 | |
|       }}
 | |
|     }
 | |
| 
 | |
| 
 | |
| The following example sets time dependent thresholds for the load check based on the current
 | |
| time of the day compared to the defined time period.
 | |
| 
 | |
|     object TimePeriod "backup" {
 | |
|       import "legacy-timeperiod"
 | |
| 
 | |
|       ranges = {
 | |
|         monday = "02:00-03:00"
 | |
|         tuesday = "02:00-03:00"
 | |
|         wednesday = "02:00-03:00"
 | |
|         thursday = "02:00-03:00"
 | |
|         friday = "02:00-03:00"
 | |
|         saturday = "02:00-03:00"
 | |
|         sunday = "02:00-03:00"
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     object Host "webserver-with-backup" {
 | |
|       check_command = "hostalive"
 | |
|       address = "127.0.0.1"
 | |
|     }
 | |
| 
 | |
|     object Service "webserver-backup-load" {
 | |
|       check_command = "load"
 | |
|       host_name = "webserver-with-backup"
 | |
| 
 | |
|       vars.load_wload1 = {{
 | |
|         if (get_time_period("backup").is_inside) {
 | |
|           return 20
 | |
|         } else {
 | |
|           return 5
 | |
|         }
 | |
|       }}
 | |
|       vars.load_cload1 = {{
 | |
|         if (get_time_period("backup").is_inside) {
 | |
|           return 40
 | |
|         } else {
 | |
|           return 10
 | |
|         }
 | |
|       }}
 | |
|     }
 | |
| 
 | |
| 
 |