summary.js 20 KB

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