Commit b72a16c4 authored by Leigh Stoller's avatar Leigh Stoller

Bring in Rob's aster graph code. Add 5 minute download of the data

from all clusters, and regen the json files. Still need to add live
updating.

NOTE: This is not ready yet, Rob needs to figure out why the labels
are broken.
parent 7564c408
......@@ -60,8 +60,9 @@ install: $(addprefix $(INSTALL_BINDIR)/, $(BIN_SCRIPTS)) \
$(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS)) \
$(addprefix $(INSTALL_LIBDIR)/, $(LIB_SCRIPTS)) \
$(addprefix $(INSTALL_LIBEXECDIR)/, $(LIBEXEC_SCRIPTS)) \
$(addprefix $(INSTALL_DIR)/opsdir/libexec/, $(USERLIBEXEC))
$(addprefix $(INSTALL_DIR)/opsdir/libexec/, $(USERLIBEXEC)) \
$(INSTALL_ETCDIR)/cloudlab-fedonly.json \
$(INSTALL_ETCDIR)/cloudlab-nofed.json
boss-install: install install-subdirs
......
......@@ -30,6 +30,9 @@
use strict;
use English;
use Getopt::Std;
use Data::Dumper;
use JSON;
use File::Basename;
#
# Look for APT things that need to be dealt with.
......@@ -307,6 +310,121 @@ sub ReportLockdownExpired()
}
#
# Gather up aggregate info. At the moment, there is still a lot of
# Utah specific goings on here.
#
sub UpdateAggregateGraphs()
{
my $freeblob = {};
my $query_result =
DBQueryWarn("select * from apt_aggregates where noupdate=0");
return 0
if (!$query_result->numrows);
while (my $row = $query_result->fetchrow_hashref()) {
my $urn = $row->{'urn'};
my $url = $row->{'weburl'} . "/node_usage/freenodes.csv";
my $name = $row->{'nickname'};
system("$WGET --no-check-certificate ".
"--timeout=30 --waitretry=30 --retry-connrefused ".
"-q -O /tmp/freenodes.csv '$url'");
next
if ($?);
if (-z "/tmp/freenodes.csv") {
print STDERR "Zero length csv file for $urn\n";
next;
}
if (!open(CSV, "/tmp/freenodes.csv")) {
print STDERR "Could not open csv file for $urn\n";
next;
}
my $line = <CSV>;
if ($line !~ /^Type,Inuse,Free$/) {
print STDERR "Invalid first line in csv file for $urn\n";
close(CSV);
next;
}
while (<CSV>) {
if ($_ =~ /^([-\w]+),(\d+),(\d+)$/) {
$freeblob->{$name}->{$1} = {"inuse" => $2, "free" => $3};
}
}
close(CSV);
}
#print Dumper($freeblob);
#
# We are currently operating from two master json files. We make a
# a copy of those, and then update them with new info, and write them
# back. This will need to be generalized at some point.
#
my $NOFED = "$TB/etc/cloudlab-nofed.json";
my $FEDONLY = "$TB/etc/cloudlab-fedonly.json";
foreach my $file ($NOFED, $FEDONLY) {
if (-e $file) {
my $data = `/bin/cat $file`;
my $obj = decode_json($data);
if (!defined($obj)) {
print STDERR "Could not decide json in $file\n";
next;
}
foreach my $cluster (@{ $obj->{'children'} }) {
my $name = $cluster->{'name'};
next
if (!exists($freeblob->{$name}));
my $total_size = 0;
my $total_inuse = 0;
foreach my $child (@{ $cluster->{'children'} }) {
my $type = $child->{'name'};
if (!exists($freeblob->{$name}->{$type})) {
print STDERR "$name: Did not get a count for $type\n";
next;
}
my $inuse = $freeblob->{$name}->{$type}->{'inuse'};
my $size = $freeblob->{$name}->{$type}->{'free'} + $inuse;
$child->{'howfull'} = int($inuse);
$child->{'size'} = int($size);
$total_inuse += $inuse;
$total_size += $size;
}
$cluster->{'howfull'} = int($total_inuse);
$cluster->{'size'} = int($total_size);
}
#print Dumper($obj);
#
# Write out new file for web ui.
#
my $tfile = "/tmp/$$.json";
if (open(JS, ">$tfile")) {
my $json_text = to_json($obj, { pretty => 1 });
print JS $json_text . "\n";
close(JS);
}
else {
print STDERR "Could not open temp json file for new data\n";
next;
}
system("/bin/mv -f $tfile $TB/www/apt/" . basename($file));
if ($?) {
print STDERR "Could not copy new json file to ".
"$TB/www/apt/" . basename($file) . "\n";
}
unlink($tfile);
}
}
return 0;
}
my $reportcounter = 0;
# Do this once at startup
......@@ -326,6 +444,7 @@ while (1) {
KillFailedInstances();
ExpireInstances();
UpdateAggregateGraphs();
# Do this once every 24 hours.
if ($reportcounter >= (24 * 60 * 60)) {
......
{
"name": "CloudLab Federates",
"children": [
{
"name": "APT",
"order": 4,
"color": "#2fad2f",
"howfull": 192,
"size": 192,
"url": "http://docs.cloudlab.us/hardware.html#%28part._apt-cluster%29",
"children": [ { "name": "r320", "size": 128, "howfull" : 110, "url": "http://docs.cloudlab.us/hardware.html#%28part._apt-cluster%29" },
{ "name": "c6220", "size": 64, "howfull" : 60, "url": "http://docs.cloudlab.us/hardware.html#%28part._apt-cluster%29" } ]
},
{
"name": "DDC",
"order": 5,
"color": "#2f64ad",
"size" : 33,
"howfull": 33,
"url": "http://docs.cloudlab.us/hardware.html#%28part._ig-ddc%29",
"children": [ { "name": "dl360", "size": 33, "howfull": 5, "url": "http://docs.cloudlab.us/hardware.html#%28part._ig-ddc%29" } ]
},
{
"name": "Utah PG",
"order": 6,
"color": "#2164ad",
"size" : 0,
"howfull": 0,
"url": "http://docs.cloudlab.us/hardware.html#%28part._ig-ddc%29",
"children": [ { "name": "pc3000", "size": 33, "howfull": 5,
"url": "http://docs.cloudlab.us/hardware.html#%28part._ig-ddc%29"
},
{ "name": "d710", "size": 33, "howfull": 5,
"url": "http://docs.cloudlab.us/hardware.html#%28part._ig-ddc%29"
}
]
}
]
}
{
"name": "CloudLab Sites",
"children": [
{
"name": "Utah",
"order": 1,
"color": "#ca3737",
"size": 315,
"howfull": 315,
"children": [ { "name": "m400", "size": 315, "howfull": 200, "url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-utah%29" } ],
"url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-utah%29"
},
{
"name": "Clemson",
"order": 2,
"color": "#ca8f37",
"size": 100,
"howfull": 100,
"url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-clemson%29",
"children": [ { "name": "c8220", "size": 96, "howfull": 30, "url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-clemson%29" },
{ "name": "c8220x", "size": 4, "howfull": 3, "url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-clemson%29" } ]
},
{
"name": "Wisconsin",
"order": 3,
"color": "#caca37",
"size": 100,
"howfull": 100,
"url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-wisconsin%29",
"children": [ { "name": "C220M4", "size": 90, "howfull": 30, "url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-wisconsin%29" },
{ "name": "C240M4", "size": 10, "howfull": 3, "url": "http://docs.cloudlab.us/hardware.html#%28part._cloudlab-wisconsin%29" } ]
}
]
}
This diff is collapsed.
require(window.APT_OPTIONS.configObject,
['js/quickvm_sup',
'js/lib/text!template/cluster-graphs.html',
'js/bilevel', 'js/liquidFillGauge'],
function (sup, clusterString)
{
'use strict';
function initialize()
{
window.APT_OPTIONS.initialize(sup);
$('#cluster-graphs').html(clusterString);
bilevelAsterGraph("/cloudlab-nofed.json",
"#status-nofed","auto","large");
bilevelAsterGraph("/cloudlab-fedonly.json",
"#status-fedonly","auto","large");
}
$(document).ready(initialize);
});
......@@ -8,7 +8,6 @@ window.APT_OPTIONS.configObject = {
'jquery-steps': 'js/lib/jquery.steps.min',
'formhelpers': 'js/lib/bootstrap-formhelpers',
'dateformat': 'js/lib/date.format',
'd3': 'js/lib/d3.v3',
'filestyle': 'js/lib/filestyle',
'marked': 'js/lib/marked',
'moment': 'js/lib/moment',
......@@ -23,8 +22,8 @@ window.APT_OPTIONS.configObject = {
'jquery-grid': { deps: ['jquery-ui'] },
'jquery-steps': { },
'formhelpers': { },
'jacks': { },
'dateformat': { exports: 'dateFormat' },
'd3': { exports: 'd3' },
'filestyle': { },
'marked' : { exports: 'marked' },
'underscore': { exports: '_' },
......
// d3.tip
// Copyright (c) 2013 Justin Palmer
//
// Tooltips for d3.js SVG visualizations
// Public - contructs a new tooltip
//
// Returns a tip
d3.tip = function() {
var direction = d3_tip_direction,
offset = d3_tip_offset,
html = d3_tip_html,
node = initNode(),
svg = null,
point = null,
target = null
function tip(vis) {
svg = getSVGNode(vis)
point = svg.createSVGPoint()
document.body.appendChild(node)
}
// Public - show the tooltip on the screen
//
// Returns a tip
tip.show = function() {
var args = Array.prototype.slice.call(arguments)
if(args[args.length - 1] instanceof SVGElement) target = args.pop()
var content = html.apply(this, args),
poffset = offset.apply(this, args),
dir = direction.apply(this, args),
nodel = d3.select(node), i = 0,
coords
nodel.html(content)
.style({ opacity: 1, 'pointer-events': 'all' })
while(i--) nodel.classed(directions[i], false)
coords = direction_callbacks.get(dir).apply(this)
nodel.classed(dir, true).style({
top: (coords.top + poffset[0]) + 'px',
left: (coords.left + poffset[1]) + 'px'
})
return tip
}
// Public - hide the tooltip
//
// Returns a tip
tip.hide = function() {
nodel = d3.select(node)
nodel.style({ opacity: 0, 'pointer-events': 'none' })
return tip
}
// Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value.
//
// n - name of the attribute
// v - value of the attribute
//
// Returns tip or attribute value
tip.attr = function(n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return d3.select(node).attr(n)
} else {
var args = Array.prototype.slice.call(arguments)
d3.selection.prototype.attr.apply(d3.select(node), args)
}
return tip
}
// Public: Proxy style calls to the d3 tip container. Sets or gets a style value.
//
// n - name of the property
// v - value of the property
//
// Returns tip or style property value
tip.style = function(n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return d3.select(node).style(n)
} else {
var args = Array.prototype.slice.call(arguments)
d3.selection.prototype.style.apply(d3.select(node), args)
}
return tip
}
// Public: Set or get the direction of the tooltip
//
// v - One of n(north), s(south), e(east), or w(west), nw(northwest),
// sw(southwest), ne(northeast) or se(southeast)
//
// Returns tip or direction
tip.direction = function(v) {
if (!arguments.length) return direction
direction = v == null ? v : d3.functor(v)
return tip
}
// Public: Sets or gets the offset of the tip
//
// v - Array of [x, y] offset
//
// Returns offset or
tip.offset = function(v) {
if (!arguments.length) return offset
offset = v == null ? v : d3.functor(v)
return tip
}
// Public: sets or gets the html value of the tooltip
//
// v - String value of the tip
//
// Returns html value or tip
tip.html = function(v) {
if (!arguments.length) return html
html = v == null ? v : d3.functor(v)
return tip
}
function d3_tip_direction() { return 'n' }
function d3_tip_offset() { return [0, 0] }
function d3_tip_html() { return ' ' }
var direction_callbacks = d3.map({
n: direction_n,
s: direction_s,
e: direction_e,
w: direction_w,
nw: direction_nw,
ne: direction_ne,
sw: direction_sw,
se: direction_se
}),
directions = direction_callbacks.keys()
function direction_n() {
var bbox = getScreenBBox()
return {
top: bbox.n.y - node.offsetHeight,
left: bbox.n.x - node.offsetWidth / 2
}
}
function direction_s() {
var bbox = getScreenBBox()
return {
top: bbox.s.y,
left: bbox.s.x - node.offsetWidth / 2
}
}
function direction_e() {
var bbox = getScreenBBox()
return {
top: bbox.e.y - node.offsetHeight / 2,
left: bbox.e.x
}
}
function direction_w() {
var bbox = getScreenBBox()
return {
top: bbox.w.y - node.offsetHeight / 2,
left: bbox.w.x - node.offsetWidth
}
}
function direction_nw() {
var bbox = getScreenBBox()
return {
top: bbox.nw.y - node.offsetHeight,
left: bbox.nw.x - node.offsetWidth
}
}
function direction_ne() {
var bbox = getScreenBBox()
return {
top: bbox.ne.y - node.offsetHeight,
left: bbox.ne.x
}
}
function direction_sw() {
var bbox = getScreenBBox()
return {
top: bbox.sw.y,
left: bbox.sw.x - node.offsetWidth
}
}
function direction_se() {
var bbox = getScreenBBox()
return {
top: bbox.se.y,
left: bbox.e.x
}
}
function initNode() {
var node = d3.select(document.createElement('div'))
node.style({
position: 'absolute',
opacity: 0,
pointerEvents: 'none',
boxSizing: 'border-box'
})
return node.node()
}
function getSVGNode(el) {
el = el.node()
if(el.tagName.toLowerCase() == 'svg')
return el
return el.ownerSVGElement
}
// Private - gets the screen coordinates of a shape
//
// Given a shape on the screen, will return an SVGPoint for the directions
// n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
// sw(southwest).
//
// +-+-+
// | |
// + +
// | |
// +-+-+
//
// Returns an Object {n, s, e, w, nw, sw, ne, se}
function getScreenBBox() {
var targetel = target || d3.event.target,
bbox = {},
matrix = targetel.getScreenCTM(),
tbbox = targetel.getBBox(),
width = tbbox.width,
height = tbbox.height,
x = tbbox.x,
y = tbbox.y,
scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
point.x = x + scrollLeft
point.y = y + scrollTop
bbox.nw = point.matrixTransform(matrix)
point.x += width
bbox.ne = point.matrixTransform(matrix)
point.y += height
bbox.se = point.matrixTransform(matrix)
point.x -= width
bbox.sw = point.matrixTransform(matrix)
point.y -= height / 2
bbox.w = point.matrixTransform(matrix)
point.x += width
bbox.e = point.matrixTransform(matrix)
point.x -= width / 2
point.y -= height / 2
bbox.n = point.matrixTransform(matrix)
point.y += height
bbox.s = point.matrixTransform(matrix)
return bbox
}
return tip
};
This diff is collapsed.
<div style="font-family:'Fira Sans'">
<link href='https://fonts.googleapis.com/css?family=Fira+Sans'
rel='stylesheet' type='text/css'></link>
<link rel="stylesheet" href="cluster-graphs/basic-tooltip.css"></link>
<!-- Big div to contain the two graphs -->
<div style="margin:auto; width: 1000px">
<!-- Nested divs to make it easy to center both the title and the graph -->
<div style="width: 500px; float: left">
<h2 style="text-align: center">CloudLab Sites</h2>
<!-- Vertically center in a fixed-height div to vertically align the
two different-sized graphs -->
<div style="height: 500px; margin: auto">
<!-- Actual graph -->
<div style="width:500px; margin: auto auto;" id="status-nofed"></div>
</div>
</div>
<div style="width:500px; float: right">
<h2 style="text-align: center">Federated Facilities</h2>
<div style="width:500px; margin: auto auto;" id="status-fedonly"></div>
</div>
</div>
</div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment