Icinga Director featured in iX magazine 04/17

Image source: https://shop.heise.de/katalog/ix-04-2017-pdf

Marianne Spiller, our fellow community member @sys_adm_ama, told us last week during Icinga Camp Berlin that she was about to publish an article about the Icinga Director. Today the iX magazine 04/17 was published and page 62 features Marianne’s article.

I’ve got myself the ebook already. Templates, change history, import sources, more modules just like PuppetDB or AWS – everything you know and love about the Director gets covered. Pretty awesome overview of the Icinga Director in “only” 3 pages. Keep in mind that the article is in German, so you might need some extra translation ;-)

Congratulations Marianne & thanks for writing such a nice article. We’re hoping for more! :-)

 

 

Monthly Snap January: Releases and Updates

Releases

The new revamped Puppet module for Icinga 2 was officially released and uploaded to PuppetForge. We’ve also released the new shiny Icinga Director 1.3.0 followed by the beautiful Business Process module 2.0.0 for Icinga Web 2.

Icinga 2 v2.6.1 and Icinga Web 2 v2.4.1 fix bugs we’ve come across after releasing the initial major versions. There also was a release for Icinga Core and Web 1.14 fixing several bugs and security issues.

(more…)

Analyse Icinga 2 problems using the console & API

Lately we’ve been investigating on a problem with the check scheduler. This resulted in check results being late and wasn’t easy to tackle – whether it’ll be the check scheduler, cluster messages or anything else. We’ve been analysing customer partner environments quite in deep and learned a lot ourselves which we like to share with you here.

One thing you can normally do is to grep the debug log and analyse the problem in deep. It is also possible to query the Icinga 2 API fetching interesting object attributes such as “last_check”, “next_check” or even “last_check_result”.

But what if you want to calculate things for better analysis e.g. fetch the number of all services in a HA cluster node where the check results are late? (more…)

Watch out – and shop!

Most of us use Icinga 2 to monitor network services, host resources or server components – but wait, there is more! Why not ask Icinga 2 to watch items in your favourite online shop and send out notifications as soon as the price has dropped below a certain amount? Antony Stone has written a slightly unusual check to track the price of Amazon items with Icinga 2. (more…)

Icinga 2: Host state calculation from all services

There is a variety of questions answered in the community support channels. Sometimes we just hack away fancy solutions directly inside the Icinga 2 DSL. Some of these examples are collected inside the documentation, others are posted on the community channels. Or they are just provided in hands-on workshops at customers waiting for sharing their stories to the world :)

This time there was the this question over at monitoring-portal.org – a host object collects a bunch of passive services and should calculate its overall state and output from the (worst) state of all referenced services.

Sounds easy. You could go for business process check returning the calculated value. Or you stick with many of the Icinga 2 configuration language features and put them altogether.

For a small test environment, I’ve generated 5 services using the random check (replace that with your real world scenario).

 

for (j in range(5)) {
  object Service "host-servicestatus-" + j {
    check_command = "random"
    check_interval = 30s
    retry_interval = 15s
    host_name = "host-servicestatus"
  }
}

The host object called “host-servicestatus” just uses the “dummy” check command provided by the ITL. This check command expects two custom attributes: “dummy_state” and “dummy_text”.

Now for the fun part – implement two lambda functions for these custom attributes using the available methods.

  vars.dummy_state = {{ ... }}
  vars.dummy_text = {{ ... }}

We want to calculate the worst state for all services on this specific host. Therefore we’ll use a temporary variable to save and update the worst state.

    var worst_state = 0

At first glance we want to selectively iterate over all service objects using the object accessor method “get_objects” with “Service” type. Then we’ll compare the service “host_name” attribute to the local scope (our host and its name). We’ll just skip all services not matching.

    for (s in get_objects(Service)) {
      if (s.host_name != host.name) {
        continue; //skip all services not referencing this host object
      }

The local to the loop variable “s” provides us with access to the all attributes for the current service object. Check whether its state is greater than 0 (not OK) and greater than the previously collected worst state. If so, store it in the local variable “worst_state”.

      if (s.state > 0 && s.state > worst_state) {
        worst_state = s.state
      }
    }

