MigrationSource.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. // @flow
  15. import moment from 'moment'
  16. import { OptionsSchemaPlugin } from '../plugins/endpoint'
  17. import { sortTasks } from './ReplicaSource'
  18. import Api from '../utils/ApiCaller'
  19. import type { MainItem } from '../types/MainItem'
  20. import type { InstanceScript } from '../types/Instance'
  21. import type { Field } from '../types/Field'
  22. import type { NetworkMap } from '../types/Network'
  23. import type { Endpoint, StorageMap } from '../types/Endpoint'
  24. import configLoader from '../utils/Config'
  25. class MigrationSourceUtils {
  26. static sortTaskUpdates(updates) {
  27. if (!updates) {
  28. return
  29. }
  30. updates.sort((a, b) => {
  31. let sortNull = !a && b ? 1 : a && !b ? -1 : !a && !b ? 0 : false
  32. if (sortNull !== false) {
  33. return sortNull
  34. }
  35. return moment(a.created_at).toDate().getTime() - moment(b.created_at).toDate().getTime()
  36. })
  37. }
  38. static sortMigrations(migrations) {
  39. migrations.sort((a, b) => moment(b.created_at).diff(moment(a.created_at)))
  40. migrations.forEach(migration => {
  41. sortTasks(migration.tasks, MigrationSourceUtils.sortTaskUpdates)
  42. })
  43. }
  44. }
  45. class MigrationSource {
  46. async getMigrations(skipLog?: boolean): Promise<MainItem[]> {
  47. let response = await Api.send({
  48. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/migrations`,
  49. skipLog,
  50. })
  51. let migrations = response.data.migrations
  52. MigrationSourceUtils.sortMigrations(migrations)
  53. return migrations
  54. }
  55. async getMigration(migrationId: string, skipLog?: boolean): Promise<MainItem> {
  56. let response = await Api.send({
  57. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/migrations/${migrationId}`,
  58. skipLog,
  59. })
  60. let migration = response.data.migration
  61. sortTasks(migration.tasks, MigrationSourceUtils.sortTaskUpdates)
  62. return migration
  63. }
  64. async recreate(opts: {
  65. sourceEndpoint: Endpoint,
  66. destEndpoint: Endpoint,
  67. instanceNames: string[],
  68. destEnv: ?{ [string]: any },
  69. updatedDestEnv: ?{ [string]: any },
  70. sourceEnv?: ?{ [string]: any },
  71. updatedSourceEnv?: ?{ [string]: any },
  72. storageMappings: ?{ [string]: any },
  73. updatedStorageMappings: ?StorageMap[],
  74. defaultStorage: ?string,
  75. updatedDefaultStorage: ?string,
  76. networkMappings: ?{ [string]: any },
  77. updatedNetworkMappings: ?NetworkMap[],
  78. defaultSkipOsMorphing: ?boolean,
  79. replicationCount: ?number,
  80. }): Promise<MainItem> {
  81. const getValue = (fieldName: string): ?string => {
  82. let updatedDestEnv = opts.updatedDestEnv && opts.updatedDestEnv[fieldName]
  83. return updatedDestEnv != null ? updatedDestEnv :
  84. (opts.destEnv && opts.destEnv[fieldName])
  85. }
  86. const sourceParser = OptionsSchemaPlugin[opts.sourceEndpoint.type] || OptionsSchemaPlugin.default
  87. const destParser = OptionsSchemaPlugin[opts.destEndpoint.type] || OptionsSchemaPlugin.default
  88. let payload: any = {}
  89. payload.migration = {
  90. origin_endpoint_id: opts.sourceEndpoint.id,
  91. destination_endpoint_id: opts.destEndpoint.id,
  92. destination_environment: {
  93. ...opts.destEnv,
  94. ...destParser.getDestinationEnv(opts.updatedDestEnv),
  95. },
  96. shutdown_instances: Boolean(opts.updatedDestEnv && opts.updatedDestEnv.shutdown_instances),
  97. replication_count: (opts.updatedDestEnv && opts.updatedDestEnv.replication_count) || opts.replicationCount || 2,
  98. instances: opts.instanceNames,
  99. notes: getValue('description') || '',
  100. }
  101. let skipOsMorphingValue = getValue('skip_os_morphing')
  102. if (skipOsMorphingValue != null) {
  103. payload.migration.skip_os_morphing = skipOsMorphingValue
  104. } else if (opts.defaultSkipOsMorphing != null) {
  105. payload.migration.skip_os_morphing = opts.defaultSkipOsMorphing
  106. }
  107. if (opts.networkMappings || (opts.updatedNetworkMappings && opts.updatedNetworkMappings.length)) {
  108. payload.migration.network_map = {
  109. ...opts.networkMappings,
  110. ...destParser.getNetworkMap(opts.updatedNetworkMappings),
  111. }
  112. }
  113. if ((opts.storageMappings && Object.keys(opts.storageMappings).length)
  114. || (opts.updatedStorageMappings && opts.updatedStorageMappings.length)) {
  115. payload.migration.storage_mappings = {
  116. ...opts.storageMappings,
  117. ...destParser.getStorageMap(opts.updatedDefaultStorage || opts.defaultStorage, opts.updatedStorageMappings),
  118. }
  119. }
  120. if (opts.sourceEnv || opts.updatedSourceEnv) {
  121. payload.migration.source_environment = {
  122. ...opts.sourceEnv,
  123. ...sourceParser.getDestinationEnv(opts.updatedSourceEnv),
  124. }
  125. }
  126. let response = await Api.send({
  127. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/migrations`,
  128. method: 'POST',
  129. data: payload,
  130. })
  131. return response.data.migration
  132. }
  133. async cancel(migrationId: string, force: ?boolean): Promise<string> {
  134. let data: any = { cancel: null }
  135. if (force) {
  136. data.cancel = { force: true }
  137. }
  138. await Api.send({
  139. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/migrations/${migrationId}/actions`,
  140. method: 'POST',
  141. data,
  142. })
  143. return migrationId
  144. }
  145. async delete(migrationId: string): Promise<string> {
  146. await Api.send({
  147. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/migrations/${migrationId}`,
  148. method: 'DELETE',
  149. })
  150. return migrationId
  151. }
  152. async migrateReplica(replicaId: string, options: Field[], userScripts: InstanceScript[]): Promise<MainItem> {
  153. let payload: any = {
  154. migration: {
  155. replica_id: replicaId,
  156. },
  157. }
  158. options.forEach(o => {
  159. payload.migration[o.name] = o.value || o.default || false
  160. })
  161. if (userScripts.length) {
  162. payload.migration.user_scripts = OptionsSchemaPlugin.default.getUserScripts(userScripts)
  163. }
  164. let response = await Api.send({
  165. url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/migrations`,
  166. method: 'POST',
  167. data: payload,
  168. })
  169. return response.data.migration
  170. }
  171. }
  172. export default new MigrationSource()