Repository: eagle Updated Branches: refs/heads/master 5e4c937e3 -> ce53540f3
[EAGLE-884] JPM support queue trend view * Add trend chart * support queue drill down * Capacity base line Author: zombieJ <[email protected]> Closes #794 from zombieJ/EAGLE-884. Project: http://git-wip-us.apache.org/repos/asf/eagle/repo Commit: http://git-wip-us.apache.org/repos/asf/eagle/commit/ce53540f Tree: http://git-wip-us.apache.org/repos/asf/eagle/tree/ce53540f Diff: http://git-wip-us.apache.org/repos/asf/eagle/diff/ce53540f Branch: refs/heads/master Commit: ce53540f3d3128ed9aeec2112d184fa214edca45 Parents: 5e4c937 Author: zombieJ <[email protected]> Authored: Tue Feb 7 17:35:43 2017 +0800 Committer: zombieJ <[email protected]> Committed: Tue Feb 7 17:35:43 2017 +0800 ---------------------------------------------------------------------- .../main/webapp/app/apps/jpm/ctrl/queueCtrl.js | 230 +++++++++++++++++++ .../src/main/webapp/app/apps/jpm/index.js | 46 ++-- .../app/apps/jpm/partials/queue/overview.html | 57 +++++ .../main/webapp/app/apps/jpm/style/index.css | 8 + eagle-server/src/main/webapp/app/dev/index.html | 6 +- .../src/main/webapp/app/dev/public/css/main.css | 9 + .../app/dev/public/js/services/pageSrv.js | 41 +++- eagle-server/src/main/webapp/app/package.json | 2 +- 8 files changed, 378 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/queueCtrl.js ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/queueCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/queueCtrl.js new file mode 100644 index 0000000..70988f4 --- /dev/null +++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/queueCtrl.js @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +(function () { + /** + * `register` without params will load the module which using require + */ + register(function (jpmApp) { + var QUEUE_ROOT = 'unassigned'; + + jpmApp.controller("queueCtrl", function ($q, $wrapState, $element, $scope, $timeout, PageConfig, Time, Entity, JPM) { + var TEXT_MAX_CAPACITY = 'Max Capacity'; + var DISPLAY_MARK_NAME = ['Guaranteed Capacity', TEXT_MAX_CAPACITY]; + + $scope.site = $wrapState.param.siteId; + $scope.currentQueue = $wrapState.param.queue; + $scope.selectedQueue = ''; + $scope.selectedSubQueue = ''; + $scope.trendLoading = true; + + PageConfig.title = "Queue"; + PageConfig.subTitle = $scope.currentQueue || "Overview"; + var navPath = PageConfig.navPath = []; + + $scope.chartOption = { + tooltip: { + formatter: function (points) { + return points[0].name + "<br/>" + + $.map(points, function (point) { + return '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' + + point.seriesName + ": " + + Math.floor(point.value) + '%'; + }).reverse().join("<br/>"); + } + }, + yAxis: [{ + axisLabel: {formatter: function (value) { + return value + '%'; + }} + }] + }; + + // Load queue tree + (function () { + var startTime = new Time('startTime'); + var endTime = startTime.clone().add(1, 'h'); + JPM + .groups('RunningQueueService', { site: $scope.site, queue: $scope.currentQueue }, ['queue', 'parentQueue'], 'count', 60, startTime, endTime) + ._promise + .then(function (list) { + $.each(list, function (i, entity) { + var parent = entity.key[1]; + $scope.parentQueue = parent === QUEUE_ROOT ? null : parent; + + // Update navigation path + navPath.push( + { + title: $scope.parentQueue || 'queue list', + icon: 'sitemap', + param: ['siteId', 'startTime', 'endTime', $scope.parentQueue && 'queue=' + $scope.parentQueue], + path: "/jpm/queue" + }, + {title: $scope.currentQueue} + ); + + return false; + }); + }); + })(); + + // Refresh Trend Chart + $scope.refresh = function () { + $scope.trendLoading = true; + var startTime = new Time('startTime'); + var endTime = new Time('endTime'); + var intervalMin = Time.diffInterval(startTime, endTime) / 1000 / 60; + var condition = {site: $scope.site}; + if ($scope.currentQueue) condition.parentQueue = $scope.currentQueue; + + var promiseList = []; + // Load sub queue trend + promiseList.push(JPM.aggMetricsToEntities( + JPM.groups('RunningQueueService', condition, ['queue', 'parentQueue'], 'max(absoluteUsedCapacity)', intervalMin, startTime, endTime) + )._promise.then(function (list) { + $scope.subQueueList = []; + + // Filter top queue + var queueTrendSeries = $.map(list, function (subList) { + var tags = subList[0].tags; + if (!$scope.currentQueue && tags.parentQueue !== QUEUE_ROOT) return; + + var name = subList[0].tags.queue; + $scope.subQueueList.push(name); + return JPM.metricsToSeries(name, subList, { + stack: "queue", + areaStyle: {normal: {}} + }); + }); + + $scope.selectedQueue = common.getValueByPath(queueTrendSeries, ['0', 'name']); + $scope.refreshQueueStatistic(); + + return queueTrendSeries; + })); + + if ($scope.currentQueue) { + // Load current queue trend + promiseList.push(JPM.aggMetricsToEntities( + JPM.groups('RunningQueueService', + { site: $scope.site, queue: $scope.currentQueue }, + ['queue'], + 'max(absoluteUsedCapacity), max(memory), max(absoluteCapacity), max(absoluteMaxCapacity)', + intervalMin, startTime, endTime) + )._promise.then(function (list) { + // Filter top queue + return $.map(list, function (valueSeries, seriesIndex) { + var seriesName = valueSeries[0].tags.queue; + var option = {}; + if (seriesIndex === 0) { + option.areaStyle = {normal: {}}; + } else if (seriesIndex === 1) { + return; + } else { + seriesName = DISPLAY_MARK_NAME[seriesIndex - 2]; + var markDisplayText = seriesName; + + if (seriesName === TEXT_MAX_CAPACITY) { + var lastMemory = list[1][list[1].length - 1].value[0]; + var lastCapacity = list[0][list[0].length - 1].value[0]; + var lastMaxCapacity = list[seriesIndex][list[seriesIndex].length - 1].value[0]; + var lastMaxMemory = lastMemory / lastCapacity * lastMaxCapacity; + lastMaxMemory *= 1024 * 1024; + + if (!isNaN(lastMaxMemory)) markDisplayText += '(Memory:' + common.number.abbr(lastMaxMemory, true, 0) + ')'; + } + + var pointValue = common.getValueByPath(valueSeries, [valueSeries.length - 1, 'value', 0], 0); + option = { + markPoint: { + silent: true, + label: { + normal: { + formatter: function () { + return markDisplayText; + }, + position: 'insideRight', + textStyle: { + color: '#333', + fontSize: 12, + } + } + }, + data: [ + { + name: '', + coord: [valueSeries.length - 1, pointValue], + symbolSize: 30, + itemStyle: { + normal: {color: 'rgba(0,0,0,0)'} + } + } + ], + }, + lineStyle: { + normal: { type: 'dotted' } + } + }; + } + + return JPM.metricsToSeries(seriesName, valueSeries, option); + }); + })); + } + + $q.all(promiseList).then(function (seriesList) { + + var subQueuesSeries = seriesList[0]; + $scope.queueTrendSeries = subQueuesSeries; + + if (seriesList[1]) { + var queueSeries = [seriesList[1][0]]; + var capacitySeries = seriesList[1].slice(1); + + if (!subQueuesSeries.length) { + $scope.queueTrendSeries = queueSeries; + } + $scope.queueTrendSeries = $scope.queueTrendSeries.concat(capacitySeries); + } + $scope.trendLoading = false; + }); + }; + + // Refresh Queue static info + $scope.refreshQueueStatistic = function () { + var startTime = new Time('startTime'); + var endTime = new Time('endTime'); + }; + + // Go to sub queue view + $scope.switchToSubQueue = function () { + $wrapState.go(".", { + queue: $scope.selectedSubQueue, + startTime: Time.format('startTime'), + endTime: Time.format('endTime'), + }); + }; + + Time.onReload(function () { + $scope.refresh(); + }, $scope); + $scope.refresh(); + + }); + }); +})(); http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js index 7348853..234c539 100644 --- a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js +++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js @@ -55,18 +55,25 @@ reloadOnSearch: false, templateUrl: "partials/job/compare.html", controller: "compareCtrl" + + }).route("jpmQueue", { + url: "/jpm/queue?queue&startTime&endTime", + site: true, + templateUrl: "partials/queue/overview.html", + controller: "queueCtrl", + resolve: { time: true } }); jpmApp.portal({name: "YARN Jobs", icon: "taxi", list: [ {name: "Overview", path: "jpm/overview"}, {name: "Job Statistics", path: "jpm/statistics"}, - {name: "Job List", path: "jpm/list"} + {name: "Job List", path: "jpm/list"}, + {name: "Queue", path: "jpm/queue"} ]}, true); jpmApp.service("JPM", function ($q, $http, Time, Site, Application) { var JPM = window._JPM = {}; - // TODO: timestamp support JPM.QUERY_LIST = '${baseURL}/rest/entities?query=${query}[${condition}]{${fields}}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}'; JPM.QUERY_GROUPS = '${baseURL}/rest/entities?query=${query}[${condition}]<${groups}>{${field}}${order}${top}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}'; JPM.QUERY_GROUPS_INTERVAL = '${baseURL}/rest/entities?query=${query}[${condition}]<${groups}>{${field}}${order}${top}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}&intervalmin=${intervalMin}&timeSeries=true'; @@ -142,6 +149,7 @@ }; JPM.condition = function (condition) { + if (typeof condition === 'string') return condition; return $.map(condition, function (value, key) { return "@" + key + '="' + value + '"'; }).join(" AND "); @@ -189,7 +197,8 @@ _list._aggInfo = { groups: groups, startTime: Time(startTime).valueOf(), - interval: intervalMin * 60 * 1000 + interval: intervalMin * 60 * 1000, + order: fields[orderId] }; _list._promise.then(function () { if(top) _list.reverse(); @@ -310,7 +319,7 @@ _list._aggInfo = { groups: groups, startTime: Time(startTime).valueOf(), - interval: intervalMin * 60 * 1000 + interval: intervalMin * 60 * 1000, }; _list._promise.then(function () { _list.reverse(); @@ -331,19 +340,23 @@ tags[group] = obj.key[j]; }); - var _subList = $.map(obj.value[0], function (value, index) { - return { - timestamp: _startTime + index * _interval, - value: [value], - tags: tags - }; - }); + $.each(obj.value, function (j, values) { + if (list._aggInfo.order && j === list.length - 1) return; - if(flatten) { - _list.push.apply(_list, _subList); - } else { - _list.push(_subList); - } + var _subList = $.map(values, function (value, index) { + return { + timestamp: _startTime + index * _interval, + value: [value], + tags: tags + }; + }); + + if(flatten) { + _list.push.apply(_list, _subList); + } else { + _list.push(_subList); + } + }); }); _list.done = true; return _list; @@ -484,4 +497,5 @@ jpmApp.require("ctrl/detailCtrl.js"); jpmApp.require("ctrl/jobTaskCtrl.js"); jpmApp.require("ctrl/compareCtrl.js"); + jpmApp.require("ctrl/queueCtrl.js"); })(); http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/queue/overview.html ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/queue/overview.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/queue/overview.html new file mode 100644 index 0000000..732fbb2 --- /dev/null +++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/queue/overview.html @@ -0,0 +1,57 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + --> + +<div class="box box-primary"> + <div class="box-header with-border"> + <h3 class="box-title"> + Queue Capacity Trend + </h3> + <div class="box-tools" ng-show="subQueueList.length"> + <select class="form-control input-sm" ng-model="selectedSubQueue" ng-change="switchToSubQueue()"> + <option value="">[View Sub Queue]</option> + <option ng-repeat="queue in subQueueList track by $index" value="{{queue}}">{{queue}}</option> + </select> + </div> + </div> + <div class="box-body"> + <div class="jpm-chart"> + <div chart class="jpm-chart-container" series="queueTrendSeries" option="chartOption"></div> + <h1 class="jpm-chart-tip" ng-if="queueTrendSeries && queueTrendSeries.length === 0">No Data</h1> + </div> + </div> + + <div ng-if="trendLoading" class="overlay"> + <i class="fa fa-refresh fa-spin"></i> + </div> +</div> + +<!-- div class="nav-tabs-custom"> + <ul class="nav nav-tabs"> + <li class="active"><a data-toggle="tab" href="#queueUser">User</a></li> + <li><a data-toggle="tab" href="#queueJob">Job</a></li> + + <li class="pull-right"> + <select class="form-control" ng-model="selectedQueue" ng-change="refreshQueueStatistic()"> + <option ng-repeat="queue in subQueueList track by $index" value="{{queue}}">{{queue}}</option> + </select> + </li> + </ul> + <div class="tab-content"> + <div class="tab-pane active"></div> + </div> +</div --> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css index fbe238f..f63d67c 100644 --- a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css +++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css @@ -39,6 +39,14 @@ height: 350px; } +.jpm-chart .jpm-chart-tip { + position: absolute; + top: 50%; + margin: -20px 0 0 0; + width: 100%; + text-align: center; +} + .with-border .jpm-chart { padding-bottom: 15px; margin-bottom: 15px; http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-server/src/main/webapp/app/dev/index.html ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/index.html b/eagle-server/src/main/webapp/app/dev/index.html index 74d5c57..26b12ee 100644 --- a/eagle-server/src/main/webapp/app/dev/index.html +++ b/eagle-server/src/main/webapp/app/dev/index.html @@ -162,13 +162,13 @@ <ol class="breadcrumb"> - <li ng-repeat="navPath in PageConfig.navPath"> + <li ng-repeat="navPath in PageConfig.getNavPath() track by $index"> <span ng-if="!navPath.path"> - <span class="fa fa-home" ng-if="$first"></span> + <span class="fa fa-{{navPath.icon || 'home'}}" ng-if="$first"></span> {{navPath.title || navPath.path}} </span> <a ng-if="navPath.path" ng-href="#{{navPath.path}}"> - <span class="fa fa-home" ng-if="$first"></span> + <span class="fa fa-{{navPath.icon || 'home'}}" ng-if="$first"></span> {{navPath.title || navPath.path}} </a> </li> http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-server/src/main/webapp/app/dev/public/css/main.css ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/public/css/main.css b/eagle-server/src/main/webapp/app/dev/public/css/main.css index 895d514..8583513 100644 --- a/eagle-server/src/main/webapp/app/dev/public/css/main.css +++ b/eagle-server/src/main/webapp/app/dev/public/css/main.css @@ -277,6 +277,15 @@ ul.stepGuide li > .title { overflow-x: auto; } +.box-header > .box-tools { + white-space: nowrap; +} + +.box-header > .box-tools > .form-control { + display: inline-block; + width: initial; +} + /* ======================================================================== * = Tab = * ======================================================================== */ http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js index 2c61087..f4ea2f4 100644 --- a/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js +++ b/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js @@ -24,7 +24,7 @@ // ============================================================ // = Page = // ============================================================ - serviceModule.service('PageConfig', function() { + serviceModule.service('PageConfig', function($wrapState) { function PageConfig() { } @@ -35,6 +35,45 @@ PageConfig.hideTitle = false; }; + var cachedNavPath = []; + var cachedGenNavPath = []; + PageConfig.getNavPath = function () { + if (cachedNavPath !== PageConfig.navPath || cachedGenNavPath.length !== cachedNavPath.length) { + cachedNavPath = PageConfig.navPath; + cachedGenNavPath = $.map(cachedNavPath, function (navPath) { + var pathEntity = $.extend({}, navPath); + + if (!pathEntity.path || !pathEntity.param) return pathEntity; + + // Parse param as `key=value` format + var params = {}; + $.each(pathEntity.param, function (i, param) { + if (!param) return; + + var match = param.match(/^([^=]+)(=(.*))?$/); + var key = match[1]; + var value = match[3]; + params[key] = value !== undefined ? value : $wrapState.param[key]; + }); + + // Generate path with param + var path = "/" + pathEntity.path.replace(/^[\\\/]/, ""); + if (params.siteId) { + pathEntity.path = "/site/" + $wrapState.param.siteId + path; + delete params.siteId; + } else { + pathEntity.path = path; + } + pathEntity.path += '?' + $.map(params, function (value, key) { + return key + '=' + value; + }).join('&'); + + return pathEntity; + }); + } + return cachedGenNavPath; + }; + return PageConfig; }); http://git-wip-us.apache.org/repos/asf/eagle/blob/ce53540f/eagle-server/src/main/webapp/app/package.json ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/package.json b/eagle-server/src/main/webapp/app/package.json index 4ee3eda..2c5d272 100644 --- a/eagle-server/src/main/webapp/app/package.json +++ b/eagle-server/src/main/webapp/app/package.json @@ -23,7 +23,7 @@ "angular-ui-router": "0.3.1", "bootstrap": "3.3.6", "d3": "3.5.16", - "echarts": "^3.3.2", + "echarts": "^3.4.0", "font-awesome": "4.7.0", "jquery": "2.2.4", "jquery-slimscroll": "1.3.6",
