| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /*
- Copyright (C) 2017 Cloudbase Solutions SRL
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- import Api from '../utils/ApiCaller'
- import { OptionsSchemaPlugin } from '../plugins/endpoint'
- import DefaultOptionsSchemaPlugin from '../plugins/endpoint/default/OptionsSchemaPlugin'
- import configLoader from '../utils/Config'
- import type { UpdateData, ReplicaItem, ReplicaItemDetails } from '../@types/MainItem'
- import type { Execution, ExecutionTasks } from '../@types/Execution'
- import type { Endpoint } from '../@types/Endpoint'
- import type { Task, ProgressUpdate } from '../@types/Task'
- import type { Field } from '../@types/Field'
- import { INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS } from '../components/organisms/WizardOptions/WizardOptions'
- export const sortTasks = (
- tasks?: Task[], taskUpdatesSortFunction?: (updates: ProgressUpdate[]) => void,
- ) => {
- if (!tasks) {
- return
- }
- let sortedTasks: any[] = []
- let buffer: Task[] = []
- let runningBuffer: Task[] = []
- let completedBuffer: Task[] = []
- if (!taskUpdatesSortFunction) {
- return
- }
- tasks.forEach(task => {
- taskUpdatesSortFunction(task.progress_updates)
- buffer.push(task)
- if (task.status === 'RUNNING') {
- runningBuffer.push(task)
- } else if (task.status === 'COMPLETED' || task.status === 'ERROR') {
- completedBuffer.push(task)
- } else {
- if (runningBuffer.length >= 2) {
- sortedTasks = sortedTasks.concat([...completedBuffer, ...runningBuffer, task])
- } else {
- sortedTasks = sortedTasks.concat([...buffer])
- }
- buffer = []
- runningBuffer = []
- completedBuffer = []
- }
- })
- if (buffer.length) {
- if (runningBuffer.length >= 2) {
- sortedTasks = sortedTasks.concat([...completedBuffer, ...runningBuffer])
- } else {
- sortedTasks = sortedTasks.concat([...buffer])
- }
- }
- tasks.splice(0, tasks.length, ...sortedTasks)
- }
- export class ReplicaSourceUtils {
- static filterDeletedExecutions(executions?: Execution[]) {
- if (!executions || !executions.length) {
- return []
- }
- return executions.filter(execution => execution.deleted_at == null)
- }
- static sortReplicas(replicas: ReplicaItem[]) {
- replicas
- .sort(
- (a, b) => new Date(b.updated_at || b.created_at).getTime()
- - new Date(a.updated_at || a.created_at)
- .getTime(),
- )
- }
- static sortExecutions(executions: Execution[]) {
- executions.sort((a, b) => a.number - b.number)
- }
- static sortTaskUpdates(updates: ProgressUpdate[]) {
- if (!updates) {
- return
- }
- updates.sort((a, b) => a.index - b.index)
- }
- }
- class ReplicaSource {
- async getReplicas(skipLog?: boolean, quietError?: boolean): Promise<ReplicaItem[]> {
- const response = await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas`,
- skipLog,
- quietError,
- })
- const replicas: ReplicaItem[] = response.data.replicas
- ReplicaSourceUtils.sortReplicas(replicas)
- return replicas
- }
- async getReplicaDetails(options: {
- replicaId: string, polling?: boolean
- }): Promise<ReplicaItemDetails> {
- const { replicaId, polling } = options
- const response = await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replicaId}`,
- skipLog: polling,
- })
- const replica: ReplicaItemDetails = response.data.replica
- replica.executions = ReplicaSourceUtils.filterDeletedExecutions(replica.executions)
- ReplicaSourceUtils.sortExecutions(replica.executions)
- return replica
- }
- async getExecutionTasks(options: {
- replicaId: string,
- executionId?: string,
- polling?: boolean,
- }): Promise<ExecutionTasks> {
- const {
- replicaId, executionId, polling,
- } = options
- const response = await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replicaId}/executions/${executionId}`,
- skipLog: polling,
- quietError: true,
- })
- const execution: ExecutionTasks = response.data.execution
- sortTasks(execution.tasks, ReplicaSourceUtils.sortTaskUpdates)
- return execution
- }
- async execute(replicaId: string, fields?: Field[]): Promise<ExecutionTasks> {
- const payload: any = { execution: { shutdown_instances: false } }
- if (fields) {
- fields.forEach(f => {
- payload.execution[f.name] = f.value || false
- })
- }
- const response = await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replicaId}/executions`,
- method: 'POST',
- data: payload,
- })
- const execution: ExecutionTasks = response.data.execution
- sortTasks(execution.tasks, ReplicaSourceUtils.sortTaskUpdates)
- return execution
- }
- async cancelExecution(
- options: { replicaId: string, executionId?: string, force?: boolean },
- ): Promise<string> {
- const data: any = { cancel: null }
- if (options.force) {
- data.cancel = { force: true }
- }
- let lastExecutionId = options.executionId
- if (!lastExecutionId) {
- const replicaDetails = await this.getReplicaDetails({ replicaId: options.replicaId })
- const lastExecution = replicaDetails.executions[replicaDetails.executions.length - 1]
- if (lastExecution.status !== 'RUNNING') {
- return options.replicaId
- }
- lastExecutionId = lastExecution.id
- }
- await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${options.replicaId}/executions/${lastExecutionId}/actions`,
- method: 'POST',
- data,
- })
- return options.replicaId
- }
- async deleteExecution(replicaId: string, executionId: string): Promise<string> {
- await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replicaId}/executions/${executionId}`,
- method: 'DELETE',
- })
- return replicaId
- }
- async delete(replicaId: string): Promise<string> {
- await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replicaId}`,
- method: 'DELETE',
- })
- return replicaId
- }
- async deleteDisks(replicaId: string): Promise<Execution> {
- const response = await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replicaId}/actions`,
- method: 'POST',
- data: { 'delete-disks': null },
- })
- return response.data.execution
- }
- async update(options: {
- replica: ReplicaItemDetails,
- sourceEndpoint: Endpoint,
- destinationEndpoint: Endpoint,
- updateData: UpdateData,
- defaultStorage: string | null | undefined,
- storageConfigDefault: string,
- }): Promise<Execution> {
- const {
- replica,
- destinationEndpoint,
- updateData,
- defaultStorage,
- storageConfigDefault,
- sourceEndpoint,
- } = options
- const sourceParser = OptionsSchemaPlugin.for(sourceEndpoint.type)
- const destinationParser = OptionsSchemaPlugin.for(destinationEndpoint.type)
- const payload: any = { replica: {} }
- if (updateData.destination.title) {
- payload.replica.notes = updateData.destination.title
- }
- if (updateData.network.length > 0) {
- payload.replica.network_map = destinationParser.getNetworkMap(updateData.network)
- }
- if (Object.keys(updateData.source).length > 0) {
- const sourceEnv = sourceParser.getDestinationEnv(updateData.source, replica.source_environment)
- if (updateData.source.minion_pool_id !== undefined) {
- payload.replica.origin_minion_pool_id = updateData.source.minion_pool_id
- }
- if (Object.keys(sourceEnv).length) {
- payload.replica.source_environment = sourceEnv
- }
- }
- if (Object.keys(updateData.destination).length > 0) {
- const destEnv = destinationParser.getDestinationEnv(updateData.destination,
- { ...replica, ...replica.destination_environment })
- const newMinionMappings = destEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS]
- if (newMinionMappings) {
- payload.replica[
- INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS
- ] = newMinionMappings
- }
- delete destEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS]
- if (updateData.destination.minion_pool_id !== undefined) {
- payload.replica.destination_minion_pool_id = updateData.destination.minion_pool_id
- }
- if (Object.keys(destEnv).length) {
- payload.replica.destination_environment = destEnv
- }
- }
- if (defaultStorage || updateData.storage.length > 0) {
- payload.replica.storage_mappings = destinationParser
- .getStorageMap(defaultStorage, updateData.storage, storageConfigDefault)
- }
- if (updateData.uploadedScripts?.length || updateData.removedScripts?.length) {
- payload.replica.user_scripts = DefaultOptionsSchemaPlugin
- .getUserScripts(
- updateData.uploadedScripts || [],
- updateData.removedScripts || [],
- replica.user_scripts,
- )
- }
- const response = await Api.send({
- url: `${configLoader.config.servicesUrls.coriolis}/${Api.projectId}/replicas/${replica.id}`,
- method: 'PUT',
- data: payload,
- })
- return response.data
- }
- }
- export default new ReplicaSource()
|