summary.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with this
  4. * work for additional information regarding copyright ownership. The ASF
  5. * licenses this file to you under the Apache License, Version 2.0 (the
  6. * "License"); you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. */
  17. var App = require('app');
  18. var batchUtils = require('utils/batch_scheduled_requests');
  19. require('views/main/service/service');
  20. require('data/service_graph_config');
  21. App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
  22. templateName: require('templates/main/service/info/summary'),
  23. /**
  24. * @property {Number} chunkSize - number of columns in Metrics section
  25. */
  26. chunkSize: 5,
  27. attributes:null,
  28. /**
  29. * @property {String} templatePathPrefix - base path for custom templates
  30. * if you want to add custom template, add <service_name>.hbs file to
  31. * templates/main/service/info/summary folder.
  32. */
  33. templatePathPrefix: 'templates/main/service/info/summary/',
  34. /** @property {Ember.View} serviceSummaryView - view to embed, computed in
  35. * <code>loadServiceSummary()</code>
  36. */
  37. serviceSummaryView: null,
  38. /**
  39. * @property {Object} serviceCustomViewsMap - custom views to embed
  40. *
  41. */
  42. serviceCustomViewsMap: function() {
  43. return {
  44. HBASE: App.MainDashboardServiceHbaseView,
  45. HDFS: App.MainDashboardServiceHdfsView,
  46. STORM: App.MainDashboardServiceStormView,
  47. YARN: App.MainDashboardServiceYARNView,
  48. RANGER: App.MainDashboardServiceRangerView,
  49. FLUME: Em.View.extend({
  50. template: Em.Handlebars.compile('' +
  51. '<tr>' +
  52. '<td>' +
  53. '{{view App.MainDashboardServiceFlumeView serviceBinding="view.service"}}' +
  54. '</td>' +
  55. '</tr>')
  56. })
  57. }
  58. }.property('serviceName'),
  59. /** @property collapsedMetrics {object[]} - metrics list for collapsed section
  60. * structure of element from list:
  61. * @property {string} header - title for section
  62. * @property {string} id - id of section for toggling, like: metric1
  63. * @property {string} toggleIndex - passed to `data-parent` attribute, like: #metric1
  64. * @property {Em.View} metricView - metric view class
  65. */
  66. collapsedSections: null,
  67. servicesHaveClients: function() {
  68. return App.get('services.hasClient');
  69. }.property('App.services.hasClient'),
  70. hasManyServers: function () {
  71. return this.get('servers').length > 1;
  72. }.property('servers'),
  73. clientsHostText: function () {
  74. if (this.get('controller.content.installedClients').length == 0) {
  75. return '';
  76. } else if (this.get("hasManyClients")) {
  77. return Em.I18n.t('services.service.summary.viewHosts');
  78. } else {
  79. return Em.I18n.t('services.service.summary.viewHost');
  80. }
  81. }.property("hasManyClients"),
  82. hasManyClients: function () {
  83. return this.get('controller.content.installedClients').length > 1;
  84. }.property('service.installedClients'),
  85. servers: function () {
  86. var result = [];
  87. var service = this.get('controller.content');
  88. if (service.get("id") == "ZOOKEEPER" || service.get("id") == "FLUME") {
  89. var servers = service.get('hostComponents').filterProperty('isMaster');
  90. if (servers.length > 0) {
  91. result = [
  92. {
  93. 'host': servers[0].get('displayName'),
  94. 'isComma': false,
  95. 'isAnd': false
  96. }
  97. ];
  98. }
  99. if (servers.length > 1) {
  100. result[0].isComma = true;
  101. result.push({
  102. 'host': servers[1].get('displayName'),
  103. 'isComma': false,
  104. 'isAnd': false
  105. });
  106. }
  107. if (servers.length > 2) {
  108. result[1].isAnd = true;
  109. result.push({
  110. 'host': Em.I18n.t('services.service.info.summary.serversHostCount').format(servers.length - 2),
  111. 'isComma': false,
  112. 'isAnd': false
  113. });
  114. }
  115. }
  116. return result;
  117. }.property('controller.content'),
  118. historyServerUI: function(){
  119. var service=this.get('controller.content');
  120. return (App.singleNodeInstall ? "http://" + App.singleNodeAlias + ":19888" : "http://" + service.get("hostComponents").findProperty('isMaster', true).get("host").get("publicHostName")+":19888");
  121. }.property('controller.content'),
  122. /**
  123. * Property related to ZOOKEEPER service, is unused for other services
  124. * @return {Object}
  125. */
  126. serversHost: function() {
  127. var service = this.get('controller.content');
  128. if (service.get("id") == "ZOOKEEPER" || service.get("id") == "FLUME") {
  129. var servers = service.get('hostComponents').filterProperty('isMaster');
  130. if (servers.length > 0) {
  131. return servers[0];
  132. }
  133. }
  134. return {};
  135. }.property('controller.content'),
  136. mastersObj: function() {
  137. return this.get('service.hostComponents').filterProperty('isMaster', true);
  138. }.property('service'),
  139. /**
  140. * Contain array with list of client components models <code>App.ClientComponent</code>.
  141. * @type {Array}
  142. */
  143. clientObj: function () {
  144. var clientComponents = this.get('controller.content.clientComponents').toArray();
  145. return clientComponents.get('length') ? clientComponents : [];
  146. }.property('service.clientComponents.@each.totalCount'),
  147. /**
  148. * Contain array with list of slave components models <code>App.SlaveComponent</code>.
  149. * @type {Array}
  150. */
  151. slavesObj: function() {
  152. var slaveComponents = this.get('controller.content.slaveComponents').toArray();
  153. return slaveComponents.get('length') ? slaveComponents : [];
  154. }.property('service.slaveComponents.@each.totalCount', 'service.slaveComponents.@each.startedCount'),
  155. data:{
  156. hive:{
  157. "database":"PostgreSQL",
  158. "databaseName":"hive",
  159. "user":"hive"
  160. }
  161. },
  162. /**
  163. * Wrapper for displayName. used to render correct display name for mysql_server
  164. */
  165. componentNameView: Ember.View.extend({
  166. template: Ember.Handlebars.compile('{{view.displayName}}'),
  167. comp : null,
  168. displayName: function(){
  169. if(this.get('comp.componentName') == 'MYSQL_SERVER'){
  170. return this.t('services.hive.databaseComponent');
  171. }
  172. return this.get('comp.displayName');
  173. }.property('comp')
  174. }),
  175. service:function () {
  176. var svc = this.get('controller.content');
  177. var svcName = svc.get('serviceName');
  178. if (svcName) {
  179. switch (svcName.toLowerCase()) {
  180. case 'hdfs':
  181. svc = App.HDFSService.find().objectAt(0);
  182. break;
  183. case 'yarn':
  184. svc = App.YARNService.find().objectAt(0);
  185. break;
  186. case 'hbase':
  187. svc = App.HBaseService.find().objectAt(0);
  188. break;
  189. case 'flume':
  190. svc = App.FlumeService.find().objectAt(0);
  191. break;
  192. case 'storm':
  193. svc = App.StormService.find().objectAt(0);
  194. break;
  195. default:
  196. break;
  197. }
  198. }
  199. return svc;
  200. }.property('controller.content.serviceName').volatile(),
  201. isHide:true,
  202. moreStatsView:Em.View.extend({
  203. tagName:"a",
  204. template:Ember.Handlebars.compile('{{t services.service.summary.moreStats}}'),
  205. attributeBindings:[ 'href' ],
  206. classNames:[ 'more-stats' ],
  207. click:function (event) {
  208. this._parentView._parentView.set('isHide', false);
  209. this.remove();
  210. },
  211. href:'javascript:void(null)'
  212. }),
  213. serviceName:function () {
  214. return this.get('service.serviceName');
  215. }.property('service'),
  216. oldServiceName:'',
  217. /*
  218. * 'Restart Required bar' start
  219. */
  220. componentsCount: null,
  221. hostsCount: null,
  222. alertsCount: function () {
  223. return this.get('controller.content.alertsCount');
  224. }.property('controller.content.alertsCount'),
  225. hasCriticalAlerts: function () {
  226. return this.get('controller.content.hasCriticalAlerts');
  227. }.property('controller.content.alertsCount'),
  228. /**
  229. * Define if service has alert definitions defined
  230. * @type {Boolean}
  231. */
  232. hasAlertDefinitions: function () {
  233. return App.AlertDefinition.find().someProperty('serviceName', this.get('controller.content.serviceName'));
  234. }.property('controller.content.serviceName'),
  235. updateComponentInformation: function() {
  236. var hc = this.get('controller.content.restartRequiredHostsAndComponents');
  237. var hostsCount = 0;
  238. var componentsCount = 0;
  239. for (var host in hc) {
  240. hostsCount++;
  241. componentsCount += hc[host].length;
  242. }
  243. this.set('componentsCount', componentsCount);
  244. this.set('hostsCount', hostsCount);
  245. }.observes('controller.content.restartRequiredHostsAndComponents'),
  246. rollingRestartSlaveComponentName : function() {
  247. return batchUtils.getRollingRestartComponentName(this.get('serviceName'));
  248. }.property('serviceName'),
  249. rollingRestartActionName : function() {
  250. var label = null;
  251. var componentName = this.get('rollingRestartSlaveComponentName');
  252. if (componentName) {
  253. label = Em.I18n.t('rollingrestart.dialog.title').format(App.format.role(componentName));
  254. }
  255. return label;
  256. }.property('rollingRestartSlaveComponentName'),
  257. restartAllStaleConfigComponents: function () {
  258. var self = this;
  259. var serviceDisplayName = this.get('service.displayName');
  260. var bodyMessage = Em.Object.create({
  261. confirmMsg: Em.I18n.t('services.service.restartAll.confirmMsg').format(serviceDisplayName),
  262. confirmButton: Em.I18n.t('services.service.restartAll.confirmButton'),
  263. additionalWarningMsg: this.get('service.passiveState') === 'OFF' ? Em.I18n.t('services.service.restartAll.warningMsg.turnOnMM').format(serviceDisplayName) : null
  264. });
  265. return App.showConfirmationFeedBackPopup(function (query) {
  266. var selectedService = self.get('service.id');
  267. batchUtils.restartAllServiceHostComponents(selectedService, true, query);
  268. }, bodyMessage);
  269. },
  270. rollingRestartStaleConfigSlaveComponents: function (componentName) {
  271. batchUtils.launchHostComponentRollingRestart(componentName.context, this.get('service.displayName'), this.get('service.passiveState') === "ON", true);
  272. },
  273. /*
  274. * 'Restart Required bar' ended
  275. */
  276. /*
  277. * Find the graph class associated with the graph name, and split
  278. * the array into sections of 5 for displaying on the page
  279. * (will only display rows with 5 items)
  280. */
  281. constructGraphObjects: function(graphNames) {
  282. var result = [], graphObjects = [], chunkSize = this.get('chunkSize');
  283. var self = this;
  284. if (!graphNames) {
  285. self.set('serviceMetricGraphs', []);
  286. self.set('isServiceMetricLoaded', true);
  287. return;
  288. }
  289. // load time range for current service from server
  290. self.getUserPref(self.get('persistKey')).complete(function () {
  291. var index = self.get('currentTimeRangeIndex');
  292. graphNames.forEach(function(graphName) {
  293. graphObjects.push(App["ChartServiceMetrics" + graphName].extend({
  294. currentTimeIndex : index
  295. }));
  296. });
  297. if (App.get('supports.customizedWidgets')) {
  298. graphObjects.push(Ember.View.extend({
  299. classNames: ['last-child'],
  300. template: Ember.Handlebars.compile('<div id="add-widget-action-box"><i class="icon-plus"></i></div>')
  301. }));
  302. }
  303. while(graphObjects.length) {
  304. result.push(graphObjects.splice(0, chunkSize));
  305. }
  306. self.set('serviceMetricGraphs', result);
  307. self.set('isServiceMetricLoaded', true);
  308. });
  309. },
  310. /**
  311. * Contains graphs for this particular service
  312. */
  313. serviceMetricGraphs: [],
  314. isServiceMetricLoaded: false,
  315. /**
  316. * Key-name to store time range in Persist
  317. * @type {string}
  318. */
  319. persistKey: function () {
  320. return 'time-range-service-' + this.get('service.serviceName');
  321. }.property(),
  322. getUserPrefSuccessCallback: function (response, request, data) {
  323. if (response) {
  324. console.log('Got persist value from server with key ' + data.key + '. Value is: ' + response);
  325. this.set('currentTimeRangeIndex', response);
  326. }
  327. },
  328. getUserPrefErrorCallback: function (request) {
  329. if (request.status == 404) {
  330. console.log('Persist did NOT find the key');
  331. this.postUserPref(this.get('persistKey'), 0);
  332. this.set('currentTimeRangeIndex', 0);
  333. }
  334. },
  335. /**
  336. * list of static actions of widget
  337. * @type {Array}
  338. */
  339. staticWidgetActions: [
  340. Em.Object.create({
  341. label: Em.I18n.t('dashboard.widgets.layout.save'),
  342. class: 'icon-download-alt',
  343. action: 'saveLayout',
  344. isAction: true
  345. }),
  346. Em.Object.create({
  347. label: Em.I18n.t('dashboard.widgets.layout.import'),
  348. class: 'icon-file',
  349. isAction: true,
  350. layouts: App.WidgetLayout.find()
  351. }),
  352. Em.Object.create({
  353. label: Em.I18n.t('dashboard.widgets.create'),
  354. class: 'icon-plus',
  355. action: 'createWidget',
  356. isAction: true
  357. })
  358. ],
  359. /**
  360. * @type {Array}
  361. */
  362. widgetActions: function() {
  363. var options = [];
  364. options.pushObjects(this.get('staticWidgetActions'));
  365. this.get('controller.widgets').forEach(function (widget) {
  366. options.push(Em.Object.create({
  367. label: widget.get('displayName'),
  368. isVisible: widget.get('isVisible'),
  369. selected: true
  370. }));
  371. }, this);
  372. return options;
  373. }.property('controller.widgets.length'),
  374. /**
  375. * call action function defined in controller
  376. * @param event
  377. */
  378. doWidgetAction: function(event) {
  379. if($.isFunction(this.get('controller')[event.context])) {
  380. this.get('controller')[event.context].apply(this.get('controller'));
  381. }
  382. },
  383. /**
  384. * time range options for service metrics, a dropdown will list all options
  385. */
  386. timeRangeOptions: [
  387. {index: 0, name: Em.I18n.t('graphs.timeRange.hour'), seconds: 3600},
  388. {index: 1, name: Em.I18n.t('graphs.timeRange.twoHours'), seconds: 7200},
  389. {index: 2, name: Em.I18n.t('graphs.timeRange.fourHours'), seconds: 14400},
  390. {index: 3, name: Em.I18n.t('graphs.timeRange.twelveHours'), seconds: 43200},
  391. {index: 4, name: Em.I18n.t('graphs.timeRange.day'), seconds: 86400},
  392. {index: 5, name: Em.I18n.t('graphs.timeRange.week'), seconds: 604800},
  393. {index: 6, name: Em.I18n.t('graphs.timeRange.month'), seconds: 2592000},
  394. {index: 7, name: Em.I18n.t('graphs.timeRange.year'), seconds: 31104000}
  395. ],
  396. currentTimeRangeIndex: 0,
  397. currentTimeRange: function() {
  398. return this.get('timeRangeOptions').objectAt(this.get('currentTimeRangeIndex'));
  399. }.property('currentTimeRangeIndex'),
  400. /**
  401. * onclick handler for a time range option
  402. */
  403. setTimeRange: function (event) {
  404. var self = this;
  405. if (event && event.context) {
  406. self.postUserPref(self.get('persistKey'), event.context.index);
  407. self.set('currentTimeRangeIndex', event.context.index);
  408. var svcName = self.get('service.serviceName');
  409. if (svcName) {
  410. var result = [], graphObjects = [], chunkSize = this.get('chunkSize');
  411. App.service_graph_config[svcName.toLowerCase()].forEach(function(graphName) {
  412. graphObjects.push(App["ChartServiceMetrics" + graphName].extend({
  413. currentTimeIndex : event.context.index
  414. }));
  415. });
  416. while(graphObjects.length) {
  417. result.push(graphObjects.splice(0, chunkSize));
  418. }
  419. self.set('serviceMetricGraphs', result);
  420. self.set('isServiceMetricLoaded', true);
  421. }
  422. }
  423. },
  424. loadServiceSummary: function () {
  425. var serviceName = this.get('serviceName');
  426. var serviceSummaryView = null;
  427. if (!serviceName) {
  428. return;
  429. }
  430. if (this.get('oldServiceName')) {
  431. // do not delete it!
  432. return;
  433. }
  434. var customServiceView = this.get('serviceCustomViewsMap')[serviceName];
  435. if (customServiceView) {
  436. serviceSummaryView = customServiceView.extend({
  437. service: this.get('service')
  438. });
  439. } else {
  440. serviceSummaryView = Em.View.extend({
  441. templateName: this.get('templatePathPrefix') + 'base',
  442. content: this
  443. });
  444. }
  445. this.set('serviceSummaryView', serviceSummaryView);
  446. this.set('oldServiceName', serviceName);
  447. }.observes('serviceName'),
  448. /**
  449. * Service metrics panel not displayed when metrics service (ex:Ganglia) is not in stack definition.
  450. */
  451. isNoServiceMetricsService: function() {
  452. return !App.get('services.serviceMetrics').length;
  453. }.property('App.services.serviceMetrics'),
  454. gangliaUrl:function () {
  455. var gangliaUrl = App.router.get('clusterController.gangliaUrl');
  456. if (!gangliaUrl) return null;
  457. var svcName = this.get('service.serviceName');
  458. if (svcName) {
  459. switch (svcName.toLowerCase()) {
  460. case 'hdfs':
  461. gangliaUrl += "/?r=hour&cs=&ce=&m=&s=by+name&c=HDPSlaves&tab=m&vn=";
  462. break;
  463. case 'hbase':
  464. gangliaUrl += "?r=hour&cs=&ce=&m=&s=by+name&c=HDPHBaseMaster&tab=m&vn=";
  465. break;
  466. default:
  467. break;
  468. }
  469. }
  470. return gangliaUrl;
  471. }.property('App.router.clusterController.gangliaUrl', 'service.serviceName'),
  472. willInsertElement: function () {
  473. App.router.get('updateController').updateServiceMetric(Em.K);
  474. },
  475. didInsertElement: function () {
  476. var svcName = this.get('service.serviceName');
  477. var isMetricsSupported = svcName != 'STORM' || App.get('isStormMetricsSupported');
  478. if (App.get('supports.customizedWidgets')) {
  479. var serviceName = this.get('controller.content.serviceName');
  480. var stackService = App.StackService.find().findProperty('serviceName', serviceName);
  481. if (stackService.get('isServiceWithWidgets')) {
  482. this.get('controller').loadWidgets();
  483. this.get('controller').loadWidgetLayouts();
  484. }
  485. }
  486. if (svcName && isMetricsSupported) {
  487. this.constructGraphObjects(App.service_graph_config[svcName.toLowerCase()]);
  488. }
  489. // adjust the summary table height
  490. var summaryTable = document.getElementById('summary-info');
  491. if (summaryTable) {
  492. var rows = $(summaryTable).find('tr');
  493. if (rows != null && rows.length > 0) {
  494. var minimumHeightSum = 20;
  495. var summaryActualHeight = summaryTable.clientHeight;
  496. // for summary window
  497. if (summaryActualHeight <= minimumHeightSum) {
  498. $(summaryTable).attr('style', "height:" + minimumHeightSum + "px;");
  499. }
  500. }
  501. }
  502. }
  503. });