stack_and_upgrade_controller.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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. App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage, {
  20. name: 'mainAdminStackAndUpgradeController',
  21. /**
  22. * @type {boolean}
  23. */
  24. isLoaded: false,
  25. /**
  26. * @type {object}
  27. * @default null
  28. */
  29. upgradeData: null,
  30. /**
  31. * @type {number}
  32. * @default null
  33. */
  34. upgradeId: null,
  35. /**
  36. * @type {string}
  37. * @default null
  38. */
  39. upgradeVersion: null,
  40. /**
  41. * @type {boolean}
  42. * @default false
  43. */
  44. isDowngrade: false,
  45. /**
  46. * version that currently applied to server
  47. * should be plain object, because stored to localStorage
  48. * @type {object|null}
  49. */
  50. currentVersion: null,
  51. /**
  52. * versions to which cluster could be upgraded
  53. * @type {Array}
  54. */
  55. targetVersions: [],
  56. /**
  57. * properties that stored to localStorage to resume wizard progress
  58. */
  59. wizardStorageProperties: ['upgradeId', 'upgradeVersion', 'currentVersion', 'isDowngrade'],
  60. /**
  61. * path to the mock json
  62. * @type {String}
  63. */
  64. mockRepoUrl: '/data/stack_versions/repo_versions_all.json',
  65. /**
  66. * api to get RepoVersions
  67. * @type {String}
  68. */
  69. realRepoUrl: function () {
  70. //TODO correct url after api will be fixed
  71. return App.get('apiPrefix') + App.get('stackVersionURL') +
  72. '/repository_versions?fields=*,operating_systems/*,operating_systems/repositories/*,operatingSystems/*,operatingSystems/repositories/*';
  73. }.property('App.stackVersionURL'),
  74. /**
  75. * path to the mock json
  76. * @type {String}
  77. */
  78. mockStackUrl: '/data/stack_versions/stack_version_all.json',
  79. /**
  80. * api to get ClusterStackVersions with repository_versions (use to init data load)
  81. * @type {String}
  82. */
  83. realStackUrl: function () {
  84. //TODO correct url after api will be fixed
  85. return App.apiPrefix + '/clusters/' + App.get('clusterName') +
  86. '/stack_versions?fields=*,repository_versions/*,repository_versions/operating_systems/repositories/*,repository_versions/operatingSystems/repositories/*';
  87. }.property('App.clusterName'),
  88. /**
  89. * api to get ClusterStackVersions without repository_versions (use to update data)
  90. * @type {String}
  91. */
  92. realUpdateUrl: function () {
  93. return App.apiPrefix + '/clusters/' + App.get('clusterName') + '/stack_versions?fields=ClusterStackVersions/*';
  94. }.property('App.clusterName'),
  95. init: function () {
  96. this.initDBProperties();
  97. },
  98. /**
  99. * restore data from localStorage
  100. */
  101. initDBProperties: function () {
  102. this.get('wizardStorageProperties').forEach(function (property) {
  103. if (this.getDBProperty(property)) {
  104. this.set(property, this.getDBProperty(property));
  105. }
  106. }, this);
  107. },
  108. /**
  109. * load all data:
  110. * - upgrade data
  111. * - stack versions
  112. * - repo versions
  113. */
  114. load: function () {
  115. var dfd = $.Deferred();
  116. var self = this;
  117. this.loadUpgradeData(true).done(function() {
  118. self.loadStackVersionsToModel(true).done(function () {
  119. self.loadRepoVersionsToModel().done(function() {
  120. var currentVersion = App.StackVersion.find().findProperty('state', 'CURRENT');
  121. if (currentVersion) {
  122. self.set('currentVersion', {
  123. repository_version: currentVersion.get('repositoryVersion.repositoryVersion'),
  124. repository_name: currentVersion.get('repositoryVersion.displayName')
  125. });
  126. }
  127. dfd.resolve();
  128. });
  129. });
  130. });
  131. return dfd.promise();
  132. },
  133. /**
  134. * load upgrade tasks by upgrade id
  135. * @return {$.Deferred}
  136. * @param {boolean} onlyState
  137. */
  138. loadUpgradeData: function (onlyState) {
  139. var upgradeId = this.get('upgradeId');
  140. var deferred = $.Deferred();
  141. if (Em.isNone(upgradeId)) {
  142. deferred.resolve();
  143. console.log('Upgrade in INIT state');
  144. } else {
  145. App.ajax.send({
  146. name: (onlyState) ? 'admin.upgrade.state' : 'admin.upgrade.data',
  147. sender: this,
  148. data: {
  149. id: upgradeId
  150. },
  151. success: 'loadUpgradeDataSuccessCallback'
  152. }).then(deferred.resolve);
  153. }
  154. return deferred.promise();
  155. },
  156. /**
  157. * parse and push upgrade tasks to controller
  158. * @param data
  159. */
  160. loadUpgradeDataSuccessCallback: function (data) {
  161. App.set('upgradeState', data.Upgrade.request_status);
  162. this.setDBProperty('upgradeState', data.Upgrade.request_status);
  163. if (data.upgrade_groups) {
  164. this.updateUpgradeData(data);
  165. }
  166. },
  167. /**
  168. * update data of Upgrade
  169. * @param {object} newData
  170. */
  171. updateUpgradeData: function (newData) {
  172. var oldData = this.get('upgradeData'),
  173. groupsMap = {},
  174. itemsMap = {},
  175. tasksMap = {};
  176. if (Em.isNone(oldData)) {
  177. this.initUpgradeData(newData);
  178. } else {
  179. //create entities maps
  180. newData.upgrade_groups.forEach(function (newGroup) {
  181. groupsMap[newGroup.UpgradeGroup.group_id] = newGroup.UpgradeGroup;
  182. newGroup.upgrade_items.forEach(function (item) {
  183. itemsMap[item.UpgradeItem.stage_id] = item.UpgradeItem;
  184. item.tasks.forEach(function (task) {
  185. tasksMap[task.Tasks.id] = task.Tasks;
  186. });
  187. })
  188. });
  189. //update existed entities with new data
  190. oldData.upgradeGroups.forEach(function (oldGroup) {
  191. oldGroup.set('status', groupsMap[oldGroup.get('group_id')].status);
  192. oldGroup.set('progress_percent', groupsMap[oldGroup.get('group_id')].progress_percent);
  193. oldGroup.upgradeItems.forEach(function (item) {
  194. item.set('status', itemsMap[item.get('stage_id')].status);
  195. item.set('progress_percent', itemsMap[item.get('stage_id')].progress_percent);
  196. item.tasks.forEach(function (task) {
  197. task.set('status', tasksMap[task.get('id')].status);
  198. });
  199. })
  200. });
  201. oldData.set('Upgrade', newData.Upgrade);
  202. }
  203. },
  204. /**
  205. * change structure of Upgrade
  206. * In order to maintain nested views in template object should have direct link to its properties, for example
  207. * item.UpgradeItem.<properties> -> item.<properties>
  208. * @param {object} newData
  209. */
  210. initUpgradeData: function (newData) {
  211. var upgradeGroups = [];
  212. //wrap all entities into App.upgradeEntity
  213. newData.upgrade_groups.forEach(function (newGroup) {
  214. var oldGroup = App.upgradeEntity.create({type: 'GROUP'}, newGroup.UpgradeGroup);
  215. var upgradeItems = [];
  216. newGroup.upgrade_items.forEach(function (item) {
  217. var oldItem = App.upgradeEntity.create({type: 'ITEM'}, item.UpgradeItem);
  218. var tasks = [];
  219. item.tasks.forEach(function (task) {
  220. tasks.pushObject(App.upgradeEntity.create({type: 'TASK'}, task.Tasks));
  221. });
  222. oldItem.set('tasks', tasks);
  223. upgradeItems.pushObject(oldItem);
  224. });
  225. upgradeItems.reverse();
  226. oldGroup.set('upgradeItems', upgradeItems);
  227. upgradeGroups.pushObject(oldGroup);
  228. });
  229. upgradeGroups.reverse();
  230. this.set('upgradeData', Em.Object.create({
  231. upgradeGroups: upgradeGroups,
  232. Upgrade: newData.Upgrade
  233. }));
  234. },
  235. /**
  236. * downgrade confirmation popup
  237. * @param {object} event
  238. */
  239. confirmDowngrade: function (event) {
  240. var self = this;
  241. var currentVersion = this.get('currentVersion');
  242. return App.showConfirmationPopup(
  243. function() {
  244. self.downgrade.call(self, currentVersion, event);
  245. },
  246. Em.I18n.t('admin.stackUpgrade.downgrade.body').format(currentVersion.repository_name),
  247. null,
  248. Em.I18n.t('admin.stackUpgrade.downgrade.title').format(currentVersion.repository_name),
  249. Em.I18n.t('admin.stackUpgrade.downgrade.proceed')
  250. );
  251. },
  252. /**
  253. * make call to start downgrade process
  254. * @param {object} currentVersion
  255. * @param {object} event
  256. */
  257. downgrade: function (currentVersion, event) {
  258. this.setUpgradeItemStatus(event.context, 'FAILED');
  259. App.ajax.send({
  260. name: 'admin.downgrade.start',
  261. sender: this,
  262. data: {
  263. value: currentVersion.repository_version,
  264. label: currentVersion.repository_name,
  265. isDowngrade: true
  266. },
  267. success: 'upgradeSuccessCallback'
  268. });
  269. },
  270. /**
  271. * make call to start upgrade process and show popup with current progress
  272. * @param {object} version
  273. */
  274. upgrade: function (version) {
  275. App.ajax.send({
  276. name: 'admin.upgrade.start',
  277. sender: this,
  278. data: version,
  279. success: 'upgradeSuccessCallback'
  280. });
  281. this.setDBProperty('currentVersion', this.get('currentVersion'));
  282. },
  283. /**
  284. * success callback of <code>upgrade()</code>
  285. * @param {object} data
  286. */
  287. upgradeSuccessCallback: function (data, opt, params) {
  288. this.set('upgradeData', null);
  289. this.set('upgradeId', data.resources[0].Upgrade.request_id);
  290. this.set('upgradeVersion', params.label);
  291. this.set('isDowngrade', !!params.isDowngrade);
  292. this.setDBProperty('upgradeVersion', params.label);
  293. this.setDBProperty('upgradeId', data.resources[0].Upgrade.request_id);
  294. this.setDBProperty('upgradeState', 'PENDING');
  295. this.setDBProperty('isDowngrade', !!params.isDowngrade);
  296. App.set('upgradeState', 'PENDING');
  297. App.clusterStatus.setClusterStatus({
  298. wizardControllerName: this.get('name'),
  299. localdb: App.db.data
  300. });
  301. this.load();
  302. this.openUpgradeDialog();
  303. },
  304. /**
  305. * send request for pre upgrade check
  306. * @param version
  307. */
  308. runPreUpgradeCheck: function(version) {
  309. var params = {
  310. value: version.get('repositoryVersion'),
  311. label: version.get('displayName')
  312. };
  313. if (App.get('supports.preUpgradeCheck')) {
  314. App.ajax.send({
  315. name: "admin.rolling_upgrade.pre_upgrade_check",
  316. sender: this,
  317. data: params,
  318. success: "runPreUpgradeCheckSuccess"
  319. });
  320. } else {
  321. this.upgrade(params);
  322. }
  323. },
  324. /**
  325. * success callback of <code>runPreUpgradeCheckSuccess()</code>
  326. * if there are some fails - it shows popup else run upgrade
  327. * @param data {object}
  328. * @param opt {object}
  329. * @param params {object}
  330. * @returns {App.ModalPopup|undefined}
  331. */
  332. runPreUpgradeCheckSuccess: function (data, opt, params) {
  333. if (data.items.someProperty('UpgradeChecks.status', "FAIL")) {
  334. var header = Em.I18n.t('popup.clusterCheck.Upgrade.header').format(params.label);
  335. var title = Em.I18n.t('popup.clusterCheck.Upgrade.title');
  336. var alert = Em.I18n.t('popup.clusterCheck.Upgrade.alert');
  337. App.showClusterCheckPopup(data, header, title, alert);
  338. } else {
  339. this.upgrade(params);
  340. }
  341. },
  342. /**
  343. * confirmation popup before install repository version
  344. */
  345. installRepoVersionConfirmation: function (repo) {
  346. var self = this;
  347. return App.showConfirmationPopup(function () {
  348. self.installRepoVersion(repo);
  349. },
  350. Em.I18n.t('admin.stackVersions.version.install.confirm').format(repo.get('displayName'))
  351. );
  352. },
  353. /**
  354. * sends request to install repoVersion to the cluster
  355. * and create clusterStackVersion resourse
  356. * @param {Em.Object} repo
  357. * @return {$.ajax}
  358. * @method installRepoVersion
  359. */
  360. installRepoVersion: function (repo) {
  361. var data = {
  362. ClusterStackVersions: {
  363. stack: repo.get('stackVersionType'),
  364. version: repo.get('stackVersionNumber'),
  365. repository_version: repo.get('repositoryVersion')
  366. },
  367. id: repo.get('id')
  368. };
  369. return App.ajax.send({
  370. name: 'admin.stack_version.install.repo_version',
  371. sender: this,
  372. data: data,
  373. success: 'installRepoVersionSuccess'
  374. });
  375. },
  376. /**
  377. * transform repo data into json for
  378. * saving changes to repository version
  379. * @param {Em.Object} repo
  380. * @returns {{operating_systems: Array}}
  381. */
  382. prepareRepoForSaving: function(repo) {
  383. var repoVersion = { "operating_systems": [] };
  384. repo.get('operatingSystems').forEach(function (os, k) {
  385. repoVersion.operating_systems.push({
  386. "OperatingSystems": {
  387. "os_type": os.get("osType")
  388. },
  389. "repositories": []
  390. });
  391. os.get('repositories').forEach(function (repository) {
  392. repoVersion.operating_systems[k].repositories.push({
  393. "Repositories": {
  394. "base_url": repository.get('baseUrl'),
  395. "repo_id": repository.get('repoId'),
  396. "repo_name": repository.get('repoName')
  397. }
  398. });
  399. });
  400. });
  401. return repoVersion;
  402. },
  403. /**
  404. * perform validation if <code>skip<code> is false and run save if
  405. * validation successfull or run save without validation is <code>skip<code> is true
  406. * @param {Em.Object} repo
  407. * @param {boolean} skip
  408. * @returns {$.Deferred}
  409. */
  410. saveRepoOS: function (repo, skip) {
  411. var self = this;
  412. var deferred = $.Deferred();
  413. this.validateRepoVersions(repo, skip).done(function(data) {
  414. if (data.length > 0) {
  415. deferred.resolve(data);
  416. } else {
  417. var repoVersion = self.prepareRepoForSaving(repo);
  418. App.ajax.send({
  419. name: 'admin.stack_versions.edit.repo',
  420. sender: this,
  421. data: {
  422. stackName: App.get('currentStackName'),
  423. stackVersion: App.get('currentStackVersionNumber'),
  424. repoVersionId: repo.get('repoVersionId'),
  425. repoVersion: repoVersion
  426. }
  427. }).success(function() {
  428. deferred.resolve([]);
  429. });
  430. }
  431. });
  432. return deferred.promise();
  433. },
  434. /**
  435. * send request for validation for each repository
  436. * @param {Em.Object} repo
  437. * @param {boolean} skip
  438. * @returns {*}
  439. */
  440. validateRepoVersions: function(repo, skip) {
  441. var deferred = $.Deferred(),
  442. totalCalls = 0,
  443. invalidUrls = [];
  444. if (skip) {
  445. deferred.resolve(invalidUrls);
  446. } else {
  447. repo.get('operatingSystems').forEach(function (os) {
  448. if (os.get('isSelected')) {
  449. os.get('repositories').forEach(function (repo) {
  450. totalCalls++;
  451. App.ajax.send({
  452. name: 'admin.stack_versions.validate.repo',
  453. sender: this,
  454. data: {
  455. repo: repo,
  456. repoId: repo.get('repoId'),
  457. baseUrl: repo.get('baseUrl'),
  458. osType: os.get('osType'),
  459. stackName: App.get('currentStackName'),
  460. stackVersion: App.get('currentStackVersionNumber')
  461. }
  462. })
  463. .success(function () {
  464. totalCalls--;
  465. if (totalCalls === 0) deferred.resolve(invalidUrls);
  466. })
  467. .error(function () {
  468. repo.set('hasError', true);
  469. invalidUrls.push(repo);
  470. totalCalls--;
  471. if (totalCalls === 0) deferred.resolve(invalidUrls);
  472. });
  473. });
  474. } else {
  475. return deferred.resolve(invalidUrls);
  476. }
  477. });
  478. }
  479. return deferred.promise();
  480. },
  481. /**
  482. * success callback for <code>installRepoVersion()<code>
  483. * saves request id to the db
  484. * @param data
  485. * @param opt
  486. * @param params
  487. * @method installStackVersionSuccess
  488. */
  489. installRepoVersionSuccess: function (data, opt, params) {
  490. App.db.set('repoVersionInstall', 'id', [data.Requests.id]);
  491. App.clusterStatus.setClusterStatus({
  492. wizardControllerName: this.get('name'),
  493. localdb: App.db.data
  494. });
  495. },
  496. /**
  497. * opens a popup with installations state per host
  498. * @param {Em.Object} version
  499. * @method showProgressPopup
  500. */
  501. showProgressPopup: function(version) {
  502. var popupTitle = Em.I18n.t('admin.stackVersions.details.install.hosts.popup.title').format(version.get('displayName'));
  503. var requestIds = App.get('testMode') ? [1] : App.db.get('repoVersionInstall', 'id');
  504. var hostProgressPopupController = App.router.get('highAvailabilityProgressPopupController');
  505. hostProgressPopupController.initPopup(popupTitle, requestIds, this);
  506. },
  507. /**
  508. * reset upgradeState to INIT when upgrade is COMPLETED
  509. * and clean auxiliary data
  510. */
  511. finish: function () {
  512. if (App.get('upgradeState') === 'COMPLETED') {
  513. this.setDBProperty('upgradeId', undefined);
  514. this.setDBProperty('upgradeState', 'INIT');
  515. this.setDBProperty('upgradeVersion', undefined);
  516. this.setDBProperty('currentVersion', undefined);
  517. this.setDBProperty('isDowngrade', undefined);
  518. App.clusterStatus.setClusterStatus({
  519. localdb: App.db.data
  520. });
  521. App.set('upgradeState', 'INIT');
  522. }
  523. }.observes('App.upgradeState'),
  524. /**
  525. * show dialog with tasks of upgrade
  526. * @return {App.ModalPopup}
  527. */
  528. openUpgradeDialog: function () {
  529. App.router.transitionTo('admin.stackUpgrade');
  530. },
  531. /**
  532. * returns url to get data for repoVersion or clusterStackVersion
  533. * @param {Boolean} stack true if load clusterStackVersion
  534. * @param {Boolean} fullLoad true if load all data
  535. * @returns {String}
  536. * @method getUrl
  537. */
  538. getUrl: function(stack, fullLoad) {
  539. if (App.get('testMode')) {
  540. return stack ? this.get('mockStackUrl') : this.get('mockRepoUrl')
  541. } else {
  542. if (fullLoad) {
  543. return stack ? this.get('realStackUrl') : this.get('realRepoUrl');
  544. } else {
  545. return this.get('realUpdateUrl');
  546. }
  547. }
  548. },
  549. /**
  550. * get stack versions from server and push it to model
  551. * @return {*}
  552. * @method loadStackVersionsToModel
  553. */
  554. loadStackVersionsToModel: function (fullLoad) {
  555. var dfd = $.Deferred();
  556. App.HttpClient.get(this.getUrl(true, fullLoad), App.stackVersionMapper, {
  557. complete: function () {
  558. dfd.resolve();
  559. }
  560. });
  561. return dfd.promise();
  562. },
  563. /**
  564. * get repo versions from server and push it to model
  565. * @return {*}
  566. * @params {Boolean} isUpdate - if true loads part of data that need to be updated
  567. * @method loadRepoVersionsToModel()
  568. */
  569. loadRepoVersionsToModel: function () {
  570. var dfd = $.Deferred();
  571. App.HttpClient.get(this.getUrl(false, true), App.repoVersionMapper, {
  572. complete: function () {
  573. dfd.resolve();
  574. }
  575. });
  576. return dfd.promise();
  577. },
  578. /**
  579. * set status to Upgrade item
  580. * @param item
  581. * @param status
  582. */
  583. setUpgradeItemStatus: function(item, status) {
  584. return App.ajax.send({
  585. name: 'admin.upgrade.upgradeItem.setState',
  586. sender: this,
  587. data: {
  588. upgradeId: item.get('request_id'),
  589. itemId: item.get('stage_id'),
  590. groupId: item.get('group_id'),
  591. status: status
  592. }
  593. }).done(function () {
  594. item.set('status', status);
  595. });
  596. }
  597. });