feat(xo-server-netbox): add primary IPs to VMs (#5879)
See xoa-support#3812 See #5633
This commit is contained in:
parent
3338a02afb
commit
6cd93a7bb0
@ -8,6 +8,7 @@
|
|||||||
> Users must be able to say: “Nice enhancement, I'm eager to test it”
|
> 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))
|
- [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
|
### Bug fixes
|
||||||
|
|
||||||
@ -39,5 +40,6 @@
|
|||||||
- @xen-orchestra/log minor
|
- @xen-orchestra/log minor
|
||||||
- @xen-orchestra/mixins patch
|
- @xen-orchestra/mixins patch
|
||||||
- xo-server-auth-ldap patch
|
- xo-server-auth-ldap patch
|
||||||
|
- xo-server-netbox minor
|
||||||
- xo-server minor
|
- xo-server minor
|
||||||
- xo-web minor
|
- xo-web minor
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import ipaddr from 'ipaddr.js'
|
import ipaddr from 'ipaddr.js'
|
||||||
import { createLogger } from '@xen-orchestra/log'
|
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')
|
const log = createLogger('xo:netbox')
|
||||||
|
|
||||||
@ -253,14 +253,26 @@ class Netbox {
|
|||||||
|
|
||||||
// Build collections for later
|
// Build collections for later
|
||||||
const netboxVms = {} // VM UUID → Netbox VM
|
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 ipsByDeviceByVm = {} // VM UUID → (VIF device → IP)
|
||||||
|
const primaryIpsByVm = {} // VM UUID → { ipv4, ipv6 }
|
||||||
|
|
||||||
const vmsToCreate = []
|
const vmsToCreate = []
|
||||||
const vmsToUpdate = []
|
let vmsToUpdate = [] // will be reused for primary IPs
|
||||||
for (const vm of Object.values(vms)) {
|
for (const vm of Object.values(vms)) {
|
||||||
vifsByVm[vm.uuid] = vm.VIFs
|
vifsByVm[vm.uuid] = vm.VIFs
|
||||||
const vmIpsByDevice = (ipsByDeviceByVm[vm.uuid] = {})
|
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) => {
|
forEach(vm.addresses, (address, key) => {
|
||||||
const device = key.split('/')[0]
|
const device = key.split('/')[0]
|
||||||
if (vmIpsByDevice[device] === undefined) {
|
if (vmIpsByDevice[device] === undefined) {
|
||||||
@ -483,6 +495,7 @@ class Netbox {
|
|||||||
const ipsToDelete = []
|
const ipsToDelete = []
|
||||||
const ipsToCreate = []
|
const ipsToCreate = []
|
||||||
const ignoredIps = []
|
const ignoredIps = []
|
||||||
|
const netboxIpsByVif = {}
|
||||||
for (const [vmUuid, vifs] of Object.entries(vifsByVm)) {
|
for (const [vmUuid, vifs] of Object.entries(vifsByVm)) {
|
||||||
const vmIpsByDevice = ipsByDeviceByVm[vmUuid]
|
const vmIpsByDevice = ipsByDeviceByVm[vmUuid]
|
||||||
if (vmIpsByDevice === undefined) {
|
if (vmIpsByDevice === undefined) {
|
||||||
@ -495,6 +508,8 @@ class Netbox {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
netboxIpsByVif[vifId] = []
|
||||||
|
|
||||||
const interface_ = interfaces[vif.uuid]
|
const interface_ = interfaces[vif.uuid]
|
||||||
const interfaceOldIps = oldNetboxIps[interface_.id] ?? []
|
const interfaceOldIps = oldNetboxIps[interface_.id] ?? []
|
||||||
|
|
||||||
@ -508,6 +523,7 @@ class Netbox {
|
|||||||
netboxIp => ipaddr.parse(netboxIp.address.split('/')[0]).toString() === ipCompactNotation
|
netboxIp => ipaddr.parse(netboxIp.address.split('/')[0]).toString() === ipCompactNotation
|
||||||
)
|
)
|
||||||
if (netboxIpIndex >= 0) {
|
if (netboxIpIndex >= 0) {
|
||||||
|
netboxIpsByVif[vifId].push(interfaceOldIps[netboxIpIndex])
|
||||||
interfaceOldIps.splice(netboxIpIndex, 1)
|
interfaceOldIps.splice(netboxIpIndex, 1)
|
||||||
} else {
|
} else {
|
||||||
const prefix = prefixes.find(({ prefix }) => {
|
const prefix = prefixes.find(({ prefix }) => {
|
||||||
@ -524,6 +540,7 @@ class Netbox {
|
|||||||
address: `${ip}/${prefix.prefix.split('/')[1]}`,
|
address: `${ip}/${prefix.prefix.split('/')[1]}`,
|
||||||
assigned_object_type: 'virtualization.vminterface',
|
assigned_object_type: 'virtualization.vminterface',
|
||||||
assigned_object_id: interface_.id,
|
assigned_object_id: interface_.id,
|
||||||
|
vifId, // needed to populate netboxIpsByVif with newly created IPs
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -537,9 +554,61 @@ class Netbox {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
ipsToDelete.length !== 0 && this.#makeRequest('/ipam/ip-addresses/', 'DELETE', ipsToDelete),
|
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')
|
log.debug('synchronized')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user