Update monit APIs. Fix corresponding bugs for charts.

Also update d3 lib

Change-Id: I0ece6b311e8fd073f7ef6ebc8af1f81c1943f072
This commit is contained in:
jiahuay 2014-09-17 18:39:08 -07:00
parent 7622386ae8
commit 4a425f596f
15 changed files with 1367 additions and 1324 deletions

View File

@ -84,6 +84,7 @@ ganttchart .chart {
ganttchart text {
font-family: "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif !important;
font-size: 14px;
}
ganttchart .axis path, ganttchart .axis line {

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,8 @@ var app = angular.module('compass', [
app.constant('settings', {
apiUrlBase: '/api',
metadataUrlBase: 'data',
monitoringUrlBase: 'http://metrics-api/monit/api/v1'
//monitoringUrlBase: 'http://metrics-api/monit/api/v1'
monitoringUrlBase: '/monit/api/v1'
});
app.config(function($stateProvider, $urlRouterProvider) {

File diff suppressed because one or more lines are too long

View File

@ -12,10 +12,34 @@
<b class="arrow fa fa-angle-down"></b>
</a>
<ul class="submenu" ng-show="isMonitoringNavOpen" style="display: block">
<li ng-repeat="nav in monitoringNav" ng-class="{active:state.includes('{{nav.name}}')}">
<a ui-sref="{{nav.name}}">
<li ng-class="{active:state.includes('cluster.monitoring.overview')}">
<a ui-sref="cluster.monitoring.overview">
<i class="menu-icon fa fa-caret-right"></i>
{{nav.display}}
Overview
</a>
</li>
<li ng-class="{active:state.includes('cluster.monitoring.topology')}">
<a ui-sref="cluster.monitoring.topology">
<i class="menu-icon fa fa-caret-right"></i>
Topology
</a>
</li>
<li ng-class="{active:state.includes('cluster.monitoring.alerts')}">
<a ui-sref="cluster.monitoring.alerts">
<i class="menu-icon fa fa-caret-right"></i>
Alerts
</a>
</li>
<li ng-class="{active:state.includes('cluster.monitoring.metrics')}">
<a ui-sref="cluster.monitoring.metrics">
<i class="menu-icon fa fa-caret-right"></i>
Metrics
</a>
</li>
<li ng-class="{active:state.includes('cluster.monitoring.charts')}">
<a ui-sref="cluster.monitoring.charts">
<i class="menu-icon fa fa-caret-right"></i>
Dashboards
</a>
</li>
</ul>

View File

@ -65,7 +65,7 @@ angular.module('compass.cluster', [
});
})
.controller('clusterCtrl', function($scope, $state, dataService, stateService, $stateParams) {
.controller('clusterCtrl', function($scope, $state, dataService, $stateParams) {
$scope.clusterId = $stateParams.id;
$scope.state = $state;
@ -73,11 +73,6 @@ angular.module('compass.cluster', [
$scope.clusterInfo = data;
});
dataService.getMonitoringNav().success(function(data) {
$scope.monitoringNav = data;
stateService.addStates($scope.monitoringNav);
});
}).directive('clusternav', function($timeout) {
return {
restrict: 'EAC',

View File

@ -4,13 +4,13 @@
</h1>
</div>
<div class="col-lg-10" ng-controller="alertsCtrl" style="width: 100%;">
<div class="col-lg-10" style="width: 100%;">
<tabset style="width: 100%;">
<tab>
<tab-heading>
<i class="ace-icon fa fa-bell"></i>Alarms
</tab-heading>
<form class="form-inline">
<!--form class="form-inline">
<select class="form-control" ng-model="renderer" ng-change="rendererChanged(renderer)">
<option ng-repeat="render in renderers" value="{{render}}">{{render}}</option>
</select>
@ -18,9 +18,9 @@
<option ng-repeat="uri in uris" value="{{uri.v}}">{{uri.displayName}}</option>
</select>
<button class="btn btn-primary" ng-click="changeSeriesData(0)">Generate Alarm Report</button>
</form>
</form-->
<div class="panel-body" id="alarmsGraphContainer">
<ganttchart data="alerts" hosts="hosts"></ganttchart>
<ganttchart data="alerts" dataready="alertDataReady"></ganttchart>
</div>
</tab>
<tab>

View File

@ -4,7 +4,7 @@
</h1>
</div>
<div class="col-lg-10" style="width: 100%;" ng-controller="metricsCtrl">
<div class="col-lg-10" style="width: 100%;">
<tabset class="ng-isolate-scope" style="width: 100%;">
<tab heading="Metrics">
<!--form class="form-inline ng-valid">
@ -21,8 +21,8 @@
<div class="col-md-4 col-sm-6">
<h3>
Metrics Browser
<a href="" class="btn btn-default pull-right" ng-click="collapseAll()">Collapse all</a>
<a href="" class="btn btn-default pull-right margin-right-5" ng-click="expandAll()">Expand all</a>
<a href="" class="btn btn-default btn-xs pull-right" ng-click="collapseAll()">Collapse all</a>
<a href="" class="btn btn-default btn-xs pull-right margin-right-5" ng-click="expandAll()">Expand all</a>
</h3>
<!-- Nested node template -->
@ -61,6 +61,7 @@
interactive="true"
useInteractiveGuideline="true"
toolTipContent="toolTipContentFunction()"
xAxisTickFormat="xAxisTickFormatFunction()"
margin="{left:50,top:50,bottom:50,right:50}"
forceY="[0]"
showLegend="true"

View File

@ -15,461 +15,148 @@ angular.module('compass.monitoring', [
templateUrl: 'src/app/monitoring/cluster-monitoring.tpl.html',
authenticate: true
})
.state('cluster.monitoring.overview', {
url: '/overview',
controller: 'moniOverviewCtrl',
templateUrl: 'src/app/monitoring/overview.tpl.html',
authenticate: true
})
.state('cluster.monitoring.topology', {
url: '/topology',
controller: 'topologyCtrl',
templateUrl: 'src/app/monitoring/topology.tpl.html',
authenticate: true
})
.state('cluster.monitoring.alerts', {
url: '/alerts',
controller: 'alertsCtrl',
templateUrl: 'src/app/monitoring/alerts.tpl.html',
authenticate: true
})
.state('cluster.monitoring.metrics', {
url: '/metrics',
controller: 'metricsCtrl',
templateUrl: 'src/app/monitoring/metrics.tpl.html',
authenticate: true
})
.state('cluster.monitoring.charts', {
url: '/charts',
templateUrl: 'src/app/monitoring/charts.tpl.html',
authenticate: true
})
})
.controller('alertsCtrl', ['$scope',
function($scope) {
$scope.options = {
renderer: 'area'
};
$scope.alertsData = {
"id": "server-1.huawei.com",
"name": "server-1.huawei.com",
"resource": "hosts",
"state": "running",
"type": "server",
"metrics": [],
"alarms": [{
"id": "critical",
"name": "critical",
"data": [{
"start": 1406831282409,
"end": 1406870037149
}, {
"start": 1406745382748,
"end": 1406761927670
}]
}, {
"id": "minor",
"name": "minor",
"data": [{
"start": 1406873957790,
"end": 1406886655198
}, {
"start": 1406774590378,
"end": 1406850781190
}]
}, {
"id": "positive",
"name": "positive",
"data": [{
"start": 1406873957790,
"end": 1406886655198
}, {
"start": 1406774590378,
"end": 1406850781190
}]
}, {
"id": "info",
"name": "info",
"data": [{
"start": 1406873957790,
"end": 1406886655198
}, {
"start": 1406774590378,
"end": 1406850781190
}]
}]
};
$scope.alerts = [{
"startDate": new Date("Sun Dec 09 01:36:45 EST 2012"),
"endDate": new Date("Sun Dec 09 02:36:45 EST 2012"),
"name": "os-controller",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 04:56:32 EST 2012"),
"endDate": new Date("Sun Dec 09 06:35:47 EST 2012"),
"name": "os-db-node",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 06:29:53 EST 2012"),
"endDate": new Date("Sun Dec 09 06:34:04 EST 2012"),
"name": "os-db-node",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 05:35:21 EST 2012"),
"endDate": new Date("Sun Dec 09 06:21:22 EST 2012"),
"name": "os-controller",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 05:00:06 EST 2012"),
"endDate": new Date("Sun Dec 09 05:05:07 EST 2012"),
"name": "os-keystone",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 03:46:59 EST 2012"),
"endDate": new Date("Sun Dec 09 04:54:19 EST 2012"),
"name": "os-image",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 04:02:45 EST 2012"),
"endDate": new Date("Sun Dec 09 04:48:56 EST 2012"),
"name": "os-image",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 03:27:35 EST 2012"),
"endDate": new Date("Sun Dec 09 03:58:43 EST 2012"),
"name": "os-controller",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 01:40:11 EST 2012"),
"endDate": new Date("Sun Dec 09 03:26:35 EST 2012"),
"name": "os-db-node",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 03:00:03 EST 2012"),
"endDate": new Date("Sun Dec 09 03:09:51 EST 2012"),
"name": "os-keystone",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 01:21:00 EST 2012"),
"endDate": new Date("Sun Dec 09 02:51:42 EST 2012"),
"name": "os-mq",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 01:08:42 EST 2012"),
"endDate": new Date("Sun Dec 09 01:33:42 EST 2012"),
"name": "os-keystone",
"status": "CRITICAL"
}, {
"startDate": new Date("Sun Dec 09 00:27:15 EST 2012"),
"endDate": new Date("Sun Dec 09 00:54:56 EST 2012"),
"name": "os-controller",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 00:29:48 EST 2012"),
"endDate": new Date("Sun Dec 09 00:44:50 EST 2012"),
"name": "os-image",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 07:39:21 EST 2012"),
"endDate": new Date("Sun Dec 09 07:43:22 EST 2012"),
"name": "os-image",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 07:00:06 EST 2012"),
"endDate": new Date("Sun Dec 09 07:05:07 EST 2012"),
"name": "os-compute2",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 08:46:59 EST 2012"),
"endDate": new Date("Sun Dec 09 09:54:19 EST 2012"),
"name": "os-compute1",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 09:02:45 EST 2012"),
"endDate": new Date("Sun Dec 09 09:48:56 EST 2012"),
"name": "os-controller",
"status": "WARNING"
}, {
"startDate": new Date("Sun Dec 09 08:27:35 EST 2012"),
"endDate": new Date("Sun Dec 09 08:58:43 EST 2012"),
"name": "os-compute2",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 08:40:11 EST 2012"),
"endDate": new Date("Sun Dec 09 08:46:35 EST 2012"),
"name": "os-mq",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 08:00:03 EST 2012"),
"endDate": new Date("Sun Dec 09 08:09:51 EST 2012"),
"name": "os-compute2",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 10:21:00 EST 2012"),
"endDate": new Date("Sun Dec 09 10:51:42 EST 2012"),
"name": "os-compute1",
"status": "SUCCESSFUL"
}, {
"startDate": new Date("Sun Dec 09 11:08:42 EST 2012"),
"endDate": new Date("Sun Dec 09 11:33:42 EST 2012"),
"name": "os-network",
"status": "CRITICAL"
}, {
"startDate": new Date("Sun Dec 09 12:27:15 EST 2012"),
"endDate": new Date("Sun Dec 09 12:54:56 EST 2012"),
"name": "os-mq",
"status": "WARNING"
}, {
"startDate": new Date("Sat Dec 08 23:12:24 EST 2012"),
"endDate": new Date("Sun Dec 09 00:26:13 EST 2012"),
"name": "os-controller",
"status": "UNKNOWN"
}];
$scope.hosts = ["os-controller", "os-db-node", "os-keystone", "os-network", "os-image", "os-mq", "os-compute1", "os-compute2"];
.controller('moniOverviewCtrl', function($scope, dataService, $stateParams) {
var clusterId = $stateParams.id;
$scope.goDash = function(locs) {
//alert(locs);
setTimeout(function() {
document.getElementById('dashboards').src = "/dash/#/dashboard/file/" + locs;
}, 600);
}
])
$scope.moniOverviewData = [];
dataService.monitorOverview(clusterId).success(function(data) {
$scope.moniOverviewData = data;
}).error(function(response) {
// TODO: error handle
});
/*
.value("graphConfigurations", {
apis: {
'simulation': {
uri: { v : "/monit/api/1.0.0/", displayName: 'Testing Cluster'},
metrics: true, alarms: true, topos: true,
},
'simulation.alarms': {
uri: "/monit/api/1.0.0/hosts/uc-server-2.huawei.com/alarms"
},
'192.168.255.85.hostgroup': {
uri: { v : '/monit/api/hostgroup/host1/metric/cpu.0.cpu.system.value',
displayName: 'Cluster 1'},
metrics: true, alarms: true
},
'192.168.255.85.topology': {
uri: { v: '/monit/api/topologies/1', displayName: 'Cluster 1'}, topos:true
}
},
renderers:{
line: {
name: "line",
metrics: true,
view: com.huawei.compass.LineGraph,
model: com.huawei.compass.LineGraphModel
},
area: {
name: "area",
metrics: true,
view: com.huawei.compass.AreaGraph,
model: com.huawei.compass.AreaGraphModel
},
sparkline: {
name: "sparkline",
metrics: true,
view: com.huawei.compass.SparkGraph,
model: com.huawei.compass.SparkGraphModel
},
all: {
name: "all",
alarms: true,
view: com.huawei.compass.BlockGraph,
model: com.huawei.compass.BlockGraphModel,
value:""
},
critical: {
name: "critical",
alarms: true,
view: com.huawei.compass.BlockGraph,
model: com.huawei.compass.BlockGraphModel,
value:"critical"
},
warning: {
name: "warning",
alarms: true,
view: com.huawei.compass.BlockGraph,
model: com.huawei.compass.BlockGraphModel,
value:"minor"
},
success: {
name: "success",
alarms: true,
view: com.huawei.compass.BlockGraph,
model: com.huawei.compass.BlockGraphModel,
value:"positive"
},
unknown: {
name: "unknown",
alarms: true,
view: com.huawei.compass.BlockGraph,
model: com.huawei.compass.BlockGraphModel,
value:"info"
},
tree: {
name: "tree",
topos: true,
view: com.huawei.compass.TreeGraph,
model: com.huawei.compass.TreeGraphModel,
value:"info"
}
},
styles:{
"switch":"/assets/images/switch.png",
"server":"/assets/images/server.png",
"service":"/assets/images/switch.png",
"running":"#669900",
"error" : "#CC0000",
"warning" : "#ffff33",
"unknown" : "#33b5e5"
}
})
*/
.controller('alarmsCtrl', ['$scope', '$http', 'graphConfigurations', 'graphService',
function($scope, $http, graphConfigurations, graphService) {
$scope.uris = graphService.getUris(graphConfigurations, "alarms");
$scope.renderers = graphService.getRenderers(graphConfigurations, "alarms");
$scope.rendererChanged = graphService.getRendererListener($scope);
$scope.uriChanged = graphService.getUriListener($scope);
$scope.alertsData = {
"id": "server-1.huawei.com",
"name": "server-1.huawei.com",
"resource": "hosts",
"state": "running",
"type": "server",
"metrics": [],
"alarms": [{
"id": "critical",
"name": "critical",
"data": [{
"start": 1406831282409,
"end": 1406870037149
}, {
"start": 1406745382748,
"end": 1406761927670
}]
}, {
"id": "minor",
"name": "minor",
"data": [{
"start": 1406873957790,
"end": 1406886655198
}, {
"start": 1406774590378,
"end": 1406850781190
}]
}, {
"id": "positive",
"name": "positive",
"data": [{
"start": 1406873957790,
"end": 1406886655198
}, {
"start": 1406774590378,
"end": 1406850781190
}]
}, {
"id": "info",
"name": "info",
"data": [{
"start": 1406873957790,
"end": 1406886655198
}, {
"start": 1406774590378,
"end": 1406850781190
}]
}]
};
$scope.changeSeriesData = graphService.getDataListener($scope, function(data) {
var uri = $scope.uri;
console.log("URI is .....", uri);
var renderer = $scope.renderer;
var isValidApi = (uri == graphConfigurations.apis["192.168.255.85.hostgroup"].uri.v);
if (isValidApi)
for (var i = 0; i < data.length; i++) data[i].name = data[i].id;
var gFormatter = com.huawei.compass.formatter.hourminute;
var gName = "Alarms";
var gData = isValidApi ? data : data.groups[0].hosts;
// TODO(jiahua): graphConfigurations.renderers[renderer] is undefined
var gProperty = graphConfigurations.renderers[renderer].value;
var view = new graphConfigurations.renderers[renderer].view({
name: gName,
model: new graphConfigurations.renderers[renderer].model({
data: gData,
propertyKey: "alarms",
propertyName: gProperty
}).model,
formatter: gFormatter,
yFormatter: d3.format('.3e'),
css: "chart-title",
width: 500,
height: 500,
css: {
"header": "chart-title",
"critical": "alarm-critical",
"major": "alarm-major",
"minor": "alarm-minor",
"info": "alarm-info",
"positive": "alarm-positive"
},
listener: function(d) {
alert("Alarm Selected (" + d.startDate + ", " + d.endDate + ")");
}
});
$.graphs.get("alarmsGraphContainer").innerHTML = "";
$.graphs.get("alarmsGraphContainer").appendChild(view);
.controller('topologyCtrl', function($scope, dataService, $stateParams) {
var clusterId = $stateParams.id;
$scope.physicalTopoData = {};
$scope.physicalTopoDataReady = "false";
dataService.monitorTopology(clusterId).success(function(data) {
$scope.physicalTopoData = data;
$scope.physicalTopoDataReady = "true";
}).error(function(response) {
// TODO: error handling
});
$scope.logicalTopoData = {};
$scope.logicalTopoDataReady = "false";
dataService.monitorServiceTopology(clusterId).success(function(data) {
$scope.logicalTopoData = data;
$scope.logicalTopoDataReady = "true";
}).error(function(response) {
// TODO: error handling
});
})
.controller('alertsCtrl', function($scope, dataService, $stateParams) {
var clusterId = $stateParams.id;
$scope.alerts = [];
$scope.alertDataReady = "false";
dataService.monitorAlarms(clusterId).success(function(data) {
$scope.alerts = data;
$scope.alertDataReady = "true";
}).error(function(response) {
//TODO: error handling
});
})
.controller('metricsCtrl', function($scope, dataService, $stateParams) {
var clusterId = $stateParams.id;
$scope.metricsTree = [];
dataService.monitorMetricsTree().success(function(data) {
$scope.metricsTree = data;
}).error(function(response) {
// TODO
});
$scope.metrics = [];
dataService.monitorMetrics().success(function(data) {
$scope.metrics = data;
}).error(function(response) {
// TODO
});
$scope.metricsData = [];
$scope.generate = function(node) {
console.log(node);
dataService.monitorClusterMetric(clusterId, node.title).success(function(data) {
$scope.metricsData = data;
}).error(function(response) {
// TODO
});
}
])
};
.controller('metricsCtrl', function($scope, dataService) {
// For Angular UI Tree
$scope.toggle = function(scope) {
scope.toggle();
scope.toggle();
};
var getRootNodesScope = function() {
return angular.element(document.getElementById("tree-root")).scope();
return angular.element(document.getElementById("tree-root")).scope();
};
$scope.collapseAll = function() {
var scope = getRootNodesScope();
scope.collapseAll();
var scope = getRootNodesScope();
scope.collapseAll();
};
$scope.expandAll = function() {
var scope = getRootNodesScope();
scope.expandAll();
var scope = getRootNodesScope();
scope.expandAll();
};
$scope.metricsTree = [];
dataService.getMetricsTreeNodes().success(function(data) {
$scope.metricsTree = data;
}).error(function(response) {
$scope.metricsTree = [];
});
$scope.generate = function(node) {
console.log(node);
// call metric api here to get data for chart
};
$scope.metricsData = [{
"key": "Series 1",
"values": [
[1, 0],
[2, 6],
[3, 5],
[4, 11],
[5, 5]
]
}, {
"key": "Series 2",
"values": [
[1, 0],
[2, 10],
[3, 5],
[4, 5],
[5, 0]
]
}, {
"key": "Series 3",
"values": [
[1, 0],
[2, 6],
[3, 5],
[4, 11],
[5, 5]
]
}, {
"key": "Series 4",
"values": [
[1, 7],
[2, 14],
[3, 14],
[4, 23],
[5, 16]
]
}];
$scope.xAxisTickFormat = function() {
// For NVD3 Line Chart
$scope.xAxisTickFormatFunction = function() {
return function(d) {
return d3.time.format('%x')(new Date(d));
}
@ -477,7 +164,6 @@ angular.module('compass.monitoring', [
$scope.toolTipContentFunction = function() {
return function(key, x, y, e, graph) {
console.log('tooltip content');
return 'Super New Tooltip' +
'<h1>' + key + '</h1>' +
'<p>' + y + ' at ' + x + '</p>'
@ -494,369 +180,4 @@ angular.module('compass.monitoring', [
}
*/
})
.controller('moniOverviewCtrl', function($scope) {
$scope.goDash = function(locs){
//alert(locs);
setTimeout(function(){document.getElementById('dashboards').src = "/dash/#/dashboard/file/"+locs;},600);
}
$scope.moniOverviewData = [{
"name": "cluster_summary",
"display_name": "Cluster Summary",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Metrics%20Dashboard",
"state": "ok"
}, {
"name": "controller",
"display_name": "Controller",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Controller",
"state": "error"
}, {
"name": "alert",
"display_name": "Alert",
"base_url": "/#/cluster/2/monitoring/alerts",
"dash": "",
"state": "",
"alerts": [{
"type": "critical",
"name": "os-keystone"
}, {
"type": "warning",
"name": "os-mq",
}, {
"type": "warning",
"name": "os-db-node"
}, {
"type": "critical",
"name": "os-network"
}, {
"type": "warning",
"name": "os-keystone"
}, {
"type": "warning",
"name": "os-compute2"
}]
}, {
"name": "compute",
"display_name": "Compute",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Compute",
"state": "ok"
}, {
"name": "security",
"display_name": "Security",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Security",
"state": "warning"
}, {
"name": "database",
"display_name": "Database",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Database",
"state": "warning"
}, {
"name": "image",
"display_name": "Image",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Image",
"state": "warning"
}, {
"name": "store",
"display_name": "Store",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Storage",
"state": "ok"
}, {
"name": "messagebus",
"display_name": "Message Bus",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Message%20Bus",
"state": "ok"
}, {
"name": "processes",
"display_name": "Processes",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Processes",
"state": "ok"
}, {
"name": "monitoring",
"display_name": "Monitoring",
"base_url": "/#/cluster/2/monitoring/charts",
"dash": "Metrics%20Dashboard",
"state": "ok"
}, {
"name": "users",
"display_name": "Users",
"base_url": "/#/users",
"dash": "",
"state": "ok"
}];
$scope.logicalTopoData = {
"name": "cluster",
"children": [{
"name": "compute",
"state": "error",
"children": [{
"name": "os-compute1",
"state": "error",
"children": [{
"name": "nova-compute",
"state": "ok"
}, {
"name": "nova-api-metadata",
}, {
"name": "nova-consoleauth",
"state": "ok"
}, {
"name": "ceilometer-agent-compute",
"state": "ok"
}, {
"name": "neutron-openvswitch-agent",
"state": "ok"
}, {
"name": "nova-novncproxy",
"state": "ok"
}]
}, {
"name": "os-compute2",
"state": "error",
"children": [{
"name": "nova-compute",
"state": "ok"
}, {
"name": "nova-api-metadata",
"state": "ok"
}, {
"name": "nova-consoleauth",
"state": "ok"
}, {
"name": "ceilometer-agent-compute",
"state": "ok"
}, {
"name": "neutron-openvswitch-agent",
"state": "ok"
}, {
"name": "nova-novncproxy",
"state": "ok"
}]
}]
}, {
"name": "controller",
"state": "ok",
"children": [{
"name": "os-controller",
"state": "ok",
"children": [{
"name": "glance-api",
"state": "ok"
}, {
"name": "glance-registry",
"state": "ok"
}, {
"name": "heat-api",
"state": "ok"
}, {
"name": "heat-api-cfn",
"state": "ok"
}, {
"name": "heat-engine",
"state": "ok"
}, {
"name": "keystone-all",
"state": "ok"
}, {
"name": "neutron-server",
"state": "ok"
}, {
"name": "nova-api",
"state": "ok"
}, {
"name": "nova-cert",
"state": "ok"
}, {
"name": "nova-conductor",
"state": "ok"
}, {
"name": "nova-consoleauth",
"state": "ok"
}, {
"name": "nova-novncproxy",
"state": "ok"
}, {
"name": "nova-objectstore",
"state": "ok"
}, {
"name": "nova-scheduler",
"state": "ok"
}, {
"name": "ceilometer-api",
"state": "ok"
}, {
"name": "ceilometer-agent-central",
"state": "ok",
"children": [{
"name": "cpu"
}, {
"name": "memory"
}]
}, {
"name": "ceilometer-collector",
"state": "ok",
"children": [{
"name": "process count"
}, {
"name": "load"
}]
}]
}]
}, {
"name": "network",
"state": "warning",
"children": [{
"name": "os-network",
"state": "warning",
"children": [{
"name": "openvswitch",
"state": "ok",
"children": [{
"name": "interface tx"
}, {
"name": "interface xx"
}]
}, {
"name": "neutron",
"state": "ok",
"children": [{
"name": "process status"
}, {
"name": "memory"
}]
}]
}]
}, {
"name": "image",
"state": "warning",
"children": [{
"name": "os-image",
"state": "warning",
"children": [{
"name": "glance",
"state": "ok",
"children": [{
"name": "image count"
}, {
"name": "process status"
}]
}, {
"name": "cinder-volume",
"state": "ok",
"children": [{
"name": "storage capacity"
}, {
"name": "process status"
}]
}, {
"name": "cinder-api",
"state": "ok"
}, {
"name": "cinder-scheduler",
"state": "ok"
}]
}]
}, {
"name": "database",
"state": "warning",
"children": [{
"name": "os-db-node",
"children": [{
"name": "mysql",
"state": "ok",
"children": [{
"name": "queries per second"
}, {
"name": "response time"
}]
}, {
"name": "redis",
"state": "ok",
"children": [{
"name": "queries per second"
}, {
"name": "response time"
}]
}]
}]
}, {
"name": "message queue",
"state": "ok",
"children": [{
"name": "os-mq",
"children": [{
"name": "rabbit-mq",
"state": "ok",
"children": [{
"name": "process count"
}, {
"name": "messages tx"
}]
}, {
"name": "mysql",
"state": "ok",
"children": [{
"name": "status"
}, {
"name": "connection"
}]
}]
}]
}]
};
$scope.physicalTopoData = {
"name": "compass-dc1",
"children": [{
"name": "172.29.8.40",
"state": "warning",
"children": [{
"name": "os-controller",
"state": "warning"
}, {
"name": "os-db-node",
"state": "ok"
}, {
"name": "os-keystone",
"state": "ok"
}, {
"name": "os-network",
"state": "ok"
}, {
"name": "os-image",
"state": "ok"
}, {
"name": "os-mq",
"state": "ok"
}, {
"name": "os-compute1",
"state": "warning"
}, {
"name": "os-compute2",
"state": "ok"
}]
}]
};
$scope.serverCount = 8;
//$scope.topoDropDown = 'service';
})
})

View File

@ -3,8 +3,7 @@
Cluster Overview
</h1>
</div>
<div class="container row side-padding-0 top-padding-0" ng-controller="moniOverviewCtrl">
<div class="container row side-padding-0 top-padding-0">
<div class="toc info-icons">
<ul class="tiles">
<li ng-repeat="tile in moniOverviewData">
@ -49,22 +48,4 @@
</li>
</ul>
</div>
</div>
<!--div class="row side-padding-10 top-padding-10" ng-controller="moniOverviewCtrl">
<div class="col-xs-12">
<div ng-repeat="role in logicalTopoData.children" class="well pull-left">
<span>{{role.name}}</span>
<span>{{role.children.length}}</span>
<br/>{{role.state}}
</div>
</div>
<div class="col-xs-12 top-padding-10" style="height: 300px">
<nvd3-stacked-area-chart data="exampleData" id="exampleId" showXAxis="true" showYAxis="true" tooltips="true" interactive="true" useInteractiveGuideline="true" toolTipContent="toolTipContentFunction()" forceY="[0]">
<svg></svg>
</nvd3-stacked-area-chart>
</div>
</div-->

View File

@ -4,16 +4,17 @@
</h1>
</div>
<div class="col-lg-10" ng-controller="moniOverviewCtrl" style="width: 100%;">
<div class="col-lg-10" style="width: 100%;">
<tabset class="ng-isolate-scope" style="width: 100%;">
<tab heading="Physical View">
Layer 2/3 Topology
<treechart id="physicalTopo" data="physicalTopoData" count="serverCount" style="width: 100%; height: 1280px; transition: 0.4s all ease-out;"></treechart>
Layer 2/3 Topology
<treechart id="physicalTopo" data="physicalTopoData" dataready="physicalTopoDataReady" style="width: 100%; height: 1280px; transition: 0.4s all ease-out;"></treechart>
</tab>
<tab heading="Service View">
Service Diagram
<circlepacking id="logicalTopo" data="logicalTopoData" style="display: block; margin-left: auto; margin-right: auto; width: 100%; height: 700px; transition: 0.4s all ease-out;"></circlepacking>
Service Diagram
<circlepacking id="logicalTopo" data="logicalTopoData" dataready="logicalTopoDataReady" style="display: block; margin-left: auto; margin-right: auto; width: 100%; height: 700px; transition: 0.4s all ease-out;"></circlepacking>
</tab>
</tabset>
</div>
</div>

View File

@ -1,6 +1,7 @@
angular.module('compass.services', [])
// stateService is used for dynamically add/edit state
/*
.service('stateService', ['$state',
function($state) {
this.addStates = function(pendingStates) {
@ -25,6 +26,7 @@ angular.module('compass.services', [])
}
}
])
*/
// dataService is used for http calls
.service('dataService', ['$http', 'settings',
@ -208,134 +210,95 @@ angular.module('compass.services', [])
this.deleteHost = function(id) {
return $http.delete(settings.apiUrlBase + '/hosts/' + id);
};
this.getMetricsTreeNodes = function() {
return $http.get(settings.metadataUrlBase + '/metrics_tree.json');
};
this.monitorHosts = function() {
// /monit/api/hosts
return $http.get(settings.monitoringUrlBase + '/hosts');
/*
return $http.jsonp(settings.monitoringUrlBase + '/hosts' + settings.jsonpSuffix);
var url = settings.monitoringUrlBase + '/hosts' ;
$http.jsonp(url).success(function (data, status, headers, config) {
console.log(data);
return data;
}).error(function (data, status, headers, config) {
//this always gets called
console.log(status);
deferred.reject(status);
return 'undefined';
});
this.deleteHost = function(clusterId, hostId) {
return $http.delete(settings.apiUrlBase + '/clusters/' + clusterId + '/hosts/' + hostId);
};
*/
this.monitorHosts = function(id) {
// This differ from the main hosts API because it has status/alert information
// /monit/api/cluster/<id>/hosts
return $http.get(settings.monitoringUrlBase + '/clusters/' + id + '/hosts');
};
this.monitorRsHostGroupMetric = function(groupName, metricName) {
// /monit/api/rshostgroup/<hostgroup>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/rshostgroup/' + groupName + '/metric/' + metricName);
};
this.monitorAlerts = function() {
// /monit/api/alarms
//return $http.get(settings.monitoringUrlBase + '/alarms');
var url = settings.monitoringUrlBase + '/alarms' ;
return $http.get(settings.monitoringUrlBase + '/hosts/');
};
this.monitorTest = function() {
return $http.get(settings.monitoringUrlBase + '/');
};
this.monitorProxy = function(px_url) {
/* this.monitorProxy = function(px_url) {
// Leave for now may delete later
// /monit/api/proxy/<path:url>
return $http.get(settings.monitoringUrlBase + '/proxy/' + px_url);
};
*/
this.monitorMetrics = function() {
// This returns a flat json list of metrics currently or historically have been collected
// /monit/api/metrics
return $http.get(settings.monitoringUrlBase + '/metrics');
};
this.monitorMetricsTree = function() {
// This will also order the metrics in a tree
// /monit/api/metricstree
return $http.get(settings.monitoringUrlBase + '/metricstree');
};
this.monitorHostMetric = function(hostName, metricName) {
// /monit/api/host/<hostname>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/host/' + hostName + '/metric/' + metricName);
this.monitorHostMetric = function(clusterId, hostName, metricName) {
// Returns a single metric for a cluster host
// /monit/api/cluster/<id>/host/<hostname>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/hosts/' + hostName + '/metric/' + metricName);
};
this.monitorHostGroupMetric = function(groupName, metricName) {
// /monit/api/hostgroup/<hostgroup>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/hostgroup/' + groupName + '/metric/' + metricName);
this.monitorHostGroupMetric = function(clusterId, groupName, metricName) {
// Returns a single metric for a cluster hostgroup
// /monit/api/cluster/<id>/hostgroup/<hostgroup>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/hostgroups/' + groupName + '/metric/' + metricName);
};
this.monitorRsHostMetric = function(hostName, metricName) {
// /monit/api/rshost/<hostname>/metric/<metricname>
var url = settings.monitoringUrlBase + '/alarms';
this.monitorClusterMetric = function(clusterId, metricName) {
// Returns a single metric for a cluster hostgroup
// /monit/api/cluster/<id>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/metrics/' + metricName);
};
this.monitorRsHostGroupMetric = function(groupName, metricName) {
// /monit/api/rshostgroup/<hostgroup>/metric/<metricname>
return $http.get(settings.monitoringUrlBase + '/rshostgroup/' + groupName + '/metric/' + metricName);
this.monitorAlarms = function(clusterId) {
// Returns all alarm data for a cluster host
// /monit/api/cluster/<id>/alarms
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/alarms');
};
this.monitorAlarms = function() {
// /monit/api/alarms
return $http.get(settings.monitoringUrlBase + '/alarms');
/*
var url = settings.monitoringUrlBase + '/alarms' ;
$http.get(url).success(function (data, status, headers, config) {
console.log(data);
return data;
}).error(function (data, status, headers, config) {
//this always gets called
console.log(status);
//deferred.reject(status);
return 'undefined';
});
*/
this.monitorEvents = function(id) {
// Reurns all event data for a cluster host
// /monit/api/cluster/<id>/events
return $http.get(settings.monitoringUrlBase + '/clusters/' + id + '/events');
};
this.monitorServices = function() {
// /monit/api/services
return $http.get(settings.monitoringUrlBase + '/services');
this.monitorTopology = function(clusterId) {
// Returns a nested json of networks and servers
// /monit/api/cluster/<id>/topology
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/topology');
};
this.monitorTopology = function() {
// /monit/api/topologies/1
return $http.get(settings.monitoringUrlBase + '/topologies/1');
this.monitorServiceTopology = function(clusterId) {
// Returns a nested json of servers, roles and metrics
// /monit/api/cluster/<id>/servicetopology
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/servicetopology');
};
this.monitorEvents = function() {
// /monit/api/events
return $http.get(settings.monitoringUrlBase + '/events');
this.monitorOverview = function(clusterId) {
// Returns a nested json for constructing the overview page
// /monit/api/cluster/<id>/overview
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/overview');
};
this.monitorEvents = function(eventID) {
// /monit/api/event/<eventId>
return $http.get(settings.monitoringUrlBase + '/event' + eventID);
this.monitorUsers = function(clusterId) {
// Returns a flat json list of all cluster users
// /monit/api/cluster/<id>/users
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/users');
};
this.monitorOverview = function() {
// /monit/api/overview
return $http.get(settings.monitoringUrlBase + '/overview');
this.monitorUser = function(clusterId, userName) {
// Returns json information on a particliar user
// /monit/api/cluster/<id>/user/<username>
return $http.get(settings.monitoringUrlBase + '/clusters/' + clusterId + '/user' + userName);
};
this.monitorUsers = function() {
// /monit/api/users
return $http.get(settings.monitoringUrlBase + '/users');
};
this.monitorEvents = function(userName) {
// /monit/api/user/<username>
return $http.get(settings.monitoringUrlBase + '/user' + userName);
};
}
])

View File

@ -123,142 +123,155 @@ angular.module('compass.charts', [])
return {
restrict: 'EAC',
scope: {
data: '='
data: '=',
dataready: '=',
id: '@'
},
link: function(scope, elem, attrs) {
//console.log(scope, elem, attrs)
var data = scope.data;
var w = 600,
h = 600,
r = 550,
x = d3.scale.linear().range([0, r]),
y = d3.scale.linear().range([0, r]),
node,
root;
var pack = d3.layout.pack()
.size([r, r])
.value(function(d) {
if (d.children === undefined || d.children.length == 0) {
return 500;
} else {
return undefined;
scope.$watch('dataready', function(newVal, oldVal) {
if (newVal != oldVal) {
if (newVal == "true") {
drawCircleGraph();
}
})
}
}, true);
var vis = d3.select("circlepacking").append("svg:svg", "h2")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + (w - r) / 2 + "," + (h - r) / 2 + ")");
function drawCircleGraph() {
node = root = data;
var elemId = scope.id;
var data = scope.data;
var nodes = pack.nodes(root);
var w = 600,
h = 600,
r = 550,
x = d3.scale.linear().range([0, r]),
y = d3.scale.linear().range([0, r]),
node,
root;
vis.selectAll("circle")
.data(nodes)
.enter().append("svg:circle")
.attr("class", function(d) {
return d.children ? "parent" : "child";
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", function(d) {
return d.r;
})
.attr("depth", function(d) {
return d.depth;
})
.on("click", function(d) {
return zoom(node == d ? root : d);
})
.on("contextmenu", function(d) {
//stop showing browser menu
d3.event.preventDefault();
})
.on("mouseover", function(d) {
//console.log("mouseover ", d)
});
vis.selectAll("text")
.data(nodes)
.enter().append("svg:text")
.attr("class", function(d) {
return d.children ? "parent" : "child";
})
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
if(d.depth == 0 || d.depth == 1) {
return y(d.y + d.r + 20);
} else if(d.depth == 2 || d.depth == 3) {
return y(d.y + d.r + 5);
var pack = d3.layout.pack()
.size([r, r])
.value(function(d) {
if (d.children === undefined || d.children.length == 0) {
return 500;
} else {
return y(d.y);
}
//return d.children ? d.y + d.r + 10 : d.y;
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("opacity", function(d) {
return d.r > 20 ? 1 : 0;
})
.text(function(d) {
return d.name;
});
return undefined;
}
})
d3.select(window).on("click", function() {
zoom(root);
});
var vis = d3.select("#" + elemId).append("svg:svg", "h2")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + (w - r) / 2 + "," + (h - r) / 2 + ")");
node = root = data;
function zoom(d, i) {
var k = r / d.r / 2;
x.domain([d.x - d.r, d.x + d.r]);
y.domain([d.y - d.r, d.y + d.r]);
var nodes = pack.nodes(root);
var t = vis.transition()
.duration(d3.event.altKey ? 7500 : 750);
t.selectAll("circle")
vis.selectAll("circle")
.data(nodes)
.enter().append("svg:circle")
.attr("class", function(d) {
return d.children ? "parent" : "child";
})
.attr("cx", function(d) {
return x(d.x);
return d.x;
})
.attr("cy", function(d) {
return y(d.y);
return d.y;
})
.attr("r", function(d) {
return k * d.r;
return d.r;
})
.attr("depth", function(d) {
return d.depth;
})
.on("click", function(d) {
return zoom(node == d ? root : d);
})
.on("contextmenu", function(d) {
//stop showing browser menu
d3.event.preventDefault();
})
.on("mouseover", function(d) {
//console.log("mouseover ", d)
});
t.selectAll("text")
vis.selectAll("text")
.data(nodes)
.enter().append("svg:text")
.attr("class", function(d) {
return d.children ? "parent" : "child";
})
.attr("x", function(d) {
return x(d.x);
return d.x;
})
.attr("y", function(d) {
if(d.depth == 0 || d.depth == 1) {
if (d.depth == 0 || d.depth == 1) {
return y(d.y + d.r + 20);
} else if(d.depth == 2 || d.depth == 3) {
} else if (d.depth == 2 || d.depth == 3) {
return y(d.y + d.r + 5);
} else {
return y(d.y);
}
//return d.children ? y(d.y + d.r + 10) : y(d.y);
//return d.children ? d.y + d.r + 10 : d.y;
})
.style("opacity", function(d) {
return k * d.r > 20 ? 1 : 0;
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("display", function(d) {
return d.r > 30 ? "block" : "none";
})
.text(function(d) {
return d.name;
});
node = d;
d3.event.stopPropagation();
d3.select(window).on("click", function() {
zoom(root);
});
function zoom(d, i) {
var k = r / d.r / 2;
x.domain([d.x - d.r, d.x + d.r]);
y.domain([d.y - d.r, d.y + d.r]);
var t = vis.transition()
.duration(d3.event.altKey ? 7500 : 750);
t.selectAll("circle")
.attr("cx", function(d) {
return x(d.x);
})
.attr("cy", function(d) {
return y(d.y);
})
.attr("r", function(d) {
return k * d.r;
});
t.selectAll("text")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
if (d.depth == 0 || d.depth == 1) {
return y(d.y + d.r + 20);
} else if (d.depth == 2 || d.depth == 3) {
return y(d.y + d.r + 5);
} else {
return y(d.y);
}
//return d.children ? y(d.y + d.r + 10) : y(d.y);
})
.style("display", function(d) {
return k * d.r > 30 ? "block" : "none";
});
node = d;
d3.event.stopPropagation();
}
}
}
@ -270,224 +283,237 @@ angular.module('compass.charts', [])
restrict: 'EAC',
scope: {
data: '=',
count: '=',
dataready: '=',
id: '@'
},
link: function(scope, elem, attrs) {
var id = scope.id;
var margin = {
top: 0,
right: 120,
bottom: 0,
left: 130
};
var serverCount = scope.count;
var serversHeight = serverCount * 68;
if (serversHeight < 500) {
serversHeight = 500;
}
var width = 1000 - margin.right - margin.left,
height = serversHeight - margin.top - margin.bottom;
imgWidth = 163;
imgHeight = 32;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var svg = d3.select("#" + id).append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = scope.data;
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "600px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 300;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
var transX = parseFloat(source.y0) - 10;
var transY = parseFloat(source.x0) - imgHeight / 2;
return "translate(" + transX + "," + transY + ")";
})
.attr("width", imgWidth)
.attr("height", imgHeight)
.on("click", click);
nodeEnter.append("image")
.attr("xlink:href", function(d) {
if (d.depth == 0)
return "assets/img/router.png";
else if (d.depth == 1)
return "assets/img/switch1.png";
else
return "assets/img/server1.png";
})
.attr("width", imgWidth)
.attr("height", imgHeight);
nodeEnter.append("rect")
.attr("width", imgWidth)
.attr("height", imgHeight)
.attr("data-state", function(d) {
return d.state
})
.style("opacity", function(d) {
if (d.depth == 0)
return 0;
else
return 0.3;
});
nodeEnter.append("text")
.attr("x", function(d) {
if (d.depth == 0)
return -5;
else
return d.children || d._children ? -8 : imgWidth + 10;
})
.attr("y", function(d) {
if (d.depth == 0)
return imgHeight / 2;
else if (d.depth == 1)
return 6;
else
return imgHeight / 2;
})
.attr("dy", ".25em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("font-size", "15px")
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
var transX = parseFloat(d.y) - 10;
var transY = parseFloat(d.x) - imgHeight / 2;
return "translate(" + transX + "," + transY + ")";
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
var transX = parseFloat(source.y) - 10;
var transY = parseFloat(source.x) - imgHeight / 2;
return "translate(" + transX + "," + transY + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
scope.$watch('dataready', function(newVal, oldVal) {
if (newVal != oldVal) {
if (newVal == "true") {
drawTree();
}
}
update(d);
}
}, true);
function drawTree() {
var elemId = scope.id,
tree = d3.layout.tree();
var margin = {
top: 0,
right: 120,
bottom: 0,
left: 130
};
// calculate servers count
var serverCount = 0;
var treeNodes = tree.nodes(scope.data).reverse();
treeNodes.forEach(function(d) {
if (d.depth == 2)
serverCount++;
});
var serversHeight = serverCount * 68;
if (serversHeight < 500) {
serversHeight = 500;
}
var width = 1000 - margin.right - margin.left,
height = serversHeight - margin.top - margin.bottom;
tree.size([height, width]);
imgWidth = 163;
imgHeight = 32;
var i = 0,
duration = 750,
root = scope.data;
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var svg = d3.select("#" + elemId).append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "600px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 300;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
var transX = parseFloat(source.y0) - 10;
var transY = parseFloat(source.x0) - imgHeight / 2;
return "translate(" + transX + "," + transY + ")";
})
.attr("width", imgWidth)
.attr("height", imgHeight)
.on("click", click);
nodeEnter.append("image")
.attr("xlink:href", function(d) {
if (d.depth == 0)
return "assets/img/router.png";
else if (d.depth == 1)
return "assets/img/switch1.png";
else
return "assets/img/server1.png";
})
.attr("width", imgWidth)
.attr("height", imgHeight);
nodeEnter.append("rect")
.attr("width", imgWidth)
.attr("height", imgHeight)
.attr("data-state", function(d) {
return d.state
})
.style("opacity", function(d) {
if (d.depth == 0)
return 0;
else
return 0.3;
});
nodeEnter.append("text")
.attr("x", function(d) {
if (d.depth == 0)
return -5;
else
return d.children || d._children ? -8 : imgWidth + 10;
})
.attr("y", function(d) {
if (d.depth == 0)
return imgHeight / 2;
else if (d.depth == 1)
return 6;
else
return imgHeight / 2;
})
.attr("dy", ".25em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("font-size", "15px")
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
var transX = parseFloat(d.y) - 10;
var transY = parseFloat(d.x) - imgHeight / 2;
return "translate(" + transX + "," + transY + ")";
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
var transX = parseFloat(source.y) - 10;
var transY = parseFloat(source.x) - imgHeight / 2;
return "translate(" + transX + "," + transY + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}
}
}
})
@ -497,7 +523,7 @@ app.directive('ganttchart', function() {
restrict: 'E',
scope: {
data: '=',
hosts: '='
dataready: '='
},
template: '<div class="pull-right"><button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'1hr\')">1 HR</button>'
+ '<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'3hr\')">3 HR</button>'
@ -506,81 +532,102 @@ app.directive('ganttchart', function() {
+ '<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain(\'1week\')">1 WEEK</button>'
+ '</div>'
+ '<div class="clear-fix"></div>',
// BUG: svg not appended initially if using templateUrl
//templateUrl: "src/common/ganttchart.tpl.html",
link: function(scope, element, attrs) {
var tasks = scope.data;
var hostnames = scope.hosts;
var taskStatus = {
"SUCCESSFUL": "bar-successful",
"CRITICAL": "bar-failed",
"WARNING": "bar-warning",
"UNKNOWN": "bar-unknown"
};
tasks.sort(function(a, b) {
return a.endDate - b.endDate;
});
var maxDate = tasks[tasks.length - 1].endDate;
tasks.sort(function(a, b) {
return a.startDate - b.startDate;
});
var minDate = tasks[0].startDate;
var format = "%H:%M";
var timeDomainString = "1day";
var gantt = d3.gantt().taskTypes(hostnames).taskStatus(taskStatus).tickFormat(format).height(450).width(800);
scope.changeTimeDomain = function(timeDomainString) {
this.timeDomainString = timeDomainString;
switch (timeDomainString) {
case "1hr":
format = "%H:%M:%S";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -1), getEndDate()]);
break;
case "3hr":
format = "%H:%M";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -3), getEndDate()]);
break;
case "6hr":
format = "%H:%M";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -6), getEndDate()]);
break;
case "1day":
format = "%H:%M";
gantt.timeDomain([d3.time.day.offset(getEndDate(), -1), getEndDate()]);
break;
case "1week":
format = "%a %H:%M";
gantt.timeDomain([d3.time.day.offset(getEndDate(), -7), getEndDate()]);
break;
default:
format = "%H:%M"
scope.$watch('dataready', function(newVal, oldVal) {
if (newVal != oldVal) {
if (newVal == "true") {
drawChart();
}
}
gantt.tickFormat(format);
gantt.redraw(tasks);
}
}, true);
gantt.timeDomainMode("fixed");
scope.changeTimeDomain(timeDomainString);
function drawChart () {
var tasks = scope.data;
var hostnames = [];
gantt(tasks);
angular.forEach(tasks, function(task) {
if(hostnames.indexOf(task) == -1) {
hostnames.push(task.name);
}
});
//var hostnames = scope.hosts;
var taskStatus = {
"SUCCESSFUL": "bar-successful",
"CRITICAL": "bar-failed",
"WARNING": "bar-warning",
"UNKNOWN": "bar-unknown"
};
tasks.sort(function(a, b) {
return a.endDate - b.endDate;
});
var maxDate = tasks[tasks.length - 1].endDate;
tasks.sort(function(a, b) {
return a.startDate - b.startDate;
});
var minDate = tasks[0].startDate;
function getEndDate() {
var lastEndDate = Date.now();
if (tasks.length > 0) {
lastEndDate = tasks[tasks.length - 1].endDate;
var format = "%H:%M";
var timeDomainString = "1day";
var gantt = d3.gantt().taskTypes(hostnames).taskStatus(taskStatus).tickFormat(format).height(450).width(800);
scope.changeTimeDomain = function(timeDomainString) {
this.timeDomainString = timeDomainString;
switch (timeDomainString) {
case "1hr":
format = "%H:%M:%S";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -1), getEndDate()]);
break;
case "3hr":
format = "%H:%M";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -3), getEndDate()]);
break;
case "6hr":
format = "%H:%M";
gantt.timeDomain([d3.time.hour.offset(getEndDate(), -6), getEndDate()]);
break;
case "1day":
format = "%H:%M";
gantt.timeDomain([d3.time.day.offset(getEndDate(), -1), getEndDate()]);
break;
case "1week":
format = "%a %H:%M";
gantt.timeDomain([d3.time.day.offset(getEndDate(), -7), getEndDate()]);
break;
default:
format = "%H:%M"
}
gantt.tickFormat(format);
gantt.redraw(tasks);
}
gantt.timeDomainMode("fixed");
scope.changeTimeDomain(timeDomainString);
gantt(tasks);
function getEndDate() {
var lastEndDate = Date.now();
if (tasks.length > 0) {
lastEndDate = tasks[tasks.length - 1].endDate;
}
return lastEndDate;
}
return lastEndDate;
}
}
}
})
})

View File

@ -0,0 +1,8 @@
<div class="pull-right">
<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain('1hr')">1 HR</button>
<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain('3hr')">3 HR</button>
<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain('6hr')">6 HR</button>
<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain('1day')">1 DAY</button>
<button type="button" class="btn btn-xs side-margin-3" ng-click="changeTimeDomain('1week')">1 WEEK</button>
</div>
<div class="clear-fix"></div>

6
v2/vendor/d3/d3.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long