host_component_view.js 12 KB

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