You’ve probably heard about NagVis already, this time we are putting our focus on a fresh Icinga Web 2 module called “map. Nicolai started his work after joining us in Berlin for Icinga Camp.

The module uses Openstreetmap and visualises your hosts on a world map based on configured custom attributes. Sweet :-)

 

Maps for Everything

The main overview can be configured for the default zoom level, longitude and latitude to center e.g. Amsterdam by default.

 

More Details

Each host which defines the custom attribute “geolocation” provides a new action in the detail view. If you click on it, the detail view will open the map putting the host into its center. You can zoom in and out, and if you don’t need the host details anymore, close it away.

 

Dashboards

The recently released v1.0.3 version also allow to add maps as Dashboard dashlets. That way you can build beautiful dashboards for your office and always have your monitoring in a global overview :)

 

Want more?

We’ve already integrated the map module into our main Icinga 2 Vagrant box. This not only includes example hosts and services with the proper longitute/latitude attributes, but also Icinga Web 2 dashboards for you to play with (the same as in the screenshots here, actually ;)).

One more thing: Lars already added a FreeBSD port and asked me how to automate the geolocation attribute configuration for hosts based on their name. Guess everyone has a certain host name pattern which includes the location, like “esfapp01″ or “ebeapp01″.

Challenge accepted. Read on if you are familiar with the Icinga 2 DSL including functions, dictionaries, conditions and more. If not, all available language and library specifics can be found in the documentation.

Define a global constant “GeoLocation” as dictionary which provides the longitude/latitude as string value, while the location name is defined as key.

/* Specify some readable locations */
const GeoLocation = {
  "amsterdam" = "52.370216,4.895168",
  "bangalore" = "12.971599,77.594563",
  "bangkok" = "13.756331,100.501765",
  "beijing" = "39.904200,116.407396",
  "berlin" = "52.524372,13.389856",
  "buenos aires" = "-34.603684,-58.381559",
  "kapstadt" = "-33.924869,18.424055"
  "new york" = "40.712784,-74.005941",
  "nuremberg" = "49.425409,11.079655",
  "london" = "51.507351,-0.127758",
  "los angeles" = "34.052234,-118.243685",
  "montreal" = "45.501689,-73.567256",
  "moscow" = "55.755826,37.617300",
  "new york" = "40.712784,-74.005941",
  "paris" = "48.856614,2.352222",
  "prague" = "50.075538,14.437800",
  "rio de janeiro" = "-22.906847,-43.172896",
  "san francisco" = "37.774929,-122.419416",
  "sydney" = "-33.865143,151.209900",
  "stockholm" = "59.329323,18.068581",
  "tokyo" = "35.689487,139.691706",
  "vienna" = "48.208174,16.373819",
  "zurich" = "47.376887,8.541694",
}

Since this dictionary contains the readable names (can be used for more host attributes), we’ll also need a short to long name mapping. The short location tag will be matched against the host name then.

/* Provide a lookup dictionary for short values in host names (you won't need it, but it is more readable) */
const GeoLocationShort = {
  "sf" = "san francisco"
  "la" = "los angeles"
}

Now create a global function called “getHostGeoLocation” which extracts the location from the host name. Remember, “esfapp01” should match “sf”. You can use any available String type methods available in the Icinga 2 DSL here.

/*
 * Extract host location from the hostname itself, "sf" is the location
 * "sf" is short for "san francisco", perform a lookup in GeoLocationShort first
 * "san francisco" should be located inside GeoLocation
 */

globals.getHostGeoLocation = function(hostName) {
  /* XXX: Use your own name pattern and extract the location */
  var loc = hostName.substr(1,2)

  if (!GeoLocationShort.contains(loc)) {
    log(LogWarning, "globals.getHostGeoLocation", "Cannot find '" + loc + "' in GeoLocationShort")
    return null
  }

  var loc_idx = GeoLocationShort[loc]
  log(LogDebug, "globals.getHostGeoLocation", "Evaluating host " + hostName + " and location " + loc + " (" + loc_idx + ") ")

  if (!GeoLocation.contains(loc_idx)) {
    log(LogWarning, "globals.getHostGeoLocation", "Cannot find '" + loc_idx + "' in GeoLocation")
    return null
  }

  return GeoLocation[loc_idx]
}

Basically the function extracts the location and does a lookup inside the two dictionaries. If nothing is found, a warning is logged – you can see that during startup and config validation. This helps finding hosts which should have a geo location but apparently don’t match your locations.

You can use the function “getHostGeoLocation” inside a host template. Since the function returns “null” if nothing was matched, we’ll only set the custom attribute “geolocation” if the function return value is set.

/*
 * Use the function inside a host template (hides the logic)
 */

template Host "geo-host" {
  var loc = getHostGeoLocation(name)

  if (loc) {
    vars.geolocation = loc
  }
}

Now let your hosts just import this specific template.

object Host "esfapp01" {
  import "geo-host"
  check_command = "dummy"
}

Reload Icinga 2 and open “esfapp01” in Icinga Web 2. Look how beautiful :-)

A complete example is provided inside the Vagrant box :)

 

Conclusion

We really love to see community members starting with most-wanted Icinga Web 2 modules. Maps and Grafana are also featured as community modules on our website :)

Thanks for your hard work, Nicolai!

PS: We will dive into this awesome module in our OSMC talk “Ops and dev stories: Integrate everything into your monitoring stack”. Nicolai will also attend the OSMC (November 21-24, 2017) and we will continue development on the hackathon on Friday. See you in Nuremberg!