host_component_view.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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. return (installedCount <= stackComponentCount)
  171. || ![App.HostComponentStatus.stopped, App.HostComponentStatus.unknown, App.HostComponentStatus.install_failed, App.HostComponentStatus.upgrade_failed, App.HostComponentStatus.init].contains(this.get('workStatus'));
  172. }.property('workStatus'),
  173. /**
  174. * Gets number of current running components that are applied to the cluster
  175. * @returns {Number}
  176. */
  177. runningComponentCounter: function () {
  178. return App.HostComponent.find().filter(function (component) {
  179. return (component.get('componentName') === this.get('content.componentName') && [App.HostComponentStatus.started, App.HostComponentStatus.starting].contains(component.get('workStatus')))
  180. }, this).length;
  181. },
  182. /**
  183. * Check if component may be reassinged to another host
  184. * @type {bool}
  185. */
  186. isReassignable: function () {
  187. return App.get('components.reassignable').contains(this.get('content.componentName')) && App.router.get('mainHostController.hostsCountMap')['TOTAL'] > 1;
  188. }.property('content.componentName'),
  189. /**
  190. * Check if component is restartable
  191. * @type {bool}
  192. */
  193. isRestartableComponent: function() {
  194. return App.get('components.restartable').contains(this.get('content.componentName'));
  195. }.property('content'),
  196. /**
  197. * Host component with some <code>workStatus</code> can't be restarted (so, disable such action in the dropdown list)
  198. * @type {bool}
  199. */
  200. isRestartComponentDisabled: Em.computed.notEqual('workStatus', App.HostComponentStatus.started),
  201. /**
  202. * Check if component configs can be refreshed
  203. * @type {bool}
  204. */
  205. isRefreshConfigsAllowed: function() {
  206. return App.get('components.refreshConfigsAllowed').contains(this.get('content.componentName'));
  207. }.property('content'),
  208. willInsertElement: function() {
  209. //make link to view instance to get decommission state
  210. this.set('content.view', this);
  211. },
  212. didInsertElement: function () {
  213. App.tooltip($('[rel=componentHealthTooltip]'));
  214. App.tooltip($('[rel=passiveTooltip]'));
  215. if (this.get('isInProgress')) {
  216. this.doBlinking();
  217. }
  218. },
  219. /**
  220. * Do blinking for 1 minute
  221. */
  222. doBlinking: function () {
  223. var workStatus = this.get('workStatus');
  224. var self = this;
  225. var pulsate = [ App.HostComponentStatus.starting, App.HostComponentStatus.stopping, App.HostComponentStatus.installing].contains(workStatus);
  226. if (pulsate && !self.get('isBlinking')) {
  227. self.set('isBlinking', true);
  228. uiEffects.pulsate(self.$('.components-health'), 1000, function () {
  229. self.set('isBlinking', false);
  230. self.doBlinking();
  231. });
  232. }
  233. },
  234. /**
  235. * Start blinking when host component is starting/stopping
  236. */
  237. startBlinking: function () {
  238. this.$('.components-health').stop(true, true);
  239. this.$('.components-health').css({opacity: 1.0});
  240. this.doBlinking();
  241. }.observes('workStatus'),
  242. /**
  243. * Get custom commands for slave components
  244. */
  245. customCommands: function() {
  246. var customCommands;
  247. var hostComponent = this.get('content');
  248. var component = App.StackServiceComponent.find(hostComponent.get('componentName'));
  249. customCommands = this.getCustomCommands(component, hostComponent, component.get('isSlave'));
  250. return customCommands;
  251. }.property('content', 'workStatus'),
  252. /**
  253. * Get a list of custom commands
  254. *
  255. * @param component
  256. * @param hostComponent
  257. * @param isSlave
  258. * @returns {Array}
  259. */
  260. getCustomCommands: function (component, hostComponent, isSlave) {
  261. isSlave = isSlave || false;
  262. if (!component || !hostComponent) {
  263. return [];
  264. }
  265. var self = this;
  266. var commands = component.get('customCommands');
  267. var customCommands = [];
  268. commands.forEach(function(command) {
  269. if (!isSlave && !self.meetsCustomCommandReq(component, command)) {
  270. return;
  271. }
  272. var commandMap = App.HostComponentActionMap.getMap(self)[command];
  273. customCommands.push({
  274. label: self.getCustomCommandLabel(command),
  275. service: component.get('serviceName'),
  276. hosts: hostComponent.get('hostName'),
  277. context: (!!commandMap && !!commandMap.context) ? commandMap.context : null,
  278. component: component.get('componentName'),
  279. command: command,
  280. disabled: !!commandMap ? !!commandMap.disabled : false
  281. });
  282. });
  283. return customCommands;
  284. },
  285. /**
  286. * Get the Label of the custom command
  287. *
  288. * @param command
  289. * @returns {String}
  290. */
  291. getCustomCommandLabel: function (command) {
  292. if (command in App.HostComponentActionMap.getMap(this) && App.HostComponentActionMap.getMap(this)[command].label)
  293. return App.HostComponentActionMap.getMap(this)[command].label;
  294. return Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(App.format.normalizeNameBySeparators(command, ["_", "-", " "]));
  295. },
  296. /**
  297. * The custom command meets the requirements to be active on master
  298. *
  299. * @param component
  300. * @param command
  301. *
  302. * @return {Boolean}
  303. */
  304. meetsCustomCommandReq: function (component, command) {
  305. var excludedMasterCommands = this.get('excludedMasterCommands');
  306. if (excludedMasterCommands.indexOf(command) >= 0) {
  307. return false;
  308. }
  309. if (component.get('cardinality') !== '1') {
  310. if (!this.get('isStart')) {
  311. if (App.HostComponent.getCount(this.get('hostComponent.componentName'), 'totalCount') > 1) {
  312. if (this.runningComponentCounter()) {
  313. return false;
  314. }
  315. } else {
  316. return false;
  317. }
  318. }
  319. }
  320. return true;
  321. }
  322. });