Various updates.
This commit is contained in:
parent
cb65ddedb6
commit
923091ca62
@ -67,6 +67,7 @@
|
||||
"lodash.map": "^3.0.0",
|
||||
"lodash.pick": "^3.0.0",
|
||||
"lodash.result": "^3.0.0",
|
||||
"lodash.snakecase": "^3.0.1",
|
||||
"lodash.startswith": "^3.0.1",
|
||||
"make-error": "^1",
|
||||
"multikey-hash": "^1.0.1",
|
||||
|
@ -3,19 +3,10 @@ import {parseSize} from '../utils'
|
||||
// ===================================================================
|
||||
|
||||
export async function create ({name, size, sr}) {
|
||||
const xapi = this.getXAPI(sr)
|
||||
|
||||
const ref = await xapi.call('VDI.create', {
|
||||
name_label: name,
|
||||
other_config: {},
|
||||
read_only: false,
|
||||
sharable: false,
|
||||
SR: sr.ref,
|
||||
type: 'user',
|
||||
virtual_size: String(parseSize(size))
|
||||
const vdi = await this.getXAPI(sr).createVdi(sr.id, parseSize(size), {
|
||||
name_label: name
|
||||
})
|
||||
|
||||
return (await xapi.call('VDI.get_record', ref)).uuid
|
||||
return vdi.$id
|
||||
}
|
||||
|
||||
create.description = 'create a new disk on a SR'
|
||||
|
@ -9,12 +9,9 @@ $isArray = require 'lodash.isarray'
|
||||
#=====================================================================
|
||||
|
||||
delete_ = $coroutine ({vdi}) ->
|
||||
xapi = @getXAPI vdi
|
||||
yield @getXAPI(vdi).deleteVdi(vdi.id)
|
||||
|
||||
# TODO: check if VDI is attached before
|
||||
yield xapi.call 'VDI.destroy', vdi.ref
|
||||
|
||||
return true
|
||||
return
|
||||
|
||||
delete_.params = {
|
||||
id: { type: 'string' },
|
||||
|
@ -1,7 +1,6 @@
|
||||
// TODO: move into vm and rename to removeInterface
|
||||
async function delete_ ({vif}) {
|
||||
// TODO: check if VIF is attached before
|
||||
await this.getXAPI(vif).call('VIF.destroy', vif.ref)
|
||||
await this.getXAPI(vif).deleteVif(vif.id)
|
||||
}
|
||||
export {delete_ as delete}
|
||||
|
||||
|
@ -35,8 +35,9 @@ create = $coroutine ({
|
||||
VDIs
|
||||
VIFs
|
||||
}) ->
|
||||
vm = yield @getXAPI(template).createVm(template.id, name_label, {
|
||||
vm = yield @getXAPI(template).createVm(template.id, {
|
||||
installRepository: installation.repository,
|
||||
nameLabel: name_label,
|
||||
vdis: VDIs,
|
||||
vifs: VIFs
|
||||
})
|
||||
@ -692,7 +693,7 @@ exports.attachDisk = attachDisk
|
||||
# FIXME: position should be optional and default to last.
|
||||
|
||||
createInterface = $coroutine ({vm, network, position, mtu, mac}) ->
|
||||
vif = yield @getXAPI(vm).createVirtualInterface(vm.id, network.id, {
|
||||
vif = yield @getXAPI(vm).createVif(vm.id, network.id, {
|
||||
mac,
|
||||
mtu,
|
||||
position
|
||||
|
205
src/xapi.js
205
src/xapi.js
@ -4,6 +4,7 @@ import find from 'lodash.find'
|
||||
import forEach from 'lodash.foreach'
|
||||
import got from 'got'
|
||||
import map from 'lodash.map'
|
||||
import snakeCase from 'lodash.snakecase'
|
||||
import unzip from 'julien-f-unzip'
|
||||
import {PassThrough} from 'stream'
|
||||
import {promisify} from 'bluebird'
|
||||
@ -75,6 +76,8 @@ const VM_RUNNING_POWER_STATES = {
|
||||
}
|
||||
export const isVmRunning = (vm) => VM_RUNNING_POWER_STATES[vm.power_state]
|
||||
|
||||
export const isVmHvm = (vm) => Boolean(vm.HVM_boot_params)
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class Xapi extends XapiBase {
|
||||
@ -196,11 +199,11 @@ export default class Xapi extends XapiBase {
|
||||
|
||||
// =================================================================
|
||||
|
||||
async _setObjectProperties (id, props) {
|
||||
async _setObjectProperties (object, props) {
|
||||
const {
|
||||
$ref: ref,
|
||||
$type: type
|
||||
} = this.getObject(id)
|
||||
} = object
|
||||
|
||||
const namespace = getNamespaceForType(type)
|
||||
|
||||
@ -208,7 +211,7 @@ export default class Xapi extends XapiBase {
|
||||
// properties that failed to be set.
|
||||
await Promise.all(map(props, (value, name) => {
|
||||
if (value != null) {
|
||||
return this.call(`${namespace}.set_${name}`, ref, value)
|
||||
return this.call(`${namespace}.set_${snakeCase(name)}`, ref, value)
|
||||
}
|
||||
}))
|
||||
}
|
||||
@ -217,7 +220,7 @@ export default class Xapi extends XapiBase {
|
||||
name_label,
|
||||
name_description
|
||||
}) {
|
||||
await this._setObjectProperties(this.pool.$id, {
|
||||
await this._setObjectProperties(this.pool, {
|
||||
name_label,
|
||||
name_description
|
||||
})
|
||||
@ -227,7 +230,7 @@ export default class Xapi extends XapiBase {
|
||||
name_label,
|
||||
name_description
|
||||
}) {
|
||||
await this._setObjectProperties(id, {
|
||||
await this._setObjectProperties(this.getObject(id), {
|
||||
name_label,
|
||||
name_description
|
||||
})
|
||||
@ -439,12 +442,27 @@ export default class Xapi extends XapiBase {
|
||||
}
|
||||
|
||||
// TODO: clean up on error.
|
||||
async createVm (templateId, nameLabel, {
|
||||
async createVm (templateId, {
|
||||
nameDescription = undefined,
|
||||
nameLabel = undefined,
|
||||
cpus = undefined,
|
||||
installRepository = undefined,
|
||||
vdis = [],
|
||||
vifs = []
|
||||
} = {}) {
|
||||
const installMethod = (() => {
|
||||
if (installRepository == null) {
|
||||
return 'none'
|
||||
}
|
||||
|
||||
try {
|
||||
installRepository = this.getObject(installRepository)
|
||||
return 'cd'
|
||||
} catch (_) {
|
||||
return 'network'
|
||||
}
|
||||
})()
|
||||
|
||||
const template = this.getObject(templateId)
|
||||
|
||||
// Clones the template.
|
||||
@ -452,73 +470,94 @@ export default class Xapi extends XapiBase {
|
||||
await this._cloneVm(template, nameLabel)
|
||||
)
|
||||
|
||||
// Creates the VIFs.
|
||||
//
|
||||
// TODO: removes existing VIFs.
|
||||
{
|
||||
let position = 0
|
||||
await Promise.all(map(vifs, vif => this._createVif(
|
||||
vm,
|
||||
this.getObject(vif.network),
|
||||
{ position: position++ }
|
||||
)))
|
||||
}
|
||||
// TODO: copy BIOS strings?
|
||||
|
||||
// TODO: ? await this.call('VM.set_PV_args', vm.$ref, 'noninteractive')
|
||||
|
||||
// Sets the number of CPUs.
|
||||
if (cpus != null) {
|
||||
await this.call('VM.set_VCPUs_at_startup')
|
||||
}
|
||||
|
||||
// Removes any preexisting entry.
|
||||
await this.call('VM.remove_from_other_config', vm.$ref, 'disks').catch(noop)
|
||||
|
||||
// TODO: remove existing VDIs (to make sure there are only those
|
||||
// wanted).
|
||||
//
|
||||
// Registers the VDIs description for the provisioner.
|
||||
if (vdis.length) {
|
||||
const {$default_SR: defaultSr} = vm.$pool
|
||||
|
||||
const vdisXml = formatXml({
|
||||
provision: {
|
||||
disk: map(vdis, (vdi, i) => {
|
||||
// Default values:
|
||||
// - VDI type: system.
|
||||
return {$: {
|
||||
bootable: String(Boolean(vdi.bootable)),
|
||||
device: String(i),
|
||||
size: String(vdi.size),
|
||||
sr: this.getObject(vdi.sr || vdi.SR, defaultSr).uuid
|
||||
}}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: set VDI name_label & name_description.
|
||||
await this.call('VM.add_to_other_config', vm.$ref, 'disks', vdisXml)
|
||||
}
|
||||
|
||||
// Removes any preexisting entry.
|
||||
await this.call('VM.remove_from_other_config', vm.$ref, 'install-repository').catch(noop)
|
||||
|
||||
if (installRepository != null) {
|
||||
await this.call('VM.add_to_other_config', vm.$ref, 'install-repository', installRepository)
|
||||
}
|
||||
// TODO: Rewrite provision XML.
|
||||
|
||||
// Creates the VDIs and executes the initial steps of the
|
||||
// installation.
|
||||
await this.call('VM.provision', vm.$ref)
|
||||
|
||||
if (installRepository != null) {
|
||||
try {
|
||||
const cd = this.getObject(installRepository)
|
||||
// Set VMs params.
|
||||
this._setObjectProperties(vm, {
|
||||
nameDescription,
|
||||
VCPUs_at_startup: cpus
|
||||
})
|
||||
|
||||
await this._insertCdIntoVm(cd, vm)
|
||||
} catch (_) {}
|
||||
// Sets boot parameters.
|
||||
{
|
||||
const isHvm = isVmHvm(vm)
|
||||
|
||||
if (isHvm) {
|
||||
if (!vdis.length || installMethod === 'network') {
|
||||
// TODO: set boot order
|
||||
}
|
||||
} else { // PV
|
||||
if (vm.PV_bootloader === 'eliloader') {
|
||||
// Removes any preexisting entry.
|
||||
await this.call('VM.remove_from_other_config', vm.$ref, 'install-repository').catch(noop)
|
||||
|
||||
if (installMethod === 'network') {
|
||||
// TODO: normalize RHEL URL?
|
||||
|
||||
await this.call('VM.add_to_other_config', vm.$ref, 'install-repository', installRepository)
|
||||
} else if (installMethod === 'cd') {
|
||||
await this.call('VM.add_to_other_config', vm.$ref, 'install-repository', 'cdrom')
|
||||
await this._insertCdIntoVm(installRepository, vm)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: set PV args.
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the VDIs.
|
||||
//
|
||||
// TODO: set vm.suspend_SR
|
||||
{
|
||||
const {$default_SR: defaultSr} = this.pool
|
||||
let position = 0
|
||||
await Promise.all(map(vdis, (vdiDescription, i) => {
|
||||
return this._createVdi(
|
||||
this.getObject(vdiDescription.sr || vdiDescription.SR, defaultSr),
|
||||
vdiDescription.size,
|
||||
{
|
||||
name_label: vdiDescription.name_label,
|
||||
name_description: vdiDescription.name_description
|
||||
}
|
||||
)
|
||||
.then(ref => this._getOrWaitObject(ref))
|
||||
.then(vdi => this._createVbd(vm, vdi, {
|
||||
// TODO: should bootable be in the description or be
|
||||
// deduced by the position in the array (i === 0)?
|
||||
bootable: vdiDescription.bootable,
|
||||
|
||||
position: position++
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
// Destroys the VIFs cloned from the template.
|
||||
await Promise.all(map(vm.$vifs, vif => this._deleteVif(vif)))
|
||||
|
||||
// Creates the VIFs specified by the user.
|
||||
{
|
||||
let position = 0
|
||||
await Promise.all(map(vifs, vif => this._createVif(
|
||||
vm,
|
||||
this.getObject(vif.network),
|
||||
{
|
||||
position: position++,
|
||||
mac: vif.mac,
|
||||
mtu: vif.mtu
|
||||
}
|
||||
)))
|
||||
}
|
||||
|
||||
// TODO: Create Cloud config drives.
|
||||
|
||||
// TODO: Assign VGPUs.
|
||||
|
||||
return vm
|
||||
}
|
||||
|
||||
@ -636,6 +675,23 @@ export default class Xapi extends XapiBase {
|
||||
return vbdRef
|
||||
}
|
||||
|
||||
async _createVdi (sr, size, {
|
||||
name_label = '',
|
||||
name_description = undefined
|
||||
} = {}) {
|
||||
return await this.call('VDI.create', {
|
||||
name_label: name_label,
|
||||
name_description: name_description,
|
||||
other_config: {},
|
||||
read_only: false,
|
||||
sharable: false,
|
||||
SR: sr.$ref,
|
||||
type: 'user',
|
||||
virtual_size: String(size)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: check whether the VDI is attached.
|
||||
async _deleteVdi (vdiId) {
|
||||
const vdi = this.getObject(vdiId)
|
||||
|
||||
@ -680,6 +736,16 @@ export default class Xapi extends XapiBase {
|
||||
)
|
||||
}
|
||||
|
||||
async createVdi (srId, size, opts) {
|
||||
return await this._getOrWaitObject(
|
||||
await this._createVdi(this.getObject(srId), size, opts)
|
||||
)
|
||||
}
|
||||
|
||||
async deleteVdi (vdiId) {
|
||||
await this._deleteVdi(this.getObject(vdiId))
|
||||
}
|
||||
|
||||
async insertCdIntoVm (cdId, vmId, force = undefined) {
|
||||
await this._insertCdIntoVm(
|
||||
this.getObject(cdId),
|
||||
@ -725,7 +791,12 @@ export default class Xapi extends XapiBase {
|
||||
return vifRef
|
||||
}
|
||||
|
||||
async createVirtualInterface (vmId, networkId, opts = undefined) {
|
||||
// TODO: check whether the VIF was unplugged before.
|
||||
async _deleteVif (vif) {
|
||||
await this.call('VIF.destroy', vif.$ref)
|
||||
}
|
||||
|
||||
async createVif (vmId, networkId, opts = undefined) {
|
||||
return await this._getOrWaitObject(
|
||||
await this._createVif(
|
||||
this.getObject(vmId),
|
||||
@ -735,6 +806,10 @@ export default class Xapi extends XapiBase {
|
||||
)
|
||||
}
|
||||
|
||||
async deleteVif (vifId) {
|
||||
await this._deleteVif(this.getObject(vifId))
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
async _doDockerAction (vmId, action, containerId) {
|
||||
|
Loading…
Reference in New Issue
Block a user