host.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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({
  23. templateName:require('templates/main/host'),
  24. content:function () {
  25. return this.get('controller.content');
  26. }.property('controller.content.length'),
  27. willInsertElement: function() {
  28. this._super();
  29. },
  30. clearFiltersObs: function() {
  31. var self = this;
  32. Em.run.next(function() {
  33. if (self.get('controller.clearFilters')) {
  34. self.clearFilters();
  35. self.clearStartIndex();
  36. }
  37. });
  38. },
  39. didInsertElement: function() {
  40. this.addObserver('controller.clearFilters', this, this.clearFiltersObs);
  41. this.clearFiltersObs();
  42. this.addObserver('content.@each.hostComponents.@each', this, this.filter);
  43. },
  44. sortView: sort.wrapperView,
  45. nameSort: sort.fieldView.extend({
  46. column: 1,
  47. name:'publicHostName',
  48. displayName: Em.I18n.t('common.name')
  49. }),
  50. ipSort: sort.fieldView.extend({
  51. column: 2,
  52. name:'ip',
  53. displayName: Em.I18n.t('common.ipAddress'),
  54. type: 'ip'
  55. }),
  56. cpuSort: sort.fieldView.extend({
  57. column: 3,
  58. name:'cpu',
  59. displayName: Em.I18n.t('common.cores'),
  60. type: 'number'
  61. }),
  62. memorySort: sort.fieldView.extend({
  63. column: 4,
  64. name:'memory',
  65. displayName: Em.I18n.t('common.ram'),
  66. type: 'number'
  67. }),
  68. diskUsageSort: sort.fieldView.extend({
  69. name:'diskUsage',
  70. displayName: Em.I18n.t('common.diskUsage')
  71. }),
  72. loadAvgSort: sort.fieldView.extend({
  73. column: 5,
  74. name:'loadAvg',
  75. displayName: Em.I18n.t('common.loadAvg'),
  76. type: 'number'
  77. }),
  78. HostView:Em.View.extend({
  79. content:null,
  80. tagName: 'tr',
  81. didInsertElement: function(){
  82. App.tooltip(this.$("[rel='HealthTooltip'], [rel='UsageTooltip'], [rel='ComponentsTooltip']"));
  83. },
  84. toggleComponents: function(event) {
  85. this.$(event.target).find('.caret').toggleClass('right');
  86. this.$('.host-components').toggle();
  87. },
  88. /**
  89. * Tooltip message for "Restart Required" icon
  90. * @returns {String}
  91. */
  92. restartRequiredComponentsMessage: function() {
  93. var restartRequiredComponents = this.get('content.hostComponents').filterProperty('staleConfigs', true);
  94. var count = restartRequiredComponents.length;
  95. if (count <= 5) {
  96. var word = (count == 1) ? Em.I18n.t('common.component') : Em.I18n.t('common.components');
  97. return Em.I18n.t('hosts.table.restartComponents.withNames').format(restartRequiredComponents.getEach('displayName').join(', ')) + ' ' + word.toLowerCase();
  98. }
  99. return Em.I18n.t('hosts.table.restartComponents.withoutNames').format(count);
  100. }.property('content.hostComponents.@each.staleConfigs'),
  101. /**
  102. * Tooltip message for "Maintenance" icon
  103. * @returns {String}
  104. */
  105. componentsInMaintenanceMessage: function() {
  106. var componentsInMaintenance = this.get('content.hostComponents').filterProperty('workStatus', App.HostComponentStatus.maintenance);
  107. var count = componentsInMaintenance.length;
  108. if (count <= 5) {
  109. return Em.I18n.t('hosts.table.componentsInMaintenance.withNames').format(componentsInMaintenance.getEach('displayName').join(', '));
  110. }
  111. return Em.I18n.t('hosts.table.componentsInMaintenance.withoutNames').format(count);
  112. }.property('content.hostComponents.@each.workStatus'),
  113. /**
  114. * String with list of host components <code>displayName</code>
  115. * @returns {String}
  116. */
  117. labels: function() {
  118. return this.get('content.hostComponents').getEach('displayName').join("<br />");
  119. }.property('content.hostComponents.@each'),
  120. /**
  121. * CSS value for disk usage bar
  122. * @returns {String}
  123. */
  124. usageStyle:function () {
  125. return "width:" + this.get('content.diskUsage') + "%";
  126. }.property('content.diskUsage')
  127. }),
  128. /**
  129. * Category view for all hosts
  130. */
  131. categoryObject: Em.Object.extend({
  132. hostsCount: function () {
  133. var statusString = this.get('healthStatusValue');
  134. var alerts = this.get('alerts');
  135. var restart = this.get('restart');
  136. var maintenance = this.get('maintenance');
  137. if(alerts) {
  138. return this.get('view.content').filterProperty('criticalAlertsCount').get('length');
  139. }
  140. else {
  141. if (restart) {
  142. return this.get('view.content').filterProperty('componentsWithStaleConfigsCount').get('length');
  143. }
  144. else {
  145. if (maintenance) {
  146. return this.get('view.content').filterProperty('componentsInMaintenanceCount').get('length');
  147. }
  148. else {
  149. if (statusString == "") {
  150. return this.get('view.content').get('length');
  151. }
  152. else {
  153. return this.get('view.content').filterProperty('healthClass', statusString ).get('length');
  154. }
  155. }
  156. }
  157. }
  158. }.property('view.content.@each.healthClass', 'view.content.@each.criticalAlertsCount', 'view.content.@each.componentsInMaintenanceCount', 'view.content.@each.hostComponents.@each.staleConfigs'),
  159. label: function () {
  160. return "%@ (%@)".fmt(this.get('value'), this.get('hostsCount'));
  161. }.property('value', 'hostsCount')
  162. }),
  163. categories: function () {
  164. var self = this;
  165. self.categoryObject.reopen({
  166. view: self,
  167. isActive: false,
  168. itemClass: function() {
  169. return this.get('isActive') ? 'active' : '';
  170. }.property('isActive')
  171. });
  172. var categories = [
  173. self.categoryObject.create({value: Em.I18n.t('common.all'), healthStatusValue: '', isActive: true, isVisible: false}),
  174. self.categoryObject.create({value: Em.I18n.t('hosts.host.healthStatusCategory.green'), healthStatusValue: 'health-status-LIVE', isVisible: true}),
  175. self.categoryObject.create({value: Em.I18n.t('hosts.host.healthStatusCategory.red'), healthStatusValue: 'health-status-DEAD-RED', isVisible: true}),
  176. self.categoryObject.create({value: Em.I18n.t('hosts.host.healthStatusCategory.orange'), healthStatusValue: 'health-status-DEAD-ORANGE', isVisible: true}),
  177. self.categoryObject.create({value: Em.I18n.t('hosts.host.healthStatusCategory.yellow'), healthStatusValue: 'health-status-DEAD-YELLOW', isVisible: true}),
  178. self.categoryObject.create({value: Em.I18n.t('hosts.host.alerts.label'), healthStatusValue: 'health-status-WITH-ALERTS', alerts: true, isVisible: true }),
  179. self.categoryObject.create({value: Em.I18n.t('common.restart'), healthStatusValue: 'health-status-RESTART', restart: true, isVisible: true }),
  180. self.categoryObject.create({value: Em.I18n.t('common.maintenance'), healthStatusValue: 'health-status-MAINTENANCE', maintenance: true, last: true, isVisible: true })
  181. ];
  182. return categories;
  183. }.property(),
  184. statusFilter: Em.View.extend({
  185. column: 0,
  186. categories: [],
  187. value: null,
  188. /**
  189. * switch active category label
  190. */
  191. onCategoryChange: function(){
  192. this.get('categories').setEach('isActive', false);
  193. this.get('categories').findProperty('healthStatusValue', this.get('value')).set('isActive', true);
  194. }.observes('value'),
  195. showClearFilter: function(){
  196. var mockEvent = {
  197. context: this.get('categories').findProperty('healthStatusValue', this.get('value'))
  198. };
  199. this.selectCategory(mockEvent);
  200. },
  201. selectCategory: function(event){
  202. var category = event.context;
  203. this.set('value', category.get('healthStatusValue'));
  204. if(category.get('alerts')) {
  205. this.get('parentView').updateFilter(0, '', 'string');
  206. this.get('parentView').updateFilter(7, '>0', 'number');
  207. this.get('parentView').updateFilter(8, '', 'number');
  208. this.get('parentView').updateFilter(9, '', 'number');
  209. }
  210. else {
  211. if(category.get('restart')) {
  212. this.get('parentView').updateFilter(0, '', 'string');
  213. this.get('parentView').updateFilter(7, '', 'number');
  214. this.get('parentView').updateFilter(8, '>0', 'number');
  215. this.get('parentView').updateFilter(9, '', 'number');
  216. }
  217. else {
  218. if(category.get('maintenance')) {
  219. this.get('parentView').updateFilter(0, '', 'string');
  220. this.get('parentView').updateFilter(7, '', 'number');
  221. this.get('parentView').updateFilter(8, '', 'number');
  222. this.get('parentView').updateFilter(9, '>0', 'number');
  223. }
  224. else {
  225. this.get('parentView').updateFilter(0, category.get('healthStatusValue'), 'string');
  226. this.get('parentView').updateFilter(7, '', 'number');
  227. this.get('parentView').updateFilter(8, '', 'number');
  228. this.get('parentView').updateFilter(9, '', 'number');
  229. }
  230. }
  231. }
  232. },
  233. clearFilter: function() {
  234. this.get('categories').setEach('isActive', false);
  235. this.set('value', '');
  236. this.showClearFilter();
  237. }
  238. }),
  239. /**
  240. * view of the alert filter implemented as a category of host statuses
  241. */
  242. alertFilter: Em.View.extend({
  243. column: 7,
  244. value: null,
  245. classNames: ['noDisplay'],
  246. showClearFilter: function(){
  247. var mockEvent = {
  248. context: this.get('parentView.categories').findProperty('healthStatusValue', 'health-status-WITH-ALERTS')
  249. };
  250. if(this.get('value')) {
  251. this.get('parentView.childViews').findProperty('column', 0).selectCategory(mockEvent);
  252. }
  253. }
  254. }),
  255. /**
  256. * view of the staleConfigs filter implemented as a category of host statuses
  257. */
  258. restartFilter: Em.View.extend({
  259. column: 8,
  260. value: null,
  261. classNames: ['noDisplay'],
  262. showClearFilter: function(){
  263. var mockEvent = {
  264. context: this.get('parentView.categories').findProperty('healthStatusValue', 'health-status-RESTART')
  265. };
  266. if(this.get('value')) {
  267. this.get('parentView.childViews').findProperty('column', 0).selectCategory(mockEvent);
  268. }
  269. }
  270. }),
  271. /**
  272. * view of the maintenance filter implemented as a category of host statuses
  273. */
  274. maintenanceFilter: Em.View.extend({
  275. column: 9,
  276. value: null,
  277. classNames: ['noDisplay'],
  278. showClearFilter: function(){
  279. var mockEvent = {
  280. context: this.get('parentView.categories').findProperty('healthStatusValue', 'health-status-MAINTENANCE')
  281. };
  282. if(this.get('value')) {
  283. this.get('parentView.childViews').findProperty('column', 0).selectCategory(mockEvent);
  284. }
  285. }
  286. }),
  287. /**
  288. * Filter view for name column
  289. * Based on <code>filters</code> library
  290. */
  291. nameFilterView: filters.createTextView({
  292. column: 1,
  293. fieldType: 'width70',
  294. onChangeValue: function(){
  295. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
  296. }
  297. }),
  298. /**
  299. * Filter view for ip column
  300. * Based on <code>filters</code> library
  301. */
  302. ipFilterView: filters.createTextView({
  303. column: 2,
  304. fieldType: 'width70',
  305. onChangeValue: function(){
  306. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
  307. }
  308. }),
  309. /**
  310. * Filter view for Cpu column
  311. * Based on <code>filters</code> library
  312. */
  313. cpuFilterView: filters.createTextView({
  314. fieldType: 'width70',
  315. fieldId: 'cpu_filter',
  316. column: 3,
  317. onChangeValue: function(){
  318. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'number');
  319. }
  320. }),
  321. /**
  322. * Filter view for LoadAverage column
  323. * Based on <code>filters</code> library
  324. */
  325. loadAvgFilterView: filters.createTextView({
  326. fieldType: 'width70',
  327. fieldId: 'load_avg_filter',
  328. column: 5,
  329. onChangeValue: function(){
  330. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'number');
  331. }
  332. }),
  333. /**
  334. * Filter view for Ram column
  335. * Based on <code>filters</code> library
  336. */
  337. ramFilterView: filters.createTextView({
  338. fieldType: 'width70',
  339. fieldId: 'ram_filter',
  340. column: 4,
  341. onChangeValue: function(){
  342. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'ambari-bandwidth');
  343. }
  344. }),
  345. /**
  346. * Filter view for HostComponents column
  347. * Based on <code>filters</code> library
  348. */
  349. componentsFilterView: filters.createComponentView({
  350. column: 6,
  351. /**
  352. * Inner FilterView. Used just to render component. Value bind to <code>mainview.value</code> property
  353. * Base methods was implemented in <code>filters.componentFieldView</code>
  354. */
  355. filterView: filters.componentFieldView.extend({
  356. templateName: require('templates/main/host/component_filter'),
  357. /**
  358. * Next three lines bind data to this view
  359. */
  360. masterComponentsBinding: 'controller.masterComponents',
  361. slaveComponentsBinding: 'controller.slaveComponents',
  362. clientComponentsBinding: 'controller.clientComponents',
  363. /**
  364. * Checkbox for quick selecting/deselecting of master components
  365. */
  366. masterComponentsChecked:false,
  367. toggleMasterComponents:function () {
  368. this.get('masterComponents').setEach('checkedForHostFilter', this.get('masterComponentsChecked'));
  369. }.observes('masterComponentsChecked'),
  370. /**
  371. * Checkbox for quick selecting/deselecting of slave components
  372. */
  373. slaveComponentsChecked:false,
  374. toggleSlaveComponents:function () {
  375. this.get('slaveComponents').setEach('checkedForHostFilter', this.get('slaveComponentsChecked'));
  376. }.observes('slaveComponentsChecked'),
  377. /**
  378. * Checkbox for quick selecting/deselecting of client components
  379. */
  380. clientComponentsChecked: false,
  381. toggleClientComponents: function() {
  382. this.get('clientComponents').setEach('checkedForHostFilter', this.get('clientComponentsChecked'));
  383. }.observes('clientComponentsChecked'),
  384. /**
  385. * Clear filter.
  386. * Called by parent view, when user clicks on <code>x</code> button(clear button)
  387. */
  388. clearFilter:function() {
  389. this.set('masterComponentsChecked', false);
  390. this.set('slaveComponentsChecked', false);
  391. this.set('clientComponentsChecked', false);
  392. this.get('masterComponents').setEach('checkedForHostFilter', false);
  393. this.get('slaveComponents').setEach('checkedForHostFilter', false);
  394. this.get('clientComponents').setEach('checkedForHostFilter', false);
  395. this._super();
  396. },
  397. /**
  398. * Onclick handler for <code>Apply filter</code> button
  399. */
  400. applyFilter:function() {
  401. this._super();
  402. var self = this;
  403. var chosenComponents = [];
  404. this.get('masterComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
  405. chosenComponents.push(item.get('id'));
  406. });
  407. this.get('slaveComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
  408. chosenComponents.push(item.get('id'));
  409. });
  410. this.get('clientComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
  411. chosenComponents.push(item.get('id'));
  412. });
  413. Em.run.next(function() {
  414. self.set('value', chosenComponents.toString());
  415. });
  416. },
  417. /**
  418. * Verify that checked checkboxes are equal to value stored in hidden field (components ids list)
  419. */
  420. checkComponents: function() {
  421. var components = this.get('value').split(',');
  422. var self = this;
  423. if (components) {
  424. components.forEach(function(componentId) {
  425. if(!self.tryCheckComponent(self, 'masterComponents', componentId)) {
  426. if(!self.tryCheckComponent(self, 'slaveComponents', componentId)) {
  427. self.tryCheckComponent(self, 'clientComponents', componentId);
  428. }
  429. }
  430. });
  431. }
  432. }.observes('value'),
  433. tryCheckComponent: function(self, category, componentId) {
  434. var c = self.get(category).findProperty('id', componentId);
  435. if (c) {
  436. if (!c.get('checkedForHostFilter')) {
  437. c.set('checkedForHostFilter', true);
  438. return true;
  439. }
  440. }
  441. return false;
  442. }
  443. }),
  444. onChangeValue: function(){
  445. this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'multiple');
  446. }
  447. }),
  448. /**
  449. * associations between host property and column index
  450. */
  451. colPropAssoc: function(){
  452. var associations = [];
  453. associations[0] = 'healthClass';
  454. associations[1] = 'publicHostName';
  455. associations[2] = 'ip';
  456. associations[3] = 'cpu';
  457. associations[4] = 'memoryFormatted';
  458. associations[5] = 'loadAvg';
  459. associations[6] = 'hostComponents';
  460. associations[7] = 'criticalAlertsCount';
  461. associations[8] = 'componentsWithStaleConfigsCount';
  462. associations[9] = 'componentsInMaintenanceCount';
  463. return associations;
  464. }.property()
  465. });