host.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. var filters = require('views/common/filter_view');
  20. var sort = require('views/common/sort_view');
  21. var date = require('utils/date');
  22. App.MainHostView = App.TableView.extend(App.TableServerProvider, {
  23. templateName:require('templates/main/host'),
  24. tableName: 'Hosts',
  25. /**
  26. * Select/deselect all visible hosts flag
  27. * @property {bool}
  28. */
  29. selectAllHosts: false,
  30. /**
  31. * Contains all selected hosts on cluster
  32. */
  33. selectedHosts: [],
  34. /**
  35. * total number of installed hosts
  36. */
  37. totalCount: function () {
  38. return this.get('controller.hostsCountMap')['TOTAL'] || 0;
  39. }.property('controller.hostsCountMap'),
  40. filteredCount: function () {
  41. return this.get('controller.filteredCount');
  42. }.property('controller.filteredCount'),
  43. /**
  44. * List of hosts in cluster
  45. * @type {Array}
  46. */
  47. content: function () {
  48. var controllerName = this.get('controller.name');
  49. var selectedHosts = App.db.getSelectedHosts(controllerName);
  50. if (this.get('controller')) {
  51. return this.get('controller.content').filter(function (host) {
  52. host.set('selected', selectedHosts.contains(host.get('hostName')));
  53. return true;
  54. });
  55. }
  56. return [];
  57. }.property('controller.content'),
  58. onRequestErrorHandler: function() {
  59. this.set('requestError', null);
  60. this.get('controller').get('dataSource').setEach('isRequested', false);
  61. this.set('filteringComplete', true);
  62. this.propertyDidChange('pageContent');
  63. }.observes('requestError'),
  64. /**
  65. * Contains content to show on the current page of data page view
  66. * @type {Array}
  67. */
  68. pageContent: function () {
  69. var content = this.get('filteredContent');
  70. if (content.length > this.get('endIndex') - this.get('startIndex') + 1) {
  71. content = content.slice(0, this.get('endIndex') - this.get('startIndex') + 1);
  72. }
  73. return content.sort(function (a, b) {
  74. return a.get('index') - b.get('index');
  75. });
  76. }.property('filteredCount'),
  77. /**
  78. * flag to toggle displaying selected hosts counter
  79. */
  80. showSelectedFilter: function () {
  81. return this.get('selectedHosts.length') > 0;
  82. }.property('selectedHosts'),
  83. /**
  84. * return filtered number of all content number information displayed on the page footer bar
  85. * @returns {String}
  86. */
  87. filteredContentInfo: function () {
  88. return this.t('hosts.filters.filteredHostsInfo').format(this.get('filteredCount'), this.get('totalCount'));
  89. }.property('filteredCount', 'totalCount'),
  90. /**
  91. * Return pagination information displayed on the page
  92. * @type {String}
  93. */
  94. paginationInfo: function () {
  95. return this.t('tableView.filters.paginationInfo').format(this.get('startIndex'), this.get('endIndex'), this.get('filteredCount'));
  96. }.property('startIndex', 'endIndex', 'filteredCount'),
  97. paginationLeftClass: function () {
  98. if (this.get("startIndex") > 1 && this.get('filteringComplete')) {
  99. return "paginate_previous";
  100. }
  101. return "paginate_disabled_previous";
  102. }.property("startIndex", 'filteringComplete'),
  103. previousPage: function () {
  104. if (this.get('paginationLeftClass') === 'paginate_previous') {
  105. this._super();
  106. }
  107. },
  108. paginationRightClass: function () {
  109. if ((this.get("endIndex")) < this.get("filteredCount") && this.get('filteringComplete')) {
  110. return "paginate_next";
  111. }
  112. return "paginate_disabled_next";
  113. }.property("endIndex", 'filteredCount', 'filteringComplete'),
  114. nextPage: function () {
  115. if (this.get('paginationRightClass') === 'paginate_next') {
  116. this._super();
  117. }
  118. },
  119. /**
  120. * Select View with list of "rows-per-page" options
  121. * @type {Ember.View}
  122. */
  123. rowsPerPageSelectView: Em.Select.extend({
  124. content: ['10', '25', '50', '100'],
  125. attributeBindings: ['disabled'],
  126. disabled: function () {
  127. return !this.get('parentView.filteringComplete');
  128. }.property('parentView.filteringComplete'),
  129. change: function () {
  130. this.get('parentView').saveDisplayLength();
  131. var self = this;
  132. if (this.get('parentView.startIndex') === 1 || this.get('parentView.startIndex') === 0) {
  133. Ember.run.next(function () {
  134. self.get('parentView').updateViewProperty();
  135. });
  136. } else {
  137. Ember.run.next(function () {
  138. self.set('parentView.startIndex', 1);
  139. });
  140. }
  141. }
  142. }),
  143. saveStartIndex: function () {
  144. this.set('controller.startIndex', this.get('startIndex'));
  145. }.observes('startIndex'),
  146. /**
  147. * Calculates default value for startIndex property after applying filter or changing displayLength
  148. */
  149. updatePaging: function (controller, property) {
  150. var displayLength = this.get('displayLength');
  151. var filteredContentLength = this.get('filteredCount');
  152. if (property == 'displayLength' && this.get('filteringComplete')) {
  153. this.set('startIndex', Math.min(1, filteredContentLength));
  154. } else if (!filteredContentLength) {
  155. this.set('startIndex', 0);
  156. } else if (this.get('startIndex') > filteredContentLength) {
  157. this.set('startIndex', Math.floor((filteredContentLength - 1) / displayLength) * displayLength + 1);
  158. } else if (!this.get('startIndex')) {
  159. this.set('startIndex', 1);
  160. }
  161. },
  162. clearFiltersObs: function() {
  163. var self = this;
  164. Em.run.next(function() {
  165. if (self.get('controller.clearFilters')) {
  166. self.clearFilters();
  167. }
  168. });
  169. },
  170. /**
  171. * Restore filter properties in view
  172. */
  173. willInsertElement: function () {
  174. this._super();
  175. this.set('startIndex', this.get('controller.startIndex'));
  176. this.addObserver('pageContent.@each.selected', this, this.selectedHostsObserver);
  177. },
  178. /**
  179. * get query parameters computed in controller
  180. * @param {bool} flag should non-filters params be skipped
  181. * @return {Array}
  182. */
  183. getQueryParameters: function (flag) {
  184. return this.get('controller').getQueryParameters(flag);
  185. },
  186. /**
  187. * stub for filter function in TableView
  188. */
  189. filter: function () {
  190. //Since filtering moved to server side, function is empty
  191. },
  192. didInsertElement: function() {
  193. this.addObserver('controller.clearFilters', this, this.clearFiltersObs);
  194. this.clearFiltersObs();
  195. this.addObserver('selectAllHosts', this, this.toggleAllHosts);
  196. this.set('controller.isCountersUpdating', true);
  197. this.get('controller').updateStatusCounters();
  198. this.addObserver('filteringComplete', this, this.overlayObserver);
  199. this.addObserver('displayLength', this, this.updatePaging);
  200. this.addObserver('filteredCount', this, this.updatePaging);
  201. this.overlayObserver();
  202. },
  203. onInitialLoad: function () {
  204. if (this.get('tableFilteringComplete')) {
  205. this.refresh();
  206. }
  207. }.observes('tableFilteringComplete'),
  208. /**
  209. * synchronize properties of view with controller to generate query parameters
  210. */
  211. updateViewProperty: function () {
  212. this.get('controller.viewProperties').findProperty('key', 'displayLength').set('viewValue', this.get('displayLength'));
  213. this.get('controller.viewProperties').findProperty('key', 'startIndex').set('viewValue', this.get('startIndex'));
  214. this.refresh();
  215. }.observes('startIndex'),
  216. willDestroyElement: function() {
  217. this.set('controller.isCountersUpdating', false);
  218. },
  219. /**
  220. * Set <code>selected</code> property for each App.Host
  221. */
  222. toggleAllHosts: function() {
  223. this.get('pageContent').setEach('selected', this.get('selectAllHosts'));
  224. },
  225. /**
  226. * Trigger updating <code>selectedHostsCount</code> only 1 time
  227. */
  228. selectedHostsObserver: function() {
  229. Ember.run.once(this, 'updateCheckedFlags');
  230. },
  231. /**
  232. * Update <code>selectAllHosts</code> value
  233. */
  234. updateCheckedFlags: function() {
  235. this.removeObserver('selectAllHosts', this, this.toggleAllHosts);
  236. if (this.get('pageContent').length) {
  237. this.set('selectAllHosts', this.get('pageContent').everyProperty('selected', true));
  238. }
  239. else {
  240. this.set('selectAllHosts', false);
  241. }
  242. this.combineSelectedFilter();
  243. //10 is an index of selected column
  244. var controllerName = this.get('controller.name');
  245. App.db.setSelectedHosts(controllerName, this.get('selectedHosts'));
  246. this.addObserver('selectAllHosts', this, this.toggleAllHosts);
  247. },
  248. /**
  249. * combine selected hosts on page with selected hosts which are filtered out but added to cluster
  250. */
  251. combineSelectedFilter: function () {
  252. var controllerName = this.get('controller.name');
  253. var previouslySelectedHosts = App.db.getSelectedHosts(controllerName);
  254. var selectedHosts = [];
  255. var hostsOnPage = this.get('pageContent').mapProperty('hostName');
  256. selectedHosts = this.get('pageContent').filterProperty('selected').mapProperty('hostName');
  257. previouslySelectedHosts.forEach(function (hostName) {
  258. if (!hostsOnPage.contains(hostName)) {
  259. selectedHosts.push(hostName);
  260. }
  261. }, this);
  262. this.set('selectedHosts', selectedHosts);
  263. },
  264. /**
  265. * filter selected hosts
  266. */
  267. filterSelected: function() {
  268. //10 is an index of selected column
  269. this.updateFilter(10, this.get('selectedHosts'), 'multiple');
  270. },
  271. /**
  272. * Show spinner when filter/sorting request is in processing
  273. * @method overlayObserver
  274. */
  275. overlayObserver: function() {
  276. var $tbody = this.$('tbody'),
  277. $overlay = this.$('.hosts-overlay'),
  278. $spinner = $($overlay).find('.spinner');
  279. if (!this.get('filteringComplete')) {
  280. if (!$tbody) return;
  281. var tbodyPos = $tbody.position();
  282. if (!tbodyPos) return;
  283. $spinner.css('display', 'block');
  284. $overlay.css({
  285. top: tbodyPos.top + 1,
  286. left: tbodyPos.left + 1,
  287. width: $tbody.width() - 1,
  288. height: $tbody.height() - 1
  289. });
  290. }
  291. },
  292. /**
  293. * Clear selectedFilter
  294. * Set <code>selected</code> to false for each host
  295. */
  296. clearSelection: function() {
  297. this.get('pageContent').setEach('selected', false);
  298. this.set('selectAllHosts', false);
  299. App.db.setSelectedHosts(this.get('controller.name'), []);
  300. this.get('selectedHosts').clear();
  301. this.filterSelected();
  302. },
  303. /**
  304. * Returs all hostNames if amount is less than {minShown} or
  305. * first elements of array (number of elements - {minShown}) converted to string
  306. * @param {Array} hostNames - array of all listed hostNames
  307. * @param {String} divider - string to separate hostNames
  308. * @param {Number} minShown - min amount of hostName to be shown
  309. * @returns {String} hostNames
  310. * @method showHostNames
  311. */
  312. showHostNames: function(hostNames, divider, minShown) {
  313. if (hostNames.length > minShown) {
  314. return hostNames.slice(0, minShown).join(divider) + divider + Em.I18n.t("installer.step8.other").format(hostNames.length - minShown);
  315. } else {
  316. return hostNames.join(divider);
  317. }
  318. },
  319. /**
  320. * Confirmation Popup for bulk Operations
  321. */
  322. bulkOperationConfirm: function(operationData, selection) {
  323. var hostsNames = [],
  324. queryParams = [];
  325. switch(selection) {
  326. case 's':
  327. hostsNames = this.get('selectedHosts');
  328. if(hostsNames.length > 0){
  329. queryParams.push({
  330. key: 'Hosts/host_name',
  331. value: hostsNames,
  332. type: 'MULTIPLE'
  333. });
  334. }
  335. break;
  336. case 'f':
  337. queryParams = this.getQueryParameters(true).filter(function (obj) {
  338. return !(obj.key == 'page_size' || obj.key == 'from');
  339. });
  340. break;
  341. }
  342. var loadingPopup = App.ModalPopup.show({
  343. header: Em.I18n.t('jobs.loadingTasks'),
  344. primary: false,
  345. secondary: false,
  346. bodyClass: Ember.View.extend({
  347. template: Ember.Handlebars.compile('<div class="spinner"></div>')
  348. })
  349. });
  350. var parameters = App.router.get('updateController').computeParameters(queryParams);
  351. if (!parameters.length) parameters = '&';
  352. App.ajax.send({
  353. name: 'hosts.bulk.operations',
  354. sender: this,
  355. data: {
  356. parameters: parameters.substring(0, parameters.length - 1),
  357. operationData: operationData,
  358. loadingPopup: loadingPopup
  359. },
  360. success: 'getHostsForBulkOperationSuccessCallback'
  361. });
  362. },
  363. convertHostsObjects: function(hosts) {
  364. var newHostArr = [];
  365. hosts.forEach(function (host) {
  366. newHostArr.push({
  367. index:host.index,
  368. id:host.id,
  369. clusterId: host.cluster_id,
  370. passiveState: host.passive_state,
  371. isRequested: host.is_requested,
  372. hostName: host.host_name,
  373. hostComponents: host.host_components
  374. })
  375. });
  376. return newHostArr;
  377. },
  378. getHostsForBulkOperationSuccessCallback: function(json, opt, param) {
  379. var self = this,
  380. operationData = param.operationData,
  381. hosts = this.convertHostsObjects(App.hostsMapper.map(json, true));
  382. // no hosts - no actions
  383. if (!hosts.length) {
  384. console.log('No bulk operation if no hosts selected.');
  385. return;
  386. }
  387. var hostNames = hosts.mapProperty('hostName');
  388. var hostsToSkip = [];
  389. if (operationData.action == "DECOMMISSION") {
  390. var hostComponentStatusMap = {}; // "DATANODE_c6401.ambari.apache.org" => "STARTED"
  391. var hostComponentIdMap = {}; // "DATANODE_c6401.ambari.apache.org" => "DATANODE"
  392. if (json.items) {
  393. json.items.forEach(function(host) {
  394. if (host.host_components) {
  395. host.host_components.forEach(function(component) {
  396. hostComponentStatusMap[component.id] = component.HostRoles.state;
  397. hostComponentIdMap[component.id] = component.HostRoles.component_name;
  398. });
  399. }
  400. });
  401. }
  402. hostsToSkip = hosts.filter(function(host) {
  403. var invalidStateComponents = host.hostComponents.filter(function(component) {
  404. return hostComponentIdMap[component] == operationData.realComponentName && hostComponentStatusMap[component] == 'INSTALLED';
  405. });
  406. return invalidStateComponents.length > 0;
  407. });
  408. }
  409. var hostNamesSkipped = hostsToSkip.mapProperty('hostName');
  410. var message;
  411. if (operationData.componentNameFormatted) {
  412. message = Em.I18n.t('hosts.bulkOperation.confirmation.hostComponents').format(operationData.message, operationData.componentNameFormatted, hostNames.length);
  413. }
  414. else {
  415. message = Em.I18n.t('hosts.bulkOperation.confirmation.hosts').format(operationData.message, hostNames.length);
  416. }
  417. param.loadingPopup.hide();
  418. App.ModalPopup.show({
  419. header: Em.I18n.t('hosts.bulkOperation.confirmation.header'),
  420. hostNames: hostNames.join("\n"),
  421. visibleHosts: self.showHostNames(hostNames, "\n", 3),
  422. hostNamesSkippedVisible: self.showHostNames(hostNamesSkipped, "\n", 3),
  423. hostNamesSkipped: function() {
  424. if (hostNamesSkipped.length) {
  425. return hostNamesSkipped.join("\n");
  426. }
  427. return false;
  428. }.property(),
  429. expanded: false,
  430. didInsertElement: function() {
  431. this.set('expanded', hostNames.length <= 3);
  432. },
  433. onPrimary: function() {
  434. self.get('controller').bulkOperation(operationData, hosts);
  435. this._super();
  436. },
  437. bodyClass: Em.View.extend({
  438. templateName: require('templates/main/host/bulk_operation_confirm_popup'),
  439. message: message,
  440. warningInfo: Em.I18n.t('hosts.bulkOperation.warningInfo.body'),
  441. textareaVisible: false,
  442. textTrigger: function() {
  443. this.set('textareaVisible', !this.get('textareaVisible'));
  444. },
  445. showAll: function() {
  446. this.set('parentView.visibleHosts', this.get('parentView.hostNames'));
  447. this.set('parentView.hostNamesSkippedVisible', this.get('parentView.hostNamesSkipped'));
  448. this.set('parentView.expanded', true);
  449. },
  450. putHostNamesToTextarea: function() {
  451. var hostNames = this.get('parentView.hostNames');
  452. if (this.get('textareaVisible')) {
  453. var wrapper = $(".task-detail-log-maintext");
  454. $('.task-detail-log-clipboard').html(hostNames).width(wrapper.width()).height(250);
  455. Em.run.next(function() {
  456. $('.task-detail-log-clipboard').select();
  457. });
  458. }
  459. }.observes('textareaVisible')
  460. })
  461. });
  462. },
  463. sortView: sort.serverWrapperView,
  464. nameSort: sort.fieldView.extend({
  465. column: 1,
  466. name:'publicHostName',
  467. displayName: Em.I18n.t('common.name')
  468. }),
  469. ipSort: sort.fieldView.extend({
  470. column: 2,
  471. name:'ip',
  472. displayName: Em.I18n.t('common.ipAddress'),
  473. type: 'ip'
  474. }),
  475. cpuSort: sort.fieldView.extend({
  476. column: 3,
  477. name:'cpu',
  478. displayName: Em.I18n.t('common.cores'),
  479. type: 'number'
  480. }),
  481. memorySort: sort.fieldView.extend({
  482. column: 4,
  483. name:'memory',
  484. displayName: Em.I18n.t('common.ram'),
  485. type: 'number'
  486. }),
  487. diskUsageSort: sort.fieldView.extend({
  488. name:'diskUsage',
  489. displayName: Em.I18n.t('common.diskUsage')
  490. }),
  491. loadAvgSort: sort.fieldView.extend({
  492. column: 5,
  493. name:'loadAvg',
  494. displayName: Em.I18n.t('common.loadAvg'),
  495. type: 'number'
  496. }),
  497. HostView:Em.View.extend({
  498. content:null,
  499. tagName: 'tr',
  500. didInsertElement: function(){
  501. App.tooltip(this.$("[rel='HealthTooltip'], [rel='UsageTooltip'], [rel='ComponentsTooltip']"));
  502. this.set('isComponentsCollapsed', true);
  503. },
  504. toggleComponents: function(event) {
  505. this.set('isComponentsCollapsed', !this.get('isComponentsCollapsed'));
  506. this.$('.host-components').toggle();
  507. },
  508. /**
  509. * Tooltip message for "Restart Required" icon
  510. * @returns {String}
  511. */
  512. restartRequiredComponentsMessage: function() {
  513. var restartRequiredComponents = this.get('content.hostComponents').filterProperty('staleConfigs', true);
  514. var count = restartRequiredComponents.length;
  515. if (count <= 5) {
  516. var word = (count == 1) ? Em.I18n.t('common.component') : Em.I18n.t('common.components');
  517. return Em.I18n.t('hosts.table.restartComponents.withNames').format(restartRequiredComponents.getEach('displayName').join(', ')) + ' ' + word.toLowerCase();
  518. }
  519. return Em.I18n.t('hosts.table.restartComponents.withoutNames').format(count);
  520. }.property('content.hostComponents.@each.staleConfigs'),
  521. /**
  522. * Tooltip message for "Maintenance" icon
  523. * @returns {String}
  524. */
  525. componentsInPassiveStateMessage: function() {
  526. var componentsInPassiveState = this.get('content.hostComponents').filter(function(component) {
  527. return component.get('passiveState') !== 'OFF';
  528. });
  529. var count = componentsInPassiveState.length;
  530. if (count <= 5) {
  531. return Em.I18n.t('hosts.table.componentsInPassiveState.withNames').format(componentsInPassiveState.getEach('displayName').join(', '));
  532. }
  533. return Em.I18n.t('hosts.table.componentsInPassiveState.withoutNames').format(count);
  534. }.property('content.hostComponents.@each.passiveState'),
  535. /**
  536. * String with list of host components <code>displayName</code>
  537. * @returns {String}
  538. */
  539. labels: function() {
  540. return this.get('content.hostComponents').getEach('displayName').join("<br />");
  541. }.property('content.hostComponents.length'),
  542. /**
  543. * CSS value for disk usage bar
  544. * @returns {String}
  545. */
  546. usageStyle:function () {
  547. return "width:" + this.get('content.diskUsage') + "%";
  548. }.property('content.diskUsage')
  549. }),
  550. /**
  551. * Update <code>hostsCount</code> in every category
  552. */
  553. updateHostsCount: function() {
  554. var hostsCountMap = this.get('controller.hostsCountMap');
  555. this.get('categories').forEach(function(category) {
  556. var hostsCount = (category.get('healthStatus').trim() === "") ? hostsCountMap['TOTAL'] : hostsCountMap[category.get('healthStatus')];
  557. if (!Em.isNone(hostsCount)) {
  558. category.set('hostsCount', hostsCount);
  559. category.set('hasHosts', (hostsCount > 0));
  560. }
  561. }, this);
  562. }.observes('controller.hostsCountMap'),
  563. /**
  564. * Category view for all hosts
  565. * @type {Object}
  566. */
  567. //@TODO maybe should be separated to two types (basing on <code>isHealthStatus</code>)
  568. categoryObject: Em.Object.extend({
  569. /**
  570. * Text used with <code>hostsCount</code> in category label
  571. * @type {String}
  572. */
  573. value: null,
  574. /**
  575. * Is category based on host health status
  576. * @type {bool}
  577. */
  578. isHealthStatus: true,
  579. /**
  580. * host health status (used if <code>isHealthStatus</code> is true)
  581. * @type {String}
  582. */
  583. healthStatusValue: '',
  584. /**
  585. * Should category be displayed on the top of the hosts table
  586. * @type {bool}
  587. */
  588. isVisible: true,
  589. /**
  590. * Is category selected now
  591. * @type {bool}
  592. */
  593. isActive: false,
  594. /**
  595. * String with path that category should observe
  596. * @type {String}
  597. */
  598. observes: null,
  599. /**
  600. * CSS-class for span in the category-link (used if <code>isHealthStatus</code> is false)
  601. * @type {String}
  602. */
  603. class: null,
  604. /**
  605. * Associated column number
  606. * @type {Number}
  607. */
  608. column: null,
  609. /**
  610. * Type of filter value (string, number, boolean)
  611. * @type {String}
  612. */
  613. type: null,
  614. /**
  615. * @type {String|Number|bool}
  616. */
  617. filterValue: null,
  618. /**
  619. * <code>App.Host</code> property that should be used to calculate <code>hostsCount</code> (used if <code>isHealthStatus</code> is false)
  620. * @type {String}
  621. */
  622. hostProperty: null,
  623. /**
  624. * Number of host in current category
  625. * @type {Number}
  626. */
  627. hostsCount: 0,
  628. /**
  629. * Determine if category has hosts
  630. * @type {bool}
  631. */
  632. hasHosts: false,
  633. /**
  634. * Add "active" class for category span-wrapper if current category is selected
  635. * @type {String}
  636. */
  637. itemClass: function() {
  638. return this.get('isActive') ? 'active' : '';
  639. }.property('isActive'),
  640. /**
  641. * Text shown on the right of category icon
  642. * @type {String}
  643. */
  644. label: function () {
  645. return "%@ (%@)".fmt(this.get('value'), this.get('hostsCount'));
  646. }.property('hostsCount')
  647. }),
  648. /**
  649. * List of categories used to filter hosts
  650. * @type {Array}
  651. */
  652. categories: function () {
  653. var self = this;
  654. var category_mocks = require('data/host/categories');
  655. return category_mocks.map(function(category_mock) {
  656. return self.categoryObject.create(category_mock);
  657. });
  658. }.property(),
  659. /**
  660. * Category for <code>selected</code> property of each App.Host
  661. */
  662. selectedCategory: function() {
  663. return this.get('categories').findProperty('selected', true);
  664. }.property('categories.@each.selected'),
  665. statusFilter: Em.View.extend({
  666. column: 0,
  667. categories: [],
  668. value: null,
  669. class: "",
  670. comboBoxLabel: function(){
  671. var selected = this.get('categories').findProperty('isActive');
  672. if (!this.get('value') || !selected) {
  673. return "%@ (%@)".fmt(Em.I18n.t('common.all'), this.get('parentView.totalCount'));
  674. } else {
  675. return "%@ (%@)".fmt(selected.get('value'), selected.get('hostsCount'))
  676. }
  677. }.property('value', 'parentView.totalCount'),
  678. /**
  679. * switch active category label
  680. */
  681. onCategoryChange: function () {
  682. this.get('categories').setEach('isActive', false);
  683. var selected = this.get('categories').findProperty('healthStatus', this.get('value'));
  684. selected.set('isActive', true);
  685. this.set('class', selected.get('class') + ' ' + selected.get('healthClass'));
  686. }.observes('value'),
  687. showClearFilter: function () {
  688. var mockEvent = {
  689. context: this.get('categories').findProperty('healthStatus', this.get('value'))
  690. };
  691. this.selectCategory(mockEvent);
  692. },
  693. /**
  694. * Trigger on Category click
  695. * @param {Object} event
  696. */
  697. selectCategory: function(event){
  698. var category = event.context;
  699. this.set('value', category.get('healthStatus'));
  700. this.get('parentView').resetFilterByColumns([0, 7, 8, 9]);
  701. if (category.get('isHealthStatus')) {
  702. this.get('parentView').updateFilter(0, category.get('healthStatus'), 'string');
  703. } else {
  704. this.get('parentView').updateFilter(category.get('column'), category.get('filterValue'), category.get('type'));
  705. }
  706. },
  707. clearFilter: function() {
  708. this.get('categories').setEach('isActive', false);
  709. this.set('value', '');
  710. this.set('class', '');
  711. this.showClearFilter();
  712. }
  713. }),
  714. /**
  715. * Filter view for name column
  716. * Based on <code>filters</code> library
  717. */
  718. nameFilterView: filters.createTextView({
  719. column: 1,
  720. fieldType: 'filter-input-width',
  721. onChangeValue: function(){
  722. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
  723. }
  724. }),
  725. /**
  726. * Filter view for ip column
  727. * Based on <code>filters</code> library
  728. */
  729. ipFilterView: filters.createTextView({
  730. column: 2,
  731. fieldType: 'filter-input-width',
  732. onChangeValue: function(){
  733. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
  734. }
  735. }),
  736. /**
  737. * Filter view for Cpu column
  738. * Based on <code>filters</code> library
  739. */
  740. cpuFilterView: filters.createTextView({
  741. fieldType: 'filter-input-width',
  742. fieldId: 'cpu_filter',
  743. column: 3,
  744. onChangeValue: function(){
  745. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'number');
  746. }
  747. }),
  748. /**
  749. * Filter view for LoadAverage column
  750. * Based on <code>filters</code> library
  751. */
  752. loadAvgFilterView: filters.createTextView({
  753. fieldType: 'filter-input-width',
  754. fieldId: 'load_avg_filter',
  755. column: 5,
  756. onChangeValue: function(){
  757. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'number');
  758. }
  759. }),
  760. /**
  761. * Filter view for Ram column
  762. * Based on <code>filters</code> library
  763. */
  764. ramFilterView: filters.createTextView({
  765. fieldType: 'filter-input-width',
  766. fieldId: 'ram_filter',
  767. column: 4,
  768. onChangeValue: function(){
  769. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'ambari-bandwidth');
  770. }
  771. }),
  772. /**
  773. * Filter view for HostComponents column
  774. * Based on <code>filters</code> library
  775. */
  776. componentsFilterView: filters.createComponentView({
  777. column: 6,
  778. /**
  779. * Inner FilterView. Used just to render component. Value bind to <code>mainview.value</code> property
  780. * Base methods was implemented in <code>filters.componentFieldView</code>
  781. */
  782. filterView: filters.componentFieldView.extend({
  783. templateName: require('templates/main/host/component_filter'),
  784. /**
  785. * Next three lines bind data to this view
  786. */
  787. masterComponentsBinding: 'controller.masterComponents',
  788. slaveComponentsBinding: 'controller.slaveComponents',
  789. clientComponentsBinding: 'controller.clientComponents',
  790. /**
  791. * Checkbox for quick selecting/deselecting of master components
  792. */
  793. masterComponentsChecked:false,
  794. toggleMasterComponents:function () {
  795. this.get('masterComponents').setEach('checkedForHostFilter', this.get('masterComponentsChecked'));
  796. }.observes('masterComponentsChecked'),
  797. /**
  798. * Checkbox for quick selecting/deselecting of slave components
  799. */
  800. slaveComponentsChecked:false,
  801. toggleSlaveComponents:function () {
  802. this.get('slaveComponents').setEach('checkedForHostFilter', this.get('slaveComponentsChecked'));
  803. }.observes('slaveComponentsChecked'),
  804. /**
  805. * Checkbox for quick selecting/deselecting of client components
  806. */
  807. clientComponentsChecked: false,
  808. toggleClientComponents: function() {
  809. this.get('clientComponents').setEach('checkedForHostFilter', this.get('clientComponentsChecked'));
  810. }.observes('clientComponentsChecked'),
  811. /**
  812. * Clear filter.
  813. * Called by parent view, when user clicks on <code>x</code> button(clear button)
  814. */
  815. clearFilter:function() {
  816. this.set('masterComponentsChecked', false);
  817. this.set('slaveComponentsChecked', false);
  818. this.set('clientComponentsChecked', false);
  819. this.get('masterComponents').setEach('checkedForHostFilter', false);
  820. this.get('slaveComponents').setEach('checkedForHostFilter', false);
  821. this.get('clientComponents').setEach('checkedForHostFilter', false);
  822. this._super();
  823. },
  824. /**
  825. * Onclick handler for <code>Apply filter</code> button
  826. */
  827. applyFilter:function() {
  828. this._super();
  829. var self = this;
  830. var chosenComponents = [];
  831. this.get('masterComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
  832. chosenComponents.push(item.get('id'));
  833. });
  834. this.get('slaveComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
  835. chosenComponents.push(item.get('id'));
  836. });
  837. this.get('clientComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
  838. chosenComponents.push(item.get('id'));
  839. });
  840. Em.run.next(function() {
  841. self.set('value', chosenComponents);
  842. });
  843. },
  844. /**
  845. * Verify that checked checkboxes are equal to value stored in hidden field (components ids list)
  846. */
  847. checkComponents: function() {
  848. var components = this.get('value');
  849. var self = this;
  850. if (components) {
  851. components.forEach(function(componentId) {
  852. if(!self.tryCheckComponent(self, 'masterComponents', componentId)) {
  853. if(!self.tryCheckComponent(self, 'slaveComponents', componentId)) {
  854. self.tryCheckComponent(self, 'clientComponents', componentId);
  855. }
  856. }
  857. });
  858. }
  859. }.observes('value'),
  860. tryCheckComponent: function(self, category, componentId) {
  861. var c = self.get(category).findProperty('id', componentId);
  862. if (c) {
  863. if (!c.get('checkedForHostFilter')) {
  864. c.set('checkedForHostFilter', true);
  865. return true;
  866. }
  867. }
  868. return false;
  869. }
  870. }),
  871. onChangeValue: function(){
  872. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'multiple');
  873. }
  874. }),
  875. /**
  876. * associations between host property and column index
  877. * @type {Array}
  878. */
  879. colPropAssoc: function () {
  880. return this.get('controller.colPropAssoc');
  881. }.property('controller.colPropAssoc'),
  882. resetStartIndex: function () {
  883. if (this.get('controller.resetStartIndex') && this.get('filteredCount') > 0) {
  884. this.set('startIndex', 1);
  885. }
  886. }.observes('controller.resetStartIndex')
  887. });