Properly remove objects for which xo.id !== xapi.$id
.
This commit is contained in:
parent
b55accd76f
commit
5ee11c7b6b
@ -51,13 +51,13 @@ function checkSelf ({ id }, permission) {
|
||||
// ===================================================================
|
||||
|
||||
const checkAuthorizationByTypes = {
|
||||
host: or(checkSelf, checkMember('$poolId')),
|
||||
host: or(checkSelf, checkMember('$pool')),
|
||||
|
||||
message: checkMember('$object'),
|
||||
|
||||
network: or(checkSelf, checkMember('$poolId')),
|
||||
network: or(checkSelf, checkMember('$pool')),
|
||||
|
||||
SR: or(checkSelf, checkMember('$poolId')),
|
||||
SR: or(checkSelf, checkMember('$pool')),
|
||||
|
||||
task: checkMember('$host'),
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {parseSize} from '../utils'
|
||||
export async function create ({name, size, sr}) {
|
||||
const vdi = await this.getXAPI(sr).createVdi(parseSize(size), {
|
||||
name_label: name,
|
||||
sr: sr.id
|
||||
sr: sr._xapiId
|
||||
})
|
||||
return vdi.$id
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export async function register ({vm}) {
|
||||
await this.getXAPI(vm).registerDockerContainer(vm.id)
|
||||
await this.getXAPI(vm).registerDockerContainer(vm._xapiId)
|
||||
}
|
||||
register.permission = 'admin'
|
||||
|
||||
@ -16,7 +16,7 @@ register.resolve = {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function deregister ({vm}) {
|
||||
await this.getXAPI(vm).unregisterDockerContainer(vm.id)
|
||||
await this.getXAPI(vm).unregisterDockerContainer(vm._xapiId)
|
||||
}
|
||||
deregister.permission = 'admin'
|
||||
|
||||
@ -33,23 +33,23 @@ deregister.resolve = {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function start ({vm, container}) {
|
||||
await this.getXAPI(vm).startDockerContainer(vm.id, container)
|
||||
await this.getXAPI(vm).startDockerContainer(vm._xapiId, container)
|
||||
}
|
||||
|
||||
export async function stop ({vm, container}) {
|
||||
await this.getXAPI(vm).stopDockerContainer(vm.id, container)
|
||||
await this.getXAPI(vm).stopDockerContainer(vm._xapiId, container)
|
||||
}
|
||||
|
||||
export async function restart ({vm, container}) {
|
||||
await this.getXAPI(vm).restartDockerContainer(vm.id, container)
|
||||
await this.getXAPI(vm).restartDockerContainer(vm._xapiId, container)
|
||||
}
|
||||
|
||||
export async function pause ({vm, container}) {
|
||||
await this.getXAPI(vm).pauseDockerContainer(vm.id, container)
|
||||
await this.getXAPI(vm).pauseDockerContainer(vm._xapiId, container)
|
||||
}
|
||||
|
||||
export async function unpause ({vm, container}) {
|
||||
await this.getXAPI(vm).unpauseDockerContainer(vm.id, container)
|
||||
await this.getXAPI(vm).unpauseDockerContainer(vm._xapiId, container)
|
||||
}
|
||||
|
||||
for (let fn of [start, stop, restart, pause, unpause]) {
|
||||
|
@ -6,23 +6,18 @@ endsWith = require 'lodash.endswith'
|
||||
got = require('got')
|
||||
startsWith = require 'lodash.startswith'
|
||||
{coroutine: $coroutine} = require 'bluebird'
|
||||
{parseXml, promisify} = require '../utils'
|
||||
{
|
||||
extractProperty,
|
||||
parseXml,
|
||||
promisify
|
||||
} = require '../utils'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
set = $coroutine (params) ->
|
||||
{host} = params
|
||||
xapi = @getXAPI host
|
||||
set = (params) ->
|
||||
host = extractProperty(params, 'host')
|
||||
|
||||
for param, field of {
|
||||
'name_label'
|
||||
'name_description'
|
||||
}
|
||||
continue unless param of params
|
||||
|
||||
yield xapi.call "host.set_#{field}", host.ref, params[param]
|
||||
|
||||
return true
|
||||
return @getXAPI(host).setHostProperties(host._xapiId, params)
|
||||
|
||||
set.description = 'changes the properties of an host'
|
||||
|
||||
@ -43,18 +38,19 @@ exports.set = set
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
restart = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'host.disable', host.ref
|
||||
yield xapi.call 'host.reboot', host.ref
|
||||
|
||||
return true
|
||||
# FIXME: set force to false per default when correctly implemented in
|
||||
# UI.
|
||||
restart = ({host, force = true}) ->
|
||||
return @getXAPI(host).rebootHost(host._xapiId, force)
|
||||
|
||||
restart.description = 'restart the host'
|
||||
|
||||
restart.params = {
|
||||
id: { type: 'string' }
|
||||
id: { type: 'string' },
|
||||
force: {
|
||||
type: 'boolean',
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
|
||||
restart.resolve = {
|
||||
@ -65,12 +61,8 @@ exports.restart = restart
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
restartAgent = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'host.restart_agent', host.ref
|
||||
|
||||
return true
|
||||
restartAgent = ({host}) ->
|
||||
return @getXAPI(host).restartHostAgent(host._xapiId)
|
||||
|
||||
restartAgent.description = 'restart the Xen agent on the host'
|
||||
|
||||
@ -79,7 +71,7 @@ restartAgent.params = {
|
||||
}
|
||||
|
||||
restartAgent.resolve = {
|
||||
host: ['id', 'host', 'operate'],
|
||||
host: ['id', 'host', 'administrate'],
|
||||
}
|
||||
|
||||
# TODO camel case
|
||||
@ -87,12 +79,8 @@ exports.restart_agent = restartAgent
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
start = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'host.power_on', host.ref
|
||||
|
||||
return true
|
||||
start = ({host}) ->
|
||||
return @getXAPI(host).powerOnHost(host._xapiId)
|
||||
|
||||
start.description = 'start the host'
|
||||
|
||||
@ -108,13 +96,8 @@ exports.start = start
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
stop = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'host.disable', host.ref
|
||||
yield xapi.call 'host.shutdown', host.ref
|
||||
|
||||
return true
|
||||
stop = ({host}) ->
|
||||
return @getXAPI(host).shutdownHost(host._xapiId)
|
||||
|
||||
stop.description = 'stop the host'
|
||||
|
||||
@ -130,12 +113,8 @@ exports.stop = stop
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
detach = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'pool.eject', host.ref
|
||||
|
||||
return true
|
||||
detach = ({host}) ->
|
||||
return @getXAPI(host).ejectHostFromPool(host._xapiId)
|
||||
|
||||
detach.description = 'eject the host of a pool'
|
||||
|
||||
@ -151,12 +130,8 @@ exports.detach = detach
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
enable = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'host.enable', host.ref
|
||||
|
||||
return true
|
||||
enable = ({host}) ->
|
||||
return @getXAPI(host).enableHost(host._xapiId)
|
||||
|
||||
enable.description = 'enable to create VM on the host'
|
||||
|
||||
@ -172,12 +147,8 @@ exports.enable = enable
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
disable = $coroutine ({host}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
yield xapi.call 'host.disable', host.ref
|
||||
|
||||
return true
|
||||
disable = ({host}) ->
|
||||
return @getXAPI(host).disableHost(host._xapiId)
|
||||
|
||||
disable.description = 'disable to create VM on the hsot'
|
||||
|
||||
@ -193,6 +164,7 @@ exports.disable = disable
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# TODO: to test and to fix.
|
||||
createNetwork = $coroutine ({host, name, description, pif, mtu, vlan}) ->
|
||||
xapi = @getXAPI host
|
||||
|
||||
@ -208,7 +180,7 @@ createNetwork = $coroutine ({host, name, description, pif, mtu, vlan}) ->
|
||||
if pif?
|
||||
vlan = vlan ? '0'
|
||||
pif = @getObject pif, 'PIF'
|
||||
yield xapi.call 'pool.create_VLAN_from_PIF', pif.ref, network_ref, vlan
|
||||
yield xapi.call 'pool.create_VLAN_from_PIF', pif._xapiRef, network_ref, vlan
|
||||
|
||||
return true
|
||||
|
||||
@ -233,7 +205,7 @@ exports.createNetwork = createNetwork
|
||||
# Throws an error if the host is not running the latest XS version
|
||||
|
||||
listMissingPatches = ({host}) ->
|
||||
return @getXAPI(host).listMissingPoolPatchesOnHost(host.id)
|
||||
return @getXAPI(host).listMissingPoolPatchesOnHost(host._xapiId)
|
||||
|
||||
listMissingPatches.params = {
|
||||
host: { type: 'string' }
|
||||
@ -250,7 +222,7 @@ listMissingPatches.description = 'return an array of missing new patches in the
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
installPatch = ({host, patch: patchUuid}) ->
|
||||
return @getXAPI(host).installPoolPatchOnHost(patchUuid, host.id)
|
||||
return @getXAPI(host).installPoolPatchOnHost(patchUuid, host._xapiId)
|
||||
|
||||
installPatch.description = 'install a patch on an host'
|
||||
|
||||
@ -268,7 +240,7 @@ exports.installPatch = installPatch
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
installAllPatches = ({host}) ->
|
||||
return @getXAPI(host).installAllPoolPatchesOnHost(host.id)
|
||||
return @getXAPI(host).installAllPoolPatchesOnHost(host._xapiId)
|
||||
|
||||
installAllPatches.description = 'install all the missing patches on a host'
|
||||
|
||||
@ -284,9 +256,8 @@ exports.installAllPatches = installAllPatches
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
stats = $coroutine ({host, granularity}) ->
|
||||
stats = yield @getXapiHostStats(host, granularity)
|
||||
return stats
|
||||
stats = ({host, granularity}) ->
|
||||
return @getXapiHostStats(host, granularity)
|
||||
|
||||
stats.description = 'returns statistic of the host'
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
async function delete_ ({message}) {
|
||||
await this.getXAPI(message).call('message.destroy', message.ref)
|
||||
async function delete_ ({ message }) {
|
||||
await this.getXAPI(message).call('message.destroy', message._xapiRef)
|
||||
}
|
||||
export {delete_ as delete}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
async function delete_ ({PBD}) {
|
||||
// TODO: check if PBD is attached before
|
||||
await this.getXAPI(PBD).call('PBD.destroy', PBD.ref)
|
||||
await this.getXAPI(PBD).call('PBD.destroy', PBD._xapiRef)
|
||||
}
|
||||
export {delete_ as delete}
|
||||
|
||||
@ -22,7 +22,7 @@ delete_.resolve = {
|
||||
|
||||
export async function disconnect ({PBD}) {
|
||||
// TODO: check if PBD is attached before
|
||||
await this.getXAPI(PBD).call('PBD.unplug', PBD.ref)
|
||||
await this.getXAPI(PBD).call('PBD.unplug', PBD._xapiRef)
|
||||
}
|
||||
|
||||
disconnect.params = {
|
||||
@ -38,7 +38,7 @@ disconnect.resolve = {
|
||||
|
||||
export async function connect ({PBD}) {
|
||||
// TODO: check if PBD is attached before
|
||||
await this.getXAPI(PBD).call('PBD.plug', PBD.ref)
|
||||
await this.getXAPI(PBD).call('PBD.plug', PBD._xapiRef)
|
||||
}
|
||||
|
||||
connect.params = {
|
||||
|
@ -1,9 +1,11 @@
|
||||
// TODO: too low level, move into host.
|
||||
|
||||
// ===================================================================
|
||||
// Delete
|
||||
|
||||
async function delete_ ({PIF}) {
|
||||
// TODO: check if PIF is attached before
|
||||
await this.getXAPI(PIF).call('PIF.destroy', PIF.ref)
|
||||
await this.getXAPI(PIF).call('PIF.destroy', PIF._xapiRef)
|
||||
}
|
||||
export {delete_ as delete}
|
||||
|
||||
@ -20,7 +22,7 @@ delete_.resolve = {
|
||||
|
||||
export async function disconnect ({PIF}) {
|
||||
// TODO: check if PIF is attached before
|
||||
await this.getXAPI(PIF).call('PIF.unplug', PIF.ref)
|
||||
await this.getXAPI(PIF).call('PIF.unplug', PIF._xapiRef)
|
||||
}
|
||||
|
||||
disconnect.params = {
|
||||
@ -35,7 +37,7 @@ disconnect.resolve = {
|
||||
|
||||
export async function connect ({PIF}) {
|
||||
// TODO: check if PIF is attached before
|
||||
await this.getXAPI(PIF).call('PIF.plug', PIF.ref)
|
||||
await this.getXAPI(PIF).call('PIF.plug', PIF._xapiRef)
|
||||
}
|
||||
|
||||
connect.params = {
|
||||
|
@ -82,7 +82,7 @@ export {uploadPatch as patch}
|
||||
|
||||
export async function mergeInto ({ source, target, force }) {
|
||||
try {
|
||||
await this.mergeXenPools(source.id, target.id, force)
|
||||
await this.mergeXenPools(source._xapiId, target._xapiId, force)
|
||||
} catch (e) {
|
||||
// FIXME: should we expose plain XAPI error messages?
|
||||
throw new JsonRpcError(e.message)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {
|
||||
ensureArray,
|
||||
extractProperty,
|
||||
forEach,
|
||||
parseXml
|
||||
} from '../utils'
|
||||
@ -7,10 +8,9 @@ import {
|
||||
// ===================================================================
|
||||
|
||||
export async function set (params) {
|
||||
const {sr} = params
|
||||
delete params.sr
|
||||
const sr = extractProperty(params, 'sr')
|
||||
|
||||
await this.getXAPI(sr).setSrProperties(sr.id, params)
|
||||
await this.getXAPI(sr).setSrProperties(sr._xapiId, params)
|
||||
}
|
||||
|
||||
set.params = {
|
||||
@ -28,7 +28,7 @@ set.resolve = {
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function scan ({SR}) {
|
||||
await this.getXAPI(SR).call('SR.scan', SR.ref)
|
||||
await this.getXAPI(SR).call('SR.scan', SR._xapiRef)
|
||||
}
|
||||
|
||||
scan.params = {
|
||||
@ -43,7 +43,7 @@ scan.resolve = {
|
||||
|
||||
// TODO: find a way to call this "delete" and not destroy
|
||||
export async function destroy ({SR}) {
|
||||
await this.getXAPI(SR).call('SR.destroy', SR.ref)
|
||||
await this.getXAPI(SR).call('SR.destroy', SR._xapiRef)
|
||||
}
|
||||
|
||||
destroy.params = {
|
||||
@ -57,7 +57,7 @@ destroy.resolve = {
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function forget ({SR}) {
|
||||
await this.getXAPI(SR).call('SR.forget', SR.ref)
|
||||
await this.getXAPI(SR).call('SR.forget', SR._xapiRef)
|
||||
}
|
||||
|
||||
forget.params = {
|
||||
@ -87,7 +87,7 @@ export async function createIso ({
|
||||
}
|
||||
const srRef = await xapi.call(
|
||||
'SR.create',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'0', // SR size 0 because ISO
|
||||
nameLabel,
|
||||
@ -140,7 +140,7 @@ export async function createNfs ({
|
||||
|
||||
const srRef = await xapi.call(
|
||||
'SR.create',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'0',
|
||||
nameLabel,
|
||||
@ -187,7 +187,7 @@ export async function createLvm ({
|
||||
|
||||
const srRef = await xapi.call(
|
||||
'SR.create',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'0',
|
||||
nameLabel,
|
||||
@ -232,7 +232,7 @@ export async function probeNfs ({
|
||||
try {
|
||||
await xapi.call(
|
||||
'SR.probe',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'nfs',
|
||||
{}
|
||||
@ -305,7 +305,7 @@ export async function createIscsi ({
|
||||
|
||||
const srRef = await xapi.call(
|
||||
'SR.create',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'0',
|
||||
nameLabel,
|
||||
@ -369,7 +369,7 @@ export async function probeIscsiIqns ({
|
||||
try {
|
||||
await xapi.call(
|
||||
'SR.probe',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'lvmoiscsi',
|
||||
{}
|
||||
@ -447,7 +447,7 @@ export async function probeIscsiLuns ({
|
||||
try {
|
||||
await xapi.call(
|
||||
'SR.probe',
|
||||
host.ref,
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'lvmoiscsi',
|
||||
{}
|
||||
@ -521,7 +521,7 @@ export async function probeIscsiExists ({
|
||||
deviceConfig.port = port
|
||||
}
|
||||
|
||||
const xml = parseXml(await xapi.call('SR.probe', host.ref, deviceConfig, 'lvmoiscsi', {}))
|
||||
const xml = parseXml(await xapi.call('SR.probe', host._xapiRef, deviceConfig, 'lvmoiscsi', {}))
|
||||
|
||||
const srs = []
|
||||
forEach(ensureArray(xml['SRlist'].SR), sr => {
|
||||
@ -562,7 +562,7 @@ export async function probeNfsExists ({
|
||||
serverpath: serverPath
|
||||
}
|
||||
|
||||
const xml = parseXml(await xapi.call('SR.probe', host.ref, deviceConfig, 'nfs', {}))
|
||||
const xml = parseXml(await xapi.call('SR.probe', host._xapiRef, deviceConfig, 'nfs', {}))
|
||||
|
||||
const srs = []
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
export async function add ({tag, object}) {
|
||||
await this.getXAPI(object).addTag(object.id, tag)
|
||||
await this.getXAPI(object).addTag(object._xapiId, tag)
|
||||
}
|
||||
|
||||
add.description = 'add a new tag to an object'
|
||||
@ -16,7 +16,7 @@ add.params = {
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function remove ({tag, object}) {
|
||||
await this.getXAPI(object).removeTag(object.id, tag)
|
||||
await this.getXAPI(object).removeTag(object._xapiId, tag)
|
||||
}
|
||||
|
||||
remove.description = 'remove an existing tag from an object'
|
||||
|
@ -1,5 +1,5 @@
|
||||
export async function cancel ({task}) {
|
||||
await this.getXAPI(task).call('task.cancel', task.ref)
|
||||
await this.getXAPI(task).call('task.cancel', task._xapiRef)
|
||||
}
|
||||
|
||||
cancel.params = {
|
||||
@ -13,7 +13,7 @@ cancel.resolve = {
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function destroy ({task}) {
|
||||
await this.getXAPI(task).call('task.destroy', task.ref)
|
||||
await this.getXAPI(task).call('task.destroy', task._xapiRef)
|
||||
}
|
||||
|
||||
destroy.params = {
|
||||
|
@ -8,7 +8,7 @@ delete_ = $coroutine ({vbd}) ->
|
||||
xapi = @getXAPI vbd
|
||||
|
||||
# TODO: check if VBD is attached before
|
||||
yield xapi.call 'VBD.destroy', vbd.ref
|
||||
yield xapi.call 'VBD.destroy', vbd._xapiRef
|
||||
|
||||
return true
|
||||
|
||||
@ -28,7 +28,7 @@ disconnect = $coroutine ({vbd}) ->
|
||||
xapi = @getXAPI vbd
|
||||
|
||||
# TODO: check if VBD is attached before
|
||||
yield xapi.call 'VBD.unplug_force', vbd.ref
|
||||
yield xapi.call 'VBD.unplug_force', vbd._xapiRef
|
||||
|
||||
return true
|
||||
|
||||
@ -48,7 +48,7 @@ connect = $coroutine ({vbd}) ->
|
||||
xapi = @getXAPI vbd
|
||||
|
||||
# TODO: check if VBD is attached before
|
||||
yield xapi.call 'VBD.plug', vbd.ref
|
||||
yield xapi.call 'VBD.plug', vbd._xapiRef
|
||||
|
||||
return true
|
||||
|
||||
@ -68,7 +68,7 @@ set = $coroutine (params) ->
|
||||
{vbd} = params
|
||||
xapi = @getXAPI vbd
|
||||
|
||||
{ref} = vbd
|
||||
{ _xapiRef: ref } = vbd
|
||||
|
||||
# VBD position
|
||||
if 'position' of params
|
||||
|
@ -9,7 +9,7 @@ $isArray = require 'lodash.isarray'
|
||||
#=====================================================================
|
||||
|
||||
delete_ = $coroutine ({vdi}) ->
|
||||
yield @getXAPI(vdi).deleteVdi(vdi.id)
|
||||
yield @getXAPI(vdi).deleteVdi(vdi._xapiId)
|
||||
|
||||
return
|
||||
|
||||
@ -30,7 +30,7 @@ set = $coroutine (params) ->
|
||||
{vdi} = params
|
||||
xapi = @getXAPI vdi
|
||||
|
||||
{ref} = vdi
|
||||
{_xapiRef: ref} = vdi
|
||||
|
||||
# Size.
|
||||
if 'size' of params
|
||||
@ -79,7 +79,7 @@ migrate = $coroutine ({vdi, sr}) ->
|
||||
xapi = @getXAPI vdi
|
||||
|
||||
# TODO: check if VDI is attached before
|
||||
yield xapi.call 'VDI.pool_migrate', vdi.ref, sr.ref, {}
|
||||
yield xapi.call 'VDI.pool_migrate', vdi._xapiRef, sr._xapiRef, {}
|
||||
|
||||
return true
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// TODO: move into vm and rename to removeInterface
|
||||
async function delete_ ({vif}) {
|
||||
await this.getXAPI(vif).deleteVif(vif.id)
|
||||
await this.getXAPI(vif).deleteVif(vif._xapiId)
|
||||
}
|
||||
export {delete_ as delete}
|
||||
|
||||
@ -16,7 +16,7 @@ delete_.resolve = {
|
||||
// TODO: move into vm and rename to disconnectInterface
|
||||
export async function disconnect ({vif}) {
|
||||
// TODO: check if VIF is attached before
|
||||
await this.getXAPI(vif).call('VIF.unplug_force', vif.ref)
|
||||
await this.getXAPI(vif).call('VIF.unplug_force', vif._xapiRef)
|
||||
}
|
||||
|
||||
disconnect.params = {
|
||||
@ -31,7 +31,7 @@ disconnect.resolve = {
|
||||
// TODO: move into vm and rename to connectInterface
|
||||
export async function connect ({vif}) {
|
||||
// TODO: check if VIF is attached before
|
||||
await this.getXAPI(vif).call('VIF.plug', vif.ref)
|
||||
await this.getXAPI(vif).call('VIF.plug', vif._xapiRef)
|
||||
}
|
||||
|
||||
connect.params = {
|
||||
|
@ -59,7 +59,7 @@ create = $coroutine ({
|
||||
VDIs
|
||||
VIFs
|
||||
}) ->
|
||||
vm = yield @getXAPI(template).createVm(template.id, {
|
||||
vm = yield @getXAPI(template).createVm(template._xapiId, {
|
||||
installRepository: installation && installation.repository,
|
||||
nameDescription: name_description,
|
||||
nameLabel: name_label,
|
||||
@ -138,7 +138,7 @@ exports.create = create
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
delete_ = ({vm, delete_disks: deleteDisks}) ->
|
||||
return @getXAPI(vm).deleteVm(vm.id, deleteDisks)
|
||||
return @getXAPI(vm).deleteVm(vm._xapiId, deleteDisks)
|
||||
|
||||
delete_.params = {
|
||||
id: { type: 'string' }
|
||||
@ -157,7 +157,7 @@ exports.delete = delete_
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
ejectCd = $coroutine ({vm}) ->
|
||||
yield @getXAPI(vm).ejectCdFromVm(vm.id)
|
||||
yield @getXAPI(vm).ejectCdFromVm(vm._xapiId)
|
||||
return
|
||||
|
||||
ejectCd.params = {
|
||||
@ -172,7 +172,7 @@ exports.ejectCd = ejectCd
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
insertCd = $coroutine ({vm, vdi, force}) ->
|
||||
yield @getXAPI(vm).insertCdIntoVm(vdi.id, vm.id, {force})
|
||||
yield @getXAPI(vm).insertCdIntoVm(vdi._xapiId, vm._xapiId, {force})
|
||||
return
|
||||
|
||||
insertCd.params = {
|
||||
@ -190,7 +190,7 @@ exports.insertCd = insertCd
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
migrate = $coroutine ({vm, host}) ->
|
||||
yield @getXAPI(vm).migrateVm(vm.id, @getXAPI(host), host.id)
|
||||
yield @getXAPI(vm).migrateVm(vm._xapiId, @getXAPI(host), host._xapiId)
|
||||
return
|
||||
|
||||
migrate.params = {
|
||||
@ -217,10 +217,10 @@ migratePool = $coroutine ({
|
||||
network
|
||||
migrationNetwork
|
||||
}) ->
|
||||
yield @getXAPI(vm).migrateVm(vm.id, @getXAPI(host), host.id, {
|
||||
migrationNetworkId: migrationNetwork?.id
|
||||
networkId: network?.id,
|
||||
srId: sr?.id,
|
||||
yield @getXAPI(vm).migrateVm(vm._xapiId, @getXAPI(host), host._xapiId, {
|
||||
migrationNetworkId: migrationNetwork?._xapiId
|
||||
networkId: network?._xapiId,
|
||||
srId: sr?._xapiId,
|
||||
})
|
||||
return
|
||||
|
||||
@ -260,7 +260,7 @@ set = $coroutine (params) ->
|
||||
{VM} = params
|
||||
xapi = @getXAPI VM
|
||||
|
||||
{ref} = VM
|
||||
{_xapiRef: ref} = VM
|
||||
|
||||
# Memory.
|
||||
if 'memory' of params
|
||||
@ -371,9 +371,9 @@ restart = $coroutine ({vm, force}) ->
|
||||
xapi = @getXAPI(vm)
|
||||
|
||||
if force
|
||||
yield xapi.call 'VM.hard_reboot', vm.ref
|
||||
yield xapi.call 'VM.hard_reboot', vm._xapiRef
|
||||
else
|
||||
yield xapi.call 'VM.clean_reboot', vm.ref
|
||||
yield xapi.call 'VM.clean_reboot', vm._xapiRef
|
||||
|
||||
return true
|
||||
|
||||
@ -391,7 +391,7 @@ exports.restart = restart
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
clone = ({vm, name, full_copy}) ->
|
||||
return @getXAPI(vm).cloneVm(vm.ref, {
|
||||
return @getXAPI(vm).cloneVm(vm._xapiRef, {
|
||||
nameLabel: name,
|
||||
fast: not full_copy
|
||||
}).then((vm) -> vm.$id)
|
||||
@ -417,15 +417,15 @@ copy = $coroutine ({
|
||||
sr,
|
||||
vm
|
||||
}) ->
|
||||
if vm.$poolId == sr.$poolId
|
||||
if vm.$pool == sr.$pool
|
||||
if vm.power_state is 'Running'
|
||||
yield checkPermissionsForSnapshot.call(this, vm)
|
||||
|
||||
return @getXAPI(vm).copyVm(vm.id, sr.id, {
|
||||
return @getXAPI(vm).copyVm(vm._xapiId, sr._xapiId, {
|
||||
nameLabel
|
||||
}).then((vm) -> vm.$id)
|
||||
|
||||
return @getXAPI(vm).remoteCopyVm(vm.id, @getXAPI(sr), sr.id, {
|
||||
return @getXAPI(vm).remoteCopyVm(vm._xapiId, @getXAPI(sr), sr._xapiId, {
|
||||
compress,
|
||||
nameLabel
|
||||
}).then((vm) -> vm.$id)
|
||||
@ -454,7 +454,7 @@ exports.copy = copy
|
||||
|
||||
# TODO: rename convertToTemplate()
|
||||
convert = $coroutine ({vm}) ->
|
||||
yield @getXAPI(vm).call 'VM.set_is_a_template', vm.ref, true
|
||||
yield @getXAPI(vm).call 'VM.set_is_a_template', vm._xapiRef, true
|
||||
|
||||
return true
|
||||
|
||||
@ -472,7 +472,7 @@ exports.convert = convert
|
||||
snapshot = $coroutine ({vm, name}) ->
|
||||
yield checkPermissionsForSnapshot.call(this, vm)
|
||||
|
||||
snapshot = yield @getXAPI(vm).snapshotVm(vm.ref, name)
|
||||
snapshot = yield @getXAPI(vm).snapshotVm(vm._xapiRef, name)
|
||||
return snapshot.$id
|
||||
|
||||
snapshot.params = {
|
||||
@ -559,7 +559,7 @@ exports.rollingBackup = rollingBackup
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
rollingDrCopy = ({vm, pool, tag, depth}) ->
|
||||
if vm.$poolId is pool.id
|
||||
if vm.$pool is pool.id
|
||||
throw new JsonRpcError('Disaster Recovery attempts to copy on the same pool')
|
||||
return @rollingDrCopyVm({vm, sr: @getObject(pool.default_SR, 'SR'), tag, depth})
|
||||
|
||||
@ -583,7 +583,7 @@ exports.rollingDrCopy = rollingDrCopy
|
||||
|
||||
start = $coroutine ({vm}) ->
|
||||
yield @getXAPI(vm).call(
|
||||
'VM.start', vm.ref
|
||||
'VM.start', vm._xapiRef
|
||||
false # Start paused?
|
||||
false # Skips the pre-boot checks?
|
||||
)
|
||||
@ -611,12 +611,12 @@ stop = $coroutine ({vm, force}) ->
|
||||
|
||||
# Hard shutdown
|
||||
if force
|
||||
yield xapi.call 'VM.hard_shutdown', vm.ref
|
||||
yield xapi.call 'VM.hard_shutdown', vm._xapiRef
|
||||
return true
|
||||
|
||||
# Clean shutdown
|
||||
try
|
||||
yield xapi.call 'VM.clean_shutdown', vm.ref
|
||||
yield xapi.call 'VM.clean_shutdown', vm._xapiRef
|
||||
catch error
|
||||
if error.code is 'VM_MISSING_PV_DRIVERS' or error.code is 'VM_LACKS_FEATURE_SHUTDOWN'
|
||||
# TODO: Improve reporting: this message is unclear.
|
||||
@ -640,7 +640,7 @@ exports.stop = stop
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
suspend = $coroutine ({vm}) ->
|
||||
yield @getXAPI(vm).call 'VM.suspend', vm.ref
|
||||
yield @getXAPI(vm).call 'VM.suspend', vm._xapiRef
|
||||
|
||||
return true
|
||||
|
||||
@ -660,7 +660,7 @@ resume = $coroutine ({vm, force}) ->
|
||||
if not force
|
||||
force = true
|
||||
|
||||
yield @getXAPI(vm).call 'VM.resume', vm.ref, false, force
|
||||
yield @getXAPI(vm).call 'VM.resume', vm._xapiRef, false, force
|
||||
|
||||
return true
|
||||
|
||||
@ -679,7 +679,7 @@ exports.resume = resume
|
||||
# revert a snapshot to its parent VM
|
||||
revert = $coroutine ({snapshot}) ->
|
||||
# Attempts a revert from this snapshot to its parent VM
|
||||
yield @getXAPI(snapshot).call 'VM.revert', snapshot.ref
|
||||
yield @getXAPI(snapshot).call 'VM.revert', snapshot._xapiRef
|
||||
|
||||
return true
|
||||
|
||||
@ -713,7 +713,7 @@ export_ = $coroutine ({vm, compress, onlyMetadata}) ->
|
||||
if vm.power_state is 'Running'
|
||||
yield checkPermissionsForSnapshot.call(this, vm)
|
||||
|
||||
stream = yield @getXAPI(vm).exportVm(vm.id, {
|
||||
stream = yield @getXAPI(vm).exportVm(vm._xapiId, {
|
||||
compress: compress ? true,
|
||||
onlyMetadata: onlyMetadata ? false
|
||||
})
|
||||
@ -779,7 +779,7 @@ exports.import = import_
|
||||
# FIXME: if position is used, all other disks after this position
|
||||
# should be shifted.
|
||||
attachDisk = $coroutine ({vm, vdi, position, mode, bootable}) ->
|
||||
yield @getXAPI(vm).attachVdiToVm(vdi.id, vm.id, {
|
||||
yield @getXAPI(vm).attachVdiToVm(vdi._xapiId, vm._xapiId, {
|
||||
bootable,
|
||||
position,
|
||||
readOnly: mode is 'RO'
|
||||
@ -808,7 +808,7 @@ exports.attachDisk = attachDisk
|
||||
# FIXME: position should be optional and default to last.
|
||||
|
||||
createInterface = $coroutine ({vm, network, position, mtu, mac}) ->
|
||||
vif = yield @getXAPI(vm).createVif(vm.id, network.id, {
|
||||
vif = yield @getXAPI(vm).createVif(vm._xapiId, network._xapiId, {
|
||||
mac,
|
||||
mtu,
|
||||
position
|
||||
@ -835,7 +835,7 @@ exports.createInterface = createInterface
|
||||
attachPci = $coroutine ({vm, pciId}) ->
|
||||
xapi = @getXAPI vm
|
||||
|
||||
yield xapi.call 'VM.add_to_other_config', vm.ref, 'pci', pciId
|
||||
yield xapi.call 'VM.add_to_other_config', vm._xapiRef, 'pci', pciId
|
||||
|
||||
return true
|
||||
|
||||
@ -855,7 +855,7 @@ exports.attachPci = attachPci
|
||||
detachPci = $coroutine ({vm}) ->
|
||||
xapi = @getXAPI vm
|
||||
|
||||
yield xapi.call 'VM.remove_from_other_config', vm.ref, 'pci'
|
||||
yield xapi.call 'VM.remove_from_other_config', vm._xapiRef, 'pci'
|
||||
|
||||
return true
|
||||
|
||||
@ -899,7 +899,7 @@ bootOrder = $coroutine ({vm, order}) ->
|
||||
|
||||
order = {order: order}
|
||||
|
||||
yield xapi.call 'VM.set_HVM_boot_params', vm.ref, order
|
||||
yield xapi.call 'VM.set_HVM_boot_params', vm._xapiRef, order
|
||||
|
||||
return true
|
||||
|
||||
|
592
src/xapi-object-to-xo.js
Normal file
592
src/xapi-object-to-xo.js
Normal file
@ -0,0 +1,592 @@
|
||||
import isArray from 'lodash.isarray'
|
||||
|
||||
import {
|
||||
ensureArray,
|
||||
extractProperty,
|
||||
forEach,
|
||||
mapToArray,
|
||||
parseXml
|
||||
} from './utils'
|
||||
import {
|
||||
isHostRunning,
|
||||
isVmHvm,
|
||||
isVmRunning,
|
||||
parseDateTime
|
||||
} from './xapi'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const {
|
||||
defineProperties,
|
||||
freeze
|
||||
} = Object
|
||||
|
||||
function link (obj, prop, idField = '$id') {
|
||||
const dynamicValue = obj[`$${prop}`]
|
||||
if (dynamicValue == null) {
|
||||
return dynamicValue // Properly handles null and undefined.
|
||||
}
|
||||
|
||||
if (isArray(dynamicValue)) {
|
||||
return mapToArray(dynamicValue, idField)
|
||||
}
|
||||
|
||||
return dynamicValue[idField]
|
||||
}
|
||||
|
||||
// Parse a string date time to a Unix timestamp (in seconds).
|
||||
//
|
||||
// If there are no data or if the timestamp is 0, returns null.
|
||||
function toTimestamp (date) {
|
||||
if (!date) {
|
||||
return null
|
||||
}
|
||||
|
||||
const ms = parseDateTime(date).getTime()
|
||||
if (!ms) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Math.round(ms / 1000)
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const TRANSFORMS = {
|
||||
pool (obj) {
|
||||
return {
|
||||
default_SR: link(obj, 'default_SR'),
|
||||
HA_enabled: Boolean(obj.ha_enabled),
|
||||
master: link(obj, 'master'),
|
||||
tags: obj.tags,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label || obj.$master.name_label
|
||||
|
||||
// TODO
|
||||
// - ? networks = networksByPool.items[pool.id] (network.$pool.id)
|
||||
// - hosts = hostsByPool.items[pool.id] (host.$pool.$id)
|
||||
// - patches = poolPatchesByPool.items[pool.id] (poolPatch.$pool.id)
|
||||
// - SRs = srsByContainer.items[pool.id] (sr.$container.id)
|
||||
// - templates = vmTemplatesByContainer.items[pool.id] (vmTemplate.$container.$id)
|
||||
// - VMs = vmsByContainer.items[pool.id] (vm.$container.id)
|
||||
// - $running_hosts = runningHostsByPool.items[pool.id] (runningHost.$pool.id)
|
||||
// - $running_VMs = runningVmsByPool.items[pool.id] (runningHost.$pool.id)
|
||||
// - $VMs = vmsByPool.items[pool.id] (vm.$pool.id)
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
host (obj) {
|
||||
const {
|
||||
$metrics: metrics,
|
||||
other_config: otherConfig
|
||||
} = obj
|
||||
|
||||
const isRunning = isHostRunning(obj)
|
||||
|
||||
return {
|
||||
address: obj.address,
|
||||
bios_strings: obj.bios_strings,
|
||||
build: obj.software_version.build_number,
|
||||
CPUs: obj.cpu_info,
|
||||
enabled: Boolean(obj.enabled),
|
||||
current_operations: obj.current_operations,
|
||||
hostname: obj.hostname,
|
||||
iSCSI_name: otherConfig.iscsi_iqn || null,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
memory: (function () {
|
||||
if (metrics) {
|
||||
const free = +metrics.memory_free
|
||||
const total = +metrics.memory_total
|
||||
|
||||
return {
|
||||
usage: total - free,
|
||||
size: total
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
usage: 0,
|
||||
total: 0
|
||||
}
|
||||
})(),
|
||||
patches: link(obj, 'patches'),
|
||||
powerOnMode: obj.power_on_mode,
|
||||
power_state: isRunning ? 'Running' : 'Halted',
|
||||
tags: obj.tags,
|
||||
version: obj.software_version.product_version,
|
||||
|
||||
// TODO: dedupe.
|
||||
PIFs: link(obj, 'PIFs'),
|
||||
$PIFs: link(obj, 'PIFs'),
|
||||
PCIs: link(obj, 'PCIs'),
|
||||
$PCIs: link(obj, 'PCIs'),
|
||||
PGPUs: link(obj, 'PGPUs'),
|
||||
$PGPUs: link(obj, 'PGPUs'),
|
||||
|
||||
$PBDs: link(obj, 'PBDs')
|
||||
|
||||
// TODO:
|
||||
// - controller = vmControllersByContainer.items[host.id]
|
||||
// - SRs = srsByContainer.items[host.id]
|
||||
// - tasks = tasksByHost.items[host.id]
|
||||
// - templates = vmTemplatesByContainer.items[host.id]
|
||||
// - VMs = vmsByContainer.items[host.id]
|
||||
// - $vCPUs = sum(host.VMs, vm => host.CPUs.number)
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
vm (obj) {
|
||||
const {
|
||||
$guest_metrics: guestMetrics,
|
||||
$metrics: metrics,
|
||||
other_config: otherConfig
|
||||
} = obj
|
||||
|
||||
const isHvm = isVmHvm(obj)
|
||||
const isRunning = isVmRunning(obj)
|
||||
|
||||
const vm = {
|
||||
// type is redefined after for controllers/, templates &
|
||||
// snapshots.
|
||||
type: 'VM',
|
||||
|
||||
addresses: guestMetrics && guestMetrics.networks || null,
|
||||
auto_poweron: Boolean(otherConfig.auto_poweron),
|
||||
boot: obj.HVM_boot_params,
|
||||
CPUs: {
|
||||
max: +obj.VCPUs_max,
|
||||
number: (
|
||||
isRunning && metrics
|
||||
? +metrics.VCPUs_number
|
||||
: +obj.VCPUs_at_startup
|
||||
)
|
||||
},
|
||||
current_operations: obj.current_operations,
|
||||
docker: (function () {
|
||||
const monitor = otherConfig['xscontainer-monitor']
|
||||
if (!monitor) {
|
||||
return
|
||||
}
|
||||
|
||||
if (monitor === 'False') {
|
||||
return {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
docker_ps: process,
|
||||
docker_info: info,
|
||||
docker_version: version
|
||||
} = otherConfig
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
info: info && parseXml(info).docker_info,
|
||||
process: process && parseXml(process).docker_ps,
|
||||
version: version && parseXml(version).docker_version
|
||||
}
|
||||
})(),
|
||||
|
||||
// TODO: there is two possible value: "best-effort" and "restart"
|
||||
high_availability: Boolean(obj.ha_restart_priority),
|
||||
|
||||
memory: (function () {
|
||||
const dynamicMin = +obj.memory_dynamic_min
|
||||
const dynamicMax = +obj.memory_dynamic_max
|
||||
const staticMin = +obj.memory_static_min
|
||||
const staticMax = +obj.memory_static_max
|
||||
|
||||
const memory = {
|
||||
dynamic: [ dynamicMin, dynamicMax ],
|
||||
static: [ staticMin, staticMax ]
|
||||
}
|
||||
|
||||
const gmMemory = guestMetrics && guestMetrics.memory
|
||||
|
||||
if (!isRunning) {
|
||||
memory.size = dynamicMax
|
||||
} else if (gmMemory && gmMemory.used) {
|
||||
memory.usage = +gmMemory.used
|
||||
memory.size = +gmMemory.total
|
||||
} else if (metrics) {
|
||||
memory.size = +metrics.memory_actual
|
||||
} else {
|
||||
memory.size = dynamicMax
|
||||
}
|
||||
|
||||
return memory
|
||||
})(),
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
other: otherConfig,
|
||||
os_version: guestMetrics && guestMetrics.os_version || null,
|
||||
power_state: obj.power_state,
|
||||
snapshot_time: toTimestamp(obj.snapshot_time),
|
||||
snapshots: link(obj, 'snapshots'),
|
||||
tags: obj.tags,
|
||||
VIFs: link(obj, 'VIFs'),
|
||||
virtualizationMode: isHvm ? 'hvm' : 'pv',
|
||||
|
||||
// <=> Are the Xen Server tools installed?
|
||||
//
|
||||
// - undefined: unknown status
|
||||
// - false: not optimized
|
||||
// - 'out of date': optimized but drivers should be updated
|
||||
// - 'up to date': optimized
|
||||
xenTools: (() => {
|
||||
if (!isRunning || !metrics) {
|
||||
// Unknown status, returns nothing.
|
||||
return
|
||||
}
|
||||
|
||||
if (!guestMetrics) {
|
||||
return false
|
||||
}
|
||||
|
||||
const { PV_drivers_version: { major, minor } } = guestMetrics
|
||||
if (major === undefined || minor === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return guestMetrics.PV_drivers_up_to_date
|
||||
? 'up to date'
|
||||
: 'out of date'
|
||||
})(),
|
||||
|
||||
$container: (
|
||||
isRunning
|
||||
? link(obj, 'resident_on')
|
||||
: link(obj, 'pool') // TODO: handle local VMs (`VM.get_possible_hosts()`).
|
||||
),
|
||||
$VBDs: link(obj, 'VBDs'),
|
||||
|
||||
// TODO: dedupe
|
||||
VGPUs: link(obj, 'VGPUs'),
|
||||
$VGPUs: link(obj, 'VGPUs')
|
||||
}
|
||||
|
||||
if (obj.is_control_domain) {
|
||||
vm.type += '-controller'
|
||||
} else if (obj.is_a_snapshot) {
|
||||
vm.type += '-snapshot'
|
||||
|
||||
vm.$snapshot_of = link(obj, 'snapshot_of')
|
||||
} else if (obj.is_a_template) {
|
||||
vm.type += '-template'
|
||||
|
||||
vm.CPUs.number = +obj.VCPUs_at_startup
|
||||
vm.template_info = {
|
||||
arch: otherConfig['install-arch'],
|
||||
disks: (function () {
|
||||
const {disks: xml} = otherConfig
|
||||
let data
|
||||
if (!xml || !(data = parseXml(xml)).provision) {
|
||||
return []
|
||||
}
|
||||
|
||||
const disks = ensureArray(data.provision.disk)
|
||||
forEach(disks, function normalize (disk) {
|
||||
disk.bootable = disk.bootable === 'true'
|
||||
disk.size = +disk.size
|
||||
disk.SR = extractProperty(disk, 'sr')
|
||||
})
|
||||
|
||||
return disks
|
||||
})(),
|
||||
install_methods: (function () {
|
||||
const {['install-methods']: methods} = otherConfig
|
||||
|
||||
return methods ? methods.split(',') : []
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHvm) {
|
||||
vm.PV_args = obj.PV_args
|
||||
}
|
||||
|
||||
return vm
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
sr (obj) {
|
||||
return {
|
||||
type: 'SR',
|
||||
|
||||
content_type: obj.content_type,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
physical_usage: +obj.physical_utilisation,
|
||||
size: +obj.physical_size,
|
||||
SR_type: obj.type,
|
||||
tags: obj.tags,
|
||||
usage: +obj.virtual_allocation,
|
||||
VDIs: link(obj, 'VDIs'),
|
||||
|
||||
$container: (
|
||||
obj.shared
|
||||
? link(obj, 'pool')
|
||||
: obj.$PBDs[0] && link(obj.$PBDs[0], 'host')
|
||||
),
|
||||
$PBDs: link(obj, 'PBDs')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
pbd (obj) {
|
||||
return {
|
||||
type: 'PBD',
|
||||
|
||||
attached: obj.currently_attached,
|
||||
host: link(obj, 'host'),
|
||||
SR: link(obj, 'SR')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
pif (obj) {
|
||||
return {
|
||||
type: 'PIF',
|
||||
|
||||
attached: Boolean(obj.currently_attached),
|
||||
device: obj.device,
|
||||
IP: obj.IP,
|
||||
MAC: obj.MAC,
|
||||
management: Boolean(obj.management), // TODO: find a better name.
|
||||
mode: obj.ip_configuration_mode,
|
||||
MTU: +obj.MTU,
|
||||
netmask: obj.netmask,
|
||||
vlan: +obj.VLAN,
|
||||
|
||||
// TODO: What is it?
|
||||
//
|
||||
// Could it mean “is this a physical interface?”.
|
||||
// How could a PIF not be physical?
|
||||
// physical: obj.physical,
|
||||
|
||||
$host: link(obj, 'host'),
|
||||
$network: link(obj, 'network')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// TODO: should we have a VDI-snapshot type like we have with VMs?
|
||||
vdi (obj) {
|
||||
if (!obj.managed) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'VDI',
|
||||
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
size: +obj.virtual_size,
|
||||
snapshots: link(obj, 'snapshots'),
|
||||
snapshot_time: toTimestamp(obj.snapshot_time),
|
||||
tags: obj.tags,
|
||||
usage: +obj.physical_utilisation,
|
||||
|
||||
$snapshot_of: link(obj, 'snapshot_of'),
|
||||
$SR: link(obj, 'SR'),
|
||||
$VBDs: link(obj, 'VBDs')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
vbd (obj) {
|
||||
return {
|
||||
type: 'VBD',
|
||||
|
||||
attached: Boolean(obj.currently_attached),
|
||||
bootable: Boolean(obj.bootable),
|
||||
is_cd_drive: obj.type === 'CD',
|
||||
position: obj.userdevice,
|
||||
read_only: obj.mode === 'RO',
|
||||
VDI: link(obj, 'VDI'),
|
||||
VM: link(obj, 'VM')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
vif (obj) {
|
||||
return {
|
||||
type: 'VIF',
|
||||
|
||||
attached: Boolean(obj.currently_attached),
|
||||
device: obj.device, // TODO: should it be cast to a number?
|
||||
MAC: obj.MAC,
|
||||
MTU: +obj.MTU,
|
||||
|
||||
$network: link(obj, 'network'),
|
||||
$VM: link(obj, 'VM')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
network (obj) {
|
||||
return {
|
||||
bridge: obj.bridge,
|
||||
MTU: +obj.MTU,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
tags: obj.tags,
|
||||
PIFs: link(obj, 'PIFs'),
|
||||
VIFs: link(obj, 'VIFs')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
message (obj) {
|
||||
return {
|
||||
body: obj.body,
|
||||
name: obj.name,
|
||||
time: toTimestamp(obj.timestamp),
|
||||
|
||||
$object: obj.obj_uuid // Special link as it is already an UUID.
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
task (obj) {
|
||||
return {
|
||||
created: toTimestamp(obj.created),
|
||||
current_operations: obj.current_operations,
|
||||
finished: toTimestamp(obj.finished),
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
progress: +obj.progress,
|
||||
result: obj.result,
|
||||
status: obj.status,
|
||||
|
||||
$host: link(obj, 'resident_on')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
host_patch (obj) {
|
||||
return {
|
||||
applied: Boolean(obj.applied),
|
||||
time: toTimestamp(obj.timestamp_applied),
|
||||
pool_patch: link(obj, 'pool_patch', '$ref'),
|
||||
|
||||
$host: link(obj, 'host')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
pool_patch (obj) {
|
||||
return {
|
||||
id: obj.$ref,
|
||||
|
||||
applied: Boolean(obj.pool_applied),
|
||||
description: obj.name_description,
|
||||
guidance: obj.after_apply_guidance,
|
||||
name: obj.name_label,
|
||||
size: +obj.size,
|
||||
uuid: obj.uuid,
|
||||
|
||||
// TODO: what does it mean, should we handle it?
|
||||
// version: obj.version,
|
||||
|
||||
// TODO: host.[$]pool_patches ←→ pool.[$]host_patches
|
||||
$host_patches: link(obj, 'host_patches')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
pci (obj) {
|
||||
return {
|
||||
type: 'PCI',
|
||||
|
||||
class_name: obj.class_name,
|
||||
device_name: obj.device_name,
|
||||
pci_id: obj.pci_id,
|
||||
|
||||
$host: link(obj, 'host')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
pgpu (obj) {
|
||||
return {
|
||||
type: 'PGPU',
|
||||
|
||||
pci: link(obj, 'PCI'),
|
||||
|
||||
// TODO: dedupe.
|
||||
host: link(obj, 'host'),
|
||||
$host: link(obj, 'host'),
|
||||
vgpus: link(obj, 'resident_VGPUs'),
|
||||
$vgpus: link(obj, 'resident_VGPUs')
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
vgpu (obj) {
|
||||
return {
|
||||
type: 'VGPU',
|
||||
|
||||
currentlyAttached: Boolean(obj.currently_attached),
|
||||
device: obj.device,
|
||||
resident_on: link(obj, 'resident_on'),
|
||||
vm: link(obj, 'VM')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default xapiObj => {
|
||||
const transform = TRANSFORMS[xapiObj.$type.toLowerCase()]
|
||||
if (!transform) {
|
||||
return
|
||||
}
|
||||
|
||||
const xoObj = transform(xapiObj)
|
||||
if (!xoObj) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!('id' in xoObj)) {
|
||||
xoObj.id = xapiObj.$id
|
||||
}
|
||||
if (!('type' in xoObj)) {
|
||||
xoObj.type = xapiObj.$type
|
||||
}
|
||||
xoObj.$pool = xapiObj.$pool.$id
|
||||
xoObj.$poolId = xoObj.$pool // TODO: deprecated, remove when no longer used in xo-web
|
||||
|
||||
// Internal properties.
|
||||
defineProperties(xoObj, {
|
||||
_xapiId: {
|
||||
value: xapiObj.$id
|
||||
},
|
||||
_xapiRef: {
|
||||
value: xapiObj.$ref
|
||||
}
|
||||
})
|
||||
|
||||
// Freezes and returns the new object.
|
||||
return freeze(xoObj)
|
||||
}
|
@ -1,549 +0,0 @@
|
||||
import isArray from 'lodash.isarray'
|
||||
|
||||
import {
|
||||
ensureArray,
|
||||
extractProperty,
|
||||
forEach,
|
||||
mapToArray,
|
||||
parseXml
|
||||
} from './utils'
|
||||
import {
|
||||
isHostRunning,
|
||||
isVmHvm,
|
||||
isVmRunning,
|
||||
parseDateTime
|
||||
} from './xapi'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
function link (obj, prop, idField = '$id') {
|
||||
const dynamicValue = obj[`$${prop}`]
|
||||
if (dynamicValue == null) {
|
||||
return dynamicValue // Properly handles null and undefined.
|
||||
}
|
||||
|
||||
if (isArray(dynamicValue)) {
|
||||
return mapToArray(dynamicValue, idField)
|
||||
}
|
||||
|
||||
return dynamicValue[idField]
|
||||
}
|
||||
|
||||
// Parse a string date time to a Unix timestamp (in seconds).
|
||||
//
|
||||
// If there are no data or if the timestamp is 0, returns null.
|
||||
function toTimestamp (date) {
|
||||
if (!date) {
|
||||
return null
|
||||
}
|
||||
|
||||
const ms = parseDateTime(date).getTime()
|
||||
if (!ms) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Math.round(ms / 1000)
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export function pool (obj) {
|
||||
return {
|
||||
default_SR: link(obj, 'default_SR'),
|
||||
HA_enabled: Boolean(obj.ha_enabled),
|
||||
master: link(obj, 'master'),
|
||||
tags: obj.tags,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label || obj.$master.name_label
|
||||
|
||||
// TODO
|
||||
// - ? networks = networksByPool.items[pool.id] (network.$pool.id)
|
||||
// - hosts = hostsByPool.items[pool.id] (host.$pool.$id)
|
||||
// - patches = poolPatchesByPool.items[pool.id] (poolPatch.$pool.id)
|
||||
// - SRs = srsByContainer.items[pool.id] (sr.$container.id)
|
||||
// - templates = vmTemplatesByContainer.items[pool.id] (vmTemplate.$container.$id)
|
||||
// - VMs = vmsByContainer.items[pool.id] (vm.$container.id)
|
||||
// - $running_hosts = runningHostsByPool.items[pool.id] (runningHost.$pool.id)
|
||||
// - $running_VMs = runningVmsByPool.items[pool.id] (runningHost.$pool.id)
|
||||
// - $VMs = vmsByPool.items[pool.id] (vm.$pool.id)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function host (obj) {
|
||||
const {
|
||||
$metrics: metrics,
|
||||
other_config: otherConfig
|
||||
} = obj
|
||||
|
||||
const isRunning = isHostRunning(obj)
|
||||
|
||||
return {
|
||||
address: obj.address,
|
||||
bios_strings: obj.bios_strings,
|
||||
build: obj.software_version.build_number,
|
||||
CPUs: obj.cpu_info,
|
||||
enabled: Boolean(obj.enabled),
|
||||
current_operations: obj.current_operations,
|
||||
hostname: obj.hostname,
|
||||
iSCSI_name: otherConfig.iscsi_iqn || null,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
memory: (function () {
|
||||
if (metrics) {
|
||||
const free = +metrics.memory_free
|
||||
const total = +metrics.memory_total
|
||||
|
||||
return {
|
||||
usage: total - free,
|
||||
size: total
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
usage: 0,
|
||||
total: 0
|
||||
}
|
||||
})(),
|
||||
patches: link(obj, 'patches'),
|
||||
powerOnMode: obj.power_on_mode,
|
||||
power_state: isRunning ? 'Running' : 'Halted',
|
||||
tags: obj.tags,
|
||||
version: obj.software_version.product_version,
|
||||
|
||||
// TODO: dedupe.
|
||||
PIFs: link(obj, 'PIFs'),
|
||||
$PIFs: link(obj, 'PIFs'),
|
||||
PCIs: link(obj, 'PCIs'),
|
||||
$PCIs: link(obj, 'PCIs'),
|
||||
PGPUs: link(obj, 'PGPUs'),
|
||||
$PGPUs: link(obj, 'PGPUs'),
|
||||
|
||||
$PBDs: link(obj, 'PBDs')
|
||||
|
||||
// TODO:
|
||||
// - controller = vmControllersByContainer.items[host.id]
|
||||
// - SRs = srsByContainer.items[host.id]
|
||||
// - tasks = tasksByHost.items[host.id]
|
||||
// - templates = vmTemplatesByContainer.items[host.id]
|
||||
// - VMs = vmsByContainer.items[host.id]
|
||||
// - $vCPUs = sum(host.VMs, vm => host.CPUs.number)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function vm (obj) {
|
||||
const {
|
||||
$guest_metrics: guestMetrics,
|
||||
$metrics: metrics,
|
||||
other_config: otherConfig
|
||||
} = obj
|
||||
|
||||
const isHvm = isVmHvm(obj)
|
||||
const isRunning = isVmRunning(obj)
|
||||
|
||||
const vm = {
|
||||
// type is redefined after for controllers/, templates &
|
||||
// snapshots.
|
||||
type: 'VM',
|
||||
|
||||
addresses: guestMetrics && guestMetrics.networks || null,
|
||||
auto_poweron: Boolean(otherConfig.auto_poweron),
|
||||
boot: obj.HVM_boot_params,
|
||||
CPUs: {
|
||||
max: +obj.VCPUs_max,
|
||||
number: (
|
||||
isRunning && metrics
|
||||
? +metrics.VCPUs_number
|
||||
: +obj.VCPUs_at_startup
|
||||
)
|
||||
},
|
||||
current_operations: obj.current_operations,
|
||||
docker: (function () {
|
||||
const monitor = otherConfig['xscontainer-monitor']
|
||||
if (!monitor) {
|
||||
return
|
||||
}
|
||||
|
||||
if (monitor === 'False') {
|
||||
return {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
docker_ps: process,
|
||||
docker_info: info,
|
||||
docker_version: version
|
||||
} = otherConfig
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
info: info && parseXml(info).docker_info,
|
||||
process: process && parseXml(process).docker_ps,
|
||||
version: version && parseXml(version).docker_version
|
||||
}
|
||||
})(),
|
||||
|
||||
// TODO: there is two possible value: "best-effort" and "restart"
|
||||
high_availability: Boolean(obj.ha_restart_priority),
|
||||
|
||||
memory: (function () {
|
||||
const dynamicMin = +obj.memory_dynamic_min
|
||||
const dynamicMax = +obj.memory_dynamic_max
|
||||
const staticMin = +obj.memory_static_min
|
||||
const staticMax = +obj.memory_static_max
|
||||
|
||||
const memory = {
|
||||
dynamic: [ dynamicMin, dynamicMax ],
|
||||
static: [ staticMin, staticMax ]
|
||||
}
|
||||
|
||||
const gmMemory = guestMetrics && guestMetrics.memory
|
||||
|
||||
if (!isRunning) {
|
||||
memory.size = dynamicMax
|
||||
} else if (gmMemory && gmMemory.used) {
|
||||
memory.usage = +gmMemory.used
|
||||
memory.size = +gmMemory.total
|
||||
} else if (metrics) {
|
||||
memory.size = +metrics.memory_actual
|
||||
} else {
|
||||
memory.size = dynamicMax
|
||||
}
|
||||
|
||||
return memory
|
||||
})(),
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
other: otherConfig,
|
||||
os_version: guestMetrics && guestMetrics.os_version || null,
|
||||
power_state: obj.power_state,
|
||||
snapshot_time: toTimestamp(obj.snapshot_time),
|
||||
snapshots: link(obj, 'snapshots'),
|
||||
tags: obj.tags,
|
||||
VIFs: link(obj, 'VIFs'),
|
||||
virtualizationMode: isHvm ? 'hvm' : 'pv',
|
||||
|
||||
// <=> Are the Xen Server tools installed?
|
||||
//
|
||||
// - undefined: unknown status
|
||||
// - false: not optimized
|
||||
// - 'out of date': optimized but drivers should be updated
|
||||
// - 'up to date': optimized
|
||||
xenTools: (() => {
|
||||
if (!isRunning || !metrics) {
|
||||
// Unknown status, returns nothing.
|
||||
return
|
||||
}
|
||||
|
||||
if (!guestMetrics) {
|
||||
return false
|
||||
}
|
||||
|
||||
const { PV_drivers_version: { major, minor } } = guestMetrics
|
||||
if (major === undefined || minor === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return guestMetrics.PV_drivers_up_to_date
|
||||
? 'up to date'
|
||||
: 'out of date'
|
||||
})(),
|
||||
|
||||
$container: (
|
||||
isRunning
|
||||
? link(obj, 'resident_on')
|
||||
: link(obj, 'pool') // TODO: handle local VMs (`VM.get_possible_hosts()`).
|
||||
),
|
||||
$VBDs: link(obj, 'VBDs'),
|
||||
|
||||
// TODO: dedupe
|
||||
VGPUs: link(obj, 'VGPUs'),
|
||||
$VGPUs: link(obj, 'VGPUs')
|
||||
}
|
||||
|
||||
if (obj.is_control_domain) {
|
||||
vm.type += '-controller'
|
||||
} else if (obj.is_a_snapshot) {
|
||||
vm.type += '-snapshot'
|
||||
|
||||
vm.$snapshot_of = link(obj, 'snapshot_of')
|
||||
} else if (obj.is_a_template) {
|
||||
vm.type += '-template'
|
||||
|
||||
vm.CPUs.number = +obj.VCPUs_at_startup
|
||||
vm.template_info = {
|
||||
arch: otherConfig['install-arch'],
|
||||
disks: (function () {
|
||||
const {disks: xml} = otherConfig
|
||||
let data
|
||||
if (!xml || !(data = parseXml(xml)).provision) {
|
||||
return []
|
||||
}
|
||||
|
||||
const disks = ensureArray(data.provision.disk)
|
||||
forEach(disks, function normalize (disk) {
|
||||
disk.bootable = disk.bootable === 'true'
|
||||
disk.size = +disk.size
|
||||
disk.SR = extractProperty(disk, 'sr')
|
||||
})
|
||||
|
||||
return disks
|
||||
})(),
|
||||
install_methods: (function () {
|
||||
const {['install-methods']: methods} = otherConfig
|
||||
|
||||
return methods ? methods.split(',') : []
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHvm) {
|
||||
vm.PV_args = obj.PV_args
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function sr (obj) {
|
||||
return {
|
||||
type: 'SR',
|
||||
|
||||
content_type: obj.content_type,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
physical_usage: +obj.physical_utilisation,
|
||||
size: +obj.physical_size,
|
||||
SR_type: obj.type,
|
||||
tags: obj.tags,
|
||||
usage: +obj.virtual_allocation,
|
||||
VDIs: link(obj, 'VDIs'),
|
||||
|
||||
$container: (
|
||||
obj.shared
|
||||
? link(obj, 'pool')
|
||||
: obj.$PBDs[0] && link(obj.$PBDs[0], 'host')
|
||||
),
|
||||
$PBDs: link(obj, 'PBDs')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function pbd (obj) {
|
||||
return {
|
||||
type: 'PBD',
|
||||
|
||||
attached: obj.currently_attached,
|
||||
host: link(obj, 'host'),
|
||||
SR: link(obj, 'SR')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function pif (obj) {
|
||||
return {
|
||||
type: 'PIF',
|
||||
|
||||
attached: Boolean(obj.currently_attached),
|
||||
device: obj.device,
|
||||
IP: obj.IP,
|
||||
MAC: obj.MAC,
|
||||
management: Boolean(obj.management), // TODO: find a better name.
|
||||
mode: obj.ip_configuration_mode,
|
||||
MTU: +obj.MTU,
|
||||
netmask: obj.netmask,
|
||||
vlan: +obj.VLAN,
|
||||
|
||||
// TODO: What is it?
|
||||
//
|
||||
// Could it mean “is this a physical interface?”.
|
||||
// How could a PIF not be physical?
|
||||
// physical: obj.physical,
|
||||
|
||||
$host: link(obj, 'host'),
|
||||
$network: link(obj, 'network')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// TODO: should we have a VDI-snapshot type like we have with VMs?
|
||||
export function vdi (obj) {
|
||||
if (!obj.managed) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'VDI',
|
||||
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
size: +obj.virtual_size,
|
||||
snapshots: link(obj, 'snapshots'),
|
||||
snapshot_time: toTimestamp(obj.snapshot_time),
|
||||
tags: obj.tags,
|
||||
usage: +obj.physical_utilisation,
|
||||
|
||||
$snapshot_of: link(obj, 'snapshot_of'),
|
||||
$SR: link(obj, 'SR'),
|
||||
$VBDs: link(obj, 'VBDs')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function vbd (obj) {
|
||||
return {
|
||||
type: 'VBD',
|
||||
|
||||
attached: Boolean(obj.currently_attached),
|
||||
bootable: Boolean(obj.bootable),
|
||||
is_cd_drive: obj.type === 'CD',
|
||||
position: obj.userdevice,
|
||||
read_only: obj.mode === 'RO',
|
||||
VDI: link(obj, 'VDI'),
|
||||
VM: link(obj, 'VM')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function vif (obj) {
|
||||
return {
|
||||
type: 'VIF',
|
||||
|
||||
attached: Boolean(obj.currently_attached),
|
||||
device: obj.device, // TODO: should it be cast to a number?
|
||||
MAC: obj.MAC,
|
||||
MTU: +obj.MTU,
|
||||
|
||||
$network: link(obj, 'network'),
|
||||
$VM: link(obj, 'VM')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function network (obj) {
|
||||
return {
|
||||
bridge: obj.bridge,
|
||||
MTU: +obj.MTU,
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
tags: obj.tags,
|
||||
PIFs: link(obj, 'PIFs'),
|
||||
VIFs: link(obj, 'VIFs')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function message (obj) {
|
||||
return {
|
||||
body: obj.body,
|
||||
name: obj.name,
|
||||
time: toTimestamp(obj.timestamp),
|
||||
|
||||
$object: obj.obj_uuid // Special link as it is already an UUID.
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function task (obj) {
|
||||
return {
|
||||
created: toTimestamp(obj.created),
|
||||
current_operations: obj.current_operations,
|
||||
finished: toTimestamp(obj.finished),
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
progress: +obj.progress,
|
||||
result: obj.result,
|
||||
status: obj.status,
|
||||
|
||||
$host: link(obj, 'resident_on')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function host_patch (obj) {
|
||||
return {
|
||||
applied: Boolean(obj.applied),
|
||||
time: toTimestamp(obj.timestamp_applied),
|
||||
pool_patch: link(obj, 'pool_patch', '$ref'),
|
||||
|
||||
$host: link(obj, 'host')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function pool_patch (obj) {
|
||||
return {
|
||||
id: obj.$ref,
|
||||
|
||||
applied: Boolean(obj.pool_applied),
|
||||
description: obj.name_description,
|
||||
guidance: obj.after_apply_guidance,
|
||||
name: obj.name_label,
|
||||
size: +obj.size,
|
||||
uuid: obj.uuid,
|
||||
|
||||
// TODO: what does it mean, should we handle it?
|
||||
// version: obj.version,
|
||||
|
||||
// TODO: host.[$]pool_patches ←→ pool.[$]host_patches
|
||||
$host_patches: link(obj, 'host_patches')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function pci (obj) {
|
||||
return {
|
||||
type: 'PCI',
|
||||
|
||||
class_name: obj.class_name,
|
||||
device_name: obj.device_name,
|
||||
pci_id: obj.pci_id,
|
||||
|
||||
$host: link(obj, 'host')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function pgpu (obj) {
|
||||
return {
|
||||
type: 'PGPU',
|
||||
|
||||
pci: link(obj, 'PCI'),
|
||||
|
||||
// TODO: dedupe.
|
||||
host: link(obj, 'host'),
|
||||
$host: link(obj, 'host'),
|
||||
vgpus: link(obj, 'resident_VGPUs'),
|
||||
$vgpus: link(obj, 'resident_VGPUs')
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function vgpu (obj) {
|
||||
return {
|
||||
type: 'VGPU',
|
||||
|
||||
currentlyAttached: Boolean(obj.currently_attached),
|
||||
device: obj.device,
|
||||
resident_on: link(obj, 'resident_on'),
|
||||
vm: link(obj, 'VM')
|
||||
}
|
||||
}
|
66
src/xapi.js
66
src/xapi.js
@ -77,7 +77,7 @@ const getNamespaceForType = (type) => typeToNamespace[type] || type
|
||||
// ===================================================================
|
||||
|
||||
// Format a date (pseudo ISO 8601) from one XenServer get by
|
||||
// xapi.call('host.get_servertime', host.ref) for example
|
||||
// xapi.call('host.get_servertime', host.$ref) for example
|
||||
export const formatDateTime = d3TimeFormat.utcFormat('%Y%m%dT%H:%M:%SZ')
|
||||
|
||||
export const parseDateTime = formatDateTime.parse
|
||||
@ -232,6 +232,16 @@ export default class Xapi extends XapiBase {
|
||||
}))
|
||||
}
|
||||
|
||||
async setHostProperties (id, {
|
||||
name_label,
|
||||
name_description
|
||||
}) {
|
||||
await this._setObjectProperties(this.getObject(id), {
|
||||
name_label,
|
||||
name_description
|
||||
})
|
||||
}
|
||||
|
||||
async setPoolProperties ({
|
||||
name_label,
|
||||
name_description
|
||||
@ -552,6 +562,60 @@ export default class Xapi extends XapiBase {
|
||||
|
||||
// =================================================================
|
||||
|
||||
// Disable the host and evacuate all its VMs.
|
||||
//
|
||||
// If `force` is false and the evacuation failed, the host is re-
|
||||
// enabled and the error is thrown.
|
||||
async _clearHost ({ $ref, ref }, force) {
|
||||
await this.call('host.disable', ref)
|
||||
|
||||
try {
|
||||
await this.call('host.evacuate', ref)
|
||||
} catch (error) {
|
||||
if (!force) {
|
||||
await this.call('host.enabled', ref)
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async disableHost (hostId) {
|
||||
await this.call('host.disable', this.getObject(hostId).$ref)
|
||||
}
|
||||
|
||||
async ejectHostFromPool (hostId) {
|
||||
await this.call('pool.eject', this.getObject(hostId).$ref)
|
||||
}
|
||||
|
||||
async enableHost (hostId) {
|
||||
await this.call('host.enable', this.getObject(hostId).$ref)
|
||||
}
|
||||
|
||||
async powerOnHost (hostId) {
|
||||
await this.call('host.power_on', this.getObject(hostId).$ref)
|
||||
}
|
||||
|
||||
async rebootHost (hostId, force = false) {
|
||||
const host = this.getObject(hostId)
|
||||
|
||||
await this._clearHost(host, force)
|
||||
await this.call('host.reboot', host.$ref)
|
||||
}
|
||||
|
||||
async restartHostAgent (hostId) {
|
||||
await this.call('host.restart_agent', this.getObject(hostId).$ref)
|
||||
}
|
||||
|
||||
async shutdownHost (hostId, force = false) {
|
||||
const host = this.getObject(hostId)
|
||||
|
||||
await this._clearHost(host, force)
|
||||
await this.call('host.shutdown', host.$ref)
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
// Clone a VM: make a fast copy by fast copying each of its VDIs
|
||||
// (using snapshots where possible) on the same SRs.
|
||||
async _cloneVm (vm, nameLabel = vm.name_label) {
|
||||
|
129
src/xo.js
129
src/xo.js
@ -21,14 +21,13 @@ import {
|
||||
verify
|
||||
} from 'hashy'
|
||||
|
||||
import * as xapiObjectsToXo from './xapi-objects-to-xo'
|
||||
import checkAuthorization from './acl'
|
||||
import Connection from './connection'
|
||||
import LevelDbLogger from './loggers/leveldb'
|
||||
import Xapi from './xapi'
|
||||
import xapiObjectToXo from './xapi-object-to-xo'
|
||||
import XapiStats from './xapi-stats'
|
||||
import {Acls} from './models/acl'
|
||||
import {autobind} from './decorators'
|
||||
import {
|
||||
createRawObject,
|
||||
forEach,
|
||||
@ -110,7 +109,7 @@ export default class Xo extends EventEmitter {
|
||||
super()
|
||||
|
||||
this._objects = new XoCollection()
|
||||
this._objects.createIndex('byRef', new XoUniqueIndex('ref'))
|
||||
this._objects.createIndex('byRef', new XoUniqueIndex('_xapiRef'))
|
||||
|
||||
// These will be initialized in start()
|
||||
//
|
||||
@ -788,7 +787,7 @@ export default class Xo extends EventEmitter {
|
||||
const targetStream = fs.createWriteStream(pathToFile, { flags: 'wx' })
|
||||
const promise = eventToPromise(targetStream, 'finish')
|
||||
|
||||
const sourceStream = await this.getXAPI(vm).exportVm(vm.id, {
|
||||
const sourceStream = await this.getXAPI(vm).exportVm(vm._xapiId, {
|
||||
compress,
|
||||
onlyMetadata: onlyMetadata || false
|
||||
})
|
||||
@ -821,7 +820,7 @@ export default class Xo extends EventEmitter {
|
||||
|
||||
async rollingSnapshotVm (vm, tag, depth) {
|
||||
const xapi = this.getXAPI(vm)
|
||||
vm = xapi.getObject(vm.id)
|
||||
vm = xapi.getObject(vm._xapiId)
|
||||
|
||||
const reg = new RegExp('^rollingSnapshot_[^_]+_' + escapeStringRegexp(tag) + '_')
|
||||
const snapshots = sortBy(filter(vm.$snapshots, snapshot => reg.test(snapshot.name_label)), 'name_label')
|
||||
@ -842,9 +841,9 @@ export default class Xo extends EventEmitter {
|
||||
const reg = new RegExp('^' + escapeStringRegexp(`${vm.name_label}_${tag}_`) + '[0-9]{8}T[0-9]{6}Z$')
|
||||
|
||||
const targetXapi = this.getXAPI(sr)
|
||||
sr = targetXapi.getObject(sr.id)
|
||||
sr = targetXapi.getObject(sr._xapiId)
|
||||
const sourceXapi = this.getXAPI(vm)
|
||||
vm = sourceXapi.getObject(vm.id)
|
||||
vm = sourceXapi.getObject(vm._xapiId)
|
||||
|
||||
const vms = []
|
||||
forEach(sr.$VDIs, vdi => {
|
||||
@ -934,48 +933,28 @@ export default class Xo extends EventEmitter {
|
||||
return server
|
||||
}
|
||||
|
||||
@autobind
|
||||
_onXenAdd (xapiObjects) {
|
||||
_onXenAdd (xapiObjects, xapiIdsToXo) {
|
||||
const {_objects: objects} = this
|
||||
forEach(xapiObjects, (xapiObject, id) => {
|
||||
const transform = xapiObjectsToXo[xapiObject.$type]
|
||||
if (!transform) {
|
||||
return
|
||||
}
|
||||
forEach(xapiObjects, (xapiObject, xapiId) => {
|
||||
const xoObject = xapiObjectToXo(xapiObject)
|
||||
|
||||
const xoObject = transform(xapiObject)
|
||||
if (!xoObject) {
|
||||
return
|
||||
}
|
||||
if (xoObject) {
|
||||
xapiIdsToXo[xapiId] = xoObject.id
|
||||
|
||||
if (!xoObject.id) {
|
||||
xoObject.id = id
|
||||
objects.set(xoObject)
|
||||
}
|
||||
xoObject.ref = xapiObject.$ref
|
||||
if (!xoObject.type) {
|
||||
xoObject.type = xapiObject.$type
|
||||
}
|
||||
|
||||
const {$pool: pool} = xapiObject
|
||||
Object.defineProperties(xoObject, {
|
||||
poolRef: { value: pool.$ref },
|
||||
$poolId: {
|
||||
enumerable: true,
|
||||
value: pool.$id
|
||||
},
|
||||
ref: { value: xapiObject.$ref }
|
||||
})
|
||||
|
||||
objects.set(xoObject)
|
||||
})
|
||||
}
|
||||
|
||||
@autobind
|
||||
_onXenRemove (xapiObjects) {
|
||||
_onXenRemove (xapiObjects, xapiIdsToXo) {
|
||||
const {_objects: objects} = this
|
||||
forEach(xapiObjects, (_, id) => {
|
||||
if (objects.has(id)) {
|
||||
objects.remove(id)
|
||||
forEach(xapiObjects, (_, xapiId) => {
|
||||
const xoId = xapiIdsToXo[xapiId]
|
||||
|
||||
if (xoId) {
|
||||
delete xapiIdsToXo[xapiId]
|
||||
|
||||
objects.unset(xoId)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -992,16 +971,41 @@ export default class Xo extends EventEmitter {
|
||||
}
|
||||
})
|
||||
|
||||
const {objects} = xapi
|
||||
objects.on('add', this._onXenAdd)
|
||||
objects.on('update', this._onXenAdd)
|
||||
objects.on('remove', this._onXenRemove)
|
||||
xapi.xo = (() => {
|
||||
const xapiIdsToXo = createRawObject()
|
||||
const onAddOrUpdate = objects => {
|
||||
this._onXenAdd(objects, xapiIdsToXo)
|
||||
}
|
||||
const onRemove = objects => {
|
||||
this._onXenRemove(objects, xapiIdsToXo)
|
||||
}
|
||||
const onFinish = () => {
|
||||
this._xapis[xapi.pool.$id] = xapi
|
||||
}
|
||||
|
||||
// Each time objects are refreshed, registers the connection with
|
||||
// the pool identifier.
|
||||
objects.on('finish', () => {
|
||||
this._xapis[xapi.pool.$id] = xapi
|
||||
})
|
||||
const { objects } = xapi
|
||||
|
||||
return {
|
||||
install () {
|
||||
objects.on('add', onAddOrUpdate)
|
||||
objects.on('update', onAddOrUpdate)
|
||||
objects.on('remove', onRemove)
|
||||
objects.on('finish', onFinish)
|
||||
|
||||
onAddOrUpdate(objects.all)
|
||||
},
|
||||
uninstall () {
|
||||
objects.removeListener('add', onAddOrUpdate)
|
||||
objects.removeListener('update', onAddOrUpdate)
|
||||
objects.removeListener('remove', onRemove)
|
||||
objects.removeListener('finish', onFinish)
|
||||
|
||||
onRemove(objects.all)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
xapi.xo.install()
|
||||
|
||||
try {
|
||||
await xapi.connect()
|
||||
@ -1028,6 +1032,7 @@ export default class Xo extends EventEmitter {
|
||||
delete this._xapis[xapi.pool.id]
|
||||
}
|
||||
|
||||
xapi.xo.uninstall()
|
||||
return xapi.disconnect()
|
||||
}
|
||||
|
||||
@ -1037,7 +1042,7 @@ export default class Xo extends EventEmitter {
|
||||
object = this.getObject(object, type)
|
||||
}
|
||||
|
||||
const {$poolId: poolId} = object
|
||||
const { $pool: poolId } = object
|
||||
if (!poolId) {
|
||||
throw new Error(`object ${object.id} does not belong to a pool`)
|
||||
}
|
||||
@ -1052,12 +1057,12 @@ export default class Xo extends EventEmitter {
|
||||
|
||||
getXapiVmStats (vm, granularity) {
|
||||
const xapi = this.getXAPI(vm)
|
||||
return this._xapiStats.getVmPoints(xapi, vm.id, granularity)
|
||||
return this._xapiStats.getVmPoints(xapi, vm._xapiId, granularity)
|
||||
}
|
||||
|
||||
getXapiHostStats (host, granularity) {
|
||||
const xapi = this.getXAPI(host)
|
||||
return this._xapiStats.getHostPoints(xapi, host.id, granularity)
|
||||
return this._xapiStats.getHostPoints(xapi, host._xapiId, granularity)
|
||||
}
|
||||
|
||||
async mergeXenPools (sourceId, targetId, force = false) {
|
||||
@ -1069,26 +1074,12 @@ export default class Xo extends EventEmitter {
|
||||
|
||||
// We don't want the events of the source XAPI to interfere with
|
||||
// the events of the new XAPI.
|
||||
{
|
||||
const {objects} = sourceXapi
|
||||
|
||||
objects.removeListener('add', this._onXenAdd)
|
||||
objects.removeListener('update', this._onXenAdd)
|
||||
objects.removeListener('remove', this._onXenRemove)
|
||||
|
||||
this._onXenRemove(objects.all)
|
||||
}
|
||||
sourceXapi.xo.uninstall()
|
||||
|
||||
try {
|
||||
await sourceXapi.joinPool(hostname, user, password, force)
|
||||
} catch (e) {
|
||||
const {objects} = sourceXapi
|
||||
|
||||
objects.on('add', this._onXenAdd)
|
||||
objects.on('update', this._onXenAdd)
|
||||
objects.on('remove', this._onXenRemove)
|
||||
|
||||
this._onXenAdd(objects.all)
|
||||
sourceXapi.xo.install()
|
||||
|
||||
throw e
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user