After the loop is finished, just return the “worst_state” variable for this function.

    return worst_state

In terms of generating an additional output text with all service names and their state, we’re using the same loop and conditional checking as above. Except we are using a temporary variable as an array of strings like this:

    var output = []

Inside the loop we’ll add the current service name and its state as string element to the “output” array.

      output.add(s.name + ": " + s.state)

Once the loop is finished, join the array elements with the separator “, ” concatenate the final output string and return it.

    return "Service summary: " + output.join(", ")

We could also concatenate the string as is but then we would need to think about the last loop run not adding the “,” character. The array join method just simplifies that step.

icinga2_host_servicestatus_web2The final solution works like a charm :-) If you say – hey I am not a coder – it helps to know Javascript, or Python or something similar of course. After all it is a pretty neat solution for helping a community member :)

 

object Host "host-servicestatus" {
  check_command = "dummy"

  vars.dummy_state = {{
    var worst_state = 0
    for (s in get_objects(Service)) {
      if (s.host_name != host.name) {
        continue; //skip all services not referencing this host object
      }

      if (s.state > 0 && s.state > worst_state) {
        worst_state = s.state
      }
    }

    return worst_state
  }}

  vars.dummy_text = {{
    var output = []

    for (s in get_objects(Service)) {
      if (s.host_name != host.name) {
        continue; //skip all services not referencing this host object
      }

      output.add(s.name + ": " + s.state)
    }

    return "Service summary: " + output.join(", ")
  }}
}

Awesome Dashing dashboards with Icinga 2

vagrant_dashingWe at NETWAYS are using Dashing on our office dashboards already. This blog post solely targets integrating yet another new API providing data – the Icinga 2 REST API introduced in v2.4.

The following instructions were taken from the existing Vagrant boxes and their puppet manifests to allow faster installation. Doing it manually shouldn’t be an issue though ;-)

Requirements

Ensure that the following packages are installed, example for RHEL 7 with EPEL enabled:

package { [ 'rubygems', 'rubygem-bundler', 'ruby-devel', 'openssl', 'gcc-c++', 'make', 'nodejs' ]:
  ensure => 'installed',
  require => Class['epel']
}

Furthermore put a specific /etc/gemrc file which disables installing the documentation for gems – this can take fairly long and is not required by default. Especially not when provisioning a Vagrant box or a Docker container.

dashing-icinga2

I’ve created that project as demo for Icinga Camp Portland with the help of the existing Icinga 1.x dashing scripts from Markus, and a new job for fetching the Icinga 2 status data from its REST API.

Clone the git repository somewhere applicable. You don’t need any webserver for it, Dashing uses Thin to run a simple webserver on its own.

vcsrepo { '/usr/share/dashing-icinga2':
  ensure   => 'present',
  path     => '/usr/share/dashing-icinga2',
  provider => 'git',
  revision => 'master',
  source   => 'https://github.com/Icinga/dashing-icinga2.git',
  force    => true,
  require  => Package['git']
}

Install the dashing gem

The installation might take pretty long when it tries to install the gem’s documentation files. Therefore the flags “–no-rdoc” and “–no-ri” ensure that this isn’t done and only the dashing gem and its dependencies are installed into the system.

exec { 'dashing-install':
  path => '/bin:/usr/bin:/sbin:/usr/sbin',
  command => "gem install --no-rdoc --no-ri dashing",
  timeout => 1800
}

Install the gems for dashing-icinga2

Next to the dashing application itself the project requires additional gems, such as a rest client for communicating with the Icinga 2 REST API (check the Gemfile for details). Additionally the bundled gems are not installed into the system’s library but locally into the dashing-icinga2 git clone underneath the “binpaths” directory (this is to prevent conflicts with rubygem packages in the first place).

