mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-26 13:51:14 +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.ControlPlaneMonitorService;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.device.DeviceService;
|
||||
import org.onosproject.ui.RequestHandler;
|
||||
import org.onosproject.ui.UiMessageHandler;
|
||||
import org.onosproject.ui.chart.ChartModel;
|
||||
@ -38,14 +37,16 @@ import org.slf4j.LoggerFactory;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
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.Type.CONTROL_MESSAGE;
|
||||
|
||||
/**
|
||||
* CpmanViewMessageHandler class implementation.
|
||||
* Message handler for control plane monitoring view related messages.
|
||||
*/
|
||||
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 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
|
||||
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 long timestamp = 0L;
|
||||
|
||||
@Override
|
||||
protected Collection<RequestHandler> createRequestHandlers() {
|
||||
return ImmutableSet.of(
|
||||
@ -83,50 +86,104 @@ public class CpmanViewMessageHandler extends UiMessageHandler {
|
||||
@Override
|
||||
protected void populateChart(ChartModel cm, ObjectNode payload) {
|
||||
String uri = string(payload, "devId");
|
||||
ControlPlaneMonitorService cpms = get(ControlPlaneMonitorService.class);
|
||||
ClusterService cs = get(ClusterService.class);
|
||||
if (!Strings.isNullOrEmpty(uri)) {
|
||||
Map<ControlMetricType, Long[]> data = Maps.newHashMap();
|
||||
DeviceId deviceId = DeviceId.deviceId(uri);
|
||||
ClusterService cs = get(ClusterService.class);
|
||||
ControlPlaneMonitorService cpms = get(ControlPlaneMonitorService.class);
|
||||
|
||||
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 {
|
||||
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());
|
||||
}
|
||||
populateMetrics(cm, data, ldt, NUM_OF_DATA_POINTS);
|
||||
}
|
||||
} else {
|
||||
DeviceService ds = get(DeviceService.class);
|
||||
ds.getAvailableDevices();
|
||||
Set<String> deviceIds = cpms.availableResources(CONTROL_MESSAGE);
|
||||
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,
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
#ov-cpman {
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.light #ov-cpman {
|
||||
color: navy;
|
||||
@ -40,22 +41,17 @@
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#ov-cpman .my-button {
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
#ov-cpman #chart-loader {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
margin-left: -100px;
|
||||
margin-top: -25px;
|
||||
z-index: 900;
|
||||
top: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.light #ov-cpman .my-button {
|
||||
color: white;
|
||||
background-color: #99d;
|
||||
}
|
||||
.dark #ov-cpman .my-button {
|
||||
color: black;
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
#ov-cpman .number {
|
||||
font-size: 140%;
|
||||
text-align: right;
|
||||
left: 50%;
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
}
|
||||
@ -1,8 +1,21 @@
|
||||
<!-- partial HTML -->
|
||||
<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"
|
||||
chart-labels="labels" chart-legend="true" chart-series="series">
|
||||
chart-labels="labels" chart-legend="true"
|
||||
chart-series="series" chart-options="options" height="100%">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -21,27 +21,48 @@
|
||||
'use strict';
|
||||
|
||||
// injected references
|
||||
var $log, $scope, $location, ks, fs, cbs;
|
||||
var $log, $scope, $location, ks, fs, cbs, ns;
|
||||
|
||||
var labels = new Array(60);
|
||||
var data = new Array(new Array(60), new Array(60), new Array(60),
|
||||
new Array(60), new Array(60), new Array(60));
|
||||
var hasDeviceId;
|
||||
|
||||
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"])
|
||||
.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;
|
||||
$log = _$log_;
|
||||
$scope = _$scope_;
|
||||
$location = _$location_;
|
||||
fs = _fs_;
|
||||
cbs = _cbs_;
|
||||
ns = _ns_;
|
||||
|
||||
params = $location.search();
|
||||
|
||||
if (params.hasOwnProperty('devId')) {
|
||||
$scope.devId = params['devId'];
|
||||
hasDeviceId = true;
|
||||
} else {
|
||||
$scope.type = 'StackedBar';
|
||||
hasDeviceId = false;
|
||||
}
|
||||
|
||||
cbs.buildChart({
|
||||
@ -50,31 +71,70 @@
|
||||
query: params
|
||||
});
|
||||
|
||||
var idx = 0;
|
||||
var date;
|
||||
$scope.$watch('chartData', function () {
|
||||
idx = 0;
|
||||
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[1][idx] = cm.outbound_packet;
|
||||
data[2][idx] = cm.flow_mod_packet;
|
||||
data[3][idx] = cm.flow_removed_packet;
|
||||
data[4][idx] = cm.request_packet;
|
||||
data[5][idx] = cm.reply_packet;
|
||||
date = new Date(cm.label);
|
||||
labels[idx] = date.getHours() + ":" + date.getMinutes();
|
||||
idx++;
|
||||
|
||||
if(hasDeviceId) {
|
||||
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',
|
||||
'FLOW-REMOVED', 'STATS-REQUEST', 'STATS-REPLY'];
|
||||
$scope.labels = labels;
|
||||
|
||||
$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');
|
||||
}]);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user