From 673248231b50592df9fb88181704e29768c71aff Mon Sep 17 00:00:00 2001 From: Kinshuk Bairagi Date: Wed, 13 Apr 2022 15:04:23 +0530 Subject: [PATCH] Admin UI Co-Authored-By: Sangam Kumar Jain --- internal/www/css/main.css | 9 ++ internal/www/header-nav.html | 7 ++ internal/www/home.html | 66 +++++++++++++ internal/www/index.html | 67 +++++++++++++ internal/www/js/config.js | 9 ++ internal/www/js/controllers.js | 170 +++++++++++++++++++++++++++++++++ internal/www/js/directives.js | 13 +++ internal/www/js/lib.js | 75 +++++++++++++++ internal/www/js/main.js | 25 +++++ internal/www/js/services.js | 12 +++ internal/www/run.sh | 3 + 11 files changed, 456 insertions(+) create mode 100644 internal/www/css/main.css create mode 100644 internal/www/header-nav.html create mode 100644 internal/www/home.html create mode 100644 internal/www/index.html create mode 100644 internal/www/js/config.js create mode 100644 internal/www/js/controllers.js create mode 100644 internal/www/js/directives.js create mode 100644 internal/www/js/lib.js create mode 100644 internal/www/js/main.js create mode 100644 internal/www/js/services.js create mode 100644 internal/www/run.sh diff --git a/internal/www/css/main.css b/internal/www/css/main.css new file mode 100644 index 00000000..54945c61 --- /dev/null +++ b/internal/www/css/main.css @@ -0,0 +1,9 @@ +.chart.c3{ + width:33%; + float: left; +} + +.font-bold { + font-weight: bold; + font-size: 14px; +} diff --git a/internal/www/header-nav.html b/internal/www/header-nav.html new file mode 100644 index 00000000..525baf51 --- /dev/null +++ b/internal/www/header-nav.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/internal/www/home.html b/internal/www/home.html new file mode 100644 index 00000000..e64a8f8f --- /dev/null +++ b/internal/www/home.html @@ -0,0 +1,66 @@ + + +
+
+
+
+

{{througputParser(getMapValSum(stat["dkv_req_count"]))}}

+
+
+
+
+
+
+
+
+
{{op}}

+

{{ througputParser(stat["dkv_req_count"][op]) }} rps

+
+ + + + + + + +
p50 {{latencyParser(stat["dkv_latency"][op].p50)}}
p90 {{latencyParser(stat["dkv_latency"][op].p90)}}
p99 {{latencyParser(stat["dkv_latency"][op].p99)}}
+
+
+
+
+
+
{{op}}

+

{{ througputParser(stat["dkv_req_count"][op]) }} rps

