Various updates.

This commit is contained in:
Julien Fontanet 2015-06-12 19:42:16 +02:00
parent cb65ddedb6
commit 923091ca62
6 changed files with 150 additions and 86 deletions

View File

@ -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",

View File

@ -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'

View File

@ -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' },

View File

@ -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}

View File

@ -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

View File

@ -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) {