First heatmap correct integration
This commit is contained in:
@@ -9,9 +9,9 @@ import foreach from 'lodash.foreach'
|
||||
import xoApi from 'xo-api'
|
||||
import xoServices from 'xo-services'
|
||||
|
||||
import sunburstChart from 'xo-sunburst-d3'
|
||||
// import treemapChart from 'xo-treemap-d3'
|
||||
import weekheatmap from 'xo-week-heatmap'
|
||||
import 'xo-sunburst-d3'
|
||||
// import 'xo-treemap-d3'
|
||||
import'xo-week-heatmap'
|
||||
|
||||
import view from './view'
|
||||
|
||||
@@ -32,28 +32,84 @@ export default angular.module('dashboard.dataviz', [
|
||||
template: view
|
||||
})
|
||||
})
|
||||
.controller('Dataviz',function($scope,$interval){
|
||||
// console.log(' in main ')
|
||||
$scope.charts={
|
||||
heatmap:null
|
||||
}
|
||||
$interval(
|
||||
function(){
|
||||
var values = [];
|
||||
for (var i = 0 ;i < 220 ; i ++){
|
||||
values.push({
|
||||
value:Math.random()*1500-750,
|
||||
date:Date.now()+ i*60*60*1000
|
||||
})
|
||||
}
|
||||
$scope.charts.heatmap = values;
|
||||
},5000
|
||||
)
|
||||
|
||||
|
||||
.filter('underStat', () => {
|
||||
let isUnderStat = object => object.type === 'host' || object.type === 'VM'
|
||||
return objects => filter(objects, isUnderStat)
|
||||
})
|
||||
.controller('DatavizStorageHierarchical', function DatavizStorageHierarchical(xoApi, $scope, $timeout,$interval, $state, bytesToSizeFilter) {
|
||||
.controller('Dataviz', function ($scope, $interval, xo, xoApi) {
|
||||
// console.log(' in main ')
|
||||
this.charts = {
|
||||
heatmap: null
|
||||
}
|
||||
|
||||
this.objects = xoApi.all
|
||||
|
||||
this.prepareMetrics = function (id) {
|
||||
this.metrics = undefined
|
||||
if (!id) {
|
||||
} else {
|
||||
this.loadingMetrics = true
|
||||
xo.vm.refreshStats('e87f47c3-0057-69a2-72c8-6a5df168af43', 2) // 2: week granularity (7 * 24 hours)
|
||||
.then(result => {
|
||||
const metrics = []
|
||||
foreach(result.cpus, (values, metricKey) => {
|
||||
const mapValues = []
|
||||
foreach(values, (value, key) => {
|
||||
mapValues.push({
|
||||
value: +value,
|
||||
date: +result.date[key] * 1000
|
||||
})
|
||||
})
|
||||
metrics.push({
|
||||
key: 'CPU ' + metricKey,
|
||||
values: mapValues
|
||||
})
|
||||
})
|
||||
foreach(result.vifs, (values, metricKey) => {
|
||||
const mapValues = []
|
||||
foreach(values, (value, key) => {
|
||||
mapValues.push({
|
||||
value: +value,
|
||||
date: +result.date[key] * 1000
|
||||
})
|
||||
})
|
||||
metrics.push({
|
||||
key: '#' + Math.floor(metricKey / 2) + ' ' + (metricKey % 2 ? 'out' : 'in'),
|
||||
values: mapValues
|
||||
})
|
||||
})
|
||||
foreach(result.xvds, (values, key) => {
|
||||
const mapValues = []
|
||||
foreach(values, (value, key) => {
|
||||
mapValues.push({
|
||||
value: +value,
|
||||
date: +result.date[key] * 1000
|
||||
})
|
||||
})
|
||||
metrics.push({
|
||||
key: 'xvd' + String.fromCharCode(Math.floor(key / 2) + 97) + ' ' + (key % 2 ? 'write' : 'read'),
|
||||
values: mapValues
|
||||
})
|
||||
})
|
||||
this.loadingMetrics = false
|
||||
this.metrics = metrics
|
||||
})
|
||||
}
|
||||
}
|
||||
// $interval(
|
||||
// function(){
|
||||
// var values = [];
|
||||
// for (var i = 0 ;i < 220 ; i ++){
|
||||
// values.push({
|
||||
// value:Math.random()*1500-750,
|
||||
// date:Date.now()+ i*60*60*1000
|
||||
// })
|
||||
// }
|
||||
// $scope.example = values;
|
||||
// },5000
|
||||
// )
|
||||
})
|
||||
.controller('DatavizStorageHierarchical', function DatavizStorageHierarchical (xoApi, $scope, $timeout, $interval, $state, bytesToSizeFilter) {
|
||||
$scope.charts = {
|
||||
selected: {},
|
||||
data: {
|
||||
@@ -61,65 +117,63 @@ export default angular.module('dashboard.dataviz', [
|
||||
children: []
|
||||
},
|
||||
click: function (d) {
|
||||
if(d.non_clickable){
|
||||
return ;
|
||||
if (d.non_clickable) {
|
||||
return
|
||||
}
|
||||
switch(d.type){
|
||||
switch (d.type) {
|
||||
case 'pool':
|
||||
$state.go('pools_view',{id: d.id});
|
||||
break;
|
||||
$state.go('pools_view', {id: d.id})
|
||||
break
|
||||
case 'host':
|
||||
$state.go('hosts_view',{id: d.id});
|
||||
break;
|
||||
$state.go('hosts_view', {id: d.id})
|
||||
break
|
||||
case 'srs':
|
||||
$state.go('SRs_view',{id: d.id});
|
||||
break;
|
||||
$state.go('SRs_view', {id: d.id})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function populateChartsData() {
|
||||
function populateChartsData () {
|
||||
function populatestorage (root, container_id) {
|
||||
let srs = filter(xoApi.getIndex('srsByContainer')[container_id], (one_srs)=>one_srs.SR_type !== 'iso' && one_srs.SR_type !== 'udev')
|
||||
|
||||
|
||||
function populatestorage(root,container_id){
|
||||
let srs = filter(xoApi.getIndex('srsByContainer')[container_id] , (one_srs)=>one_srs.SR_type !== 'iso' && one_srs.SR_type !== 'udev')
|
||||
|
||||
foreach(srs, function(one_srs){
|
||||
let srs_used_size=0,
|
||||
srs_storage = {
|
||||
foreach(srs, function (one_srs) {
|
||||
let srs_used_size = 0
|
||||
const srs_storage = {
|
||||
name: one_srs.name_label,
|
||||
id: one_srs.id,
|
||||
children: [],
|
||||
size:one_srs.size,
|
||||
textSize:bytesToSizeFilter(one_srs.size),
|
||||
type:'srs'
|
||||
size: one_srs.size,
|
||||
textSize: bytesToSizeFilter(one_srs.size),
|
||||
type: 'srs'
|
||||
}
|
||||
|
||||
root.size+=one_srs.size
|
||||
foreach(one_srs.VDIs, function(vdi_id){
|
||||
root.size += one_srs.size
|
||||
foreach(one_srs.VDIs, function (vdi_id) {
|
||||
let vdi = xoApi.get(vdi_id)
|
||||
if(vdi && vdi.name_label.indexOf('.iso') === -1){
|
||||
let vdi_storage={
|
||||
if (vdi && vdi.name_label.indexOf('.iso') === -1) {
|
||||
let vdi_storage = {
|
||||
name: vdi.name_label,
|
||||
id: vdi_id,
|
||||
size: vdi.size,
|
||||
textSize : bytesToSizeFilter(vdi.size),
|
||||
type:'vdi',
|
||||
non_clickable:true
|
||||
textSize: bytesToSizeFilter(vdi.size),
|
||||
type: 'vdi',
|
||||
non_clickable: true
|
||||
}
|
||||
srs_used_size+=vdi.size
|
||||
srs_used_size += vdi.size
|
||||
srs_storage.children.push(vdi_storage)
|
||||
}
|
||||
})
|
||||
if(one_srs.size > srs_used_size){// some unallocated space
|
||||
if (one_srs.size > srs_used_size) {// some unallocated space
|
||||
srs_storage.children.push({
|
||||
color:'white',
|
||||
color: 'white',
|
||||
name: 'Free',
|
||||
id: 'free'+one_srs.id,
|
||||
size: one_srs.size-srs_used_size ,
|
||||
textSize : bytesToSizeFilter( one_srs.size-srs_used_size),
|
||||
type:'vdi',
|
||||
non_clickable:true
|
||||
id: 'free' + one_srs.id,
|
||||
size: one_srs.size - srs_used_size,
|
||||
textSize: bytesToSizeFilter(one_srs.size - srs_used_size),
|
||||
type: 'vdi',
|
||||
non_clickable: true
|
||||
})
|
||||
}
|
||||
root.children.push(srs_storage)
|
||||
@@ -127,8 +181,6 @@ export default angular.module('dashboard.dataviz', [
|
||||
root.textSize = bytesToSizeFilter(root.size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
let storage_children,
|
||||
pools,
|
||||
hostsByPool,
|
||||
@@ -146,25 +198,25 @@ export default angular.module('dashboard.dataviz', [
|
||||
name: pool.name_label || 'no pool',
|
||||
id: pool_id,
|
||||
children: [],
|
||||
size:0,
|
||||
size: 0,
|
||||
color: !!pool.name_label ? null : 'white',
|
||||
type:'pool',
|
||||
type: 'pool',
|
||||
non_clickable: !pool.name_label
|
||||
}
|
||||
pool_shared_storage = {
|
||||
name: 'Shared',
|
||||
id: 'Shared'+pool_id,
|
||||
id: 'Shared' + pool_id,
|
||||
children: [],
|
||||
size:0,
|
||||
type:'host',
|
||||
non_clickable:true
|
||||
size: 0,
|
||||
type: 'host',
|
||||
non_clickable: true
|
||||
}
|
||||
|
||||
populatestorage(pool_shared_storage,pool_id);
|
||||
populatestorage(pool_shared_storage, pool_id)
|
||||
pool_storage.children.push(pool_shared_storage)
|
||||
pool_storage.size += pool_shared_storage.size
|
||||
|
||||
//by hosts
|
||||
// by hosts
|
||||
|
||||
pool_ram = {
|
||||
name: pool.name_label || 'no pool',
|
||||
|
||||
@@ -47,7 +47,31 @@
|
||||
.grid-cell
|
||||
.panel.panel-default
|
||||
.panel-heading.panel-title
|
||||
weekheatmap(
|
||||
chart-data="charts.heatmap"
|
||||
days="['Sun','Mon','Tue','Wed','Thu','Fri']"
|
||||
)
|
||||
//- i.xo-icon-memory
|
||||
| Stat Heatmap
|
||||
.panel-body.text-center
|
||||
form
|
||||
.grid-sm
|
||||
.grid-cell.grid--gutters
|
||||
.container-fluid
|
||||
.form-group
|
||||
ui-select(ng-model = 'ctrl.selected', ng-change = 'ctrl.prepareMetrics(ctrl.selected.id)')
|
||||
ui-select-match(placeholder = 'Choose an object')
|
||||
i(class = 'xo-icon-{{ $select.selected.type | lowercase }}')
|
||||
| {{ $select.selected.name_label }}
|
||||
ui-select-choices(repeat = 'object in ctrl.objects | underStat | orderBy:["type", "name_label"]')
|
||||
div
|
||||
i(class = 'xo-icon-{{ object.type | lowercase }}')
|
||||
| {{ object.name_label }}
|
||||
span(ng-if="(object.type === 'SR' || object.type === 'VM') && object.$container")
|
||||
| ({{ (object.$container | resolve).name_label }})
|
||||
.grid-cell.grid--gutters
|
||||
.container-fluid
|
||||
span(ng-if = 'ctrl.loadingMetrics')
|
||||
| Loading metrics ...
|
||||
i.fa.fa-circle-o-notch.fa-spin
|
||||
.form-group(ng-if = 'ctrl.metrics')
|
||||
ui-select(ng-model = 'ctrl.selectedMetric')
|
||||
ui-select-match(placeholder = 'Choose a metric') {{ $select.selected.key }}
|
||||
ui-select-choices(repeat = 'metric in ctrl.metrics | orderBy:["key"]') {{ metric.key }}
|
||||
weekheatmap(ng-if = 'ctrl.selectedMetric', chart-data="ctrl.selectedMetric.values", days="['Sun','Mon','Tue','Wed','Thu','Fri']")
|
||||
|
||||
215
app/node_modules/xo-week-heatmap/index.js
generated
vendored
215
app/node_modules/xo-week-heatmap/index.js
generated
vendored
@@ -1,130 +1,127 @@
|
||||
'use strict'
|
||||
|
||||
import angular from 'angular'
|
||||
import d3 from 'd3'
|
||||
import clone from 'lodash.clonedeep'
|
||||
import 'd3'
|
||||
import 'lodash.clonedeep'
|
||||
import foreach from 'lodash.foreach'
|
||||
import pluck from 'lodash.pluck'
|
||||
|
||||
angular
|
||||
.module('xoWebApp.directives')
|
||||
.directive('weekheatmap', heatmap);
|
||||
angular
|
||||
.module('xoWebApp.directives')
|
||||
.directive('weekheatmap', heatmap)
|
||||
|
||||
function heatmap () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {
|
||||
chartData: '=',
|
||||
days: '=',
|
||||
steps: '=',
|
||||
colors: '='
|
||||
},
|
||||
link,
|
||||
// @todo : should load template.html instead of this ugly inline string
|
||||
template: `<div ng-if="matrix.length >0">
|
||||
<table style="border-spacing:2px;border-collapse:separate;margin-bottom:20px;width:100%">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th ng-repeat="hour in hours" style="text-align:center">
|
||||
{{hour}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="(pos,day) in days" style="">
|
||||
<th style="border:none;">{{day}}</th>
|
||||
<td
|
||||
ng-repeat="hour in hours"
|
||||
tooltip="{{matrix[pos][hour].nb > 1 ? \'avg: \'+matrix[pos][hour].value / matrix[pos][hour].nb: \'val: \'+matrix[pos][hour].value}}"
|
||||
style=" transition: background-color 300ms linear; width:38px;height:38px;padding:1px;background-color:{{matrix[pos][hour].color}};border-radius:4px">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="legend">
|
||||
<div class="pull-left" ng-repeat="interval in intervals" style="width:75px;">
|
||||
<div style="background-color:{{interval.color}};height:20px"> </div>
|
||||
<div style="text-align:right;color:gray" > >= {{interval.start}} </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
function heatmap(){
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {
|
||||
chartData: '=',
|
||||
days: '=',
|
||||
steps: '=',
|
||||
colors: '='
|
||||
},
|
||||
link:link,
|
||||
//@todo : should load template.html instead of this ugly inline string
|
||||
template:'<div ng-if="matrix.length >0">\
|
||||
<table style="border-spacing:2px;border-collapse:separate;margin-bottom:20px;width:100%">\
|
||||
<tr>\
|
||||
<th></th>\
|
||||
<th ng-repeat="hour in hours" style="text-align:center">\
|
||||
{{hour}}\
|
||||
</th>\
|
||||
</tr>\
|
||||
<tr ng-repeat="(pos,day) in days" style="">\
|
||||
<th style="border:none;">{{day}}</th>\
|
||||
<td\
|
||||
ng-repeat="hour in hours" \
|
||||
title="{{matrix[pos][hour].nb > 1 ? \'val: \'+round(matrix[pos][hour].value) : \'avg: \'+round(matrix[pos][hour].value)}}"\
|
||||
style=" transition: background-color 300ms linear; width:38px;height:38px;padding:1px;background-color:{{matrix[pos][hour].color}};border-radius:4px">\
|
||||
</td> \
|
||||
</tr>\
|
||||
</table>\
|
||||
<div class="legend">\
|
||||
<div class="pull-left" ng-repeat="interval in intervals" style="width:75px;">\
|
||||
<div style="background-color:{{interval.color}};height:20px"> </div>\
|
||||
<div style="text-align:right;color:gray" > >= {{interval.start}} </div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>',
|
||||
function link (scope, element, attrs) {
|
||||
scope.matrix = []
|
||||
scope.days = scope.days || ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
|
||||
scope.colors = scope.colors || ['#ffffee', '#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58']
|
||||
|
||||
}
|
||||
scope.hours = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
|
||||
scope.matrix =[];
|
||||
scope.days = scope.days || ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
|
||||
scope.round = Math.round;
|
||||
scope.colors = scope.colors || ["white","#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]
|
||||
if (scope.steps) {
|
||||
scope.intervals = []
|
||||
|
||||
scope.hours = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
|
||||
|
||||
if(scope.steps){
|
||||
scope.intervals = [];
|
||||
|
||||
for( var i = 1 ; i < scope.steps.length ;i ++){
|
||||
scope.intervals.push({
|
||||
start:scope.steps[i-1],
|
||||
end:scope.steps[i],
|
||||
color:scope.colors[i]
|
||||
})
|
||||
}
|
||||
}
|
||||
scope.$watch(function(){return scope.chartData}, function (newVal) {
|
||||
// console.log(scope.chartData);
|
||||
if(!scope.chartData){
|
||||
return;
|
||||
}
|
||||
|
||||
scope.min = Math.min.apply(null,pluck(scope.chartData,'value'))
|
||||
scope.max = Math.max.apply(null,pluck(scope.chartData,'value'))
|
||||
if(!scope.steps){
|
||||
scope.intervals = [];
|
||||
foreach(scope.colors, function(color,idx){
|
||||
scope.intervals.push({
|
||||
start:Math.round(scope.min+idx*(scope.max-scope.min)/scope.colors.length),
|
||||
end:Math.round(scope.min+(1+idx)*(scope.max-scope.min)/scope.colors.length),
|
||||
color:color
|
||||
})
|
||||
for (var i = 1; i < scope.steps.length; i++) {
|
||||
scope.intervals.push({
|
||||
start: scope.steps[i - 1],
|
||||
end: scope.steps[i],
|
||||
color: scope.colors[i]
|
||||
})
|
||||
}
|
||||
var matrix=[]
|
||||
foreach(scope.chartData, function(datum){
|
||||
var d = new Date(datum.date),
|
||||
v = datum.value,
|
||||
day = d.getDay(),
|
||||
hour = d.getHours();
|
||||
if(!matrix[day]){
|
||||
matrix[day] = []
|
||||
}
|
||||
if(!matrix[day][hour]){
|
||||
matrix[day][hour] = {
|
||||
value:v,
|
||||
nb:1
|
||||
}
|
||||
}else{
|
||||
matrix[day][hour].value = (v+matrix[day][hour].value)/(matrix[day][hour].nb+1)
|
||||
matrix[day][hour].nb ++;
|
||||
}
|
||||
matrix[day][hour].color = color( matrix[day][hour].value)
|
||||
|
||||
})
|
||||
scope.matrix = matrix;
|
||||
|
||||
}
|
||||
scope.$watch(function () {return scope.chartData}, function (newVal) {
|
||||
// console.log(scope.chartData);
|
||||
displayData()
|
||||
})
|
||||
|
||||
function color(value){
|
||||
var color = null;
|
||||
foreach(scope.intervals, function(interval,pos){
|
||||
if(interval.start <= value && interval.end > value){
|
||||
color = interval.color;
|
||||
}
|
||||
function displayData () {
|
||||
if (!scope.chartData) {
|
||||
return
|
||||
}
|
||||
|
||||
scope.min = Math.min.apply(null, pluck(scope.chartData, 'value'))
|
||||
scope.max = Math.max.apply(null, pluck(scope.chartData, 'value'))
|
||||
if (!scope.steps) {
|
||||
scope.intervals = []
|
||||
foreach(scope.colors, function (color, idx) {
|
||||
scope.intervals.push({
|
||||
start: scope.min + idx * (scope.max - scope.min) / scope.colors.length,
|
||||
end: scope.min + (1 + idx) * (scope.max - scope.min) / scope.colors.length,
|
||||
color: color
|
||||
})
|
||||
})
|
||||
return color;
|
||||
}
|
||||
var matrix = []
|
||||
foreach(scope.chartData, function (datum) {
|
||||
const d = new Date(datum.date)
|
||||
const v = datum.value
|
||||
const day = d.getDay()
|
||||
const hour = d.getHours()
|
||||
if (!matrix[day]) {
|
||||
matrix[day] = []
|
||||
}
|
||||
if (!matrix[day][hour]) {
|
||||
matrix[day][hour] = {
|
||||
value: v,
|
||||
nb: 1
|
||||
}
|
||||
} else {
|
||||
matrix[day][hour].value += v
|
||||
matrix[day][hour].nb++
|
||||
}
|
||||
matrix[day][hour].color = color(matrix[day][hour].value / matrix[day][hour].nb)
|
||||
|
||||
})
|
||||
scope.matrix = matrix
|
||||
}
|
||||
|
||||
function color (value) {
|
||||
var color = null
|
||||
foreach(scope.intervals, function (interval, pos) {
|
||||
if (interval.start <= value && value < interval.end || (pos === scope.intervals.length - 1 && value === interval.end)) {
|
||||
color = interval.color
|
||||
}
|
||||
})
|
||||
return color
|
||||
}
|
||||
|
||||
|
||||
|
||||
displayData()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user