You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eagle.apache.org by ji...@apache.org on 2017/02/07 09:36:03 UTC

eagle git commit: [EAGLE-884] JPM support queue trend view

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 <sm...@gmail.com>

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 <sm...@gmail.com>
Authored: Tue Feb 7 17:35:43 2017 +0800
Committer: zombieJ <sm...@gmail.com>
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",