host_component_view.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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 uiEffects = require('utils/ui_effects');
  20. App.HostComponentView = Em.View.extend({
  21. templateName: require('templates/main/host/details/host_component'),
  22. /**
  23. * @type {App.HostComponent}
  24. */
  25. content: null,
  26. excludedMasterCommands: ['DECOMMISSION', 'RECOMMISSION'],
  27. /**
  28. * @type {App.HostComponent}
  29. */
  30. hostComponent: function () {
  31. var hostComponent = null;
  32. var serviceComponent = this.get('content');
  33. var host = App.router.get('mainHostDetailsController.content');
  34. if (host) {
  35. hostComponent = host.get('hostComponents').findProperty('componentName', serviceComponent.get('componentName'));
  36. }
  37. return hostComponent;
  38. }.property('content', 'App.router.mainHostDetailsController.content'),
  39. /**
  40. * @type {String}
  41. */
  42. workStatus: Em.computed.firstNotBlank('hostComponent.workStatus', 'content.workStatus'),
  43. /**
  44. * Return host component text status
  45. * @type {String}
  46. */
  47. componentTextStatus: Em.computed.firstNotBlank('hostComponent.componentTextStatus', 'content.componentTextStatus'),
  48. /**
  49. * CSS-class for host component status
  50. * @type {String}
  51. */
  52. statusClass: function () {
  53. //Class when install failed
  54. if (this.get('workStatus') === App.HostComponentStatus.install_failed) {
  55. return 'health-status-color-red icon-cog';
  56. }
  57. //Class when installing
  58. if (this.get('workStatus') === App.HostComponentStatus.installing) {
  59. return 'health-status-color-blue icon-cog';
  60. }
  61. //For all other cases
  62. return 'health-status-' + App.HostComponentStatus.getKeyName(this.get('workStatus'));
  63. }.property('workStatus'),
  64. /**
  65. * CSS-icon-class for host component status
  66. * @type {String}
  67. */
  68. statusIconClass: function () {
  69. return Em.getWithDefault({
  70. 'health-status-started': App.healthIconClassGreen,
  71. 'health-status-starting': App.healthIconClassGreen,
  72. 'health-status-installed': App.healthIconClassRed,
  73. 'health-status-stopping': App.healthIconClassRed,
  74. 'health-status-unknown': App.healthIconClassYellow,
  75. 'health-status-DEAD-ORANGE': App.healthIconClassOrange
  76. }, this.get('statusClass'), '');
  77. }.property('statusClass'),
  78. /**
  79. * CSS-class for disabling drop-down menu with list of host component actions
  80. * Disabled if host's <code>healthClass</code> is health-status-DEAD-YELLOW (lost heartbeat)
  81. * Disabled if component's action list is empty
  82. * @type {String}
  83. */
  84. disabled: function () {
  85. return ( (this.get('parentView.content.healthClass') === "health-status-DEAD-YELLOW") || (this.get('noActionAvailable') === 'hidden' && this.get('isRestartComponentDisabled'))) ? 'disabled' : '';
  86. }.property('parentView.content.healthClass', 'noActionAvailable', 'isRestartComponentDisabled'),
  87. /**
  88. * For Upgrade failed state
  89. * @type {bool}
  90. */
  91. isUpgradeFailed: Em.computed.equal('workStatus', App.HostComponentStatus.upgrade_failed),
  92. /**
  93. * For Install failed state
  94. * @type {bool}
  95. */
  96. isInstallFailed: Em.computed.equal('workStatus', App.HostComponentStatus.install_failed),
  97. /**
  98. * For Started and Starting states
  99. * @type {bool}
  100. */
  101. isStart: Em.computed.existsIn('workStatus', [App.HostComponentStatus.started, App.HostComponentStatus.starting]),
  102. /**
  103. * For Installed state
  104. * @type {bool}
  105. */
  106. isStop: Em.computed.equal('workStatus', App.HostComponentStatus.stopped),
  107. /**
  108. * For Installing state
  109. * @type {bool}
  110. */
  111. isInstalling: Em.computed.equal('workStatus', App.HostComponentStatus.installing),
  112. /**
  113. * For Init state
  114. * @type {bool}
  115. */
  116. isInit: Em.computed.equal('workStatus', App.HostComponentStatus.init),
  117. /**
  118. * For Stopping or Starting states
  119. * @type {bool}
  120. */
  121. isInProgress: Em.computed.existsIn('workStatus', [App.HostComponentStatus.stopping, App.HostComponentStatus.starting]),
  122. withoutActions: Em.computed.existsIn('workStatus', [App.HostComponentStatus.starting, App.HostComponentStatus.stopping, App.HostComponentStatus.unknown, App.HostComponentStatus.disabled]),
  123. /**
  124. * No action available while component is starting/stopping/unknown
  125. * @type {String}
  126. */
  127. noActionAvailable: Em.computed.ifThenElse('withoutActions', 'hidden', ''),
  128. /**
  129. * For OFF <code>passiveState</code> of host component
  130. * @type {bool}
  131. */
  132. isActive: Em.computed.equal('content.passiveState', 'OFF'),
  133. /**
  134. * Tooltip message for switch maintenance mode option
  135. * @type {Strting}
  136. */
  137. maintenanceTooltip: function () {
  138. switch (this.get('content.passiveState')) {
  139. case 'IMPLIED_FROM_SERVICE':
  140. return Em.I18n.t('passiveState.disabled.impliedFromHighLevel').format(this.get('content.displayName'), this.get('content.service.serviceName'));
  141. case 'IMPLIED_FROM_HOST':
  142. return Em.I18n.t('passiveState.disabled.impliedFromHighLevel').format(this.get('content.displayName'), this.get('content.host.hostName'));
  143. case 'IMPLIED_FROM_SERVICE_AND_HOST':
  144. return Em.I18n.t('passiveState.disabled.impliedFromServiceAndHost').format(this.get('content.displayName'), this.get('content.service.serviceName'), this.get('content.host.hostName'));
  145. default:
  146. return '';
  147. }
  148. }.property('content.passiveState'),
  149. /**
  150. * Shows whether we need to show Delete button
  151. * @type {bool}
  152. */
  153. isDeletableComponent: function () {
  154. return App.get('components.deletable').contains(this.get('content.componentName'));
  155. }.property('content'),
  156. /**
  157. * Host component with some <code>workStatus</code> can't be moved (so, disable such action in the dropdown list)
  158. * @type {boolean}
  159. */
  160. isMoveComponentDisabled: function () {
  161. return App.get('allHostNames').length === App.HostComponent.find().filterProperty('componentName', this.get('content.componentName')).mapProperty('hostName').length;
  162. }.property('content.componentName', 'App.allHostNames'),
  163. /**
  164. * Host component with some <code>workStatus</code> can't be deleted (so, disable such action in the dropdown list)
  165. * @type {bool}
  166. */
  167. isDeleteComponentDisabled: function () {
  168. var stackComponentCount = App.StackServiceComponent.find(this.get('hostComponent.componentName')).get('minToInstall');
  169. var installedCount = App.HostComponent.getCount(this.get('hostComponent.componentName'), 'totalCount');
  170. if(this.get('hostComponent.componentName') == 'MYSQL_SERVER' && this.get('hostComponent.serviceDisplayName') == 'Hive') {
  171. var db_type=App.db.getConfigs().findProperty('type','hive-env').properties['hive_database'];
  172. var status=[App.HostComponentStatus.stopped, App.HostComponentStatus.unknown, App.HostComponentStatus.install_failed, App.HostComponentStatus.upgrade_failed, App.HostComponentStatus.init].contains(this.get('workStatus'));
  173. if(db_type.indexOf('Existing') > -1 && status)
  174. return false;
  175. else
  176. return true;
  177. }
  178. return (installedCount <= stackComponentCount)
  179. || ![App.HostComponentStatus.stopped, App.HostComponentStatus.unknown, App.HostComponentStatus.install_failed, App.HostComponentStatus.upgrade_failed, App.HostComponentStatus.init].contains(this.get('workStatus'));
  180. }.property('workStatus'),
  181. /**
  182. * Gets number of current running components that are applied to the cluster
  183. * @returns {Number}
  184. */
  185. runningComponentCounter: function () {
  186. return App.HostComponent.find().filter(function (component) {
  187. return (component.get('componentName') === this.get('content.componentName') && [App.HostComponentStatus.started, App.HostComponentStatus.starting].contains(component.get('workStatus')))
  188. }, this).length;
  189. },
  190. /**
  191. * Check if component may be reassinged to another host
  192. * @type {bool}
  193. */
  194. isReassignable: function () {
  195. return App.get('components.reassignable').contains(this.get('content.componentName')) && App.router.get('mainHostController.hostsCountMap')['TOTAL'] > 1;
  196. }.property('content.componentName'),
  197. /**
  198. * Check if component is restartable
  199. * @type {bool}
  200. */
  201. isRestartableComponent: function() {
  202. return App.get('components.restartable').contains(this.get('content.componentName'));
  203. }.property('content'),
  204. /**
  205. * Host component with some <code>workStatus</code> can't be restarted (so, disable such action in the dropdown list)
  206. * @type {bool}
  207. */
  208. isRestartComponentDisabled: Em.computed.notEqual('workStatus', App.HostComponentStatus.started),
  209. /**
  210. * Check if component configs can be refreshed
  211. * @type {bool}
  212. */
  213. isRefreshConfigsAllowed: function() {
  214. return App.get('components.refreshConfigsAllowed').contains(this.get('content.componentName'));
  215. }.property('content'),
  216. willInsertElement: function() {
  217. //make link to view instance to get decommission state
  218. this.set('content.view', this);
  219. },
  220. didInsertElement: function () {
  221. App.tooltip($('[rel=componentHealthTooltip]'));
  222. App.tooltip($('[rel=passiveTooltip]'));
  223. if (this.get('isInProgress')) {
  224. this.doBlinking();
  225. }
  226. },
  227. /**
  228. * Do blinking for 1 minute
  229. */
  230. doBlinking: function () {
  231. var workStatus = this.get('workStatus');
  232. var self = this;
  233. var pulsate = [ App.HostComponentStatus.starting, App.HostComponentStatus.stopping, App.HostComponentStatus.installing].contains(workStatus);
  234. if (pulsate && !self.get('isBlinking')) {
  235. self.set('isBlinking', true);
  236. uiEffects.pulsate(self.$('.components-health'), 1000, function () {
  237. self.set('isBlinking', false);
  238. self.doBlinking();
  239. });
  240. }
  241. },
  242. /**
  243. * Start blinking when host component is starting/stopping
  244. */
  245. startBlinking: function () {
  246. this.$('.components-health').stop(true, true);
  247. this.$('.components-health').css({opacity: 1.0});
  248. this.doBlinking();
  249. }.observes('workStatus'),
  250. /**
  251. * Get custom commands for slave components
  252. */
  253. customCommands: function() {
  254. var customCommands;
  255. var hostComponent = this.get('content');
  256. var component = App.StackServiceComponent.find(hostComponent.get('componentName'));
  257. customCommands = this.getCustomCommands(component, hostComponent, component.get('isSlave'));
  258. return customCommands;
  259. }.property('content', 'workStatus'),
  260. /**
  261. * Get a list of custom commands
  262. *
  263. * @param component
  264. * @param hostComponent
  265. * @param isSlave
  266. * @returns {Array}
  267. */
  268. getCustomCommands: function (component, hostComponent, isSlave) {
  269. isSlave = isSlave || false;
  270. if (!component || !hostComponent) {
  271. return [];
  272. }
  273. var self = this;
  274. var commands = component.get('customCommands');
  275. var customCommands = [];
  276. commands.forEach(function(command) {
  277. if (!isSlave && !self.meetsCustomCommandReq(component, command)) {
  278. return;
  279. }
  280. var commandMap = App.HostComponentActionMap.getMap(self)[command];
  281. // push command if either there is no map or map is not instructing to hide command from this view
  282. if (!commandMap || !commandMap.hideFromComponentView) {
  283. customCommands.push({
  284. label: self.getCustomCommandLabel(command),
  285. service: component.get('serviceName'),
  286. hosts: hostComponent.get('hostName'),
  287. context: (!!commandMap && !!commandMap.context) ? commandMap.context : null,
  288. component: component.get('componentName'),
  289. command: command,
  290. disabled: !!commandMap ? !!commandMap.disabled : false
  291. });
  292. }
  293. });
  294. return customCommands;
  295. },
  296. /**
  297. * Get the Label of the custom command
  298. *
  299. * @param command
  300. * @returns {String}
  301. */
  302. getCustomCommandLabel: function (command) {
  303. if (command in App.HostComponentActionMap.getMap(this) && App.HostComponentActionMap.getMap(this)[command].label)
  304. return App.HostComponentActionMap.getMap(this)[command].label;
  305. return Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(App.format.normalizeNameBySeparators(command, ["_", "-", " "]));
  306. },
  307. /**
  308. * The custom command meets the requirements to be active on master
  309. *
  310. * @param component
  311. * @param command
  312. *
  313. * @return {Boolean}
  314. */
  315. meetsCustomCommandReq: function (component, command) {
  316. var excludedMasterCommands = this.get('excludedMasterCommands');
  317. if (excludedMasterCommands.indexOf(command) >= 0) {
  318. return false;
  319. }
  320. if (component.get('cardinality') !== '1') {
  321. if (!this.get('isStart')) {
  322. if (App.HostComponent.getCount(this.get('hostComponent.componentName'), 'totalCount') > 1) {
  323. if (this.runningComponentCounter()) {
  324. return false;
  325. }
  326. } else {
  327. return false;
  328. }
  329. }
  330. }
  331. return true;
  332. }
  333. });