upgrade_wizard_view.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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 stringUtils = require('utils/string_utils');
  20. App.upgradeWizardView = Em.View.extend({
  21. controllerBinding: 'App.router.mainAdminStackAndUpgradeController',
  22. templateName: require('templates/main/admin/stack_upgrade/stack_upgrade_wizard'),
  23. /**
  24. * @type {Array}
  25. */
  26. failedStatuses: ['HOLDING_FAILED', 'HOLDING_TIMEDOUT', 'FAILED', 'TIMED_OUT', 'ABORTED'],
  27. /**
  28. * @type {Array}
  29. */
  30. activeStatuses: ['HOLDING_FAILED', 'HOLDING_TIMEDOUT', 'FAILED', 'TIMED_OUT', 'HOLDING', 'IN_PROGRESS', 'ABORTED'],
  31. /**
  32. * update timer
  33. * @type {number|null}
  34. * @default null
  35. */
  36. updateTimer: null,
  37. /**
  38. * update timer of Upgrade Item
  39. * @type {number|null}
  40. * @default null
  41. */
  42. upgradeItemTimer: null,
  43. /**
  44. * @type {boolean}
  45. */
  46. isLoaded: false,
  47. /**
  48. * @type {boolean}
  49. */
  50. isDetailsOpened: false,
  51. /**
  52. * @type {boolean}
  53. */
  54. outsideView: true,
  55. /**
  56. * Downgrade should be available only if target version higher than current, so we can't downgrade
  57. * when downgrade already started
  58. * @type {boolean}
  59. */
  60. isDowngradeAvailable: Em.computed.and('!controller.isDowngrade', 'controller.downgradeAllowed'),
  61. /**
  62. * progress value is rounded to floor
  63. * @type {number}
  64. */
  65. overallProgress: function () {
  66. return Math.floor(this.get('controller.upgradeData.Upgrade.progress_percent'));
  67. }.property('controller.upgradeData.Upgrade.progress_percent'),
  68. /**
  69. * upgrade groups, reversed and PENDING ones are hidden
  70. * @type {Array}
  71. */
  72. upgradeGroups: function () {
  73. return this.get('controller.upgradeData.upgradeGroups') || [];
  74. }.property('controller.upgradeData.upgradeGroups'),
  75. /**
  76. * currently active group
  77. * @type {object|undefined}
  78. */
  79. activeGroup: function () {
  80. if (App.get('upgradeSuspended')) return;
  81. return this.get('upgradeGroups').find(function (item) {
  82. return this.get('activeStatuses').contains(item.get('status'));
  83. }, this);
  84. }.property('upgradeGroups.@each.status'),
  85. /**
  86. * if upgrade group is in progress it should have currently running item
  87. * @type {object|undefined}
  88. */
  89. runningItem: Em.computed.findBy('activeGroup.upgradeItems', 'status', 'IN_PROGRESS'),
  90. /**
  91. * if upgrade group is failed it should have failed item
  92. * @type {object|undefined}
  93. */
  94. failedItem: function () {
  95. return this.get('activeGroup.upgradeItems') && this.get('activeGroup.upgradeItems').find(function (item) {
  96. return this.get('failedStatuses').contains(item.get('status'));
  97. }, this);
  98. }.property('activeGroup.upgradeItems.@each.status'),
  99. /**
  100. * upgrade doesn't have any failed or manual or running item
  101. * @type {boolean}
  102. */
  103. noActiveItem: function () {
  104. return (Em.isNone(this.get('failedItem')) && Em.isNone(this.get('runningItem')) && Em.isNone(this.get('manualItem'))) &&
  105. !['INIT', 'COMPLETED', 'ABORTED'].contains(App.get('upgradeState'));
  106. }.property('failedItem', 'runningItem', 'manualItem', 'App.upgradeState'),
  107. /**
  108. * details of currently active task
  109. * @type {object|null}
  110. */
  111. taskDetails: function () {
  112. if (this.get('runningItem')) {
  113. return this.get('runningItem').get('tasks').findProperty('status', 'IN_PROGRESS');
  114. } else if (this.get('failedItem')) {
  115. return this.get('failedItem').get('tasks').find(function (task) {
  116. return this.get('failedStatuses').contains(task.get('status'));
  117. }, this);
  118. } else {
  119. return null;
  120. }
  121. }.property('failedItem.tasks.@each.status', 'runningItem.tasks.@each.status'),
  122. /**
  123. * indicate whether failed item can be skipped or retried in order to continue Upgrade
  124. * @type {boolean}
  125. */
  126. isHoldingState: function () {
  127. return Boolean(this.get('failedItem.status') &&
  128. this.get('failedItem.status').contains('HOLDING') || this.get('failedItem.status') === 'ABORTED');
  129. }.property('failedItem.status'),
  130. /**
  131. * @type {boolean}
  132. */
  133. isManualDone: false,
  134. /**
  135. * if manualItem has been switched then isManualDone flag should be reset
  136. */
  137. resetManualDone: function() {
  138. this.set('isManualDone', false);
  139. }.observes('manualItem'),
  140. /**
  141. * @type {boolean}
  142. */
  143. isManualProceedDisabled: Em.computed.or('!isManualDone', 'controller.requestInProgress'),
  144. /**
  145. * if upgrade group is manual it should have manual item
  146. * @type {object|undefined}
  147. */
  148. manualItem: Em.computed.findBy('activeGroup.upgradeItems', 'status', 'HOLDING'),
  149. /**
  150. * plain manual item
  151. * @type {object|undefined}
  152. */
  153. plainManualItem: function () {
  154. return this.get('manualItem') && ![
  155. this.get('controller.finalizeContext'),
  156. this.get("controller.slaveFailuresContext"),
  157. this.get("controller.serviceCheckFailuresContext")
  158. ].contains(this.get('manualItem.context'));
  159. }.property('manualItem.context'),
  160. /**
  161. * manualItem: indicate whether the step is "Slave component failures", a dialog with instructions will show up for manual steps
  162. * @type {boolean}
  163. */
  164. isSlaveComponentFailuresItem: function () {
  165. var item = this.get('activeGroup.upgradeItems') && this.get('activeGroup.upgradeItems').findProperty('context', this.get("controller.slaveFailuresContext"));
  166. return item && ['HOLDING', 'HOLDING_FAILED'].contains(item.get('status'));
  167. }.property('activeGroup.upgradeItems.@each.status', 'activeGroup.upgradeItems.@each.context'),
  168. /**
  169. * manualItem: indicate whether the step is "Service check failures", a dialog with instructions will show up for manual steps
  170. * @type {boolean}
  171. */
  172. isServiceCheckFailuresItem: Em.computed.equalProperties('manualItem.context', 'controller.serviceCheckFailuresContext'),
  173. /**
  174. * manualItem: indicate whether the step is Finalize
  175. * @type {boolean}
  176. */
  177. isFinalizeItem: Em.computed.equalProperties('manualItem.context', 'controller.finalizeContext'),
  178. /**
  179. * label of Upgrade status
  180. * @type {string}
  181. */
  182. upgradeStatusLabel: function() {
  183. var labelKey = null;
  184. switch (this.get('controller.upgradeData.Upgrade.request_status')) {
  185. case 'QUEUED':
  186. case 'PENDING':
  187. case 'IN_PROGRESS':
  188. labelKey = 'admin.stackUpgrade.state.inProgress';
  189. break;
  190. case 'COMPLETED':
  191. labelKey = 'admin.stackUpgrade.state.completed';
  192. break;
  193. case 'ABORTED':
  194. labelKey = 'admin.stackUpgrade.state.paused';
  195. break;
  196. case 'TIMEDOUT':
  197. case 'FAILED':
  198. case 'HOLDING_FAILED':
  199. case 'HOLDING_TIMEDOUT':
  200. case 'HOLDING':
  201. labelKey = 'admin.stackUpgrade.state.paused';
  202. break;
  203. }
  204. if (labelKey) {
  205. labelKey += (this.get('controller.isDowngrade')) ? '.downgrade' : "";
  206. return Em.I18n.t(labelKey);
  207. } else {
  208. return "";
  209. }
  210. }.property('controller.upgradeData.Upgrade.request_status', 'controller.isDowngrade'),
  211. /**
  212. * toggle details box
  213. */
  214. toggleDetails: function () {
  215. this.toggleProperty('isDetailsOpened');
  216. },
  217. /**
  218. * close details block if no active task present
  219. */
  220. closeDetails: function () {
  221. if (this.get('noActiveItem')) {
  222. this.set('isDetailsOpened', false);
  223. }
  224. }.observes('noActiveItem'),
  225. /**
  226. * start polling upgrade data
  227. */
  228. startPolling: function () {
  229. var self = this;
  230. if (App.get('clusterName')) {
  231. this.get('controller').loadUpgradeData().done(function () {
  232. self.set('isLoaded', true);
  233. self.doPolling();
  234. });
  235. }
  236. }.observes('App.clusterName'),
  237. getSkippedServiceChecks: function () {
  238. if (this.get('isFinalizeItem')) {
  239. if (!this.get('controller.areSkippedServiceChecksLoaded')) {
  240. var self = this;
  241. App.ajax.send({
  242. name: 'admin.upgrade.service_checks',
  243. sender: this,
  244. data: {
  245. upgradeId: this.get('controller.upgradeId')
  246. },
  247. success: 'getSkippedServiceChecksSuccessCallback'
  248. }).complete(function () {
  249. self.set('controller.areSkippedServiceChecksLoaded', true);
  250. });
  251. }
  252. } else {
  253. this.set('controller.areSkippedServiceChecksLoaded', false);
  254. }
  255. }.observes('isFinalizeItem'),
  256. getSkippedServiceChecksSuccessCallback: function (data) {
  257. if (data.items && data.items.length) {
  258. var lastItemWithChecks = data.items[data.items.length - 1];
  259. if (lastItemWithChecks && lastItemWithChecks.upgrade_items && lastItemWithChecks.upgrade_items.length) {
  260. var skippedServiceChecks = [];
  261. lastItemWithChecks.upgrade_items.forEach(function (item) {
  262. if (item.tasks && item.tasks.length) {
  263. item.tasks.forEach(function (task) {
  264. var detail = Em.get(task, 'Tasks.command_detail');
  265. if (detail && detail.startsWith('SERVICE_CHECK ')) {
  266. skippedServiceChecks.push(App.format.role(detail.replace('SERVICE_CHECK ', ''), true));
  267. }
  268. });
  269. }
  270. });
  271. skippedServiceChecks = skippedServiceChecks.uniq();
  272. this.set('controller.skippedServiceChecks', skippedServiceChecks);
  273. }
  274. }
  275. },
  276. /**
  277. * get slave-component failure hosts
  278. */
  279. getSlaveComponentItem: function() {
  280. var controller = this.get('controller');
  281. if (this.get('isSlaveComponentFailuresItem')) {
  282. if (!this.get('controller.areSlaveComponentFailuresHostsLoaded')) {
  283. var item = this.get('activeGroup.upgradeItems') && this.get('activeGroup.upgradeItems').findProperty('context', this.get("controller.slaveFailuresContext"));
  284. controller.getUpgradeItem(item, 'getSlaveComponentItemSuccessCallback').complete(function () {
  285. controller.set('areSlaveComponentFailuresHostsLoaded', true);
  286. });
  287. }
  288. } else {
  289. controller.set('areSlaveComponentFailuresHostsLoaded', false);
  290. }
  291. }.observes('isSlaveComponentFailuresItem'),
  292. /**
  293. * get service names of Service Check failures
  294. */
  295. getServiceCheckItem: function() {
  296. var controller = this.get('controller');
  297. if (this.get('isServiceCheckFailuresItem')) {
  298. if (!this.get('controller.areServiceCheckFailuresServicenamesLoaded')) {
  299. controller.getUpgradeItem(this.get('manualItem'), 'getServiceCheckItemSuccessCallback').complete(function () {
  300. controller.set('areServiceCheckFailuresServicenamesLoaded', true);
  301. });
  302. }
  303. } else {
  304. controller.set('areServiceCheckFailuresServicenamesLoaded', false);
  305. }
  306. }.observes('isServiceCheckFailuresItem'),
  307. /**
  308. * start polling upgrade data
  309. */
  310. willInsertElement: function () {
  311. this.startPolling();
  312. },
  313. /**
  314. * stop polling upgrade data
  315. */
  316. willDestroyElement: function () {
  317. clearTimeout(this.get('updateTimer'));
  318. clearTimeout(this.get('upgradeItemTimer'));
  319. this.set('isLoaded', false);
  320. },
  321. /**
  322. * load upgrade data with time interval
  323. */
  324. doPolling: function () {
  325. var self = this;
  326. this.set('updateTimer', setTimeout(function () {
  327. self.get('controller').loadUpgradeData().done(function() {
  328. self.doPolling();
  329. });
  330. }, App.bgOperationsUpdateInterval));
  331. },
  332. /**
  333. * poll for tasks when item is expanded
  334. */
  335. doUpgradeItemPolling: function () {
  336. var self = this;
  337. var item = this.get('runningItem') || this.get('failedItem');
  338. if (item && this.get('isDetailsOpened')) {
  339. this.get('controller').getUpgradeItem(item).complete(function () {
  340. self.set('upgradeItemTimer', setTimeout(function () {
  341. self.doUpgradeItemPolling();
  342. }, App.bgOperationsUpdateInterval));
  343. });
  344. } else {
  345. clearTimeout(this.get('upgradeItemTimer'));
  346. }
  347. }.observes('isDetailsOpened'),
  348. /**
  349. * set current upgrade item state to FAILED (for HOLDING_FAILED) or TIMED_OUT (for HOLDING_TIMED_OUT)
  350. * in order to ignore fail and continue Upgrade
  351. * @param {object} event
  352. */
  353. continue: function (event) {
  354. this.get('controller').setUpgradeItemStatus(event.context, event.context.get('status').slice(8));
  355. this.set('isDetailsOpened', false);
  356. },
  357. /**
  358. * set current upgrade item state to PENDING in order to retry Upgrade
  359. * @param {object} event
  360. */
  361. retry: function (event) {
  362. this.get('controller').setUpgradeItemStatus(event.context, 'PENDING');
  363. this.set('isDetailsOpened', false);
  364. },
  365. /**
  366. * set current upgrade item state to COMPLETED in order to proceed
  367. * @param {object} event
  368. */
  369. complete: function (event) {
  370. this.get('controller').setUpgradeItemStatus(event.context, 'COMPLETED');
  371. this.set('isManualDone', false);
  372. },
  373. pauseUpgrade: function() {
  374. this.get('controller').suspendUpgrade();
  375. this.get('parentView').closeWizard();
  376. },
  377. /**
  378. * pause upgrade confirmation popup
  379. * @param {object} event
  380. */
  381. confirmPauseUpgrade: function () {
  382. var self = this;
  383. return App.showConfirmationPopup(
  384. function() {
  385. self.pauseUpgrade();
  386. },
  387. Em.I18n.t('admin.stackUpgrade.pauseUpgrade.warning').format("upgrade"),
  388. null,
  389. Em.I18n.t('common.warning'),
  390. Em.I18n.t('admin.stackUpgrade.pauseUpgrade')
  391. );
  392. },
  393. /**
  394. * pause downgrade confirmation popup
  395. * @param {object} event
  396. */
  397. confirmPauseDowngrade: function () {
  398. var self = this;
  399. return App.showConfirmationPopup(
  400. function() {
  401. self.pauseUpgrade();
  402. },
  403. Em.I18n.t('admin.stackUpgrade.pauseUpgrade.warning').format("downgrade"),
  404. null,
  405. Em.I18n.t('common.warning'),
  406. Em.I18n.t('admin.stackUpgrade.pauseDowngrade')
  407. );
  408. },
  409. /**
  410. * @type {string}
  411. */
  412. failedHostsMessage: function() {
  413. var count = this.get('controller.slaveComponentStructuredInfo.hosts.length') || 0;
  414. return Em.I18n.t('admin.stackUpgrade.failedHosts.showHosts').format(count);
  415. }.property('controller.slaveComponentStructuredInfo.hosts'),
  416. showFailedHosts: function() {
  417. return App.ModalPopup.show({
  418. header: Em.I18n.t('admin.stackUpgrade.failedHosts.header'),
  419. bodyClass: App.FailedHostsPopupBodyView,
  420. secondary: null,
  421. primary: Em.I18n.t('common.close'),
  422. content: this.get('controller.slaveComponentStructuredInfo')
  423. });
  424. }
  425. });