Browse Source

Add Icinga2 state. Thanks to Claudia for the groundworks!

Signed-off-by: Maximilian Wilhelm <max@rfc2324.org>
Maximilian Wilhelm 7 years ago
parent
commit
e6d0cb349a

+ 14 - 0
icinga2/api.conf

@@ -0,0 +1,14 @@
+#
+# The API listener is used for distributed monitoring setups. (Salt managed)
+#
+
+object ApiListener "api" {
+	cert_path = "/etc/icinga2/pki/ffhohost.cert.pem"
+	key_path  = "/etc/icinga2/pki/ffhohost.key.pem"
+	ca_path   = "/etc/ssl/certs/ffho-cacert.pem"
+
+	ticket_salt = TicketSalt
+
+	accept_commands = true
+	accept_config = true
+}

+ 17 - 0
icinga2/commands.d/cpu_usage.conf

@@ -0,0 +1,17 @@
+#
+# CPU-Check von https://github.com/iamcheko/check_cpu_usage
+#
+
+object CheckCommand "cpu_usage" {
+	import "plugin-check-command"
+	command = [ FFHOPluginDir + "/check_cpu_usage" ]
+	arguments = {
+		"--timeout" = "$cpu_usage_timeout$"
+		"--critical" = "$cpu_usage_critical$"
+		"--warning" = "$cpu_usage_warning$"
+		"--statfile" = "$cpu_usage_statfile$"
+		"--gapfile" = "$cpu_usage_gapfile$"
+		"--names" = "$cpu_usage_names$"
+		"--details" = "$cpu_usage_details$"
+	}
+}

+ 63 - 0
icinga2/commands.d/nagios-plugins-contrib.conf

@@ -0,0 +1,63 @@
+#
+# Checks aus nagios-plugins-contrib (Salt managed)
+#
+
+### SSL-Cert-Check
+object CheckCommand "ssl_cert" {
+	import "plugin-check-command"
+	command = [ PluginDir + "/check_ssl_cert" ]
+	arguments = {
+		"-H" = "$ssl_cert_H$"
+		"-A" = "$ssl_cert_A$"
+		"-C" = "$ssl_cert_C$"
+		"-e" = "$ssl_cert_e$"
+		"-f" = "$ssl_cert_f$"
+		"-i" = "$ssl_cert_i$"
+		"-n" = "$ssl_cert_n$"
+		"-N" = "$ssl_cert_N$"
+		"-o" = "$ssl_cert_o$"
+		"-p" = "$ssl_cert_p$"
+		"-P" = "$ssl_cert_P$"
+		"-s" = "$ssl_cert_s$"
+		"-S" = "$ssl_cert_S$"
+		"-r" = "$ssl_cert_r$"
+		"-t" = "$ssl_cert_t$"
+		"-w" = "$ssl_cert_w$"
+		"-d" = "$ssl_cert_d$"
+	}
+}
+
+### Memory-Check
+object CheckCommand "memory" {
+	import "plugin-check-command"
+	command = [ PluginDir + "/check_memory" ]
+	arguments = {
+		"--warning" = "$memory.warning$"
+		"--critical" = "$memory.critical$"
+		"--unit" = "$memory.unit$"
+		"--timeout" = "$memory.timeout$"
+	}
+	vars.memory.warning = 10
+	vars.memory.critical = 5
+	vars.memory.unit = "M"
+}
+
+### Uptime-Check
+object CheckCommand "uptime" {
+	import "plugin-check-command"
+	command = [ PluginDir + "/check_uptime" ]
+	arguments = {
+		"-T" = "$uptime_T$"
+		"-w" = "$uptime_w$"
+		"-c" = "$uptime_c$"
+		"-f" = "$uptime_f$"
+		"-P" = "$uptime_P$"
+		"-t" = "$uptime_t$"
+	}
+}
+
+### libs-Check
+object CheckCommand "libs" {
+	import "plugin-check-command"
+	command = [ PluginDir + "/check_libs" ]
+}

+ 38 - 0
icinga2/commands.d/network.conf

