host.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  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 validator = require('utils/validator');
  20. App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
  21. name: 'mainHostController',
  22. clearFilters: null,
  23. filteredCount: 0,
  24. /**
  25. * total number of installed hosts
  26. * @type {number}
  27. */
  28. totalCount: function () {
  29. return this.get('hostsCountMap')['TOTAL'] || 0;
  30. }.property('hostsCountMap'),
  31. /**
  32. * @type {boolean}
  33. * @default false
  34. */
  35. resetStartIndex: false,
  36. /**
  37. * flag responsible for updating status counters of hosts
  38. */
  39. isCountersUpdating: false,
  40. hostsCountMap: {},
  41. startIndex: 1,
  42. /**
  43. * true if any host page filter changed
  44. */
  45. filterChangeHappened: false,
  46. /**
  47. * if true, do not clean stored filter before hosts page rendering.
  48. */
  49. showFilterConditionsFirstLoad: false,
  50. content: App.Host.find(),
  51. allHostStackVersions: App.HostStackVersion.find(),
  52. /**
  53. * filterProperties support follow types of filter:
  54. * MATCH - match of RegExp
  55. * EQUAL - equality "="
  56. * LESS - "<"
  57. * MORE - ">"
  58. * MULTIPLE - multiple values to compare
  59. * CUSTOM - substitute values with keys "{#}" in alias
  60. */
  61. filterProperties: [
  62. {
  63. name: 'hostName',
  64. key: 'Hosts/host_name',
  65. type: 'MATCH'
  66. },
  67. {
  68. name: 'ip',
  69. key: 'Hosts/ip',
  70. type: 'MATCH'
  71. },
  72. {
  73. name: 'cpu',
  74. key: 'Hosts/cpu_count',
  75. type: 'EQUAL'
  76. },
  77. {
  78. name: 'memoryFormatted',
  79. key: 'Hosts/total_mem',
  80. type: 'EQUAL'
  81. },
  82. {
  83. name: 'loadAvg',
  84. key: 'metrics/load/load_one',
  85. type: 'EQUAL'
  86. },
  87. {
  88. name: 'rack',
  89. key: 'Hosts/rack_info',
  90. type: 'MATCH'
  91. },
  92. {
  93. name: 'hostComponents',
  94. key: 'host_components/HostRoles/component_name',
  95. type: 'EQUAL'
  96. },
  97. {
  98. name: 'services',
  99. key: 'host_components/HostRoles/service_name',
  100. type: 'MATCH'
  101. },
  102. {
  103. name: 'state',
  104. key: 'host_components/HostRoles/state',
  105. type: 'MATCH'
  106. },
  107. {
  108. name: 'healthClass',
  109. key: 'Hosts/host_status',
  110. type: 'EQUAL'
  111. },
  112. {
  113. name: 'criticalWarningAlertsCount',
  114. key: '(alerts_summary/CRITICAL{0}|alerts_summary/WARNING{1})',
  115. type: 'CUSTOM'
  116. },
  117. {
  118. name: 'componentsWithStaleConfigsCount',
  119. key: 'host_components/HostRoles/stale_configs',
  120. type: 'EQUAL'
  121. },
  122. {
  123. name: 'componentsInPassiveStateCount',
  124. key: 'host_components/HostRoles/maintenance_state',
  125. type: 'MULTIPLE'
  126. },
  127. {
  128. name: 'selected',
  129. key: 'Hosts/host_name',
  130. type: 'MULTIPLE'
  131. },
  132. {
  133. name: 'version',
  134. key: 'stack_versions/repository_versions/RepositoryVersions/display_name',
  135. type: 'EQUAL'
  136. },
  137. {
  138. name: 'versionState',
  139. key: 'stack_versions/HostStackVersions/state',
  140. type: 'EQUAL'
  141. },
  142. {
  143. name: 'hostStackVersion',
  144. key: 'stack_versions',
  145. type: 'EQUAL'
  146. },
  147. {
  148. name: 'componentState',
  149. key: [
  150. '(host_components/HostRoles/component_name={0})',
  151. '(host_components/HostRoles/component_name={0}&host_components/HostRoles/state={1})',
  152. '(host_components/HostRoles/component_name={0}&host_components/HostRoles/desired_admin_state={1})',
  153. '(host_components/HostRoles/component_name={0}&host_components/HostRoles/maintenance_state={1})'
  154. ],
  155. type: 'COMBO'
  156. }
  157. ],
  158. sortProps: [
  159. {
  160. name: 'hostName',
  161. key: 'Hosts/host_name'
  162. },
  163. {
  164. name: 'ip',
  165. key: 'Hosts/ip'
  166. },
  167. {
  168. name: 'cpu',
  169. key: 'Hosts/cpu_count'
  170. },
  171. {
  172. name: 'memoryFormatted',
  173. key: 'Hosts/total_mem'
  174. },
  175. {
  176. name: 'diskUsage',
  177. //TODO disk_usage is relative property and need support from API, metrics/disk/disk_free used temporarily
  178. key: 'metrics/disk/disk_free'
  179. },
  180. {
  181. name: 'rack',
  182. key: 'Hosts/rack_info'
  183. },
  184. {
  185. name: 'loadAvg',
  186. key: 'metrics/load/load_one'
  187. }
  188. ],
  189. /**
  190. * Validate and convert input string to valid url parameter.
  191. * Detect if user have passed string as regular expression or extend
  192. * string to regexp.
  193. *
  194. * @param {String} value
  195. * @return {String}
  196. **/
  197. getRegExp: function (value) {
  198. value = validator.isValidMatchesRegexp(value) ? value.replace(/(\.+\*?|(\.\*)+)$/, '') + '.*' : '^$';
  199. value = /^\.\*/.test(value) || value == '^$' ? value : '.*' + value;
  200. return value;
  201. },
  202. /**
  203. * Sort by host_name by default
  204. * @method getSortProps
  205. * @returns {{value: 'asc|desc', name: string, type: 'SORT'}[]}
  206. */
  207. getSortProps: function () {
  208. var controllerName = this.get('name'),
  209. db = App.db.getSortingStatuses(controllerName);
  210. if (db && db.everyProperty('status', 'sorting')) {
  211. App.db.setSortingStatuses(controllerName, {
  212. name: 'hostName',
  213. status: 'sorting_asc'
  214. });
  215. }
  216. return this._super();
  217. },
  218. /**
  219. * get query parameters computed from filter properties, sort properties and custom properties of view
  220. * @param {boolean} [skipNonFilterProperties]
  221. * @return {Array}
  222. * @method getQueryParameters
  223. */
  224. getQueryParameters: function (skipNonFilterProperties) {
  225. skipNonFilterProperties = skipNonFilterProperties || false;
  226. var queryParams = [],
  227. savedFilterConditions = App.db.getFilterConditions(this.get('name')) || [],
  228. colPropAssoc = this.get('colPropAssoc'),
  229. filterProperties = this.get('filterProperties'),
  230. sortProperties = this.get('sortProps'),
  231. oldProperties = App.router.get('updateController.queryParams.Hosts');
  232. this.set('resetStartIndex', false);
  233. queryParams.pushObjects(this.getPaginationProps());
  234. savedFilterConditions.forEach(function (filter) {
  235. var property = filterProperties.findProperty('name', colPropAssoc[filter.iColumn]);
  236. if (property && filter.value.length > 0 && !filter.skipFilter) {
  237. var result = {
  238. key: property.key,
  239. value: filter.value,
  240. type: property.type,
  241. isFilter: true,
  242. isComponentRelatedFilter: ([13,15].indexOf(filter.iColumn) != -1)
  243. };
  244. if (filter.type === 'string' && sortProperties.someProperty('name', colPropAssoc[filter.iColumn])) {
  245. if (Em.isArray(filter.value)) {
  246. for(var i = 0; i < filter.value.length; i++) {
  247. filter.value[i] = this.getRegExp(filter.value[i]);
  248. }
  249. } else {
  250. result.value = this.getRegExp(filter.value);
  251. }
  252. }
  253. if (filter.type === 'number' || filter.type === 'ambari-bandwidth') {
  254. result.type = this.getComparisonType(filter.value);
  255. result.value = this.getProperValue(filter.value);
  256. }
  257. // enter an exact number for RAM filter, need to do a range number match for this
  258. if (filter.type === 'ambari-bandwidth' && result.type == 'EQUAL' && result.value) {
  259. var valuePair = this.convertMemoryToRange(filter.value);
  260. queryParams.push({
  261. key: result.key,
  262. value: valuePair[0],
  263. type: 'MORE'
  264. });
  265. queryParams.push({
  266. key: result.key,
  267. value: valuePair[1],
  268. type: 'LESS'
  269. });
  270. } else if (filter.type === 'ambari-bandwidth' && result.type != 'EQUAL' && result.value){
  271. // enter a comparison type, eg > 1, just do regular match
  272. result.value = this.convertMemory(filter.value);
  273. queryParams.push(result);
  274. } else if (filter.type === 'sub-resource') {
  275. filter.value.forEach(function (item) {
  276. queryParams.push({
  277. key: result.key + "/" + item.property,
  278. value: item.value,
  279. type: 'EQUAL'
  280. });
  281. }, this);
  282. } else {
  283. queryParams.push(result);
  284. }
  285. }
  286. }, this);
  287. if (queryParams.filterProperty('isFilter').length !== oldProperties.filterProperty('isFilter').length) {
  288. queryParams.findProperty('key', 'from').value = 0;
  289. this.set('resetStartIndex', true);
  290. } else {
  291. queryParams.filterProperty('isFilter').forEach(function (queryParam) {
  292. var oldProperty = oldProperties.filterProperty('isFilter').findProperty('key', queryParam.key);
  293. if (!oldProperty || JSON.stringify(oldProperty.value) !== JSON.stringify(queryParam.value)) {
  294. queryParams.findProperty('key', 'from').value = 0;
  295. this.set('resetStartIndex', true);
  296. }
  297. }, this);
  298. }
  299. if (!skipNonFilterProperties) {
  300. queryParams.pushObjects(this.getSortProps());
  301. }
  302. return queryParams;
  303. },
  304. /**
  305. * update status counters of hosts
  306. */
  307. updateStatusCounters: function () {
  308. var self = this;
  309. if (this.get('isCountersUpdating')) {
  310. App.ajax.send({
  311. name: 'host.status.counters',
  312. sender: this,
  313. data: {},
  314. success: 'updateStatusCountersSuccessCallback',
  315. error: 'updateStatusCountersErrorCallback',
  316. callback: function() {
  317. setTimeout(function () {
  318. self.updateStatusCounters();
  319. }, App.get('hostStatusCountersUpdateInterval'));
  320. }
  321. });
  322. }
  323. },
  324. /**
  325. * success callback on <code>updateStatusCounters()</code>
  326. * map counters' value to categories
  327. * @param data
  328. */
  329. updateStatusCountersSuccessCallback: function (data) {
  330. var hostsCountMap = {
  331. 'HEALTHY': data.Clusters.health_report['Host/host_status/HEALTHY'],
  332. 'UNHEALTHY': data.Clusters.health_report['Host/host_status/UNHEALTHY'],
  333. 'ALERT': data.Clusters.health_report['Host/host_status/ALERT'],
  334. 'UNKNOWN': data.Clusters.health_report['Host/host_status/UNKNOWN'],
  335. 'health-status-RESTART': data.Clusters.health_report['Host/stale_config'],
  336. 'health-status-PASSIVE_STATE': data.Clusters.health_report['Host/maintenance_state'],
  337. 'TOTAL': data.Clusters.total_hosts
  338. };
  339. this.set('hostsCountMap', hostsCountMap);
  340. },
  341. /**
  342. * success callback on <code>updateStatusCounters()</code>
  343. */
  344. updateStatusCountersErrorCallback: Em.K,
  345. /**
  346. * Return value without predicate
  347. * @param {String} value
  348. * @return {String}
  349. */
  350. getProperValue: function (value) {
  351. return (['>', '<', '='].contains(value.charAt(0))) ? value.substr(1, value.length) : value;
  352. },
  353. /**
  354. * Return value converted to kilobytes
  355. * @param {String} value
  356. * @return {number}
  357. */
  358. convertMemory: function (value) {
  359. var scale = value.charAt(value.length - 1);
  360. // first char may be predicate for comparison
  361. value = this.getProperValue(value);
  362. var parsedValue = parseFloat(value);
  363. if (isNaN(parsedValue)) {
  364. return value;
  365. }
  366. switch (scale) {
  367. case 'g':
  368. parsedValue *= 1048576;
  369. break;
  370. case 'm':
  371. parsedValue *= 1024;
  372. break;
  373. case 'k':
  374. break;
  375. default:
  376. //default value in GB
  377. parsedValue *= 1048576;
  378. }
  379. return Math.round(parsedValue);
  380. },
  381. /**
  382. * Return value converted to a range of kilobytes
  383. * @param {String} value
  384. * @return {Array}
  385. */
  386. convertMemoryToRange: function (value) {
  387. var scale = value.charAt(value.length - 1);
  388. // first char may be predicate for comparison
  389. value = this.getProperValue(value);
  390. var parsedValue = parseFloat(value);
  391. if (isNaN(parsedValue)) {
  392. return [0, 0];
  393. }
  394. var parsedValuePair = this.rangeConvertNumber(parsedValue, scale);
  395. var multiplyingFactor = 1;
  396. switch (scale) {
  397. case 'g':
  398. multiplyingFactor = 1048576;
  399. break;
  400. case 'm':
  401. multiplyingFactor = 1024;
  402. break;
  403. case 'k':
  404. break;
  405. default:
  406. //default value in GB
  407. multiplyingFactor = 1048576;
  408. }
  409. parsedValuePair[0] = Math.round( parsedValuePair[0] * multiplyingFactor);
  410. parsedValuePair[1] = Math.round( parsedValuePair[1] * multiplyingFactor);
  411. return parsedValuePair;
  412. },
  413. /**
  414. * Return value converted to a range of kilobytes
  415. * eg, return value 1.83 g will target 1.82500 ~ 1.83499 g
  416. * eg, return value 1.8 k will target 1.7500 ~ 1.8499 k
  417. * eg, return value 1.8 m will target 1.7500 ~ 1.8499 m
  418. * @param {number} value
  419. * @param {String} scale
  420. * @return {Array}
  421. */
  422. rangeConvertNumber: function (value, scale) {
  423. if (isNaN(value)) {
  424. return [0, 0];
  425. }
  426. var valuePair = [];
  427. switch (scale) {
  428. case 'g':
  429. valuePair = [value - 0.005000, value + 0.004999999];
  430. break;
  431. case 'm':
  432. case 'k':
  433. valuePair = [value - 0.05000, value + 0.04999];
  434. break;
  435. default:
  436. //default value in GB
  437. valuePair = [value - 0.005000, value + 0.004999999];
  438. }
  439. return valuePair;
  440. },
  441. /**
  442. * Return comparison type depending on populated predicate
  443. * @param {string} value
  444. * @return {string}
  445. */
  446. getComparisonType: function (value) {
  447. var comparisonChar = value.charAt(0);
  448. var result = 'EQUAL';
  449. if (isNaN(comparisonChar)) {
  450. switch (comparisonChar) {
  451. case '>':
  452. result = 'MORE';
  453. break;
  454. case '<':
  455. result = 'LESS';
  456. break;
  457. }
  458. }
  459. return result;
  460. },
  461. labelValueMap: {},
  462. /**
  463. * Filter hosts by componentName of <code>component</code>
  464. * @param {App.HostComponent} component
  465. */
  466. filterByComponent: function (component) {
  467. if (!component) return;
  468. var componentName = component.get('componentName');
  469. var displayName = App.format.role(componentName, false);
  470. var colPropAssoc = this.get('colPropAssoc');
  471. var map = this.get('labelValueMap');
  472. var filterForComponent = {
  473. iColumn: 15,
  474. value: componentName + ':ALL',
  475. type: 'string'
  476. };
  477. map[displayName] = componentName;
  478. map['All'] = 'ALL';
  479. var filterStr = '"' + displayName + '"' + ': "All"';
  480. App.db.setFilterConditions(this.get('name'), [filterForComponent]);
  481. App.db.setComboSearchQuery(this.get('name'), filterStr);
  482. },
  483. /**
  484. * Filter hosts by stack version and state
  485. * @param {String} displayName
  486. * @param {Array} states
  487. */
  488. filterByStack: function (displayName, states) {
  489. if (Em.isNone(displayName) || Em.isNone(states) || !states.length) return;
  490. var colPropAssoc = this.get('colPropAssoc');
  491. var map = this.get('labelValueMap');
  492. var stateFilterStrs = [];
  493. var versionFilter = {
  494. iColumn: 16,
  495. value: displayName,
  496. type: 'string'
  497. };
  498. var stateFilter = {
  499. iColumn: 17,
  500. value: states,
  501. type: 'string'
  502. };
  503. map["Stack Version"] = colPropAssoc[versionFilter.iColumn];
  504. map["Version State"] = colPropAssoc[stateFilter.iColumn];
  505. stateFilter.value.forEach(function(state) {
  506. map[App.HostStackVersion.formatStatus(state)] = state;
  507. stateFilterStrs.push('"Version State": "' + App.HostStackVersion.formatStatus(state) + '"');
  508. });
  509. var versionFilterStr = '"Stack Version": "' + versionFilter.value + '"';
  510. App.db.setFilterConditions(this.get('name'), [versionFilter, stateFilter]);
  511. App.db.setComboSearchQuery(this.get('name'), [versionFilterStr, stateFilterStrs.join(' ')].join(' '));
  512. },
  513. goToHostAlerts: function (event) {
  514. var host = event && event.context;
  515. if (host) {
  516. App.router.transitionTo('main.hosts.hostDetails.alerts', host);
  517. }
  518. },
  519. /**
  520. * remove selected hosts
  521. */
  522. removeHosts: function () {
  523. var hosts = this.get('content');
  524. var selectedHosts = hosts.filterProperty('isChecked');
  525. this.get('fullContent').removeObjects(selectedHosts);
  526. },
  527. /**
  528. * remove hosts with id equal host_id
  529. * @param {String} host_id
  530. */
  531. checkRemoved: function (host_id) {
  532. var hosts = this.get('content');
  533. var selectedHosts = hosts.filterProperty('id', host_id);
  534. this.get('fullContent').removeObjects(selectedHosts);
  535. },
  536. /**
  537. * associations between host property and column index
  538. * @type {Array}
  539. */
  540. colPropAssoc: function () {
  541. var associations = [];
  542. associations[0] = 'healthClass';
  543. associations[1] = 'hostName';
  544. associations[2] = 'ip';
  545. associations[3] = 'cpu';
  546. associations[4] = 'memoryFormatted';
  547. associations[5] = 'loadAvg';
  548. associations[6] = 'hostComponents';
  549. associations[7] = 'criticalWarningAlertsCount';
  550. associations[8] = 'componentsWithStaleConfigsCount';
  551. associations[9] = 'componentsInPassiveStateCount';
  552. associations[10] = 'selected';
  553. associations[11] = 'hostStackVersion';
  554. associations[12] = 'rack';
  555. associations[13] = 'services';
  556. associations[14] = 'state';
  557. associations[15] = 'componentState';
  558. associations[16] = 'version';
  559. associations[17] = 'versionState';
  560. return associations;
  561. }.property()
  562. });