UI for server device driver
This patch introduces a graphical user interface
that visualizes the CPU utilization of server devices
using bar charts.
Code optimizations and a bug fix is applied after first
code review.
Additional bar plots are implemented, visualizing
average throughput and latency per core,
when these statistics are present.
Added external library in Bazel's BUILD file.
Fixed scaling and font issues in the UIs after getting
feedback from ONOS reviewers.
Change-Id: I92972ef871e6a91dd70cdffd8cd650f498ffca26
Signed-off-by: Georgios Katsikas <katsikas.gp@gmail.com>
diff --git a/drivers/server/src/main/resources/app/view/throughput/throughput.css b/drivers/server/src/main/resources/app/view/throughput/throughput.css
new file mode 100644
index 0000000..2914588
--- /dev/null
+++ b/drivers/server/src/main/resources/app/view/throughput/throughput.css
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Throughput UI -- CSS file
+ */
+
+#ov-throughput {
+ padding: 20px;
+ position: relative;
+}
+.light #ov-throughput {
+ color: navy;
+}
+.dark #ov-throughput {
+ color: #88f;
+}
+
+#ov-throughput .button-panel {
+ margin: 10px;
+ width: 200px;
+}
+
+.light #ov-throughput .button-panel {
+ background-color: #ccf;
+}
+.dark #ov-throughput .button-panel {
+ background-color: #444;
+}
+
+#ov-throughput #chart-loader {
+ position: absolute;
+ width: 200px;
+ height: 50px;
+ margin-left: -100px;
+ margin-top: -25px;
+ z-index: 900;
+ top: 50%;
+ text-align: center;
+ left: 50%;
+ font-size: 25px;
+ font-weight: bold;
+ color: #ccc;
+}
\ No newline at end of file
diff --git a/drivers/server/src/main/resources/app/view/throughput/throughput.html b/drivers/server/src/main/resources/app/view/throughput/throughput.html
new file mode 100644
index 0000000..2535794
--- /dev/null
+++ b/drivers/server/src/main/resources/app/view/throughput/throughput.html
@@ -0,0 +1,27 @@
+<!-- partial HTML -->
+<div id="ov-throughput">
+ <div id="chart-loader" ng-show="!devId && showLoader">
+ No Servers
+ </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>
+ <div class="ctrl-btns">
+ <select ng-options="deviceId as deviceId for deviceId in deviceIds"
+ ng-model="selectedItem" ng-change="onChange(selectedItem)">
+ <option value="">-- select a device --</option>
+ </select>
+ </div>
+ <canvas id="line" class="chart chart-line" chart-data="data"
+ chart-labels="labels" chart-legend="true"
+ chart-series="series" chart-options="options" height="100%">
+ </canvas>
+ </div>
+</div>
diff --git a/drivers/server/src/main/resources/app/view/throughput/throughput.js b/drivers/server/src/main/resources/app/view/throughput/throughput.js
new file mode 100644
index 0000000..c16e2c0
--- /dev/null
+++ b/drivers/server/src/main/resources/app/view/throughput/throughput.js
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Throughput View Module
+ */
+(function () {
+ 'use strict';
+
+ // injected references
+ var $log, $scope, $location, ks, fs, cbs, ns;
+
+ var hasDeviceId;
+ // TODO: Pass this dynamically
+ var coresNb = 16;
+
+ var labels = new Array(1);
+ var data = new Array(coresNb);
+ for (var i = 0; i < coresNb; i++) {
+ data[i] = new Array(1);
+ data[i][0] = 0;
+ }
+
+ angular.module('ovThroughput', ["chart.js"])
+ .controller('OvThroughputCtrl',
+ ['$log', '$scope', '$location', 'FnService', 'ChartBuilderService', 'NavService',
+
+ 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 {
+ hasDeviceId = false;
+ }
+
+ cbs.buildChart({
+ scope: $scope,
+ tag: 'throughput',
+ query: params
+ });
+
+ $scope.$watch('chartData', function () {
+ if (!fs.isEmptyObject($scope.chartData)) {
+ $scope.showLoader = false;
+ var length = $scope.chartData.length;
+ labels = new Array(length);
+ for (var i = 0; i < coresNb; i++) {
+ data[i] = new Array(length);
+ }
+
+ $scope.chartData.forEach(
+ function (cm, idx) {
+ // TODO: Squeeze using a working loop?
+ data[0][idx] = cm.throughput_0;
+ data[1][idx] = cm.throughput_1;
+ data[2][idx] = cm.throughput_2;
+ data[3][idx] = cm.throughput_3;
+ data[4][idx] = cm.throughput_4;
+ data[5][idx] = cm.throughput_5;
+ data[6][idx] = cm.throughput_6;
+ data[7][idx] = cm.throughput_7;
+ data[8][idx] = cm.throughput_8;
+ data[9][idx] = cm.throughput_9;
+ data[10][idx] = cm.throughput_10;
+ data[11][idx] = cm.throughput_11;
+ data[12][idx] = cm.throughput_12;
+ data[13][idx] = cm.throughput_13;
+ data[14][idx] = cm.throughput_14;
+ data[15][idx] = cm.throughput_15;
+
+ labels[idx] = cm.label;
+ }
+ );
+ }
+
+ $scope.labels = labels;
+ $scope.data = data;
+
+ $scope.options = {
+ scales: {
+ yAxes: [{
+ type: 'linear',
+ position: 'left',
+ id: 'y-axis-throughput',
+ ticks: {
+ min: 0,
+ max: 100,
+ fontSize: 28,
+ },
+ scaleLabel: {
+ display: true,
+ labelString: 'Throughput/CPU Core (Gbps)',
+ fontSize: 28,
+ }
+ }],
+ xAxes: [{
+ id: 'x-axis-servers-cores',
+ ticks: {
+ fontSize: 28,
+ },
+ scaleLabel: {
+ display: true,
+ fontSize: 28,
+ }
+ }]
+ }
+ };
+
+ $scope.onClick = function (points, evt) {
+ var label = labels[points[0]._index];
+ if (label) {
+ ns.navTo('throughput', { devId: label });
+ $log.log(label);
+ }
+ };
+
+ if (!fs.isEmptyObject($scope.annots)) {
+ $scope.deviceIds = JSON.parse($scope.annots.deviceIds);
+ }
+
+ $scope.onChange = function (deviceId) {
+ ns.navTo('throughput', { devId: deviceId });
+ };
+ });
+
+ $scope.series = new Array(coresNb);
+ for (var i = 0; i < coresNb; i++) {
+ $scope.series[i] = 'Throughput-CPU ' + i;
+ }
+
+ $scope.labels = labels;
+ $scope.data = data;
+
+ // TODO: For some reason, this assignment does not work
+ $scope.chartColors = [
+ '#e6194b', // Red
+ '#3cb44b', // Green
+ '#ffe119', // Yellow
+ '#0082c8', // Blue
+ '#f58231', // Orange
+ '#808080', // Grey
+ '#fffac8', // Beige
+ '#aaffc3', // Mint
+ '#911eb4', // Purple
+ '#46f0f0', // Cyan
+ '#d2f53c', // Lime
+ '#800000', // Maroon
+ '#000000', // Black
+ '#f032e6', // Magenta
+ '#008080', // Teal
+ '#808000', // Olive
+ '#aa6e28' // Brown
+ ];
+ Chart.defaults.global.colours = $scope.chartColors;
+
+ $scope.showLoader = true;
+
+ $log.log('OvThroughputCtrl has been created');
+ }]);
+
+}());