upgrade_wizard_view.js 15 KB

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