917 lines
25 KiB
JavaScript
917 lines
25 KiB
JavaScript
import * as sensitiveValues from './sensitive-values'
|
|
import ensureArray from './_ensureArray'
|
|
import { extractIpFromVmNetworks } from './_extractIpFromVmNetworks'
|
|
import {
|
|
extractProperty,
|
|
forEach,
|
|
isEmpty,
|
|
mapFilter,
|
|
mapToArray,
|
|
parseXml,
|
|
} from './utils'
|
|
import {
|
|
getVmDomainType,
|
|
isHostRunning,
|
|
isVmRunning,
|
|
parseDateTime,
|
|
} from './xapi'
|
|
import { useUpdateSystem } from './xapi/utils'
|
|
|
|
// ===================================================================
|
|
|
|
const ALLOCATION_BY_TYPE = {
|
|
ext: 'thin',
|
|
file: 'thin',
|
|
hba: 'thick',
|
|
iscsi: 'thick',
|
|
lvhd: 'thick',
|
|
lvhdofcoe: 'thick',
|
|
lvhdohba: 'thick',
|
|
lvhdoiscsi: 'thick',
|
|
lvm: 'thick',
|
|
lvmofcoe: 'thick',
|
|
lvmohba: 'thick',
|
|
lvmoiscsi: 'thick',
|
|
nfs: 'thin',
|
|
ocfs: 'thick',
|
|
ocfsohba: 'thick',
|
|
ocfsoiscsi: 'thick',
|
|
rawhba: 'thick',
|
|
rawiscsi: 'thick',
|
|
shm: 'thin',
|
|
smb: 'thin',
|
|
udev: 'thick',
|
|
xosan: 'thin',
|
|
zfs: 'thin',
|
|
}
|
|
|
|
// ===================================================================
|
|
|
|
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 (Array.isArray(dynamicValue)) {
|
|
return mapToArray(dynamicValue, idField)
|
|
}
|
|
|
|
return dynamicValue[idField]
|
|
}
|
|
|
|
// Parse a string date time to a Unix timestamp (in seconds).
|
|
//
|
|
// If the value is a number or can be converted as one, it is assumed
|
|
// to already be a timestamp and returned.
|
|
//
|
|
// If there are no data or if the timestamp is 0, returns null.
|
|
function toTimestamp(date) {
|
|
if (!date) {
|
|
return null
|
|
}
|
|
|
|
const timestamp = +date
|
|
|
|
// Not NaN.
|
|
// eslint-disable-next-line no-self-compare
|
|
if (timestamp === timestamp) {
|
|
return timestamp
|
|
}
|
|
|
|
const ms = parseDateTime(date, 0)
|
|
|
|
return ms === 0 ? null : Math.round(ms / 1000)
|
|
}
|
|
|
|
// https://github.com/xenserver/xenadmin/blob/093ab0bcd6c4b3dd69da7b1e63ef34bb807c1ddb/XenModel/XenAPI-Extensions/VM.cs#L773-L827
|
|
const getVmGuestToolsProps = vm => {
|
|
const { $metrics: metrics, $guest_metrics: guestMetrics } = vm
|
|
if (!isVmRunning(vm) || metrics === undefined || guestMetrics === undefined) {
|
|
return {}
|
|
}
|
|
|
|
const { major, minor } = guestMetrics.PV_drivers_version
|
|
const hasPvVersion = major !== undefined && minor !== undefined
|
|
|
|
// "PV_drivers_detected" field doesn't exist on XS < 7
|
|
const pvDriversDetected = guestMetrics.PV_drivers_detected ?? hasPvVersion
|
|
|
|
return {
|
|
// Linux VMs don't have the flag "feature-static-ip-setting"
|
|
managementAgentDetected:
|
|
hasPvVersion || guestMetrics.other['feature-static-ip-setting'] === '1',
|
|
pvDriversDetected,
|
|
pvDriversVersion: hasPvVersion ? `${major}.${minor}` : undefined,
|
|
pvDriversUpToDate: pvDriversDetected
|
|
? guestMetrics.PV_drivers_up_to_date
|
|
: undefined,
|
|
}
|
|
}
|
|
|
|
// ===================================================================
|
|
|
|
const TRANSFORMS = {
|
|
pool(obj) {
|
|
const cpuInfo = obj.cpu_info
|
|
return {
|
|
current_operations: obj.current_operations,
|
|
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,
|
|
xosanPackInstallationTime: toTimestamp(
|
|
obj.other_config.xosan_pack_installation_time
|
|
),
|
|
cpus: {
|
|
cores: cpuInfo && +cpuInfo.cpu_count,
|
|
sockets: cpuInfo && +cpuInfo.socket_count,
|
|
},
|
|
zstdSupported: obj.restrictions.restrict_zstd_export === 'false',
|
|
|
|
// 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, dependents) {
|
|
dependents[obj.metrics] = obj.$id
|
|
|
|
const {
|
|
$metrics: metrics,
|
|
other_config: otherConfig,
|
|
software_version: softwareVersion,
|
|
} = obj
|
|
|
|
const isRunning = isHostRunning(obj)
|
|
let supplementalPacks
|
|
|
|
if (useUpdateSystem(obj)) {
|
|
supplementalPacks = []
|
|
|
|
forEach(obj.$updates, update => {
|
|
const formattedUpdate = {
|
|
name: update.name_label,
|
|
description: update.name_description,
|
|
author: update.key.split('-')[3],
|
|
version: update.version,
|
|
guidance: update.after_apply_guidance,
|
|
hosts: link(update, 'hosts'),
|
|
vdi: link(update, 'vdi'),
|
|
size: update.installation_size,
|
|
}
|
|
|
|
if (update.name_label.startsWith('XS')) {
|
|
// It's a patch update but for homogeneity, we're still using pool_patches
|
|
} else {
|
|
supplementalPacks.push(formattedUpdate)
|
|
}
|
|
})
|
|
}
|
|
|
|
const cpuInfo = obj.cpu_info
|
|
|
|
return {
|
|
// Deprecated
|
|
CPUs: cpuInfo,
|
|
|
|
address: obj.address,
|
|
bios_strings: obj.bios_strings,
|
|
build: softwareVersion.build_number,
|
|
chipset_info: {
|
|
iommu:
|
|
obj.chipset_info.iommu !== undefined
|
|
? obj.chipset_info.iommu === 'true'
|
|
: undefined,
|
|
},
|
|
enabled: Boolean(obj.enabled),
|
|
cpus: {
|
|
cores: cpuInfo && +cpuInfo.cpu_count,
|
|
sockets: cpuInfo && +cpuInfo.socket_count,
|
|
},
|
|
current_operations: obj.current_operations,
|
|
hostname: obj.hostname,
|
|
iscsiIqn: obj.iscsi_iqn ?? otherConfig.iscsi_iqn ?? '',
|
|
zstdSupported: obj.license_params.restrict_zstd_export === 'false',
|
|
license_params: obj.license_params,
|
|
license_server: obj.license_server,
|
|
license_expiry: toTimestamp(obj.license_params.expiry),
|
|
logging: obj.logging,
|
|
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,
|
|
size: 0,
|
|
|
|
// Deprecated
|
|
total: 0,
|
|
}
|
|
})(),
|
|
multipathing: otherConfig.multipathing === 'true',
|
|
patches: link(obj, 'patches'),
|
|
powerOnMode: obj.power_on_mode,
|
|
power_state: metrics ? (isRunning ? 'Running' : 'Halted') : 'Unknown',
|
|
startTime: toTimestamp(otherConfig.boot_time),
|
|
supplementalPacks:
|
|
supplementalPacks ||
|
|
mapFilter(softwareVersion, (value, key) => {
|
|
let author, name
|
|
if (([author, name] = key.split(':')).length === 2) {
|
|
const [description, version] = value.split(', ')
|
|
return {
|
|
name,
|
|
description,
|
|
author,
|
|
version: version.split(' ')[1],
|
|
}
|
|
}
|
|
}),
|
|
agentStartTime: toTimestamp(otherConfig.agent_start_time),
|
|
rebootRequired:
|
|
softwareVersion.product_brand === 'XCP-ng'
|
|
? toTimestamp(otherConfig.boot_time) <
|
|
+otherConfig.rpm_patch_installation_time
|
|
: !isEmpty(obj.updates_requiring_reboot),
|
|
tags: obj.tags,
|
|
version: softwareVersion.product_version,
|
|
productBrand: softwareVersion.product_brand,
|
|
hvmCapable: obj.capabilities.some(capability =>
|
|
capability.startsWith('hvm')
|
|
),
|
|
|
|
// Only exists on XCP-ng/CH >= 8.2
|
|
certificates: obj.$certificates?.map(({ fingerprint, not_after }) => ({
|
|
fingerprint,
|
|
notAfter: toTimestamp(not_after),
|
|
})),
|
|
|
|
// 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, dependents) {
|
|
dependents[obj.guest_metrics] = obj.$id
|
|
dependents[obj.metrics] = obj.$id
|
|
|
|
const {
|
|
$guest_metrics: guestMetrics,
|
|
$metrics: metrics,
|
|
other_config: otherConfig,
|
|
} = obj
|
|
|
|
const domainType = getVmDomainType(obj)
|
|
const isHvm = domainType === 'hvm'
|
|
const isRunning = isVmRunning(obj)
|
|
const xenTools = (() => {
|
|
if (!isRunning || !metrics) {
|
|
// Unknown status, returns nothing.
|
|
return
|
|
}
|
|
|
|
if (guestMetrics === undefined) {
|
|
return false
|
|
}
|
|
|
|
const { major, minor } = guestMetrics.PV_drivers_version
|
|
|
|
if (major === undefined || minor === undefined) {
|
|
return false
|
|
}
|
|
|
|
return {
|
|
major: +major,
|
|
minor: +minor,
|
|
version: +`${major}.${minor}`,
|
|
}
|
|
})()
|
|
|
|
let resourceSet = otherConfig['xo:resource_set']
|
|
if (resourceSet) {
|
|
try {
|
|
resourceSet = JSON.parse(resourceSet)
|
|
} catch (_) {
|
|
resourceSet = undefined
|
|
}
|
|
}
|
|
|
|
// Build a { taskId → operation } map instead of forwarding the
|
|
// { taskRef → operation } map directly
|
|
const currentOperations = {}
|
|
const { $xapi } = obj
|
|
forEach(obj.current_operations, (operation, ref) => {
|
|
const task = $xapi.getObjectByRef(ref, undefined)
|
|
if (task !== undefined) {
|
|
currentOperations[task.$id] = operation
|
|
}
|
|
})
|
|
|
|
const networks = guestMetrics?.networks ?? {}
|
|
|
|
// Merge old ipv4 protocol with the new protocol
|
|
// See: https://github.com/xapi-project/xen-api/blob/324bc6ee6664dd915c0bbe57185f1d6243d9ed7e/ocaml/xapi/xapi_guest_agent.ml#L59-L81
|
|
const addresses = {}
|
|
for (const key in networks) {
|
|
const [, i] = /^(\d+)\/ip$/.exec(key) ?? []
|
|
addresses[i !== undefined ? `${i}/ipv4/0` : key] = networks[key]
|
|
}
|
|
|
|
const vm = {
|
|
// type is redefined after for controllers/, templates &
|
|
// snapshots.
|
|
type: 'VM',
|
|
|
|
addresses,
|
|
affinityHost: link(obj, 'affinity'),
|
|
auto_poweron: otherConfig.auto_poweron === 'true',
|
|
bios_strings: obj.bios_strings,
|
|
blockedOperations: obj.blocked_operations,
|
|
boot: obj.HVM_boot_params,
|
|
CPUs: {
|
|
max: +obj.VCPUs_max,
|
|
number:
|
|
isRunning && metrics && xenTools
|
|
? +metrics.VCPUs_number
|
|
: +obj.VCPUs_at_startup,
|
|
},
|
|
current_operations: currentOperations,
|
|
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,
|
|
containers: ensureArray(process && parseXml(process).docker_ps.item),
|
|
process: process && parseXml(process).docker_ps, // deprecated (only used in v4)
|
|
version: version && parseXml(version).docker_version,
|
|
}
|
|
})(),
|
|
expNestedHvm: obj.platform['exp-nested-hvm'] === 'true',
|
|
mainIpAddress: extractIpFromVmNetworks(guestMetrics?.networks),
|
|
high_availability: 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
|
|
})(),
|
|
installTime: metrics && toTimestamp(metrics.install_time),
|
|
name_description: obj.name_description,
|
|
name_label: obj.name_label,
|
|
other: otherConfig,
|
|
os_version: (guestMetrics && guestMetrics.os_version) || null,
|
|
power_state: obj.power_state,
|
|
hasVendorDevice: obj.has_vendor_device,
|
|
resourceSet,
|
|
snapshots: link(obj, 'snapshots'),
|
|
startDelay: +obj.start_delay,
|
|
startTime: metrics && toTimestamp(metrics.start_time),
|
|
tags: obj.tags,
|
|
VIFs: link(obj, 'VIFs'),
|
|
virtualizationMode: domainType,
|
|
|
|
// deprecated, use pvDriversVersion instead
|
|
xenTools,
|
|
...getVmGuestToolsProps(obj),
|
|
|
|
// TODO: handle local VMs (`VM.get_possible_hosts()`).
|
|
$container: isRunning ? link(obj, 'resident_on') : link(obj, 'pool'),
|
|
$VBDs: link(obj, 'VBDs'),
|
|
|
|
// TODO: dedupe
|
|
VGPUs: link(obj, 'VGPUs'),
|
|
$VGPUs: link(obj, 'VGPUs'),
|
|
nicType: obj.platform.nic_type,
|
|
}
|
|
|
|
if (isHvm) {
|
|
;({ vga: vm.vga = 'cirrus', videoram: vm.videoram = 4 } = obj.platform)
|
|
}
|
|
|
|
const coresPerSocket = obj.platform['cores-per-socket']
|
|
if (coresPerSocket !== undefined) {
|
|
vm.coresPerSocket = +coresPerSocket
|
|
}
|
|
|
|
if (obj.is_control_domain) {
|
|
vm.type += '-controller'
|
|
} else if (obj.is_a_snapshot) {
|
|
vm.type += '-snapshot'
|
|
|
|
vm.snapshot_time = toTimestamp(obj.snapshot_time)
|
|
vm.$snapshot_of = link(obj, 'snapshot_of')
|
|
} else if (obj.is_a_template) {
|
|
vm.type += '-template'
|
|
|
|
if (obj.other_config.default_template === 'true') {
|
|
vm.id = obj.$ref // use refs for templates as they
|
|
}
|
|
|
|
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 methods = otherConfig['install-methods']
|
|
|
|
return methods ? methods.split(',') : []
|
|
})(),
|
|
install_repository: otherConfig['install-repository'],
|
|
}
|
|
}
|
|
|
|
let tmp
|
|
if ((tmp = obj.VCPUs_params)) {
|
|
tmp.cap && (vm.cpuCap = +tmp.cap)
|
|
tmp.mask && (vm.cpuMask = tmp.mask.split(',').map(_ => +_))
|
|
tmp.weight && (vm.cpuWeight = +tmp.weight)
|
|
}
|
|
|
|
if (!isHvm) {
|
|
vm.PV_args = obj.PV_args
|
|
}
|
|
|
|
return vm
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
sr(obj) {
|
|
const srType = obj.type
|
|
return {
|
|
type: 'SR',
|
|
|
|
content_type: obj.content_type,
|
|
|
|
// TODO: Should it replace usage?
|
|
physical_usage: +obj.physical_utilisation,
|
|
|
|
allocationStrategy: ALLOCATION_BY_TYPE[srType],
|
|
current_operations: obj.current_operations,
|
|
name_description: obj.name_description,
|
|
name_label: obj.name_label,
|
|
size: +obj.physical_size,
|
|
shared: Boolean(obj.shared),
|
|
SR_type: srType,
|
|
tags: obj.tags,
|
|
usage: +obj.virtual_allocation,
|
|
VDIs: link(obj, 'VDIs'),
|
|
other_config: obj.other_config,
|
|
sm_config: obj.sm_config,
|
|
|
|
$container:
|
|
obj.shared || !obj.$PBDs[0]
|
|
? link(obj, 'pool')
|
|
: link(obj.$PBDs[0], 'host'),
|
|
$PBDs: link(obj, 'PBDs'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
pbd(obj) {
|
|
return {
|
|
type: 'PBD',
|
|
|
|
attached: Boolean(obj.currently_attached),
|
|
host: link(obj, 'host'),
|
|
SR: link(obj, 'SR'),
|
|
device_config: sensitiveValues.replace(
|
|
obj.device_config,
|
|
'* obfuscated *'
|
|
),
|
|
otherConfig: obj.other_config,
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
pif(obj) {
|
|
const metrics = obj.$metrics
|
|
|
|
return {
|
|
type: 'PIF',
|
|
|
|
attached: Boolean(obj.currently_attached),
|
|
isBondMaster: !isEmpty(obj.bond_master_of),
|
|
isBondSlave: obj.bond_slave_of !== 'OpaqueRef:NULL',
|
|
device: obj.device,
|
|
deviceName: metrics && metrics.device_name,
|
|
dns: obj.DNS,
|
|
disallowUnplug: Boolean(obj.disallow_unplug),
|
|
gateway: obj.gateway,
|
|
ip: obj.IP,
|
|
mac: obj.MAC,
|
|
management: Boolean(obj.management), // TODO: find a better name.
|
|
carrier: Boolean(metrics && metrics.carrier),
|
|
mode: obj.ip_configuration_mode,
|
|
mtu: +obj.MTU,
|
|
netmask: obj.netmask,
|
|
// A non physical PIF is a "copy" of an existing physical PIF (same device)
|
|
// A physical PIF cannot be unplugged
|
|
physical: Boolean(obj.physical),
|
|
vlan: +obj.VLAN,
|
|
speed: metrics && +metrics.speed,
|
|
$host: link(obj, 'host'),
|
|
$network: link(obj, 'network'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
vdi(obj) {
|
|
const vdi = {
|
|
type: 'VDI',
|
|
|
|
missing: obj.missing,
|
|
name_description: obj.name_description,
|
|
name_label: obj.name_label,
|
|
parent: obj.sm_config['vhd-parent'],
|
|
size: +obj.virtual_size,
|
|
snapshots: link(obj, 'snapshots'),
|
|
tags: obj.tags,
|
|
usage: +obj.physical_utilisation,
|
|
VDI_type: obj.type,
|
|
current_operations: obj.current_operations,
|
|
|
|
$SR: link(obj, 'SR'),
|
|
$VBDs: link(obj, 'VBDs'),
|
|
}
|
|
|
|
if (obj.is_a_snapshot) {
|
|
vdi.type += '-snapshot'
|
|
vdi.snapshot_time = toTimestamp(obj.snapshot_time)
|
|
vdi.$snapshot_of = link(obj, 'snapshot_of')
|
|
} else if (!obj.managed) {
|
|
vdi.type += '-unmanaged'
|
|
}
|
|
|
|
return vdi
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
vbd(obj) {
|
|
return {
|
|
type: 'VBD',
|
|
|
|
attached: Boolean(obj.currently_attached),
|
|
bootable: Boolean(obj.bootable),
|
|
device: obj.device || null,
|
|
is_cd_drive: obj.type === 'CD',
|
|
position: obj.userdevice,
|
|
read_only: obj.mode === 'RO',
|
|
VDI: link(obj, 'VDI'),
|
|
VM: link(obj, 'VM'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
vif(obj) {
|
|
const txChecksumming = obj.other_config['ethtool-tx']
|
|
return {
|
|
type: 'VIF',
|
|
|
|
allowedIpv4Addresses: obj.ipv4_allowed,
|
|
allowedIpv6Addresses: obj.ipv6_allowed,
|
|
attached: Boolean(obj.currently_attached),
|
|
device: obj.device, // TODO: should it be cast to a number?
|
|
lockingMode: obj.locking_mode,
|
|
MAC: obj.MAC,
|
|
MTU: +obj.MTU,
|
|
other_config: obj.other_config,
|
|
|
|
// See: https://xapi-project.github.io/xen-api/networking.html
|
|
txChecksumming: !(txChecksumming === 'false' || txChecksumming === 'off'),
|
|
|
|
// in kB/s
|
|
rateLimit: (() => {
|
|
if (obj.qos_algorithm_type === 'ratelimit') {
|
|
const { kbps } = obj.qos_algorithm_params
|
|
if (kbps !== undefined) {
|
|
return +kbps
|
|
}
|
|
}
|
|
})(),
|
|
|
|
$network: link(obj, 'network'),
|
|
$VM: link(obj, 'VM'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
network(obj) {
|
|
return {
|
|
automatic: obj.other_config?.automatic === 'true',
|
|
bridge: obj.bridge,
|
|
current_operations: obj.current_operations,
|
|
defaultIsLocked: obj.default_locking_mode === 'disabled',
|
|
MTU: +obj.MTU,
|
|
name_description: obj.name_description,
|
|
name_label: obj.name_label,
|
|
other_config: obj.other_config,
|
|
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 {
|
|
allowedOperations: obj.allowed_operations,
|
|
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,
|
|
xapiRef: obj.$ref,
|
|
|
|
$host: link(obj, 'resident_on'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
host_patch(obj) {
|
|
const poolPatch = obj.$pool_patch
|
|
return {
|
|
type: 'patch',
|
|
|
|
applied: Boolean(obj.applied),
|
|
enforceHomogeneity: poolPatch.pool_applied,
|
|
description: poolPatch.name_description,
|
|
name: poolPatch.name_label,
|
|
pool_patch: poolPatch.$ref,
|
|
size: +poolPatch.size,
|
|
guidance: poolPatch.after_apply_guidance,
|
|
time: toTimestamp(obj.timestamp_applied),
|
|
|
|
$host: link(obj, 'host'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
pool_patch(obj) {
|
|
return {
|
|
id: obj.$ref,
|
|
|
|
dataUuid: obj.uuid, // UUID of the patch file as stated in Citrix's XML file
|
|
description: obj.name_description,
|
|
guidance: obj.after_apply_guidance,
|
|
name: obj.name_label,
|
|
size: +obj.size,
|
|
uuid: obj.$ref,
|
|
|
|
// TODO: means that the patch must be applied on every host
|
|
// applied: Boolean(obj.pool_applied),
|
|
|
|
// 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',
|
|
|
|
dom0Access: obj.dom0_access,
|
|
enabledVgpuTypes: link(obj, 'enabled_VGPU_types'),
|
|
gpuGroup: link(obj, 'GPU_group'),
|
|
isSystemDisplayDevice: Boolean(obj.is_system_display_device),
|
|
pci: link(obj, 'PCI'),
|
|
supportedVgpuMaxCapcities: link(obj, 'supported_VGPU_max_capacities'),
|
|
supportedVgpuTypes: link(obj, 'supported_VGPU_types'),
|
|
|
|
// 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,
|
|
gpuGroup: link(obj, 'GPU_group'),
|
|
otherConfig: obj.other_config,
|
|
resident_on: link(obj, 'resident_on'),
|
|
vgpuType: link(obj, '$type'),
|
|
vm: link(obj, 'VM'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
gpu_group(obj) {
|
|
return {
|
|
type: 'gpuGroup',
|
|
|
|
allocation: obj.allocation_algorithm,
|
|
enabledVgpuTypes: link(obj, 'enabled_VGPU_types'),
|
|
gpuTypes: obj.GPU_types,
|
|
name_description: obj.name_description,
|
|
name_label: obj.name_label,
|
|
otherConfig: obj.other_config,
|
|
pgpus: link(obj, 'PGPUs'),
|
|
supportedVgpuTypes: link(obj, 'supported_VGPU_types'),
|
|
vgpus: link(obj, 'VGPUs'),
|
|
}
|
|
},
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
vgpu_type(obj) {
|
|
return {
|
|
type: 'vgpuType',
|
|
|
|
experimental: Boolean(obj.experimental),
|
|
framebufferSize: obj.framebuffer_size,
|
|
gpuGroups: link(obj, 'enabled_on_GPU_groups'),
|
|
maxHeads: obj.max_heads,
|
|
maxResolutionX: obj.max_resolution_x,
|
|
maxResolutionY: obj.max_resolution_y,
|
|
modelName: obj.model_name,
|
|
pgpus: link(obj, 'enabled_on_PGPUs'),
|
|
vendorName: obj.vendor_name,
|
|
vgpus: link(obj, 'VGPUs'),
|
|
}
|
|
},
|
|
}
|
|
|
|
// ===================================================================
|
|
|
|
export default function xapiObjectToXo(xapiObj, dependents = {}) {
|
|
const transform = TRANSFORMS[xapiObj.$type.toLowerCase()]
|
|
if (!transform) {
|
|
return
|
|
}
|
|
|
|
const xoObj = transform(xapiObj, dependents)
|
|
if (!xoObj) {
|
|
return
|
|
}
|
|
|
|
if (!('id' in xoObj)) {
|
|
xoObj.id = xapiObj.$id
|
|
}
|
|
if (!('type' in xoObj)) {
|
|
xoObj.type = xapiObj.$type
|
|
}
|
|
if ('uuid' in xapiObj && !('uuid' in xoObj)) {
|
|
xoObj.uuid = xapiObj.uuid
|
|
}
|
|
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)
|
|
}
|