parent
fcaf6b7923
commit
63c676ebfe
@ -95,6 +95,7 @@
|
||||
"serve-static": "^1.9.2",
|
||||
"stack-chain": "^1.3.3",
|
||||
"struct-fu": "^1.0.0",
|
||||
"tar-stream": "^1.5.2",
|
||||
"through2": "^2.0.0",
|
||||
"trace": "^2.0.1",
|
||||
"ws": "^1.1.1",
|
||||
@ -102,7 +103,8 @@
|
||||
"xml2js": "~0.4.6",
|
||||
"xo-acl-resolver": "^0.2.1",
|
||||
"xo-collection": "^0.4.0",
|
||||
"xo-remote-parser": "^0.3"
|
||||
"xo-remote-parser": "^0.3",
|
||||
"xo-vmdk-to-vhd": "0.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^6.0.4",
|
||||
|
@ -943,13 +943,13 @@ exports.export = export_;
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
handleVmImport = $coroutine (req, res, { xapi, srId }) ->
|
||||
handleVmImport = $coroutine (req, res, { data, srId, type, xapi }) ->
|
||||
# Timeout seems to be broken in Node 4.
|
||||
# See https://github.com/nodejs/node/issues/3319
|
||||
req.setTimeout(43200000) # 12 hours
|
||||
|
||||
try
|
||||
vm = yield xapi.importVm(req, { srId })
|
||||
vm = yield xapi.importVm(req, { data, srId, type })
|
||||
res.end(format.response(0, vm.$id))
|
||||
catch e
|
||||
res.writeHead(500)
|
||||
@ -958,7 +958,7 @@ handleVmImport = $coroutine (req, res, { xapi, srId }) ->
|
||||
return
|
||||
|
||||
# TODO: "sr_id" can be passed in URL to target a specific SR
|
||||
import_ = $coroutine ({host, sr}) ->
|
||||
import_ = $coroutine ({ data, host, sr, type }) ->
|
||||
if not sr
|
||||
if not host
|
||||
throw new InvalidParameters('you must provide either host or SR')
|
||||
@ -974,13 +974,45 @@ import_ = $coroutine ({host, sr}) ->
|
||||
|
||||
return {
|
||||
$sendTo: yield @registerHttpRequest(handleVmImport, {
|
||||
data,
|
||||
srId: sr._xapiId,
|
||||
type,
|
||||
xapi
|
||||
})
|
||||
}
|
||||
|
||||
import_.params = {
|
||||
data: {
|
||||
type: 'object',
|
||||
optional: true,
|
||||
properties: {
|
||||
descriptionLabel: { type: 'string' },
|
||||
disks: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
capacity: { type: 'integer' },
|
||||
descriptionLabel: { type: 'string' },
|
||||
nameLabel: { type: 'string' },
|
||||
path: { type: 'string' },
|
||||
position: { type: 'integer' }
|
||||
}
|
||||
},
|
||||
optional: true
|
||||
},
|
||||
memory: { type: 'integer' },
|
||||
nameLabel: { type: 'string' },
|
||||
nCpus: { type: 'integer' },
|
||||
networks: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
optional: true
|
||||
},
|
||||
}
|
||||
},
|
||||
host: { type: 'string', optional: true },
|
||||
type: { type: 'string', optional: true },
|
||||
sr: { type: 'string', optional: true }
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,9 @@ import fatfs from 'fatfs'
|
||||
import find from 'lodash/find'
|
||||
import includes from 'lodash/includes'
|
||||
import sortBy from 'lodash/sortBy'
|
||||
import tarStream from 'tar-stream'
|
||||
import unzip from 'julien-f-unzip'
|
||||
import vmdkToVhd from 'xo-vmdk-to-vhd'
|
||||
import { defer } from 'promise-toolbox'
|
||||
import {
|
||||
wrapError as wrapXapiError,
|
||||
@ -48,6 +50,7 @@ import {
|
||||
} from '../api-errors'
|
||||
|
||||
import mixins from './mixins'
|
||||
import OTHER_CONFIG_TEMPLATE from './other-config-template'
|
||||
import {
|
||||
asBoolean,
|
||||
asInteger,
|
||||
@ -1428,18 +1431,114 @@ export default class Xapi extends XapiBase {
|
||||
return vmRef
|
||||
}
|
||||
|
||||
@deferrable.onFailure
|
||||
async _importOvaVm ($onFailure, stream, {
|
||||
descriptionLabel,
|
||||
disks,
|
||||
memory,
|
||||
nameLabel,
|
||||
networks,
|
||||
nCpus
|
||||
}, sr) {
|
||||
// 1. Create VM.
|
||||
const vm = await this._getOrWaitObject(
|
||||
await this._createVmRecord({
|
||||
...OTHER_CONFIG_TEMPLATE,
|
||||
memory_dynamic_max: memory,
|
||||
memory_dynamic_min: memory,
|
||||
memory_static_max: memory,
|
||||
name_description: descriptionLabel,
|
||||
name_label: nameLabel,
|
||||
VCPUs_at_startup: nCpus,
|
||||
VCPUs_max: nCpus
|
||||
})
|
||||
)
|
||||
$onFailure(() => this._deleteVm(vm))
|
||||
// Disable start and change the VM name label during import.
|
||||
await Promise.all([
|
||||
this.addForbiddenOperationToVm(vm.$id, 'start', 'OVA import in progress...'),
|
||||
this._setObjectProperties(vm, { name_label: `[Importing...] ${nameLabel}` })
|
||||
])
|
||||
|
||||
// 2. Create VDIs & Vifs.
|
||||
const vdis = {}
|
||||
const vifDevices = await this.call('VM.get_allowed_VIF_devices', vm.$ref)
|
||||
await Promise.all(
|
||||
map(disks, async disk => {
|
||||
const vdi = vdis[disk.path] = await this.createVdi(disk.capacity, {
|
||||
name_description: disk.descriptionLabel,
|
||||
name_label: disk.nameLabel,
|
||||
sr: sr.$ref
|
||||
})
|
||||
$onFailure(() => this._deleteVdi(vdi)::pCatch(noop))
|
||||
|
||||
return this._createVbd(vm, vdi, { position: disk.position })
|
||||
}).concat(map(networks, (networkId, i) => (
|
||||
this._createVif(vm, this.getObject(networkId), {
|
||||
device: vifDevices[i]
|
||||
})
|
||||
)))
|
||||
)
|
||||
|
||||
// 3. Import VDIs contents.
|
||||
await new Promise((resolve, reject) => {
|
||||
const extract = tarStream.extract()
|
||||
|
||||
stream.on('error', reject)
|
||||
|
||||
extract.on('finish', resolve)
|
||||
extract.on('error', reject)
|
||||
extract.on('entry', async (entry, stream, cb) => {
|
||||
// Not a disk to import.
|
||||
const vdi = vdis[entry.name]
|
||||
if (!vdi) {
|
||||
stream.on('end', cb)
|
||||
stream.resume()
|
||||
return
|
||||
}
|
||||
|
||||
const vhdStream = await vmdkToVhd(stream)
|
||||
await this._importVdiContent(vdi, vhdStream, VDI_FORMAT_RAW)
|
||||
|
||||
// See: https://github.com/mafintosh/tar-stream#extracting
|
||||
// No import parallelization.
|
||||
cb()
|
||||
})
|
||||
stream.pipe(extract)
|
||||
})
|
||||
|
||||
// Enable start and restore the VM name label after import.
|
||||
await Promise.all([
|
||||
this.removeForbiddenOperationFromVm(vm.$id, 'start'),
|
||||
this._setObjectProperties(vm, { name_label: nameLabel })
|
||||
])
|
||||
return vm
|
||||
}
|
||||
|
||||
// TODO: an XVA can contain multiple VMs
|
||||
async importVm (stream, {
|
||||
data,
|
||||
onlyMetadata = false,
|
||||
srId
|
||||
srId,
|
||||
type = 'xva'
|
||||
} = {}) {
|
||||
const sr = srId && this.getObject(srId)
|
||||
|
||||
if (type === 'xva') {
|
||||
return /* await */ this._getOrWaitObject(await this._importVm(
|
||||
stream,
|
||||
srId && this.getObject(srId),
|
||||
sr,
|
||||
onlyMetadata
|
||||
))
|
||||
}
|
||||
|
||||
if (type === 'ova') {
|
||||
return this._getOrWaitObject(await this._importOvaVm(stream, data, sr))
|
||||
}
|
||||
|
||||
throw new Error(`unsupported type: '${type}'`)
|
||||
}
|
||||
|
||||
async migrateVm (vmId, hostXapi, hostId, {
|
||||
migrationNetworkId,
|
||||
mapVifsNetworks,
|
||||
|
51
src/xapi/other-config-template.js
Normal file
51
src/xapi/other-config-template.js
Normal file
@ -0,0 +1,51 @@
|
||||
const OTHER_CONFIG_TEMPLATE = {
|
||||
actions_after_crash: 'restart',
|
||||
actions_after_reboot: 'restart',
|
||||
actions_after_shutdown: 'destroy',
|
||||
affinity: null,
|
||||
blocked_operations: {},
|
||||
ha_always_run: false,
|
||||
HVM_boot_params: {
|
||||
order: 'cdn'
|
||||
},
|
||||
HVM_boot_policy: 'BIOS order',
|
||||
HVM_shadow_multiplier: 1,
|
||||
is_a_template: false,
|
||||
memory_dynamic_max: 4294967296,
|
||||
memory_dynamic_min: 4294967296,
|
||||
memory_static_max: 4294967296,
|
||||
memory_static_min: 134217728,
|
||||
order: 0,
|
||||
other_config: {
|
||||
vgpu_pci: '',
|
||||
base_template_name: 'Other install media',
|
||||
mac_seed: '5e88eb6a-d680-c47f-a94a-028886971ba4',
|
||||
'install-methods': 'cdrom'
|
||||
},
|
||||
PCI_bus: '',
|
||||
platform: {
|
||||
timeoffset: '0',
|
||||
nx: 'true',
|
||||
acpi: '1',
|
||||
apic: 'true',
|
||||
pae: 'true',
|
||||
hpet: 'true',
|
||||
viridian: 'true'
|
||||
},
|
||||
protection_policy: 'OpaqueRef:NULL',
|
||||
PV_args: '',
|
||||
PV_bootloader: '',
|
||||
PV_bootloader_args: '',
|
||||
PV_kernel: '',
|
||||
PV_legacy_args: '',
|
||||
PV_ramdisk: '',
|
||||
recommendations: '<restrictions><restriction field="memory-static-max" max="137438953472" /><restriction field="vcpus-max" max="32" /><restriction property="number-of-vbds" max="255" /><restriction property="number-of-vifs" max="7" /><restriction field="has-vendor-device" value="false" /></restrictions>',
|
||||
shutdown_delay: 0,
|
||||
start_delay: 0,
|
||||
user_version: 1,
|
||||
VCPUs_at_startup: 1,
|
||||
VCPUs_max: 1,
|
||||
VCPUs_params: {},
|
||||
version: 0
|
||||
}
|
||||
export { OTHER_CONFIG_TEMPLATE as default }
|
Loading…
Reference in New Issue
Block a user