+
+ + + + + + + +
p50 {{latencyParser(stat["dkv_latency"][op].p50)}}
p90 {{latencyParser(stat["dkv_latency"][op].p90)}}
p99 {{latencyParser(stat["dkv_latency"][op].p99)}}
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
{{j}}
+
+
diff --git a/internal/www/index.html b/internal/www/index.html new file mode 100644 index 00000000..728567f5 --- /dev/null +++ b/internal/www/index.html @@ -0,0 +1,67 @@ + + + + DKV Dashboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/internal/www/js/config.js b/internal/www/js/config.js new file mode 100644 index 00000000..2a41e88d --- /dev/null +++ b/internal/www/js/config.js @@ -0,0 +1,9 @@ +CONFIG={ + "router": { + "Home":{ + "path":"/", + "controller":"HomeCtrl", + "templateUrl": "home.html", + }, + }, +}; diff --git a/internal/www/js/controllers.js b/internal/www/js/controllers.js new file mode 100644 index 00000000..c0c06952 --- /dev/null +++ b/internal/www/js/controllers.js @@ -0,0 +1,170 @@ +(function() { + + + angular.module("DKV.Dashboard.Controllers", ['DKV.Dashboard.Services','chart.js', 'dataGrid', 'pagination', 'ngCookies' ,'ngclipboard','mgcrea.ngStrap','ngSanitize']) + + .config(['ChartJsProvider', function (ChartJsProvider) { + ChartJsProvider.setOptions({ + responsive: false + }); + ChartJsProvider.setOptions('line', { + showLines: true, + animation: { + duration:0 + }, + legend: { + display: true, + position: 'bottom', + labels:{ + boxWidth:20 + } + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + }, + gridLines: { + display:false + } + }], + xAxes: [{ + type: 'time', + ticks: { + maxRotation: 0, + minRotation: 0, + autoSkip: true, + maxTickLimit: 3 + }, + gridLines: { + display:false + } + }] + }, + tooltips: { + intersect:false, + callbacks : { + title: function(tooltipItems, data) { + return tooltipItems[0].xLabel.toLocaleTimeString(); + } + } + }, + elements: { + line: { + fill: false, + borderWidth: 1 + }, + point: { + radius: 1 + } + + } + }); + }]) + + + .controller("HomeCtrl", [ '$location', '$scope', '$rootScope','$timeout','$sce','DKVService', + function($location, $scope, $rootScope ,$timeout, $sce, DKVService) { + /* discover master nodes from here*/ + $scope.masters = { + "m1":"http://127.0.0.1:7081", + "m2":"http://127.0.0.1:7082", + "m3":"http://127.0.0.1:7083", + } + + $scope.througputParser = througputParser + $scope.latencyParser = latencyParser + $scope.getMapValSum = getMapValSum + + $scope.ops1 = ["getLin","getSeq","mgetLin","mgetSeq",] + $scope.ops2 = ["put","mput","cas","del"] + + $scope.source = new EventSource(DKVService.GetClusterData($scope.masters.m1)); + $scope.source.addEventListener('message', function(e) { + data = JSON.parse(e.data) + $scope.stat = data["global"] + delete data["global"] + $scope.stats = data + /* initialize after the first population */ + if ( $scope.series === undefined ) { + initMetric() + } + + console.log(new Date($scope.stat.ts)) + console.log(new Date()) + $scope.tsEvent.push(new Date($scope.stat.ts)); + + for ( i = 0 ; i < $scope.series.length; i++ ) { + $scope.ts.rrate.data[i].push(getMapValSum($scope.stats[$scope.series[i]]["dkv_req_count"])) + $scope.ts.error.data[i].push(getMapValSum($scope.stats[$scope.series[i]]["dkv_req_count"]) / 100) + avgLatency = getAvgLatency($scope.stats[$scope.series[i]]["dkv_latency"]) + $scope.ts.p50.data[i].push(avgLatency.p50) + $scope.ts.p99.data[i].push(avgLatency.p99) + } + + if ( $scope.tsEvent.length > 60 ) { + $scope.tsEvent.splice(0,1); + for ( i = 0 ; i < $scope.series.length; i++ ) { + $scope.ts.rrate.data[i].splice(0,1); + $scope.ts.error.data[i].splice(0,1); + $scope.ts.p50.data[i].splice(0,1); + $scope.ts.p99.data[i].splice(0,1); + } + } + + }); + + function createStat(parser,titleText) { + var stat = { series : $scope.series, colors: $scope.colors, label: $scope.tsEvent, data : getEmptyArray($scope.series.length), + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + yAxes: [{ + ticks: { + callback: function (value, index, values) { + return parser(value) + } + }, + }] + }, + title: { + display: true, text: titleText, + fontSize: 13, fontColor: "#000000" + }, + legend: { + display: false + }, + elements: { + line: { + fill: false, + borderWidth: 1.7 + }, + point: { + radius: 0 + } + + } + } + }; + return stat + } + function initMetric() { + $scope.ts = {} + $scope.tsEvent = [] + $scope.series = Object.keys($scope.stats) + $scope.colors = [colorGreen,colorOrange,colorBlue,colorRed,colorViolet,colorDeepBlue] + + $scope.ts.rrate = createStat(througputParser,"request rate") + $scope.ts.error = createStat(percentageParser,"error rate") + $scope.ts.p50 = createStat(latencyParser,"50th percentile") + $scope.ts.p99 = createStat(latencyParser,"99th percentile") + } + + window.setInterval(function(){ + $scope.$apply(function () { + }); + }, 1000); + } + ]) +}()); diff --git a/internal/www/js/directives.js b/internal/www/js/directives.js new file mode 100644 index 00000000..104cc171 --- /dev/null +++ b/internal/www/js/directives.js @@ -0,0 +1,13 @@ +(function() { + + angular.module("DKV.Dashboard.Directives", ["DKV.Dashboard.Controllers"]) + .directive("headerNav", function() { + return { + restrict: "E", + templateUrl: "header-nav.html", + controller: function($scope, $rootScope, $location, $sce) { + $scope.config = CONFIG; + } + }; + }) +}()); diff --git a/internal/www/js/lib.js b/internal/www/js/lib.js new file mode 100644 index 00000000..85feb6a2 --- /dev/null +++ b/internal/www/js/lib.js @@ -0,0 +1,75 @@ +var througputParser = function (d) { + if ( d === undefined ) { + return 0 + } + var sizes = ['', 'K', 'M', 'B', 'T']; + if (d < 1) return d.toFixed(1); + var i = Math.floor(Math.log(d) / Math.log(1000)); + var base = d / Math.pow(1000, i); + if ( Math.round(base) === base ){ + return base + ' ' + sizes[i] + } + return base.toFixed(1) + ' ' + sizes[i]; +}; + +var latencyParser = function (d) { + if ( d === undefined ) { + return 0 + } + d = d * 1000000 + var sizes = ['µs', 'ms', 's']; + var i = Math.floor(Math.log(d) / Math.log(1000)); + var base = d / Math.pow(1000, i); + if ( Math.round(base) === base ){ + return base + ' ' + sizes[i] + } + return base.toFixed(1) + ' ' + sizes[i]; +}; + +var getMapValSum = function (d) { + let sum = 0; + for (let key in d) { + sum += d[key]; + } + return sum +} + +var getAvgLatency = function (d) { + latency = { p50: 0 , p90 : 0 , p99 : 0} + + console.log(d) + for (let key in d ) { + latency.p50 += d[key].p50 + latency.p90 += d[key].p90 + latency.p99 += d[key].p99 + } + console.log(latency) + + count = Object.keys(d).length + latency.p50 /= count + latency.p90 /= count + latency.p99 /= count + + console.log(latency) + return latency +} + +var percentageParser = function (d) { + if (Math.round(d) === d) return Math.round(d)+" %"; + return d.toFixed(2)+" %"; +} + +function getEmptyArray(size){ + var data = []; + for (i = 0; i < size; i++) { + data.push([]); + } + return data; +} +var colorGreen = {backgroundColor: '#62a043', borderColor: '#62a043', hoverBackgroundColor: '#62a043', hoverBorderColor: '#62a043'}; +var colorOrange = {backgroundColor: '#dc923f', borderColor: '#dc923f', hoverBackgroundColor: '#dc923f', hoverBorderColor: '#dc923f'}; +var colorBlue = {backgroundColor: '#0a9bdc', borderColor: '#0a9bdc', hoverBackgroundColor: '#0a9bdc', hoverBorderColor: '#0a9bdc'}; +var colorRed = {backgroundColor: '#bc4040', borderColor: '#bc4040', hoverBackgroundColor: '#bc4040', hoverBorderColor: '#bc4040'}; +var colorYellow = {backgroundColor: '#ffe50c', borderColor: '#ffe50c', hoverBackgroundColor: '#ffe50c', hoverBorderColor: '#ffe50c'}; +var colorViolet = {backgroundColor: '#9263ff', borderColor: '#9263ff', hoverBackgroundColor: '#9263ff', hoverBorderColor: '#9263ff'}; +var colorDeepBlue = {backgroundColor: '#0001ff', borderColor: '#0001ff', hoverBackgroundColor: '#0001ff', hoverBorderColor: '#0001ff'}; diff --git a/internal/www/js/main.js b/internal/www/js/main.js new file mode 100644 index 00000000..75ab49d5 --- /dev/null +++ b/internal/www/js/main.js @@ -0,0 +1,25 @@ +(function() { + + angular.module("DKV.Dashboard",['ngRoute', 'DKV.Dashboard.Services', 'DKV.Dashboard.Controllers', 'DKV.Dashboard.Directives']) + + /** routes */ + .config( function($routeProvider,$locationProvider) { + for (id in CONFIG.router) { + $routeProvider.when(CONFIG.router[id].path, { + templateUrl: CONFIG.router[id].templateUrl, + controller: CONFIG.router[id].controller + }); + } + $routeProvider.otherwise({ + redirectTo: "/" + }); + $locationProvider.html5Mode(true); + }) + + .run(['$rootScope', '$location', '$anchorScroll', function($rootScope, $location, $anchorScroll) { + $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) { + if ($location.hash()) $anchorScroll(); + }); + }]); + +}()); diff --git a/internal/www/js/services.js b/internal/www/js/services.js new file mode 100644 index 00000000..31b81464 --- /dev/null +++ b/internal/www/js/services.js @@ -0,0 +1,12 @@ +(function() { + angular.module("DKV.Dashboard.Services", []) + + .factory("DKVService", [ '$http','$sce', function($http,$sce) { + return { + GetClusterData: function(endpoint) { + return $sce.trustAsResourceUrl(endpoint+"/metrics/cluster") + }, + } + }]) +}()); + diff --git a/internal/www/run.sh b/internal/www/run.sh new file mode 100644 index 00000000..f4a4c5fb --- /dev/null +++ b/internal/www/run.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +python -m SimpleHTTPServer 8989 \ No newline at end of file