diff --git a/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java b/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java index 2d4e28f028..8003933fab 100644 --- a/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java +++ b/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java @@ -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 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 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 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 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 deviceIds = cpms.availableResources(CONTROL_MESSAGE); + for (String deviceId : deviceIds) { + Map data = + populateDeviceMetrics(cpms, cs, DeviceId.deviceId(deviceId)); + Map 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 data) { + private Map populateDeviceMetrics(ControlPlaneMonitorService cpms, + ClusterService cs, DeviceId deviceId) { + Map 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 generateMatrix(ControlPlaneMonitorService cpms, + ClusterService cs, DeviceId deviceId) { + Map 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 data, LocalDateTime time, int numOfDp) { + for (int i = 0; i < numOfDp; i++) { + Map 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, diff --git a/apps/cpman/app/src/main/resources/app/view/cpman/cpman.css b/apps/cpman/app/src/main/resources/app/view/cpman/cpman.css index 7cdcc7eb04..ed68c40b18 100644 --- a/apps/cpman/app/src/main/resources/app/view/cpman/cpman.css +++ b/apps/cpman/app/src/main/resources/app/view/cpman/cpman.css @@ -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; } \ No newline at end of file diff --git a/apps/cpman/app/src/main/resources/app/view/cpman/cpman.html b/apps/cpman/app/src/main/resources/app/view/cpman/cpman.html index a07e5441ca..edf24adeb4 100644 --- a/apps/cpman/app/src/main/resources/app/view/cpman/cpman.html +++ b/apps/cpman/app/src/main/resources/app/view/cpman/cpman.html @@ -1,8 +1,21 @@
-
+
+ No Data +
+
+ + +
+
+

+ Chart for Device {{devId || "(No device selected)"}} +

+ chart-labels="labels" chart-legend="true" + chart-series="series" chart-options="options" height="100%">
diff --git a/apps/cpman/app/src/main/resources/app/view/cpman/cpman.js b/apps/cpman/app/src/main/resources/app/view/cpman/cpman.js index 8474629980..30e020a4f2 100644 --- a/apps/cpman/app/src/main/resources/app/view/cpman/cpman.js +++ b/apps/cpman/app/src/main/resources/app/view/cpman/cpman.js @@ -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'); }]);