@@ -0,0 +1,38 @@
+#
+# FFHO Check Commands for network related stuff (Salt Managed)
+#
+
+object CheckCommand "ifupdown2" {
+        import "plugin-check-command"
+
+	command = [ FFHOPluginDir + "/check_ifupdown2" ]
+}
+
+
+object CheckCommand "bird_ospf" {
+	import "plugin-check-command"
+
+	command = [ FFHOPluginDir + "/check_bird_ospf" ]
+
+	arguments = {
+		"-6" = {
+			set_if = "$ipv6$"
+		}
+	}
+
+	vars.ipv6 = false
+}
+
+object CheckCommand "bird_ibgp" {
+	import "plugin-check-command"
+
+	command = [ FFHOPluginDir + "/check_bird_ibgp" ]
+
+	arguments = {
+		"-6" = {
+			set_if = "$ipv6$"
+		}
+	}
+
+	vars.ipv6 = false
+}

+ 30 - 0
icinga2/constants.conf

@@ -0,0 +1,30 @@
+/**
+ * This file defines global constants which can be used in
+ * the other configuration files. (Salt managed)
+ */
+
+/* The directory which contains the plugins from the Monitoring Plugins project. */
+const PluginDir = "/usr/lib/nagios/plugins"
+
+/* The directory which contains the Manubulon plugins.
+ * Check the documentation, chapter "SNMP Manubulon Plugin Check Commands", for details.
+ */
+const ManubulonPluginDir = "/usr/lib/nagios/plugins"
+
+/* The directory which you use to store additional plugins which ITL provides user contributed command definitions for.
+ * Check the documentation, chapter "Plugins Contribution", for details.
+ */
+const PluginContribDir = "/usr/lib/nagios/plugins"
+
+/* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`.
+ * This should be the common name from the API certificate.
+ */
+//const NodeName = "localhost"
+
+/* Our local zone name. */
+const ZoneName = NodeName
+
+/* Secret key for remote node tickets */
+const TicketSalt = ""
+
+const FFHOPluginDir = "/usr/local/share/monitoring-plugins"

+ 41 - 0
icinga2/host.conf.tmpl

@@ -0,0 +1,41 @@
+#
+# {{ node_id }}
+#
+{%- set roles = node_config.get ('roles', []) %}
+{%- set sites = node_config.get ('sites', []) %}
+{%- set address = salt['ffho_net.get_loopback_ip'] (node_config, node_id, 'v4') %}
+{%- set address6 = salt['ffho_net.get_loopback_ip'] (node_config, node_id, 'v6') %}
+
+{%- if 'icinga2server' not in roles %}
+object Endpoint "{{ node_id }}" {
+        host = "{{ node_id }}"
+}
+
+object Zone "{{ node_id }}" {
+	endpoints = [ "{{ node_id }}" ]
+	parent = "master"
+}
+{%- endif %}
+
+object Host "{{ node_id }}" {
+	import "generic-host"
+
+	display_name = "{{ node_id }}"
+
+	address = "{{ address }}"
+	address6 = "{{ address6 }}"
+
+	vars.os = "Linux"
+
+	vars.roles = [
+{%- for role in roles|sort %}
+		"{{ role }}",
+{%- endfor %}
+	]
+
+	vars.sites = [
+{%- for site in sites|sort %}
+		"{{ site }}",
+{%- endfor %}
+	]
+}									

+ 57 - 0
icinga2/icinga2.conf

@@ -0,0 +1,57 @@
+#
+# Icinga2 main configuration for nodes (Salt managed)
+#
+
+/**
+ * Icinga 2 configuration file
+ * - this is where you define settings for the Icinga application including
+ * which hosts/services to check.
+ *
+ * For an overview of all available configuration options please refer
+ * to the documentation that is distributed as part of Icinga 2.
+ */
+
+/**
+ * The constants.conf defines global constants.
+ */
+include "constants.conf"
+
+/**
+ * The zones.conf defines zones for a cluster setup.
+ * Not required for single instance setups.
+ */
+include "zones.conf"
+
+/**
+ * The Icinga Template Library (ITL) provides a number of useful templates
+ * and command definitions.
+ * Common monitoring plugin command definitions are included separately.
+ */
+include <itl>
+include <plugins>
+include <plugins-contrib>
+include <manubulon>
+
+/**
+ * This includes the NSClient++ check commands. These command definitions
+ * are required on a master node when a client is used as command endpoint.
+ */
+include <nscp>
+
+/**
+ * The features-available directory contains a number of configuration
+ * files for features which can be enabled and disabled using the
+ * icinga2 feature enable / icinga2 feature disable CLI commands.
+ * These commands work by creating and removing symbolic links in
+ * the features-enabled directory.
+ */
+include "features-enabled/*.conf"
+
+/**
+ * The repository.d directory contains all configuration objects
+ * managed by the 'icinga2 repository' CLI commands.
+ */
+include_recursive "repository.d"
+
+# Include command definitions
+include_recursive "commands.d"

