Skip to content

Instantly share code, notes, and snippets.

@jguice
Last active July 2, 2016 08:00
Show Gist options
  • Save jguice/2fc5f9f454cb53e3e335 to your computer and use it in GitHub Desktop.
Save jguice/2fc5f9f454cb53e3e335 to your computer and use it in GitHub Desktop.
Personal Dashboard POC

These are notes and code snippets for setting up a "personal dashboard". The impetus for this was that I wanted a way to visually monitor ad-hoc things on our servers. If they prove to be useful monitors then the idea is that they get moved into grafana, kibana, etc. where everyone can share them.

Sometimes you just want to run a command on a bunch of servers and graph/display the output in a nice way. :)

knife ssh can be handy for this if you're using chef, but I wanted something with even less overhead/setup.

This POC uses dashing and ansible only so the requirements are:

  1. install dashing (modern ruby, bundler, some gems)
  2. install ansible (pip or provided OS package) and have ssh access to the servers

Since this is a POC I'm not going to detail how to set this up completely just yet. Here's the basic idea though using an uptime display as an example (and some code snippets):

jobs

After installing dashing and ansible I setup a job for uptime. It looks something like this:

uptime.rb

require 'json'
require 'pp'

groups = %w(foo bar baz)

job = 'uptime'
inventory_file = '/path/to/inventory' # ansible formatted inventory file

SCHEDULER.every '600s' do
  # run ansible to generate uptime data for all groups
  groups.each do |group|
    output_dir = "/path/to/ansible_output/#{job}-#{group}"
    command = "ansible -t #{output_dir} -i #{inventory_file} #{group} -m shell -a 'uptime' --sudo"
    system(command)

    # parse output of files into appropriate structure
    hosts = Dir.glob(File.join(output_dir, '*'))
    uptimes = Hash.new({ value: '?'}) # default value indicates unknown uptime

    hosts.each do |h|
      hostname = File.basename(h).gsub('.some.domain.to.remove', '')
      contents = JSON.parse(File.open(h, 'r').read)
      uptime = contents['stdout'].match(/up(.*?),\s+\d+ user/)[1]

      # check date
      age = uptime.match(/(\d+) days/)

      if age && age[1].to_i > 30
        color = '#BD4932'
      else
        color = 'rgba(255, 255, 255, 0.7)'
      end

      uptimes[h] = { label: hostname, value: uptime, style: { color: color} }
    end

    send_event(group, { items: uptimes.values })
  end
end

As part of dashing the above job runs automatically and executes the ansible commmand every 5 minutes. The resulting JSON output is parsed and cleaned up a bit, then sent to the dashboard view (in the usual dashing way).

display

The next piece is a dashboard display. Something like this:

uptime.erb

<% content_for :title do %>System Uptimes<% end %>
<div class="gridster">
  <ul>
    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
      <div style="background-color: #96bf48;" data-id="foo" data-view="List" data-unordered="true" data-title="FOO"></div>
    </li>
    <li data-row="1" data-col="2" data-sizex="1" data-sizey="1">
      <div style="background-color: #ff9618;"data-id="bar" data-view="List" data-unordered="true" data-title="BAR"></div>
    </li>
    <li data-row="1" data-col="3" data-sizex="1" data-sizey="1">
      <div style="background-color: #47bbb3;" data-id="baz" data-view="List" data-unordered="true" data-title="BAZ"></div>
    </li>
  </ul>
</div>

After that it's just a matter of firing up dashing and hitting the instance with a browser to see the data.

inventory file

I should note that the inventory file is a standard ansible inventory file with servers listed by group (foo bar baz).

next steps

I'm going to continue playing with this locally and trying out different use-cases I had in mind (and using some of the other dashing widgets available).

My hope is to arrive at a reusable framework where ansible commands, hosts, and dashboard can be quickly and easily defined (possibly by just coping an existing similar example). If that day comes I'll move this into a separate repo and type some proper setup instructions. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment