In [ ]:
import sqlite3
import pandas
import json
import re
import os
import math
import glob
from qumulo.rest_client import RestClient
from IPython.core.display import display, HTML, Javascript, display_javascript
In [ ]:
%%javascript
requirejs.config({
paths: {
'd3': 'https://cdnjs.cloudflare.com/ajax/libs/d3/4.7.4/d3',
'd3-color': 'https://d3js.org/d3-color.v1.min',
'd3-interpolate': 'https://d3js.org/d3-interpolate.v1.min',
'd3-chromatic': 'https://d3js.org/d3-scale-chromatic.v1.min'
}
});
require(['d3', 'd3-color', 'd3-interpolate', 'd3-chromatic'], function(d3, d3_color, d3_interpolate, d3_chromatic) {
window.d3 = Object.assign({}, d3, d3_color, d3_interpolate, d3_chromatic);
});
In [ ]:
def read_dir(path, level, root_cap, thresh=0.001):
try:
the_dir = rc.fs.read_dir_aggregates(path=path)
except:
return
if float(the_dir['total_capacity']) / root_cap > thresh:
yield {"path": path,
"level": level,
"cap": the_dir['total_capacity'],
"cap_perc": float(the_dir['total_capacity']) / root_cap
}
for d in the_dir['files']:
if (float(d['capacity_usage']) / root_cap) > thresh and d['type'] == 'FS_FILE_TYPE_DIRECTORY':
for d in read_dir(re.sub('//', '/', path + '/') + d['name'], level+1, root_cap):
yield d
elif (float(d['capacity_usage']) / root_cap) > thresh:
file_d = {"path": re.sub('//', '/', path + '/') + d['name'],
"level": level,
"cap": d['capacity_usage'],
"cap_perc": float(d['capacity_usage']) / root_cap
}
yield file_d
In [ ]:
def delete_tree(path):
# print("deleting: %s" % (path, ))
# out = rc.fs.delete_tree(path)
return "not actually deleting: %s" % (path, )
In [ ]:
conf = json.loads(open('/mnt/product/qumulo-historical-data/config.json').read())
clusters = {}
for cl in conf['clusters']:
clusters[cl['cluster']] = cl
In [ ]:
def get_all_data(cluster_name, start_dir='/'):
global rc
cluster_conf = clusters[cluster_name]
rc = RestClient(cluster_name, 8000)
rc.login(cluster_conf['user'], cluster_conf['pass'])
dfs = []
for db_file in glob.glob('/mnt/product/qumulo-historical-data/data/%s/*' % (cluster_name,)):
print("Read sqlite db file: %s" % (db_file, ))
cn = sqlite3.connect(db_file)
try:
dfs.append(pandas.read_sql('SELECT * FROM iops_tput_path_hour', cn))
except:
print("table doesn't exist")
df_activity = pandas.concat(dfs).groupby(['path']) \
.agg({'total_iops':'sum',
'total_data':'sum',
'read_data':'sum',
'write_data':'sum'}) \
.reset_index()
df_activity.set_index(['path'], inplace=True)
# this does a pretty large tree walk, with no parallelization. Can take a few minutes on large clusters.
root_dir = rc.fs.read_dir_aggregates(start_dir)
root_cap = int(root_dir['total_capacity'])
print("Begin the recursive walking.")
df_capacity = pandas.DataFrame(read_dir(start_dir, 1, root_cap))
print("Completed the recursive walking.")
df = pandas.merge(df_capacity, df_activity.reset_index(), how='left').fillna(0)
df.to_csv('capacity.csv')
# df[(df['cap_perc'] > 0.01) & (df['write_data'] <= 0) & (df['level'] <= 4)].sort_values(['cap_perc'], ascending=False)
In [ ]:
%%html
<style>
svg{
shape-rendering: crispEdges;
}
tspan {
font-size: 10px;
}
.node rect{
stroke: rgba(255, 255, 255, 0.2);
stroke-width: 1;
}
.node.node--hover rect{
stroke: rgba(255, 255, 255, 0.9);
stroke-width: 1;
}
#id2{
position: absolute;
top: 12px;
left: 12px;
border: 1px solid #555;
z-index: 100;
background-color: white;
-webkit-box-shadow: 0px 9px 29px 0px rgba(66,66,66,1);
-moz-box-shadow: 0px 9px 29px 0px rgba(66,66,66,1);
box-shadow: 0px 9px 29px 0px rgba(66,66,66,1);
}
#close_button{
position: absolute;
top: 10px;
right: 10px;
background-color: #cccccc;
font-weight: bold;
border: 1px solid #333333;
border-radius: 18px;
height: 36px;
width: 36px;
font-size: 24px;
z-index: 101;
text-align: center;
cursor: pointer;
}
text.light{
fill: white;
}
</style>
In [ ]:
%%javascript
window.draw_treemap = function(the_metric){
var w = jQuery(window).width()-24;
var h = jQuery(window).height()-24;
var svg = d3.select("body").append("svg").attr("id", "id2")
svg.attr("width", w).attr("height", h);
var max_depth = 5;
svg.selectAll("*").remove();
jQuery(document).keypress(function(e) {
console.log(e.which);
if(e.which == 92){
jQuery("svg#id2").remove();
}
})
console.clear();
d3.csv('capacity.csv', function(data){
data = data.filter(function(d){return d.level <= max_depth})
data.forEach(function(d){
d.path = '[root]' + (d.path =='/'?'':d.path);
d.cap = +d.cap;
d.cap_perc = +d.cap_perc;
d.level = +d.level;
d.read_data = +d.read_data;
d.write_data = +d.write_data;
d.total_iops = +d.total_iops;
d.total_data = +d.total_data;
});
var stratify = d3.stratify()
.id(function(d) { return d.path; })
.parentId(function(d) { return d.path.substring(0, d.path.lastIndexOf("/")); });
var root = stratify(data)
root.eachBefore(function(d){
if('children' in d){
d.children.forEach(function(child){
d.data.cap_perc -= child.data.cap_perc;
})
}
})
root
.sum(function(d) { return d.cap_perc; })
.sort(function(a, b) { return b.height - a.height || b.value - a.value; });
var offset = 3;
var close_button = d3.select("body").append("div")
.attr("id", "close_button")
.text("X")
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var format = function(dd){
if(dd >= 0.0001){
return (dd*100).toFixed("2")
}else{
return ""
}
};
var domain = [0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10, 50];
var range = [d3.interpolateRdYlBu(0.99),
d3.interpolateRdYlBu(0.8),
d3.interpolateRdYlBu(0.6),
d3.interpolateRdYlBu(0.4),
d3.interpolateRdYlBu(0.3),
d3.interpolateRdYlBu(0.2),
d3.interpolateRdYlBu(0.01),
]
var color = d3.scaleLinear().domain(domain).range(range);
var stratify = d3.stratify()
.parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf("/")); });
var treemap = d3.treemap()
.tile(d3.treemapResquarify.ratio(1.6))
.size([w, h])
.paddingInner(0)
.paddingOuter(offset)
.paddingTop(function(d) { return d.depth < 4 ? 19 : offset; })
.round(true);
treemap(root);
var cell = svg
.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; })
.attr("class", "node")
.each(function(d) { d.node = this; })
.on("mouseover", hovered(true))
.on("click", clicked())
.on("mouseout", hovered(false));
cell.append("rect")
.attr("id", function(d) { return "rect-" + d.id; })
.attr("width", function(d) { return d.x1 - d.x0; })
.attr("height", function(d) { return d.y1 - d.y0; })
.style("fill", function(d) {
// if(d.data.total_data == 0){
// return "rgb(80, 120, 240)"
// }
return color(d.data[the_metric] / d.data.cap);
})
.style("opacity", function(d){
return 1.0;
// if(d.data.total_data > 0){
// return 0.1;
// }else{
// return 1.0;
// }
})
;
cell.append("clipPath")
.attr("id", function(d) { return "clip-" + d.id; })
.append("use")
.attr("xlink:href", function(d) { return "#rect-" + d.id + ""; });
var label = cell.append("text")
.attr("class", function(d){ return (d.data[the_metric] / d.data.cap < 0.0001?"light":"dark")})
.attr("clip-path", function(d) { return "url(#clip-" + d.id + ")"; })
label
.filter(function(d) { return d.children; })
.selectAll("tspan")
.data(function(d) { return d.id.substring(d.id.lastIndexOf("/") + 1).split(/(?=[A-Z][^A-Z])/g).concat("\xa0" + format(d.data[the_metric]/d.data.cap) + "\xa0" + (d.data.cap/1000000000000).toFixed(2)+"TB"); })
.enter().append("tspan")
.attr("x", function(d, i) { return i ? null : 4; })
.attr("y", 13)
.text(function(d) { return d.replace("[root]/", "/"); })
label
.filter(function(d) { return !d.children; })
.selectAll("tspan")
.data(function(d) { return d.id.substring(d.id.lastIndexOf("/") + 1).split(/(?=[A-Z][^A-Z])/g).concat(format(d.data[the_metric]/d.data.cap) + "\xa0" + (d.data.cap/1000000000000).toFixed(2)+"TB"); })
.enter().append("tspan")
.attr("x", 4)
.attr("y", function(d, i) { return 13 + i * 10; })
.text(function(d) { return d.replace("[root]/", "/"); })
cell.append("title")
.text(function(d) { return d.id + "\n" + format(d.data[the_metric]/d.data.cap) + "\n" + (d.data.cap/1000000000000).toFixed(2)+"TB"; });
jQuery("#close_button").click(function(e){
jQuery("svg#id2").remove();
setTimeout(function(){
jQuery("#close_button").remove();
}, 100)
})
});
}
function hovered(hover) {
return function(d) {
d3.selectAll(d.ancestors().map(function(d) { return d.node; }))
.classed("node--hover", hover)
.select("rect")
.attr("width", function(d) { return d.x1 - d.x0 - hover; })
.attr("height", function(d) { return d.y1 - d.y0 - hover; });
};
}
function handle_output(out_obj, out){
console.log("Command completed!")
console.log(out_obj.content.data['text/plain'])
}
function clicked(){
return function(d){
prompt("Tree delete qq command", "qq fs_delete_tree --path \"" + d.id.replace("[root]", "") + "\"")
var kernel = IPython.notebook.kernel;
var callbacks = {iopub : {'output' : handle_output}};
kernel.execute('del_path = "' + d.id.replace("[root]", "") + '"');
kernel.execute('delete_tree(del_path)', callbacks, {silent:false});
}
}
In [ ]:
get_all_data('gravytrain')
jso = Javascript("draw_treemap('write_data')")
display_javascript(jso)
In [ ]: