All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit b72a16c4 authored by Leigh B Stoller's avatar Leigh B 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)) \ ...@@ -60,8 +60,9 @@ install: $(addprefix $(INSTALL_BINDIR)/, $(BIN_SCRIPTS)) \
$(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS)) \ $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS)) \
$(addprefix $(INSTALL_LIBDIR)/, $(LIB_SCRIPTS)) \ $(addprefix $(INSTALL_LIBDIR)/, $(LIB_SCRIPTS)) \
$(addprefix $(INSTALL_LIBEXECDIR)/, $(LIBEXEC_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 boss-install: install install-subdirs
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
use strict; use strict;
use English; use English;
use Getopt::Std; use Getopt::Std;
use Data::Dumper;
use JSON;
use File::Basename;
# #
# Look for APT things that need to be dealt with. # Look for APT things that need to be dealt with.
...@@ -307,6 +310,121 @@ sub ReportLockdownExpired() ...@@ -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; my $reportcounter = 0;
# Do this once at startup # Do this once at startup
...@@ -326,6 +444,7 @@ while (1) { ...@@ -326,6 +444,7 @@ while (1) {
KillFailedInstances(); KillFailedInstances();
ExpireInstances(); ExpireInstances();
UpdateAggregateGraphs();
# Do this once every 24 hours. # Do this once every 24 hours.
if ($reportcounter >= (24 * 60 * 60)) { 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 = { ...@@ -8,7 +8,6 @@ window.APT_OPTIONS.configObject = {
'jquery-steps': 'js/lib/jquery.steps.min', 'jquery-steps': 'js/lib/jquery.steps.min',
'formhelpers': 'js/lib/bootstrap-formhelpers', 'formhelpers': 'js/lib/bootstrap-formhelpers',
'dateformat': 'js/lib/date.format', 'dateformat': 'js/lib/date.format',
'd3': 'js/lib/d3.v3',
'filestyle': 'js/lib/filestyle', 'filestyle': 'js/lib/filestyle',
'marked': 'js/lib/marked', 'marked': 'js/lib/marked',
'moment': 'js/lib/moment', 'moment': 'js/lib/moment',
...@@ -23,8 +22,8 @@ window.APT_OPTIONS.configObject = { ...@@ -23,8 +22,8 @@ window.APT_OPTIONS.configObject = {
'jquery-grid': { deps: ['jquery-ui'] }, 'jquery-grid': { deps: ['jquery-ui'] },
'jquery-steps': { }, 'jquery-steps': { },
'formhelpers': { }, 'formhelpers': { },
'jacks': { },
'dateformat': { exports: 'dateFormat' }, 'dateformat': { exports: 'dateFormat' },
'd3': { exports: 'd3' },
'filestyle': { }, 'filestyle': { },
'marked' : { exports: 'marked' }, 'marked' : { exports: 'marked' },
'underscore': { exports: '_' }, '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