MigrationSource.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 { Field } from '../types/Field'
  21. import type { NetworkMap } from '../types/Network'
  22. import type { Endpoint, StorageMap } from '../types/Endpoint'
  23. import { servicesUrl } from '../constants'
  24. class MigrationSourceUtils {
  25. static sortTaskUpdates(updates) {
  26. if (!updates) {
  27. return
  28. }
  29. updates.sort((a, b) => {
  30. let sortNull = !a && b ? 1 : a && !b ? -1 : !a && !b ? 0 : false
  31. if (sortNull !== false) {
  32. return sortNull
  33. }
  34. return moment(a.created_at).toDate().getTime() - moment(b.created_at).toDate().getTime()
  35. })
  36. }
  37. static sortMigrations(migrations) {
  38. migrations.sort((a, b) => moment(b.created_at).diff(moment(a.created_at)))
  39. migrations.forEach(migration => {
  40. sortTasks(migration.tasks, MigrationSourceUtils.sortTaskUpdates)
  41. })
  42. }
  43. }
  44. class MigrationSource {
  45. async getMigrations(skipLog?: boolean): Promise<MainItem[]> {
  46. let response = await Api.send({
  47. url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/detail`,
  48. skipLog,
  49. })
  50. let migrations = response.data.migrations
  51. MigrationSourceUtils.sortMigrations(migrations)
  52. return migrations
  53. }
  54. async getMigration(migrationId: string, skipLog?: boolean): Promise<MainItem> {
  55. let response = await Api.send({
  56. url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/${migrationId}`,
  57. skipLog,
  58. })
  59. let migration = response.data.migration
  60. sortTasks(migration.tasks, MigrationSourceUtils.sortTaskUpdates)
  61. return migration
  62. }
  63. async recreate(opts: {
  64. sourceEndpoint: Endpoint,
  65. destEndpoint: Endpoint,
  66. instanceNames: string[],
  67. destEnv: ?{ [string]: any },
  68. updatedDestEnv: ?{ [string]: any },
  69. sourceEnv?: ?{ [string]: any },
  70. updatedSourceEnv?: ?{ [string]: any },
  71. storageMappings: ?{ [string]: any },
  72. updatedStorageMappings: ?StorageMap[],
  73. networkMappings: ?{ [string]: any },
  74. updatedNetworkMappings: ?NetworkMap[],
  75. }): Promise<MainItem> {
  76. const getValue = (fieldName: string): ?string => {
  77. return (opts.updatedDestEnv && opts.updatedDestEnv[fieldName]) ||
  78. (opts.destEnv && opts.destEnv[fieldName])
  79. }
  80. const sourceParser = OptionsSchemaPlugin[opts.sourceEndpoint.type] || OptionsSchemaPlugin.default
  81. const destParser = OptionsSchemaPlugin[opts.destEndpoint.type] || OptionsSchemaPlugin.default
  82. let payload: any = {}
  83. payload.migration = {
  84. origin_endpoint_id: opts.sourceEndpoint.id,
  85. destination_endpoint_id: opts.destEndpoint.id,
  86. destination_environment: {
  87. ...opts.destEnv,
  88. ...destParser.getDestinationEnv(opts.updatedDestEnv),
  89. },
  90. shutdown_instances: Boolean(opts.updatedDestEnv && opts.updatedDestEnv.shutdown_instances),
  91. replication_count: (opts.updatedDestEnv && opts.updatedDestEnv.replication_count) || 2,
  92. instances: opts.instanceNames,
  93. notes: getValue('description') || '',
  94. }
  95. if (getValue('skip_os_morphing') != null) {
  96. payload.migration.skip_os_morphing = getValue('skip_os_morphing')
  97. }
  98. if (opts.networkMappings || (opts.updatedNetworkMappings && opts.updatedNetworkMappings.length)) {
  99. payload.migration.network_map = {
  100. ...opts.networkMappings,
  101. ...destParser.getNetworkMap(opts.updatedNetworkMappings),
  102. }
  103. }
  104. if ((opts.storageMappings && Object.keys(opts.storageMappings).length)
  105. || (opts.updatedStorageMappings && opts.updatedStorageMappings.length)) {
  106. payload.migration.storage_mappings = {
  107. ...opts.storageMappings,
  108. ...destParser.getStorageMap(getValue('default_storage'), opts.updatedStorageMappings),
  109. }
  110. }
  111. if (opts.sourceEnv || opts.updatedSourceEnv) {
  112. payload.migration.source_environment = {
  113. ...opts.sourceEnv,
  114. ...sourceParser.getDestinationEnv(opts.updatedSourceEnv),
  115. }
  116. }
  117. let response = await Api.send({
  118. url: `${servicesUrl.coriolis}/${Api.projectId}/migrations`,
  119. method: 'POST',
  120. data: payload,
  121. })
  122. return response.data.migration
  123. }
  124. async cancel(migrationId: string): Promise<string> {
  125. await Api.send({
  126. url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/${migrationId}/actions`,
  127. method: 'POST',
  128. data: { cancel: null },
  129. })
  130. return migrationId
  131. }
  132. async delete(migrationId: string): Promise<string> {
  133. await Api.send({
  134. url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/${migrationId}`,
  135. method: 'DELETE',
  136. })
  137. return migrationId
  138. }
  139. async migrateReplica(replicaId: string, options: Field[]): Promise<MainItem> {
  140. let payload = {
  141. migration: {
  142. replica_id: replicaId,
  143. },
  144. }
  145. options.forEach(o => {
  146. payload.migration[o.name] = o.value || o.default || false
  147. })
  148. let response = await Api.send({
  149. url: `${servicesUrl.coriolis}/${Api.projectId}/migrations`,
  150. method: 'POST',
  151. data: payload,
  152. })
  153. return response.data.migration
  154. }
  155. }
  156. export default new MigrationSource()