feat(xo-server-netbox): add primary IPs to VMs (#5879)

See xoa-support#3812
See #5633
This commit is contained in:
Pierre Donias 2021-08-20 12:47:29 +02:00 committed by GitHub
parent 3338a02afb
commit 6cd93a7bb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 4 deletions

View File

@ -8,6 +8,7 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [New network] Ability for pool's admin to create a new network within the pool (PR [#5873](https://github.com/vatesfr/xen-orchestra/pull/5873))
- [Netbox] Synchronize primary IPv4 and IPv6 addresses [#5633](https://github.com/vatesfr/xen-orchestra/issues/5633) (PR [#5879](https://github.com/vatesfr/xen-orchestra/pull/5879))
### Bug fixes
@ -39,5 +40,6 @@
- @xen-orchestra/log minor
- @xen-orchestra/mixins patch
- xo-server-auth-ldap patch
- xo-server-netbox minor
- xo-server minor
- xo-web minor

View File

@ -1,7 +1,7 @@
import assert from 'assert'
import ipaddr from 'ipaddr.js'
import { createLogger } from '@xen-orchestra/log'
import { find, flatten, forEach, groupBy, isEmpty, keyBy, mapValues, trimEnd, zipObject } from 'lodash'
import { find, flatten, forEach, groupBy, isEmpty, keyBy, mapValues, omit, trimEnd, zipObject } from 'lodash'
const log = createLogger('xo:netbox')
@ -253,14 +253,26 @@ class Netbox {
// Build collections for later
const netboxVms = {} // VM UUID → Netbox VM
const vifsByVm = {} // VM UUID → VIF
const vifsByVm = {} // VM UUID → VIF UUID[]
const ipsByDeviceByVm = {} // VM UUID → (VIF device → IP)
const primaryIpsByVm = {} // VM UUID → { ipv4, ipv6 }
const vmsToCreate = []
const vmsToUpdate = []
let vmsToUpdate = [] // will be reused for primary IPs
for (const vm of Object.values(vms)) {
vifsByVm[vm.uuid] = vm.VIFs
const vmIpsByDevice = (ipsByDeviceByVm[vm.uuid] = {})
if (primaryIpsByVm[vm.uuid] === undefined) {
primaryIpsByVm[vm.uuid] = {}
}
if (vm.addresses['0/ipv4/0'] !== undefined) {
primaryIpsByVm[vm.uuid].ipv4 = vm.addresses['0/ipv4/0']
}
if (vm.addresses['0/ipv6/0'] !== undefined) {
primaryIpsByVm[vm.uuid].ipv6 = ipaddr.parse(vm.addresses['0/ipv6/0']).toString()
}
forEach(vm.addresses, (address, key) => {
const device = key.split('/')[0]
if (vmIpsByDevice[device] === undefined) {
@ -483,6 +495,7 @@ class Netbox {
const ipsToDelete = []
const ipsToCreate = []
const ignoredIps = []
const netboxIpsByVif = {}
for (const [vmUuid, vifs] of Object.entries(vifsByVm)) {
const vmIpsByDevice = ipsByDeviceByVm[vmUuid]
if (vmIpsByDevice === undefined) {
@ -495,6 +508,8 @@ class Netbox {
continue
}
netboxIpsByVif[vifId] = []
const interface_ = interfaces[vif.uuid]
const interfaceOldIps = oldNetboxIps[interface_.id] ?? []
@ -508,6 +523,7 @@ class Netbox {
netboxIp => ipaddr.parse(netboxIp.address.split('/')[0]).toString() === ipCompactNotation
)
if (netboxIpIndex >= 0) {
netboxIpsByVif[vifId].push(interfaceOldIps[netboxIpIndex])
interfaceOldIps.splice(netboxIpIndex, 1)
} else {
const prefix = prefixes.find(({ prefix }) => {
@ -524,6 +540,7 @@ class Netbox {
address: `${ip}/${prefix.prefix.split('/')[1]}`,
assigned_object_type: 'virtualization.vminterface',
assigned_object_id: interface_.id,
vifId, // needed to populate netboxIpsByVif with newly created IPs
})
}
}
@ -537,9 +554,61 @@ class Netbox {
await Promise.all([
ipsToDelete.length !== 0 && this.#makeRequest('/ipam/ip-addresses/', 'DELETE', ipsToDelete),
ipsToCreate.length !== 0 && this.#makeRequest('/ipam/ip-addresses/', 'POST', ipsToCreate),
ipsToCreate.length !== 0 &&
this.#makeRequest(
'/ipam/ip-addresses/',
'POST',
ipsToCreate.map(ip => omit(ip, 'vifId'))
).then(newNetboxIps => {
newNetboxIps.forEach((newNetboxIp, i) => {
const { vifId } = ipsToCreate[i]
if (netboxIpsByVif[vifId] === undefined) {
netboxIpsByVif[vifId] = []
}
netboxIpsByVif[vifId].push(newNetboxIp)
})
}),
])
// Primary IPs
vmsToUpdate = []
Object.entries(netboxVms).forEach(([vmId, netboxVm]) => {
if (netboxVm.primary_ip4 !== null && netboxVm.primary_ip6 !== null) {
return
}
const newNetboxVm = { id: netboxVm.id }
const vifs = vifsByVm[vmId]
vifs.forEach(vifId => {
const netboxIps = netboxIpsByVif[vifId]
const vmMainIps = primaryIpsByVm[vmId]
netboxIps?.forEach(netboxIp => {
const address = netboxIp.address.split('/')[0]
if (
newNetboxVm.primary_ip4 === undefined &&
address === vmMainIps.ipv4 &&
netboxVm.primary_ip4?.address !== netboxIp.address
) {
newNetboxVm.primary_ip4 = netboxIp.id
}
if (
newNetboxVm.primary_ip6 === undefined &&
address === vmMainIps.ipv6 &&
netboxVm.primary_ip6?.address !== netboxIp.address
) {
newNetboxVm.primary_ip6 = netboxIp.id
}
})
})
if (newNetboxVm.primary_ip4 !== undefined || newNetboxVm.primary_ip6 !== undefined) {
vmsToUpdate.push(newNetboxVm)
}
})
if (vmsToUpdate.length > 0) {
await this.#makeRequest('/virtualization/virtual-machines/', 'PATCH', vmsToUpdate)
}
log.debug('synchronized')
}