host.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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. };
  243. if (filter.type === 'string' && sortProperties.someProperty('name', colPropAssoc[filter.iColumn])) {
  244. if (Em.isArray(filter.value)) {
  245. for(var i = 0; i < filter.value.length; i++) {
  246. filter.value[i] = this.getRegExp(filter.value[i]);
  247. }
  248. } else {
  249. result.value = this.getRegExp(filter.value);
  250. }
  251. }
  252. if (filter.type === 'number' || filter.type === 'ambari-bandwidth') {
  253. result.type = this.getComparisonType(filter.value);
  254. result.value = this.getProperValue(filter.value);
  255. }
  256. // enter an exact number for RAM filter, need to do a range number match for this
  257. if (filter.type === 'ambari-bandwidth' && result.type == 'EQUAL' && result.value) {
  258. var valuePair = this.convertMemoryToRange(filter.value);
  259. queryParams.push({
  260. key: result.key,
  261. value: valuePair[0],
  262. type: 'MORE'
  263. });
  264. queryParams.push({
  265. key: result.key,
  266. value: valuePair[1],
  267. type: 'LESS'
  268. });
  269. } else if (filter.type === 'ambari-bandwidth' && result.type != 'EQUAL' && result.value){
  270. // enter a comparison type, eg > 1, just do regular match
  271. result.value = this.convertMemory(filter.value);
  272. queryParams.push(result);
  273. } else if (filter.type === 'sub-resource') {
  274. filter.value.forEach(function (item) {
  275. queryParams.push({
  276. key: result.key + "/" + item.property,
  277. value: item.value,
  278. type: 'EQUAL'
  279. });
  280. }, this);
  281. } else {
  282. queryParams.push(result);
  283. }
  284. }
  285. }, this);
  286. if (queryParams.filterProperty('isFilter').length !== oldProperties.filterProperty('isFilter').length) {
  287. queryParams.findProperty('key', 'from').value = 0;
  288. this.set('resetStartIndex', true);
  289. } else {
  290. queryParams.filterProperty('isFilter').forEach(function (queryParam) {
  291. var oldProperty = oldProperties.filterProperty('isFilter').findProperty('key', queryParam.key);
  292. if (!oldProperty || JSON.stringify(oldProperty.value) !== JSON.stringify(queryParam.value)) {
  293. queryParams.findProperty('key', 'from').value = 0;
  294. this.set('resetStartIndex', true);
  295. }
  296. }, this);
  297. }
  298. if (!skipNonFilterProperties) {
  299. queryParams.pushObjects(this.getSortProps());
  300. }
  301. return queryParams;
  302. },
  303. /**
  304. * update status counters of hosts
  305. */
  306. updateStatusCounters: function () {
  307. var self = this;
  308. if (this.get('isCountersUpdating')) {
  309. App.ajax.send({
  310. name: 'host.status.counters',
  311. sender: this,
  312. data: {},
  313. success: 'updateStatusCountersSuccessCallback',
  314. error: 'updateStatusCountersErrorCallback',
  315. callback: function() {
  316. setTimeout(function () {
  317. self.updateStatusCounters();
  318. }, App.get('hostStatusCountersUpdateInterval'));
  319. }
  320. });
  321. }
  322. },
  323. /**
  324. * success callback on <code>updateStatusCounters()</code>
  325. * map counters' value to categories
  326. * @param data
  327. */
  328. updateStatusCountersSuccessCallback: function (data) {
  329. var hostsCountMap = {
  330. 'HEALTHY': data.Clusters.health_report['Host/host_status/HEALTHY'],
  331. 'UNHEALTHY': data.Clusters.health_report['Host/host_status/UNHEALTHY'],
  332. 'ALERT': data.Clusters.health_report['Host/host_status/ALERT'],
  333. 'UNKNOWN': data.Clusters.health_report['Host/host_status/UNKNOWN'],
  334. 'health-status-RESTART': data.Clusters.health_report['Host/stale_config'],
  335. 'health-status-PASSIVE_STATE': data.Clusters.health_report['Host/maintenance_state'],
  336. 'TOTAL': data.Clusters.total_hosts
  337. };
  338. this.set('hostsCountMap', hostsCountMap);
  339. },
  340. /**
  341. * success callback on <code>updateStatusCounters()</code>
  342. */
  343. updateStatusCountersErrorCallback: Em.K,
  344. /**
  345. * Return value without predicate
  346. * @param {String} value
  347. * @return {String}
  348. */
  349. getProperValue: function (value) {
  350. return (['>', '<', '='].contains(value.charAt(0))) ? value.substr(1, value.length) : value;
  351. },
  352. /**
  353. * Return value converted to kilobytes
  354. * @param {String} value
  355. * @return {number}
  356. */
  357. convertMemory: function (value) {
  358. var scale = value.charAt(value.length - 1);
  359. // first char may be predicate for comparison
  360. value = this.getProperValue(value);
  361. var parsedValue = parseFloat(value);
  362. if (isNaN(parsedValue)) {
  363. return value;
  364. }
  365. switch (scale) {
  366. case 'g':
  367. parsedValue *= 1048576;
  368. break;
  369. case 'm':
  370. parsedValue *= 1024;
  371. break;
  372. case 'k':
  373. break;
  374. default:
  375. //default value in GB
  376. parsedValue *= 1048576;
  377. }
  378. return Math.round(parsedValue);
  379. },
  380. /**
  381. * Return value converted to a range of kilobytes
  382. * @param {String} value
  383. * @return {Array}
  384. */
  385. convertMemoryToRange: function (value) {
  386. var scale = value.charAt(value.length - 1);
  387. // first char may be predicate for comparison
  388. value = this.getProperValue(value);
  389. var parsedValue = parseFloat(value);
  390. if (isNaN(parsedValue)) {
  391. return [0, 0];
  392. }
  393. var parsedValuePair = this.rangeConvertNumber(parsedValue, scale);
  394. var multiplyingFactor = 1;
  395. switch (scale) {
  396. case 'g':
  397. multiplyingFactor = 1048576;
  398. break;
  399. case 'm':
  400. multiplyingFactor = 1024;
  401. break;
  402. case 'k':
  403. break;
  404. default:
  405. //default value in GB
  406. multiplyingFactor = 1048576;
  407. }
  408. parsedValuePair[0] = Math.round( parsedValuePair[0] * multiplyingFactor);
  409. parsedValuePair[1] = Math.round( parsedValuePair[1] * multiplyingFactor);
  410. return parsedValuePair;
  411. },
  412. /**
  413. * Return value converted to a range of kilobytes
  414. * eg, return value 1.83 g will target 1.82500 ~ 1.83499 g
  415. * eg, return value 1.8 k will target 1.7500 ~ 1.8499 k
  416. * eg, return value 1.8 m will target 1.7500 ~ 1.8499 m
  417. * @param {number} value
  418. * @param {String} scale
  419. * @return {Array}
  420. */
  421. rangeConvertNumber: function (value, scale) {
  422. if (isNaN(value)) {
  423. return [0, 0];
  424. }
  425. var valuePair = [];
  426. switch (scale) {
  427. case 'g':
  428. valuePair = [value - 0.005000, value + 0.004999999];
  429. break;
  430. case 'm':
  431. case 'k':
  432. valuePair = [value - 0.05000, value + 0.04999];
  433. break;
  434. default:
  435. //default value in GB
  436. valuePair = [value - 0.005000, value + 0.004999999];
  437. }
  438. return valuePair;
  439. },
  440. /**
  441. * Return comparison type depending on populated predicate
  442. * @param {string} value
  443. * @return {string}
  444. */
  445. getComparisonType: function (value) {
  446. var comparisonChar = value.charAt(0);
  447. var result = 'EQUAL';
  448. if (isNaN(comparisonChar)) {
  449. switch (comparisonChar) {
  450. case '>':
  451. result = 'MORE';
  452. break;
  453. case '<':
  454. result = 'LESS';
  455. break;
  456. }
  457. }
  458. return result;
  459. },
  460. labelValueMap: {},
  461. /**
  462. * Filter hosts by componentName of <code>component</code>
  463. * @param {App.HostComponent} component
  464. */
  465. filterByComponent: function (component) {
  466. if (!component) return;
  467. var componentName = component.get('componentName');
  468. var displayName = App.format.role(componentName, false);
  469. var colPropAssoc = this.get('colPropAssoc');
  470. var map = this.get('labelValueMap');
  471. var filterForComponent = {
  472. iColumn: 15,
  473. value: componentName + ':ALL',
  474. type: 'string'
  475. };
  476. map[displayName] = componentName;
  477. map['All'] = 'ALL';
  478. var filterStr = '"' + displayName + '"' + ': "All"';
  479. App.db.setFilterConditions(this.get('name'), [filterForComponent]);
  480. App.db.setComboSearchQuery(this.get('name'), filterStr);
  481. },
  482. /**
  483. * Filter hosts by stack version and state
  484. * @param {String} displayName
  485. * @param {Array} states
  486. */
  487. filterByStack: function (displayName, states) {
  488. if (Em.isNone(displayName) || Em.isNone(states) || !states.length) return;
  489. var colPropAssoc = this.get('colPropAssoc');
  490. var map = this.get('labelValueMap');
  491. var stateFilterStrs = [];
  492. var versionFilter = {
  493. iColumn: 16,
  494. value: displayName,
  495. type: 'string'
  496. };
  497. var stateFilter = {
  498. iColumn: 17,
  499. value: states,
  500. type: 'string'
  501. };
  502. map["Stack Version"] = colPropAssoc[versionFilter.iColumn];
  503. map["Version State"] = colPropAssoc[stateFilter.iColumn];
  504. stateFilter.value.forEach(function(state) {
  505. map[App.HostStackVersion.formatStatus(state)] = state;
  506. stateFilterStrs.push('"Version State": "' + App.HostStackVersion.formatStatus(state) + '"');
  507. });
  508. var versionFilterStr = '"Stack Version": "' + versionFilter.value + '"';
  509. App.db.setFilterConditions(this.get('name'), [versionFilter, stateFilter]);
  510. App.db.setComboSearchQuery(this.get('name'), [versionFilterStr, stateFilterStrs.join(' ')].join(' '));
  511. },
  512. goToHostAlerts: function (event) {
  513. var host = event && event.context;
  514. if (host) {
  515. App.router.transitionTo('main.hosts.hostDetails.alerts', host);
  516. }
  517. },
  518. /**
  519. * remove selected hosts
  520. */
  521. removeHosts: function () {
  522. var hosts = this.get('content');
  523. var selectedHosts = hosts.filterProperty('isChecked');
  524. this.get('fullContent').removeObjects(selectedHosts);
  525. },
  526. /**
  527. * remove hosts with id equal host_id
  528. * @param {String} host_id
  529. */
  530. checkRemoved: function (host_id) {
  531. var hosts = this.get('content');
  532. var selectedHosts = hosts.filterProperty('id', host_id);
  533. this.get('fullContent').removeObjects(selectedHosts);
  534. },
  535. /**
  536. * associations between host property and column index
  537. * @type {Array}
  538. */
  539. colPropAssoc: function () {
  540. var associations = [];
  541. associations[0] = 'healthClass';
  542. associations[1] = 'hostName';
  543. associations[2] = 'ip';
  544. associations[3] = 'cpu';
  545. associations[4] = 'memoryFormatted';
  546. associations[5] = 'loadAvg';
  547. associations[6] = 'hostComponents';
  548. associations[7] = 'criticalWarningAlertsCount';
  549. associations[8] = 'componentsWithStaleConfigsCount';
  550. associations[9] = 'componentsInPassiveStateCount';
  551. associations[10] = 'selected';
  552. associations[11] = 'hostStackVersion';
  553. associations[12] = 'rack';
  554. associations[13] = 'services';
  555. associations[14] = 'state';
  556. associations[15] = 'componentState';
  557. associations[16] = 'version';
  558. associations[17] = 'versionState';
  559. return associations;
  560. }.property()
  561. });