mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-27 06:11:47 +01:00
[ONOS-3851] Implement default Web GUI page for CPMan
- Reduce the datapoints to 20, resolve cold start problem - Code refactoring for CpmanViewMessageHandler - Code refactoring for cpman.js - Show "No Data" message when client does not receive any data - Clean up cpman.css - Specify default colors for charting - Resolve ArrayIndexOutofBoundsException when the number returned dataset is less the number what we expected Change-Id: I67ab3160ab66f92eaffeffc2d61c7d0e17be0512
This commit is contained in:
parent
812aa5a5fe
commit
d5be69f3de
@ -27,7 +27,6 @@ import org.onosproject.cpman.ControlLoadSnapshot;
|
|||||||
import org.onosproject.cpman.ControlMetricType;
|
import org.onosproject.cpman.ControlMetricType;
|
||||||
import org.onosproject.cpman.ControlPlaneMonitorService;
|
import org.onosproject.cpman.ControlPlaneMonitorService;
|
||||||
import org.onosproject.net.DeviceId;
|
import org.onosproject.net.DeviceId;
|
||||||
import org.onosproject.net.device.DeviceService;
|
|
||||||
import org.onosproject.ui.RequestHandler;
|
import org.onosproject.ui.RequestHandler;
|
||||||
import org.onosproject.ui.UiMessageHandler;
|
import org.onosproject.ui.UiMessageHandler;
|
||||||
import org.onosproject.ui.chart.ChartModel;
|
import org.onosproject.ui.chart.ChartModel;
|
||||||
@ -38,14 +37,16 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.LongStream;
|
||||||
|
|
||||||
import static org.onosproject.cpman.ControlResource.CONTROL_MESSAGE_METRICS;
|
import static org.onosproject.cpman.ControlResource.CONTROL_MESSAGE_METRICS;
|
||||||
import static org.onosproject.cpman.ControlResource.Type.CONTROL_MESSAGE;
|
import static org.onosproject.cpman.ControlResource.Type.CONTROL_MESSAGE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CpmanViewMessageHandler class implementation.
|
* Message handler for control plane monitoring view related messages.
|
||||||
*/
|
*/
|
||||||
public class CpmanViewMessageHandler extends UiMessageHandler {
|
public class CpmanViewMessageHandler extends UiMessageHandler {
|
||||||
|
|
||||||
@ -55,12 +56,14 @@ public class CpmanViewMessageHandler extends UiMessageHandler {
|
|||||||
private static final String CPMAN_DATA_RESP = "cpmanDataResponse";
|
private static final String CPMAN_DATA_RESP = "cpmanDataResponse";
|
||||||
private static final String CPMANS = "cpmans";
|
private static final String CPMANS = "cpmans";
|
||||||
|
|
||||||
// TODO: we assume that server side always returns 60 data points
|
// TODO: we assume that server side always returns 20 data points
|
||||||
// to feed 1 hour time slots, later this should make to be configurable
|
// to feed 1 hour time slots, later this should make to be configurable
|
||||||
private static final int NUM_OF_DATA_POINTS = 60;
|
private static final int NUM_OF_DATA_POINTS = 20;
|
||||||
|
|
||||||
private static final int MILLI_CONV_UNIT = 1000;
|
private static final int MILLI_CONV_UNIT = 1000;
|
||||||
|
|
||||||
|
private long timestamp = 0L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Collection<RequestHandler> createRequestHandlers() {
|
protected Collection<RequestHandler> createRequestHandlers() {
|
||||||
return ImmutableSet.of(
|
return ImmutableSet.of(
|
||||||
@ -83,50 +86,104 @@ public class CpmanViewMessageHandler extends UiMessageHandler {
|
|||||||
@Override
|
@Override
|
||||||
protected void populateChart(ChartModel cm, ObjectNode payload) {
|
protected void populateChart(ChartModel cm, ObjectNode payload) {
|
||||||
String uri = string(payload, "devId");
|
String uri = string(payload, "devId");
|
||||||
|
ControlPlaneMonitorService cpms = get(ControlPlaneMonitorService.class);
|
||||||
|
ClusterService cs = get(ClusterService.class);
|
||||||
if (!Strings.isNullOrEmpty(uri)) {
|
if (!Strings.isNullOrEmpty(uri)) {
|
||||||
Map<ControlMetricType, Long[]> data = Maps.newHashMap();
|
|
||||||
DeviceId deviceId = DeviceId.deviceId(uri);
|
DeviceId deviceId = DeviceId.deviceId(uri);
|
||||||
ClusterService cs = get(ClusterService.class);
|
|
||||||
ControlPlaneMonitorService cpms = get(ControlPlaneMonitorService.class);
|
|
||||||
|
|
||||||
if (cpms.availableResources(CONTROL_MESSAGE).contains(deviceId.toString())) {
|
if (cpms.availableResources(CONTROL_MESSAGE).contains(deviceId.toString())) {
|
||||||
LocalDateTime ldt = null;
|
Map<ControlMetricType, Long[]> data = generateMatrix(cpms, cs, deviceId);
|
||||||
|
LocalDateTime ldt = new LocalDateTime(timestamp * MILLI_CONV_UNIT);
|
||||||
|
|
||||||
try {
|
populateMetrics(cm, data, ldt, NUM_OF_DATA_POINTS);
|
||||||
for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
|
|
||||||
ControlLoadSnapshot cls = cpms.getLoad(cs.getLocalNode().id(),
|
|
||||||
cmt, NUM_OF_DATA_POINTS, TimeUnit.MINUTES,
|
|
||||||
Optional.of(deviceId)).get();
|
|
||||||
data.put(cmt, ArrayUtils.toObject(cls.recent()));
|
|
||||||
if (ldt == null) {
|
|
||||||
ldt = new LocalDateTime(cls.time() * MILLI_CONV_UNIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < NUM_OF_DATA_POINTS; i++) {
|
|
||||||
Map<String, Long> local = Maps.newHashMap();
|
|
||||||
for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
|
|
||||||
local.put(StringUtils.lowerCase(cmt.name()), data.get(cmt)[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
local.put(LABEL, ldt.minusMinutes(NUM_OF_DATA_POINTS - i).toDateTime().getMillis());
|
|
||||||
|
|
||||||
populateMetric(cm.addDataPoint(ldt.minusMinutes(NUM_OF_DATA_POINTS - i)
|
|
||||||
.toDateTime().getMillis()), local);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
log.warn(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DeviceService ds = get(DeviceService.class);
|
Set<String> deviceIds = cpms.availableResources(CONTROL_MESSAGE);
|
||||||
ds.getAvailableDevices();
|
for (String deviceId : deviceIds) {
|
||||||
|
Map<ControlMetricType, Long> data =
|
||||||
|
populateDeviceMetrics(cpms, cs, DeviceId.deviceId(deviceId));
|
||||||
|
Map<String, Long> local = Maps.newHashMap();
|
||||||
|
for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
|
||||||
|
local.put(StringUtils.lowerCase(cmt.name()), data.get(cmt));
|
||||||
|
}
|
||||||
|
// TODO: need to find a way to present device id using long type
|
||||||
|
String shortId = StringUtils.substring(deviceId,
|
||||||
|
deviceId.length() - 2, deviceId.length());
|
||||||
|
local.put(LABEL, Long.valueOf(shortId));
|
||||||
|
populateMetric(cm.addDataPoint(Long.valueOf(shortId)), local);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateAllDevs(ChartModel.DataPoint dataPoint, Map<String, Long> data) {
|
private Map<ControlMetricType, Long> populateDeviceMetrics(ControlPlaneMonitorService cpms,
|
||||||
|
ClusterService cs, DeviceId deviceId) {
|
||||||
|
Map<ControlMetricType, Long> data = Maps.newHashMap();
|
||||||
|
for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
|
||||||
|
ControlLoadSnapshot cls;
|
||||||
|
try {
|
||||||
|
cls = cpms.getLoad(cs.getLocalNode().id(),
|
||||||
|
cmt, NUM_OF_DATA_POINTS, TimeUnit.MINUTES,
|
||||||
|
Optional.of(deviceId)).get();
|
||||||
|
data.put(cmt, Math.round(LongStream.of(cls.recent()).average().getAsDouble()));
|
||||||
|
timestamp = cls.time();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
log.warn(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<ControlMetricType, Long[]> generateMatrix(ControlPlaneMonitorService cpms,
|
||||||
|
ClusterService cs, DeviceId deviceId) {
|
||||||
|
Map<ControlMetricType, Long[]> data = Maps.newHashMap();
|
||||||
|
for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
|
||||||
|
ControlLoadSnapshot cls;
|
||||||
|
try {
|
||||||
|
cls = cpms.getLoad(cs.getLocalNode().id(),
|
||||||
|
cmt, NUM_OF_DATA_POINTS, TimeUnit.MINUTES,
|
||||||
|
Optional.of(deviceId)).get();
|
||||||
|
|
||||||
|
// TODO: in some cases, the number of returned dataset is
|
||||||
|
// less than what we expected (expected -1)
|
||||||
|
// As a workaround, we simply fill the slot with 0 values,
|
||||||
|
// such a bug should be fixed with updated RRD4J lib...
|
||||||
|
data.put(cmt, ArrayUtils.toObject(fillData(cls.recent(), NUM_OF_DATA_POINTS)));
|
||||||
|
timestamp = cls.time();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
log.warn(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long[] fillData(long[] origin, int expected) {
|
||||||
|
if (origin.length == expected) {
|
||||||
|
return origin;
|
||||||
|
} else {
|
||||||
|
long[] filled = new long[expected];
|
||||||
|
for (int i = 0; i < expected; i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
filled[i] = 0;
|
||||||
|
} else {
|
||||||
|
filled[i] = origin[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateMetrics(ChartModel cm, Map<ControlMetricType,
|
||||||
|
Long[]> data, LocalDateTime time, int numOfDp) {
|
||||||
|
for (int i = 0; i < numOfDp; i++) {
|
||||||
|
Map<String, Long> local = Maps.newHashMap();
|
||||||
|
for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
|
||||||
|
local.put(StringUtils.lowerCase(cmt.name()), data.get(cmt)[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
local.put(LABEL, time.minusMinutes(numOfDp - i).toDateTime().getMillis());
|
||||||
|
|
||||||
|
populateMetric(cm.addDataPoint(time.minusMinutes(numOfDp - i)
|
||||||
|
.toDateTime().getMillis()), local);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateMetric(ChartModel.DataPoint dataPoint,
|
private void populateMetric(ChartModel.DataPoint dataPoint,
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#ov-cpman {
|
#ov-cpman {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.light #ov-cpman {
|
.light #ov-cpman {
|
||||||
color: navy;
|
color: navy;
|
||||||
@ -40,22 +41,17 @@
|
|||||||
background-color: #444;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ov-cpman .my-button {
|
#ov-cpman #chart-loader {
|
||||||
cursor: pointer;
|
position: absolute;
|
||||||
padding: 4px;
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
margin-left: -100px;
|
||||||
|
margin-top: -25px;
|
||||||
|
z-index: 900;
|
||||||
|
top: 50%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
left: 50%;
|
||||||
|
font-size: 25px;
|
||||||
.light #ov-cpman .my-button {
|
font-weight: bold;
|
||||||
color: white;
|
color: #ccc;
|
||||||
background-color: #99d;
|
|
||||||
}
|
|
||||||
.dark #ov-cpman .my-button {
|
|
||||||
color: black;
|
|
||||||
background-color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ov-cpman .number {
|
|
||||||
font-size: 140%;
|
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
@ -1,8 +1,21 @@
|
|||||||
<!-- partial HTML -->
|
<!-- partial HTML -->
|
||||||
<div id="ov-cpman">
|
<div id="ov-cpman">
|
||||||
<div>
|
<div id="chart-loader" ng-show="!devId && showLoader">
|
||||||
|
No Data
|
||||||
|
</div>
|
||||||
|
<div ng-show="!devId">
|
||||||
|
<canvas id="bar" class="chart chart-bar" chart-data="data"
|
||||||
|
chart-labels="labels" chart-legend="true" chart-click="onClick"
|
||||||
|
chart-series="series" chart-options="options" height="100%">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
<div ng-show="devId">
|
||||||
|
<h2>
|
||||||
|
Chart for Device {{devId || "(No device selected)"}}
|
||||||
|
</h2>
|
||||||
<canvas id="line" class="chart chart-line" chart-data="data"
|
<canvas id="line" class="chart chart-line" chart-data="data"
|
||||||
chart-labels="labels" chart-legend="true" chart-series="series">
|
chart-labels="labels" chart-legend="true"
|
||||||
|
chart-series="series" chart-options="options" height="100%">
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,27 +21,48 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// injected references
|
// injected references
|
||||||
var $log, $scope, $location, ks, fs, cbs;
|
var $log, $scope, $location, ks, fs, cbs, ns;
|
||||||
|
|
||||||
var labels = new Array(60);
|
var hasDeviceId;
|
||||||
var data = new Array(new Array(60), new Array(60), new Array(60),
|
|
||||||
new Array(60), new Array(60), new Array(60));
|
var labels = new Array(1);
|
||||||
|
var data = new Array(6);
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
data[i] = new Array(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var date, max, merged;
|
||||||
|
|
||||||
|
function ceil(num) {
|
||||||
|
if (isNaN(num)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var pre = num.toString().length - 1
|
||||||
|
var pow = Math.pow(10, pre);
|
||||||
|
return (Math.ceil(num / pow)) * pow;
|
||||||
|
}
|
||||||
|
|
||||||
angular.module('ovCpman', ["chart.js"])
|
angular.module('ovCpman', ["chart.js"])
|
||||||
.controller('OvCpmanCtrl',
|
.controller('OvCpmanCtrl',
|
||||||
['$log', '$scope', '$location', 'FnService', 'ChartBuilderService',
|
['$log', '$scope', '$location', 'FnService', 'ChartBuilderService', 'NavService',
|
||||||
|
|
||||||
function (_$log_, _$scope_, _$location_, _fs_, _cbs_) {
|
function (_$log_, _$scope_, _$location_, _fs_, _cbs_, _ns_) {
|
||||||
var params;
|
var params;
|
||||||
$log = _$log_;
|
$log = _$log_;
|
||||||
$scope = _$scope_;
|
$scope = _$scope_;
|
||||||
$location = _$location_;
|
$location = _$location_;
|
||||||
fs = _fs_;
|
fs = _fs_;
|
||||||
cbs = _cbs_;
|
cbs = _cbs_;
|
||||||
|
ns = _ns_;
|
||||||
|
|
||||||
params = $location.search();
|
params = $location.search();
|
||||||
|
|
||||||
if (params.hasOwnProperty('devId')) {
|
if (params.hasOwnProperty('devId')) {
|
||||||
$scope.devId = params['devId'];
|
$scope.devId = params['devId'];
|
||||||
|
hasDeviceId = true;
|
||||||
|
} else {
|
||||||
|
$scope.type = 'StackedBar';
|
||||||
|
hasDeviceId = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
cbs.buildChart({
|
cbs.buildChart({
|
||||||
@ -50,31 +71,70 @@
|
|||||||
query: params
|
query: params
|
||||||
});
|
});
|
||||||
|
|
||||||
var idx = 0;
|
|
||||||
var date;
|
|
||||||
$scope.$watch('chartData', function () {
|
$scope.$watch('chartData', function () {
|
||||||
idx = 0;
|
|
||||||
if (!fs.isEmptyObject($scope.chartData)) {
|
if (!fs.isEmptyObject($scope.chartData)) {
|
||||||
$scope.chartData.forEach(function (cm) {
|
$scope.showLoader = false;
|
||||||
|
var length = $scope.chartData.length;
|
||||||
|
labels = new Array(length);
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
data[i] = new Array(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.chartData.forEach(function (cm, idx) {
|
||||||
data[0][idx] = cm.inbound_packet;
|
data[0][idx] = cm.inbound_packet;
|
||||||
data[1][idx] = cm.outbound_packet;
|
data[1][idx] = cm.outbound_packet;
|
||||||
data[2][idx] = cm.flow_mod_packet;
|
data[2][idx] = cm.flow_mod_packet;
|
||||||
data[3][idx] = cm.flow_removed_packet;
|
data[3][idx] = cm.flow_removed_packet;
|
||||||
data[4][idx] = cm.request_packet;
|
data[4][idx] = cm.request_packet;
|
||||||
data[5][idx] = cm.reply_packet;
|
data[5][idx] = cm.reply_packet;
|
||||||
date = new Date(cm.label);
|
|
||||||
labels[idx] = date.getHours() + ":" + date.getMinutes();
|
if(hasDeviceId) {
|
||||||
idx++;
|
date = new Date(cm.label);
|
||||||
|
labels[idx] = date.getHours() + ":" + date.getMinutes();
|
||||||
|
} else {
|
||||||
|
labels[idx] = cm.label;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
merged = [].concat.apply([], data);
|
||||||
|
max = Math.max.apply(null, merged);
|
||||||
|
$scope.labels = labels;
|
||||||
|
$scope.data = data;
|
||||||
|
$scope.options = {
|
||||||
|
scaleOverride : true,
|
||||||
|
scaleSteps : 10,
|
||||||
|
scaleStepWidth : ceil(max) / 10,
|
||||||
|
scaleStartValue : 0
|
||||||
|
};
|
||||||
|
$scope.onClick = function (points, evt) {
|
||||||
|
if (points[0]) {
|
||||||
|
// TODO: this will be replaced with real device id
|
||||||
|
var tmpId = 'of:000000000000020' + points[0].label;
|
||||||
|
ns.navTo('cpman', { devId: tmpId });
|
||||||
|
$log.log(points[0].label);
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.series = ['INBOUND', 'OUTBOUND', 'FLOW-MOD',
|
$scope.series = ['INBOUND', 'OUTBOUND', 'FLOW-MOD',
|
||||||
'FLOW-REMOVED', 'STATS-REQUEST', 'STATS-REPLY'];
|
'FLOW-REMOVED', 'STATS-REQUEST', 'STATS-REPLY'];
|
||||||
$scope.labels = labels;
|
$scope.labels = labels;
|
||||||
|
|
||||||
$scope.data = data;
|
$scope.data = data;
|
||||||
|
|
||||||
|
$scope.chartColors = [
|
||||||
|
'#286090',
|
||||||
|
'#F7464A',
|
||||||
|
'#46BFBD',
|
||||||
|
'#FDB45C',
|
||||||
|
'#97BBCD',
|
||||||
|
'#4D5360',
|
||||||
|
'#8c4f9f'
|
||||||
|
];
|
||||||
|
Chart.defaults.global.colours = $scope.chartColors;
|
||||||
|
|
||||||
|
$scope.showLoader = true;
|
||||||
|
|
||||||
$log.log('OvCpmanCtrl has been created');
|
$log.log('OvCpmanCtrl has been created');
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user