+ 67 - 0
icinga2/icinga2.conf.H_icinga2.in.ffho.net

@@ -0,0 +1,67 @@
+#
+# Icinga2 main configuration for nodes (Salt managed)
+#
+
+/**
+ * Icinga 2 configuration file
+ * - this is where you define settings for the Icinga application including
+ * which hosts/services to check.
+ *
+ * For an overview of all available configuration options please refer
+ * to the documentation that is distributed as part of Icinga 2.
+ */
+
+/**
+ * The constants.conf defines global constants.
+ */
+include "constants.conf"
+
+/**
+ * The zones.conf defines zones for a cluster setup.
+ * Not required for single instance setups.
+ */
+include "zones.conf"
+
+/**
+ * The Icinga Template Library (ITL) provides a number of useful templates
+ * and command definitions.
+ * Common monitoring plugin command definitions are included separately.
+ */
+include <itl>
+include <plugins>
+include <plugins-contrib>
+include <manubulon>
+
+/**
+ * This includes the NSClient++ check commands. These command definitions
+ * are required on a master node when a client is used as command endpoint.
+ */
+include <nscp>
+
+/**
+ * The features-available directory contains a number of configuration
+ * files for features which can be enabled and disabled using the
+ * icinga2 feature enable / icinga2 feature disable CLI commands.
+ * These commands work by creating and removing symbolic links in
+ * the features-enabled directory.
+ */
+include "features-enabled/*.conf"
+
+/**
+ * The repository.d directory contains all configuration objects
+ * managed by the 'icinga2 repository' CLI commands.
+ */
+include_recursive "repository.d"
+
+/**
+ * Although in theory you could define all your objects in this file
+ * the preferred way is to create separate directories and files in the conf.d
+ * directory. Each of these files must have the file extension ".conf".
+ */
+include_recursive "conf.d"
+
+# Include command defintions
+include_recursive "commands.d"
+
+# Include FFHO server configuration
+include_recursive "ffho-conf.d"

+ 211 - 0
icinga2/init.sls

