Sample Dashboard

Monitoring with Dashing

This is a side project I’ve been working on for several months now.

Part of our daily routine at work involves monitoring the different systems ensuring our data center runs smoothly. This ranges from finding out whether every server is getting enough power, drawing too much power, to whether it’s even running at all.

To make this possible, our backend consists of tools to fetch, store, and process data from the sensors and power meters attached to every server rack. Nagios is the application that actively checks the status of every host on its database like power load, temperature, and humidity just to name a few parameters, and stores them in data sets. On top of that, another tool called Graphite interprets these data sets and renders a graph.

 

2014-09-29 11.31.46

A Graphite Graph

One disadvantage of Graphite rendered graphs is that the graphs are rendered to PNG files. Which means, it does not display graphs in real time. On top of that, graphite can only display one graph at a time.  We needed a different system that can automatically update and display our metrics aesthetically.

Enter Dashing..

What is Dashing?

UGent S10 Dashboard. Note: All values in kilowatt (kW) except PUE and Fluid Temperature meters

Dashing is a dashboard framework developed by the people at Shopify that lets you easily create your own dashboard. A typical dashing dashboard is composed of widgets that can display data in different ways (Graphs, meters, lists, text, ..). On top of the widgets out of the box, you can further customize it by creating your own widgets. The sky is the limit. More info on Dashing here.

Fetching and parsing JSON data

To fetch data from Graphite, Dashing needs a job written in ruby to perform the following steps:

  1. Request JSON formatted data from a given URL
  2. Parse the data (i.e. convert the text data into a format Dashing understands)
  3. Send the parsed data to the appropriate widget to be displayed.

In this case, scripts from thomasvm were used and modified for this application.

require "rest-client"
require "json"
require "date"
require "uri"
 
class Graphite
    # Pass in the url to where you graphite instance is hosted
    def initialize(arg)
        @arg = arg
    end
 
    def get_value(datapoint)
        value = datapoint[0] || 0
        return value.round(2)
    end
 
    # This is the raw query method, it will fetch the 
    # JSON from the Graphite and parse it
    def query(name, since)
        url = URI.encode("https://metrics.example.com/render?#{@arg}&from=#{since}&until=-80min&format=json"
        result = JSON.parse(response.body, :symbolize_names => true)
        return result.first
    end
 
    # This is high-level function that will fetch a set of datapoints
    # since the given start point and convert it into a format that the
    # graph widget of Dashing can understand
    def points(name, since)
        stats = query name, since
        datapoints = stats[:datapoints]
 
        ipoints = []
        count = 1
 
        (datapoints.select { |el| not el[0].nil? }).each do|item|
            ipoints << { x: count, y: get_value(item)}
            count += 1
        end
 
        return ipoints
    end
 
    # Not all Dashing widgets need a set of points, often just 
    # the current value is enough. This method does just that, it fetches
    # the value for last point-in-time and returns it
    def value(name, since)
        stats = query name, since
        last = (stats[:datapoints].select { |el| not el[0].nil? }).last
 
        return get_value(last)
    end
end

This helper class does all the dirty work of fetching and parsing the data. A separate job is needed to create an instance of this class by setting up the parameters, sending the resulting data to the widget, and looping the same process after a set time span.

require "~/dashtest/lib/graphite.rb"
 
SCHEDULER.every '10s', :first_in => 0 do
    # Create an instance of our helper class
    pue = Graphite.new "target=divideSeries(scale(sumSeries(powerm.e4010004.modbus-e4010004.gauge-TotalActivePower.value,powerm.e4010021.modbus-e4010021.gauge-TotalActivePower.value),0.001),sumSeries(sumSeries(nagios.g{0,1}e0a.check_snmp_apc_modpdu___TotalPwr.TotalPwr),sumSeries(nagios.g{0,1}e1b.check_snmp_apc_modpdu___TotalPwr.TotalPwr),sumSeries(nagios.v{2,3}e0a.check_snmp_apc_modpdu___TotalPwr.TotalPwr),sumSeries(nagios.v{2,3}e1b.check_snmp_apc_modpdu___TotalPwr.TotalPwr),nagios.i0e0bd.check_snmp_apc_modpdu___TotalPwr.TotalPwr,nagios.i1e0b.check_snmp_apc_modpdu___TotalPwr.TotalPwr,nagios.i1r4e0au.check_snmp_apc_modpdu___TotalPwr.TotalPwr,nagios.i1r4e1bu.check_snmp_apc_modpdu___TotalPwr.TotalPwr,scale(sumSeries(powerm.e0e0m0ad.modbus-Tier1HoofdstroomRij0.gauge-RealPowerTotal.value,powerm.e1e0m0ad.modbus-Tier1HoofdstroomRij1.gauge-RealPowerTotal.value,powerm.e9e0m0au.modbus-Tier1NoodstroomA.gauge-RealPowerTotal.value,powerm.e9e0m0bu.modbus-Tier1NoodstroomB.gauge-RealPowerTotal.value),0.001)))"
 
    # get the current value
    puev = pue.value "S10_PUE", "-1h"
 
 # get points for the last half 30 days
    puep = pue.points "S10_PUE", "-30d"
 
    # sends the data to widget 'S10PUE'
    send_event 'S10PUE', { current: puev, value: puev, points: puep }
 
end

Ruby scripts aside, that’s how Dashing works in a nutshell.

Posted in IT, work and tagged , , , , , , , , , .

Leave a Reply

Your email address will not be published. Required fields are marked *