ReplicaSource.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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 Api from '../utils/ApiCaller'
  17. import { OptionsSchemaPlugin } from '../plugins/endpoint'
  18. import { servicesUrl } from '../constants'
  19. import type { MainItem, UpdateData } from '../types/MainItem'
  20. import type { Execution } from '../types/Execution'
  21. import type { Endpoint } from '../types/Endpoint'
  22. import type { Task, ProgressUpdate } from '../types/Task'
  23. import type { Field } from '../types/Field'
  24. export const sortTasks = (tasks: Task[], taskUpdatesSortFunction: (updates: ProgressUpdate[]) => void) => {
  25. if (!tasks) {
  26. return
  27. }
  28. let sortedTasks = []
  29. let buffer = []
  30. let runningBuffer = []
  31. let completedBuffer = []
  32. tasks.forEach(task => {
  33. taskUpdatesSortFunction(task.progress_updates)
  34. buffer.push(task)
  35. if (task.status === 'RUNNING') {
  36. runningBuffer.push(task)
  37. } else if (task.status === 'COMPLETED' || task.status === 'ERROR') {
  38. completedBuffer.push(task)
  39. } else {
  40. if (runningBuffer.length >= 2) {
  41. sortedTasks = sortedTasks.concat([...completedBuffer, ...runningBuffer, task])
  42. } else {
  43. sortedTasks = sortedTasks.concat([...buffer])
  44. }
  45. buffer = []
  46. runningBuffer = []
  47. completedBuffer = []
  48. }
  49. })
  50. if (buffer.length) {
  51. if (runningBuffer.length >= 2) {
  52. sortedTasks = sortedTasks.concat([...completedBuffer, ...runningBuffer])
  53. } else {
  54. sortedTasks = sortedTasks.concat([...buffer])
  55. }
  56. }
  57. tasks.splice(0, tasks.length, ...sortedTasks)
  58. }
  59. class ReplicaSourceUtils {
  60. static filterDeletedExecutionsInReplicas(replicas) {
  61. return replicas.map(replica => {
  62. replica.executions = ReplicaSourceUtils.filterDeletedExecutions(replica.executions)
  63. return replica
  64. })
  65. }
  66. static filterDeletedExecutions(executions) {
  67. if (!executions || !executions.length) {
  68. return executions
  69. }
  70. return executions.filter(execution => execution.deleted_at == null)
  71. }
  72. static sortReplicas(replicas) {
  73. if (replicas.length === 1) {
  74. ReplicaSourceUtils.sortExecutions(replicas[0].executions)
  75. return
  76. }
  77. replicas.sort((a, b) => {
  78. ReplicaSourceUtils.sortExecutions(a.executions)
  79. ReplicaSourceUtils.sortExecutions(b.executions)
  80. let aLastExecution = a.executions && a.executions.length ? a.executions[a.executions.length - 1] : null
  81. let bLastExecution = b.executions && b.executions.length ? b.executions[b.executions.length - 1] : null
  82. let aLastTime = aLastExecution ? aLastExecution.updated_at || aLastExecution.created_at : null
  83. let bLastTime = bLastExecution ? bLastExecution.updated_at || bLastExecution.created_at : null
  84. let aTime = aLastTime || a.updated_at || a.created_at
  85. let bTime = bLastTime || b.updated_at || b.created_at
  86. return moment(bTime).diff(moment(aTime))
  87. })
  88. }
  89. static sortExecutions(executions) {
  90. if (executions) {
  91. executions.sort((a, b) => a.number - b.number)
  92. }
  93. }
  94. static sortExecutionsAndTasks(executions) {
  95. this.sortExecutions(executions)
  96. executions.forEach(execution => {
  97. sortTasks(execution.tasks, ReplicaSourceUtils.sortTaskUpdates)
  98. })
  99. }
  100. static sortTaskUpdates(updates) {
  101. if (!updates) {
  102. return
  103. }
  104. updates.sort((a, b) => moment(a.created_at).toDate().getTime() - moment(b.created_at).toDate().getTime())
  105. }
  106. }
  107. class ReplicaSource {
  108. async getReplicas(skipLog?: boolean): Promise<MainItem[]> {
  109. let response = await Api.send({
  110. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/detail`,
  111. skipLog,
  112. })
  113. let replicas = response.data.replicas
  114. replicas = ReplicaSourceUtils.filterDeletedExecutionsInReplicas(replicas)
  115. ReplicaSourceUtils.sortReplicas(replicas)
  116. return replicas
  117. }
  118. async getReplicaExecutions(replicaId: string, skipLog?: boolean): Promise<Execution[]> {
  119. let response = await Api.send({
  120. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}/executions/detail`,
  121. skipLog,
  122. })
  123. let executions = response.data.executions
  124. ReplicaSourceUtils.sortExecutionsAndTasks(executions)
  125. return executions
  126. }
  127. async getReplica(replicaId: string, skipLog?: boolean): Promise<MainItem> {
  128. let response = await Api.send({
  129. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}`,
  130. skipLog,
  131. })
  132. let replica = response.data.replica
  133. replica.executions = ReplicaSourceUtils.filterDeletedExecutions(replica.executions)
  134. ReplicaSourceUtils.sortExecutions(replica.executions)
  135. return replica
  136. }
  137. async execute(replicaId: string, fields?: Field[]): Promise<Execution> {
  138. let payload = { execution: { shutdown_instances: false } }
  139. if (fields) {
  140. fields.forEach(f => {
  141. payload.execution[f.name] = f.value || false
  142. })
  143. }
  144. let response = await Api.send({
  145. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}/executions`,
  146. method: 'POST',
  147. data: payload,
  148. })
  149. let execution = response.data.execution
  150. sortTasks(execution.tasks, ReplicaSourceUtils.sortTaskUpdates)
  151. return execution
  152. }
  153. async cancelExecution(replicaId: string, executionId: string): Promise<string> {
  154. await Api.send({
  155. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}/executions/${executionId}/actions`,
  156. method: 'POST',
  157. data: { cancel: null },
  158. })
  159. return replicaId
  160. }
  161. async deleteExecution(replicaId: string, executionId: string): Promise<string> {
  162. await Api.send({
  163. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}/executions/${executionId}`,
  164. method: 'DELETE',
  165. })
  166. return replicaId
  167. }
  168. async delete(replicaId: string): Promise<string> {
  169. await Api.send({
  170. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}`,
  171. method: 'DELETE',
  172. })
  173. return replicaId
  174. }
  175. async deleteDisks(replicaId: string): Promise<Execution> {
  176. let response = await Api.send({
  177. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replicaId}/actions`,
  178. method: 'POST',
  179. data: { 'delete-disks': null },
  180. })
  181. return response.data.execution
  182. }
  183. async update(replica: MainItem, destinationEndpoint: Endpoint, updateData: UpdateData, storageConfigDefault: string): Promise<Execution> {
  184. const parser = OptionsSchemaPlugin[destinationEndpoint.type] || OptionsSchemaPlugin.default
  185. let payload = { replica: {} }
  186. if (updateData.network.length > 0) {
  187. payload.replica.network_map = parser.getNetworkMap(updateData.network)
  188. }
  189. if (Object.keys(updateData.destination).length > 0) {
  190. payload.replica.destination_environment = parser.getDestinationEnv(updateData.destination, replica.destination_environment)
  191. }
  192. if (Object.keys(updateData.source).length > 0) {
  193. payload.replica.source_environment = parser.getDestinationEnv(updateData.source, replica.source_environment)
  194. }
  195. let defaultStorage = updateData.destination && updateData.destination.default_storage
  196. if (defaultStorage || updateData.storage.length > 0) {
  197. payload.replica.storage_mappings = parser.getStorageMap(defaultStorage, updateData.storage, storageConfigDefault)
  198. }
  199. let response = await Api.send({
  200. url: `${servicesUrl.coriolis}/${Api.projectId}/replicas/${replica.id}`,
  201. method: 'PUT',
  202. data: payload,
  203. })
  204. return response.data
  205. }
  206. }
  207. export default new ReplicaSource()