@@ -0,0 +1,211 @@
+#
+# Icinga2
+#
+{% set roles = salt['pillar.get']('nodes:' ~ grains.id ~ ':roles', []) %}
+
+include:
+  - apt
+
+
+# Install icinga2 package
+icinga2:
+  pkg.installed:
+    - name: icinga2
+  service.running:
+    - enable: True
+    - reload: True
+
+
+# Install plugins (official + our own)
+monitoring-plugin-pkgs:
+  pkg.installed:
+    - pkgs:
+      - monitoring-plugins
+      - nagios-plugins-contrib
+      - libyaml-syck-perl
+      - libnagios-plugin-perl
+      - lsof
+    - watch_in:
+      - service: icinga2
+
+ffho-plugins:
+  file.recurse:
+    - name: /usr/local/share/monitoring-plugins/
+    - source: salt://icinga2/plugins/
+    - file_mode: 755
+    - dir_mode: 755
+    - user: root
+    - group: root
+
+
+# Icinga2 master config (for master and all nodes)
+/etc/icinga2/icinga2.conf:
+  file.managed:
+    - source:
+      - salt://icinga2/icinga2.conf.H_{{ grains.id }}
+      - salt://icinga2/icinga2.conf
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+
+# Add FFHOPluginDir
+/etc/icinga2/constants.conf:
+  file.managed:
+    - source: salt://icinga2/constants.conf
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+
+# Connect "master" and client zones
+/etc/icinga2/zones.conf:
+  file.managed:
+    - source:
+      - salt://icinga2/zones.conf.H_{{ grains.id }}
+      - salt://icinga2/zones.conf
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+
+# Install host cert + key readable for icinga
+{% set pillar_name = 'nodes:' ~ grains['id'] ~ ':certs:' ~ grains['id'] %}
+/etc/icinga2/pki/ffhohost.cert.pem:
+  file.managed:
+    {% if salt['pillar.get'](pillar_name ~ ':cert') == "file" %}
+    - source: salt://certs/certs/{{ cn }}.cert.pem
+    {% else %}
+    - contents_pillar: {{ pillar_name }}:cert
+    {% endif %}
+    - user: root
+    - group: root
+    - mode: 644
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+/etc/icinga2/pki/ffhohost.key.pem:
+  file.managed:
+    - contents_pillar: {{ pillar_name }}:privkey
+    - user: root
+    - group: nagios
+    - mode: 440
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+
+# Activate Icinga2 features: API
+{% for feature in ['api'] %}
+/etc/icinga2/features-enabled/{{ feature }}.conf:
+  file.symlink:
+    - target: "../features-available/{{ feature }}.conf"
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+{% endfor %}
+
+
+# Install command definitions
+/etc/icinga2/commands.d:
+  file.recurse:
+    - source: salt://icinga2/commands.d
+    - file_mode: 644
+    - dir_mode: 755
+    - user: root
+    - group: root
+    - clean: true
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+   
+
+################################################################################
+#                               Icinga2 Server                                 #
+################################################################################
+{% if 'icinga2server' in roles %}
+
+# Create directory for ffho specific configs
+/etc/icinga2/ffho-conf.d:
+  file.directory:
+    - makedirs: true
+    - require:
+      - pkg: icinga2
+
+
+# Install command definitions
+/etc/icinga2/ffho-conf.d/services:
+  file.recurse:
+    - source: salt://icinga2/services
+    - file_mode: 644
+    - dir_mode: 755
+    - user: root
+    - group: root
+    - clean: true
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+
+# Create client node/zone objects
+/etc/icinga2/ffho-conf.d/hosts/:
+  file.directory:
+    - makedirs: true
+    - require:
+      - pkg: icinga2
+
+  # Generate config file for every client known to pillar
+  {% for node_id, node_config in salt['pillar.get']('nodes', {}).items () %}
+    {% if node_config.get ('icinga2', "") != 'ignore' %}
+/etc/icinga2/ffho-conf.d/hosts/{{ node_id }}.conf:
+  file.managed:
+    - source: salt://icinga2/host.conf.tmpl
+    - template: jinja
+    - context:
+      node_id: {{ node_id }}
+      node_config: {{ node_config }}
+    - require:
+      - file: /etc/icinga2/ffho-conf.d/hosts/
+    - watch_in:
+      - service: icinga2
+    {% endif %}
+  {% endfor %}
+
+
+
+
+################################################################################
+#                               Icinga2 Client                                 #
+################################################################################
+{% else %}
+
+# Nodes should accept config and commands from Icinga2 server
+/etc/icinga2/features-available/api.conf:
+  file.managed:
+    - source: salt://icinga2/api.conf
+    - require:
+      - pkg: icinga2
+    - watch_in:
+      - service: icinga2
+
+
+/etc/icinga2/ffho-conf.d/:
+  file.absent:
+    - watch_in:
+      - service: icinga2
+
+/etc/icinga2/check-commands.conf:
+  file.absent:
+    - watch_in:
+      - service: icinga2
+{% endif %}
+

+ 131 - 0
icinga2/plugins/check_bird_ospf

