/*
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 .
*/
// @flow
import moment from 'moment'
import { OptionsSchemaPlugin } from '../plugins/endpoint'
import { sortTasks } from './ReplicaSource'
import Api from '../utils/ApiCaller'
import type { MainItem } from '../types/MainItem'
import type { Field } from '../types/Field'
import type { NetworkMap } from '../types/Network'
import type { Endpoint, StorageMap } from '../types/Endpoint'
import { servicesUrl } from '../constants'
class MigrationSourceUtils {
static sortTaskUpdates(updates) {
if (!updates) {
return
}
updates.sort((a, b) => {
let sortNull = !a && b ? 1 : a && !b ? -1 : !a && !b ? 0 : false
if (sortNull !== false) {
return sortNull
}
return moment(a.created_at).toDate().getTime() - moment(b.created_at).toDate().getTime()
})
}
static sortMigrations(migrations) {
migrations.sort((a, b) => moment(b.created_at).diff(moment(a.created_at)))
migrations.forEach(migration => {
sortTasks(migration.tasks, MigrationSourceUtils.sortTaskUpdates)
})
}
}
class MigrationSource {
async getMigrations(skipLog?: boolean): Promise {
let response = await Api.send({
url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/detail`,
skipLog,
})
let migrations = response.data.migrations
MigrationSourceUtils.sortMigrations(migrations)
return migrations
}
async getMigration(migrationId: string, skipLog?: boolean): Promise {
let response = await Api.send({
url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/${migrationId}`,
skipLog,
})
let migration = response.data.migration
sortTasks(migration.tasks, MigrationSourceUtils.sortTaskUpdates)
return migration
}
async recreate(opts: {
sourceEndpoint: Endpoint,
destEndpoint: Endpoint,
instanceNames: string[],
destEnv: ?{ [string]: any },
updatedDestEnv: ?{ [string]: any },
sourceEnv?: ?{ [string]: any },
updatedSourceEnv?: ?{ [string]: any },
storageMappings: ?{ [string]: any },
updatedStorageMappings: ?StorageMap[],
networkMappings: ?{ [string]: any },
updatedNetworkMappings: ?NetworkMap[],
}): Promise {
const getValue = (fieldName: string): ?string => {
return (opts.updatedDestEnv && opts.updatedDestEnv[fieldName]) ||
(opts.destEnv && opts.destEnv[fieldName])
}
const sourceParser = OptionsSchemaPlugin[opts.sourceEndpoint.type] || OptionsSchemaPlugin.default
const destParser = OptionsSchemaPlugin[opts.destEndpoint.type] || OptionsSchemaPlugin.default
let payload: any = {}
payload.migration = {
origin_endpoint_id: opts.sourceEndpoint.id,
destination_endpoint_id: opts.destEndpoint.id,
destination_environment: {
...opts.destEnv,
...destParser.getDestinationEnv(opts.updatedDestEnv),
},
shutdown_instances: Boolean(opts.updatedDestEnv && opts.updatedDestEnv.shutdown_instances),
replication_count: (opts.updatedDestEnv && opts.updatedDestEnv.replication_count) || 2,
instances: opts.instanceNames,
notes: getValue('description') || '',
}
if (getValue('skip_os_morphing') != null) {
payload.migration.skip_os_morphing = getValue('skip_os_morphing')
}
if (opts.networkMappings || (opts.updatedNetworkMappings && opts.updatedNetworkMappings.length)) {
payload.migration.network_map = {
...opts.networkMappings,
...destParser.getNetworkMap(opts.updatedNetworkMappings),
}
}
if ((opts.storageMappings && Object.keys(opts.storageMappings).length)
|| (opts.updatedStorageMappings && opts.updatedStorageMappings.length)) {
payload.migration.storage_mappings = {
...opts.storageMappings,
...destParser.getStorageMap(getValue('default_storage'), opts.updatedStorageMappings),
}
}
if (opts.sourceEnv || opts.updatedSourceEnv) {
payload.migration.source_environment = {
...opts.sourceEnv,
...sourceParser.getDestinationEnv(opts.updatedSourceEnv),
}
}
let response = await Api.send({
url: `${servicesUrl.coriolis}/${Api.projectId}/migrations`,
method: 'POST',
data: payload,
})
return response.data.migration
}
async cancel(migrationId: string): Promise {
await Api.send({
url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/${migrationId}/actions`,
method: 'POST',
data: { cancel: null },
})
return migrationId
}
async delete(migrationId: string): Promise {
await Api.send({
url: `${servicesUrl.coriolis}/${Api.projectId}/migrations/${migrationId}`,
method: 'DELETE',
})
return migrationId
}
async migrateReplica(replicaId: string, options: Field[]): Promise {
let payload = {
migration: {
replica_id: replicaId,
},
}
options.forEach(o => {
payload.migration[o.name] = o.value || o.default || false
})
let response = await Api.send({
url: `${servicesUrl.coriolis}/${Api.projectId}/migrations`,
method: 'POST',
data: payload,
})
return response.data.migration
}
}
export default new MigrationSource()