exec { 'dashing-bundle-install':
  path => '/bin:/usr/bin:/sbin:/usr/sbin',
  command => "cd /usr/share/dashing-icinga2 && bundle install --path binpaths", # use binpaths to prevent 'ruby bundler: command not found: thin'
  timeout => 1800
}

Dashing startup script

Put a small startup script somewhere executable to (re)start the Dashing application.

file { 'restart-dashing':
  name => '/usr/local/bin/restart-dashing',
  owner => root,
  group => root,
  mode => '0755',
  source => "puppet:////vagrant/files/usr/local/bin/restart-dashing",
}

Dashing runs as Thin process which puts its pid into the local tree. It is merely all about killing the process, removing the pid and then starting dashing again. “-d” puts the process into daemonize mode (not foreground) as well as “-p 8005” tells the application where to listen for browsers connecting to. Adjust that for your needs :)

#!/bin/bash

cd /usr/share/dashing-icinga2
kill -9 $(cat tmp/pids/thin.pid)
rm -f tmp/pids/thin.pid
/usr/local/bin/dashing start -d -p 8005

Now run Dashing.

exec { 'dashing-start':
  path => '/bin:/usr/bin:/sbin:/usr/sbin',
  command => "/usr/local/bin/restart-dashing",
  require => Service['icinga2'],
}

Configure the Icinga 2 API

The dashing job script just requires read-only access to the /v1/status endpoint. Being lazy I’ve just enabled everything but you should consider limited access :)

object ApiUser "dashing" {
  password = "icinga2ondashingr0xx"
  client_cn = NodeName
  permissions = [ "*" ]
}

Configure the Dashing job

There’s a bug in Dashing where job scripts ignore the settings from the config.ru file so there is no other way than to put the Icinga 2 REST API credentials and PKI paths directly into the jobs/icinga2.rb file.

$node_name = Socket.gethostbyname(Socket.gethostname).first
if defined? settings.icinga2_api_nodename
  node_name = settings.icinga2_api_nodename
end
#$api_url_base = "https://192.168.99.100:4665"
$api_url_base = "https://localhost:5665"
if defined? settings.icinga2_api_url
  api_url_base = settings.icinga2_api_url
end
$api_username = "dashing"
if defined? settings.icinga2_api_username
  api_username = settings.icinga2_api_username
end
$api_password = "icinga2ondashingr0xx"
if defined? settings.icinga2_api_password
  api_password = settings.icinga2_api_password
end

Modifications?

You really should know your HTML and Ruby foo before starting to modify the dashboards. The main widget used inside the dashboards/icinga2.erb file is “Simplemon” defined as data-view attribute. It is already provided inside the dashing-icinga2 repository. data-row and data-col define the location on the dashboard matrix.

    <li data-row="2" data-col="2" data-sizex="1" data-sizey="1">
      <div data-id="icinga-host-down" data-view="Simplemon" data-title="Hosts Down"></div>
    </li>

The important part is the data-id attribute – that’s the value coming from the icinga2 job defined in jobs/icinga2.erb.

The job update interval is set to 1 second in jobs/icinga2.erb:

SCHEDULER.every '1s' do

Connecting to the Icinga 2 REST API, fetching the status data as JSON and then iterating over these dictionaries is pretty straight forward. Additional programming examples can be found inside the Icinga 2 documentation.

Take the “hosts down” example from above:

hosts_down = status["num_hosts_down"].to_int

Now send the event to dashing by calling the send_event function providing the previosuly extracted value and the demanded color.

  send_event('icinga-host-down', {
   value: hosts_down.to_s,
   color: 'red' })

In case you’re wondering which values are fetched, let dashing run in foreground and print the “status” dictionary to get an idea about possible keys and values. Or query the Icinga 2 REST API with your own client first.

More?

You can play around with an already pre-installed environment inside the icinga2x Vagrant box and if you’re interested in an automated setup, check the puppet provisioner manifest.