@@ -0,0 +1,131 @@
+#!/usr/bin/perl -W
+#
+# Maximilian Wilhelm <max@rfc2324.org>
+#  --  Tue 04 Apr 2017 07:00:50 PM CEST
+#
+
+use strict;
+
+# Should we check the OSPF process for IPv4 or IPv6?
+my $cmds = {
+	"-4" => "birdc",
+	"-6" => "birdc6",
+};
+
+# Default to Legacy IP
+my $cmd = $cmds->{"-4"};
+
+if ($ARGV[0]) {
+	unless (defined $cmds->{$ARGV[0]}) {
+		print STDERR "Usage: $0 [ -4 | -6 ]\n";
+		exit (1);
+	}
+
+	$cmd = $cmds->{$ARGV[0]};
+}
+
+my $code = 0;
+my $msg = "";
+
+if (not open (INTERFACES, "$cmd show ospf interface |")) {
+	print "Failed to read OSPFv4 interfaces: $!\n";
+	exit (2);
+}
+
+if (not open (NEIGHBORS, "$cmd show ospf neighbors |")) {
+	print "Failed to read OSPFv4 neighbors: $!\n";
+	exit (2);
+}
+
+# Store any configured OSPF interfaces
+my $interfaces = {};
+my $interface = undef;
+while (my $line = <INTERFACES>) {
+	chomp ($line);
+
+	# Create entry in interface hash
+	if ($line =~ /^Interface (.+) \(/) {
+		$interface = $1;
+		$interfaces->{$interface} = {};
+	}
+	
+	# Store Type and State attributes
+	elsif ($line =~ m/(Type|State): (.+)$/) {
+		$interfaces->{$interface}->{$1} = $2;
+	}
+}
+
+close (INTERFACES);
+
+
+# Delete any stub interfaces from our list
+for my $iface (keys %{$interfaces}) {
+	if ($interfaces->{$iface}->{State} =~ m/\(stub\)/) {
+		delete $interfaces->{$iface};
+	}
+}
+
+
+my @ok = ();
+my @broken = ();
+my @down = ();
+
+# Check all neighor states
+while (my $line = <NEIGHBORS>) {
+	chomp ($line);
+
+	if ($line =~ m@^([[:xdigit:].:]+)\s+(\d+)\s+([[:alnum:]/-]+)\s+([0-9:]+)\s+([[:alnum:]_.-]+)\s+([[:xdigit:].:]+)@) {
+		my ($peer, $state, $ifname) = ($1, $3, $5);
+		my $interface = $interfaces->{$ifname};
+
+		# Mark interfaces as "up" in bird
+		$interface->{up} = 1;
+
+		# State FULL is awesome.
+		if ($state =~ m@Full@) {
+			push @ok, "$ifname/$peer";
+		}
+
+		# In broadcast areas there are only two FULL sessions (to the DR and BDR),
+		# all other sessions will be 2-Way/Other which is perfectly fine.
+		elsif ($state eq "2-Way/Other" and $interface->{Type} eq "broadcast") {
+			push @ok, "$ifname/$peer";
+		}
+
+		# Everything else is considered broken.
+		# Likely some ExStart/* etc. pointing to possible MTU troubles.
+		else {
+			push @broken, "$ifname/$peer:$state";
+		}
+	}
+}
+
+close (NEIGHBORS);
+
+
+# Check for any interfaces which should have (at least) an OSPF peer
+# but don't appear in the neighbors list
+for my $iface (keys %{$interfaces}) {
+	if (not defined $interfaces->{$iface}->{up}) {
+		push @down, $iface;
+	}
+}
+
+
+# Any down interfaces?
+if (@down) {
+	$code = 2;
+	$msg .= "DOWN: " . join (', ', @down) . " ";
+}
+
+# Any broken sessions?
+if (@broken) {
+	# Issue a warning when there are issues..
+	if ($code < 2) {
+		$code = 1
+	}
+	$msg .= "BROKEN: " . join (', ', @broken) . " ";
+}
+
+print $msg . "OK: " . join (', ', @ok) . "\n";
+exit ($code);

+ 334 - 0
icinga2/plugins/check_cpu_usage

@@ -0,0 +1,334 @@
+#!/usr/bin/perl
+#-------------------------------------------------------------------------------
+#
+#   Programm        : check_cpu_usage
+#
+#-------------------------------------------------------------------------------
+#
+#   Beschreibung    : Nagios check script to check the CPU Usage by monitoring
+#                     /proc/stat.
+#
+#   Author          : Marek Zavesicky
+#   Version         : $Revision: $
+#   Erstellt        : 2013/08/26
+#   Letztes Update  : $Date: $
+#
+#   $Id: $
+#   Change history  :
+#                     $Log: $
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 2 of the License, or (at your
+#   option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+#   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+#   for more details.
+#
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+#                     Pragma
+#-------------------------------------------------------------------------------
+use strict;
+use warnings;
+
+#-------------------------------------------------------------------------------
+#                     Used Modules
+#-------------------------------------------------------------------------------
+# use lib "/usr/local/nrpe/perl/lib";
+use Nagios::Plugin;
+#use Monitoring::Plugin;
+use Data::Dumper;
+
+#-------------------------------------------------------------------------------
+#   Function        : readStatFile
+#-------------------------------------------------------------------------------
+#   Description     : Parses the stat file and extract the counters, then store
+#                     them in a hash
+#
+#   Input           : $np           Nagios::Plugins reference
+#                     $file         A file with status informations
+#                     $names        Array that has the order of the fields
+#   Output          : $stat         A hash object with the newdata stats
+#-------------------------------------------------------------------------------
+sub readStatFile
+{
+    my $np = shift;
+    my $file = shift;
+    my $names = shift;
+    my $stat = ();
+
+    open( FILE, "<", $file ) or $np->nagios_die( "cant open file $file" );
+
+    while ( <FILE> )
+    {
+        next unless ( $_ =~ /^cpu/ );
+
+        my ( $name, @cpudata ) = split( '\s+', $_ );
+        $stat->{ $name } = \@cpudata;
+    }
+    close( FILE );
+
+    return $stat;
+}
+
+#-------------------------------------------------------------------------------
+#   Function        : writeStatFile
+#-------------------------------------------------------------------------------
+#   Description     : Write the olddata object to the gapfile
+#
+#   Input           : $np           Nagios::Plugins reference
+#                     $objects      the data objects
+#                     $names        Array that has the order of the fields
+#   Output          : -
+#-------------------------------------------------------------------------------
+sub writeStatFile
+{
+    my $np = shift;
+    my $objects = shift;
+    my $names = shift;
+
+    open( FILE, ">", $np->opts->gapfile ) or $np->nagios_die( "cant open file $np->opts->gapfile" );
+    foreach ( keys %{ $objects->{ newdata } } )
+    {
+        my $string = $_ . " ";
+        $string .= join( " ", @{ $objects->{ newdata }->{ $_ } } );
+        print FILE $string . "\n";
+    }
+    close( FILE );
+}
+
+#-------------------------------------------------------------------------------
+#   Function        : calculateData
+#-------------------------------------------------------------------------------
+#   Description     : Calculate the diference between old and new data and store
+#                     the result in an array. Summarize all differences and
+#                     calculate the percentage of each value.
+#
+#   Input           : $np           Nagios::Plugins reference
+#                     $objects      the data objects
+#                     $names        Array that has the order of the fields
+#   Output          : -
+#-------------------------------------------------------------------------------
+sub calculateData
+{
+    my $name = shift;
+    my $objects = shift;
+    my $names = shift;
+    my @results;
+    my $sum = 0;
+    my $cpu = ();
+
+    for ( my $i = 0; $i < scalar @$names; $i++ )
+    {
+        if ( defined( $objects->{ olddata }->{ $name }->[ $i ] ) )
+        {
+            push( @results, $objects->{ newdata }->{ $name }->[ $i ] - $objects->{ olddata }->{ $name }->[ $i ] );
+            $sum += $results[ $i ];
+        }
+        else
+        {
+            push( @results, 0 );
+        }
+    }
+
+    for ( my $i = 0; $i < scalar @$names; $i++ )
+    {
+        $cpu->[ $i ] = $results[ $i ] * 100 / $sum;
+    }
+    return $cpu;
+}
+
+#-------------------------------------------------------------------------------
+#   Function        : processData
+#-------------------------------------------------------------------------------
+#   Description     : Process the hashes of cpu's.
+#
+#   Input           : $np           Nagios::Plugins reference
+#                     $objects      the data objects
+#                     $names        Array that has the order of the fields
+#   Output          : -
+#-------------------------------------------------------------------------------
+sub processData
+{
+    my $np = shift;
+    my $objects = shift;
+    my $names = shift;
+    my $percent = ();
+
+    $percent->{ cpu } = calculateData( "cpu", $objects, $names );
+
+    if ( $np->opts->details )
+    {
+        foreach ( sort keys %{ $objects->{ olddata } } )
+        {
+            next if ( $_ eq 'cpu' );
+
+            $percent->{ $_ } = calculateData( $_, $objects, $names );
+        }
+    }
+    return $percent;
+}
+
+#-------------------------------------------------------------------------------
+#   Function        : compareData
+#-------------------------------------------------------------------------------
+#   Description     : Compare data with threshold if needed and generate the
+#                     performance data string.
+#
+#   Input           : $np           Nagios::Plugins reference
+#                     $objects      the data objects
+#                     $names        Array that has the order of the fields
+#   Output          : -
+#-------------------------------------------------------------------------------
+sub compareData
+{
+    my $np          = shift;
+    my $objects     = shift;
+    my $names       = shift;
+    my $warnings    = [ split( ',', $np->opts->warning ) ];
+    my $criticals   = [ split( ',', $np->opts->critical ) ];
+    my $string      = "";
+    my $result      = 0;
+    my $res;
+
+    foreach ( sort keys %{ $objects->{ olddata } } )
+    {
+        # if details are not needed, grab the next line
+        unless ( $np->opts->details )
+        {
+            next unless ( $_ eq 'cpu' );
+        }
+
+        # compute all columns and check the thresholds
+        $string .= uc( $_ );
+        for ( my $i = 0; $i < scalar @$names; $i++ )
+        {
+            if ( defined( $$warnings[ $i ] ) and defined( $$criticals[ $i ] ) and ( $$warnings[ $i ] ne "none" ) and ( $$criticals[ $i ] ne "none" ) )
+            {
+                $res = $np->check_threshold( check      => $objects->{ percent }->{ $_ }->[ $i ],
+                                             warning    => $$warnings[ $i ],
+                                             critical   => $$criticals[ $i ] );
+                # Evaluate the new return code, do not overwrite higher severity by lower
+                $result = ( $res > $result ? $res : $result );
+                $np->add_perfdata(
+                    label       => $_ . "_" . $$names[ $i ],
+                    value       => $objects->{ percent }->{ $_ }->[ $i ],
+                    warning     => $$warnings[ $i ],
+                    critical    => $$criticals[ $i ],
+                    uom         => "%"
+                );
+            }
+            else
+            {
+                $np->add_perfdata(
+                    label       => $_ . "_" . $$names[ $i ],
+                    value       => $objects->{ percent }->{ $_ }->[ $i ],
+                    uom         => "%"
+                );
+            }
+            # create the message string
+            if ( $$names[ $i ] =~ /^user/ or $$names[ $i ] =~ /^system/  or $$names[ $i ] =~ /^idle/ or $$names[ $i ] =~ /^iowait/ or $$names[ $i ] =~ /^steal/ )
+            {
+                $string .= sprintf( " (%s=%3.2f)", $$names[ $i ], $objects->{ percent }->{ $_ }->[ $i ] );
+            }
+        }
+        $string .= ", ";
+    }
+
+    # strip the last two characters from string
+    return ( $result, substr( $string, 0, -2 ) );
+}
+
+#-------------------------------------------------------------------------------
+#   Main
+#-------------------------------------------------------------------------------
+my $objects = ();
+my $names = [];
+my $np = Nagios::Plugin->new(
+    shortname =>    "Linux CPU Usage",
+    usage =>        "Usage: %s < arguments > " .
+                    "arguments: \n" .
+                    "   [ -t|--timeout=<timeout> ]      timeout\n" .
+                    "   [ -c|--critical=<threshold> ]   critical threshold\n" .
+                    "   [ -w|--warning=<threshold> ]    warning threshold\n" .
+                    "   [ -s|--statfile=<file> ]        name of the stat file (default /proc/stat)\n" .
+                    "   [ -g|--gapfile=<file> ]         name of the gap file (default /tmp/check_cpu_usage.gap.tmp)\n" .
+                    "   [ -n|--names=<list> ]           comma separated list of names representing the column in the stats file\n" .
+                    "   [ -d|--details ]                show detailed information for each core\n",
+    plugin  =>      'check_cpu_usage'
+);
+
+$np->add_arg(
+    spec =>         'warning|w=s',
+    help =>         "--warning -w\n   a list of threshold for warning in the same order as names\n" .
+                    "   (default none,none,none,none,none,none,none,none,none,none,none,none,none,none)",
+    default =>      "none,none,none,none,none,none,none,none,none,none,none,none,none,none",
+    required =>     0
+);
+
+$np->add_arg(
+    spec =>         'critical|c=s',
+    help =>         "--critical -c\n   a list of threshold for critical in the same order as names\n" .
+                    "   (default none,none,none,none,none,none,none,none,none,none,none,none,none,none)",
+    default =>      "none,none,none,none,none,none,none,none,none,none,none,none,none,none",
+    required =>     0
+);
+
+$np->add_arg(
+    spec =>         'statfile|s=s',
+    help =>         "--statfile -s\n   name of the stat file (default /proc/stat)",
+    default =>      "/proc/stat",
+    required =>     0
+);
+
+$np->add_arg(
+    spec =>         'gapfile|g=s',
+    help =>         "--gapfile -g\n   name of the gap file (default /tmp/check_cpu_usage.gap.tmp)",
+    default =>      "/tmp/check_cpu_usage.gap.tmp",
+    required =>     0
+);
+
+$np->add_arg(
+    spec =>         'details|d',
+    help =>         "--details -d\n   show detailed information for each core",
+    required =>     0
+);
+
+
+$np->add_arg(
+    spec =>         'names|n=s',
+    help =>         "--names -n\n   a comma separated list of names representing the column in the stats file. See 'man proc' for details\n" .
+                    "   (default user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice,nyd1,nyd2,nyd3)",
+    default =>      "user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice,nyd1,nyd2,nyd3",
+    required =>     0
+);
+
+# read the cli options
+$np->getopts;
+
+# rearange scalar lists into arrays
+$names = [ split( ',', $np->opts->names ) ];
+
+# read data from stats and store it to a hash
+$objects->{ newdata } = readStatFile( $np, $np->opts->statfile, $names );
+
+if ( -e $np->opts->gapfile )
+{
+    # read data from stored file and store it to a hash
+    $objects->{ olddata } = readStatFile( $np, $np->opts->gapfile, $names );
+
+    # calculate difference and percent for the data
+    $objects->{ percent } = processData( $np, $objects, $names );
+}
+
+# write the new data to the gapfile
+writeStatFile( $np, $objects, $names );
+
+print Dumper( $names, $objects ) if ( $np->opts->verbose );
+
+# compare data with warning and critical threshold and exit
+$np->nagios_exit( compareData( $np, $objects, $names ) );

+ 99 - 0
icinga2/services/network.conf

@@ -0,0 +1,99 @@
+#
+# Network related checks (FFHO-built)
+#
+
+
+#
+# ifupdown2
+apply Service "ifupdown2" {
+        import "generic-service"
+
+	check_command = "ifupdown2"
+
+	assign where host.address && host.vars.os == "Linux"
+}
+
+
+#
+# bird process
+apply Service "bird" {
+	import "generic-service"
+
+	check_command = "procs"
+
+	if (host.name != NodeName) {
+		command_endpoint = host.name
+	}
+
+	vars.procs_command = "bird"
+	vars.procs_critical = "1:"
+	vars.procs_warning = ":1"
+	check_interval = 5m
+	max_check_attempts = 3
+	retry_interval = 3m
+
+	assign where host.address && host.vars.os == "Linux" && "router" in host.vars.roles
+}
+
+apply Service "bird6" {
+	import "generic-service"
+
+	check_command = "procs"
+
+	if (host.name != NodeName) {
+		command_endpoint = host.name
+	}
+
+	vars.procs_command = "bird6"
+	vars.procs_critical = "1:"
+	vars.procs_warning = ":1"
+	check_interval = 5m
+	max_check_attempts = 3
+	retry_interval = 3m
+
+	assign where host.address && host.vars.os == "Linux" && "router" in host.vars.roles
+}
+
+
+#
+# bird OSPF
+apply Service "bird_ospf" {
+	import "generic-service"
+
+	check_command = "bird_ospf"
+
+	if (host.name != NodeName) {
+		command_endpoint = host.name
+	}
+
+	assign where host.address && host.vars.os == "Linux" && "router" in host.vars.roles
+}
+
+apply Service "bird_ospf6" {
+	import "generic-service"
+
+	check_command = "bird_ospf"
+	vars.ipv6 = true
+
+	if (host.name != NodeName) {
+		command_endpoint = host.name
+	}
+
+	assign where host.address && host.vars.os == "Linux" && "router" in host.vars.roles
+}
+
+
+#
+# bird iBGP
+apply Service "bird_ibgp" {
+	import "generic-service"
+
+	check_command = "bird_ibgp"
+
+	if (host.name != NodeName) {
+		command_endpoint = host.name
+	}
+
+	assign where host.address && host.vars.os == "Linux" && "router" in host.vars.roles
+}
+

+ 25 - 0
icinga2/zones.conf

@@ -0,0 +1,25 @@
+#
+# zones.conf (Salt managed)
+#
+
+object Endpoint NodeName {
+	host = NodeName
+}
+
+object Zone ZoneName {
+	endpoints = [ NodeName ]
+	parent = "master"
+}
+
+object Endpoint "icinga2.in.ffho.net" {
+	host = "icinga2.in.ffho.net"
+	port = "5665"
+}
+
+object Zone "master" {
+	endpoints = [ "icinga2.in.ffho.net" ]
+}
+
+object Zone "global-templates" {
+	global = true
+}

+ 11 - 0
icinga2/zones.conf.H_icinga2.in.ffho.net

@@ -0,0 +1,11 @@
+#
+# zones.conf (Salt managed)
+#
+
+object Endpoint NodeName {
+	host = NodeName
+}
+
+object Zone "master" {
+	endpoints = [ NodeName ]
+}

+ 1 - 0
top.sls

@@ -6,6 +6,7 @@ base:
     - bash
     - certs
     - console-tools
+    - icinga2
     - kernel
     - locales
     - mosh