feat(xo-server-netbox): rewrite (#6950)

Fixes #6038, Fixes #6135, Fixes #6024, Fixes #6036
See https://xcp-ng.org/forum/topic/6070
See zammad#5695
See https://xcp-ng.org/forum/topic/6149
See https://xcp-ng.org/forum/topic/6332

Complete rewrite of the plugin. Main functional changes:
- Synchronize VM description
- Fix duplicated VMs in Netbox after disconnecting one pool
- Migrating a VM from one pool to another keeps VM data added manually
- Fix largest IP prefix being picked instead of smallest
- Fix synchronization not working if some pools are unavailable
- Better error messages
This commit is contained in:
Pierre Donias
2023-07-27 10:07:26 +02:00
committed by GitHub
parent 1add3fbf9d
commit 3b1bcc67ae
8 changed files with 560 additions and 459 deletions

View File

@@ -8,6 +8,13 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [Backup/Restore] Button to open the raw log in the REST API (PR [#6936](https://github.com/vatesfr/xen-orchestra/pull/6936))
- [Netbox] New major version. BREAKING: in order for this new version to work, you need to assign the type `virtualization > vminterface` to the custom field `UUID` in your Netbox instance. [See documentation](https://xen-orchestra.com/docs/advanced.html#netbox). [#6038](https://github.com/vatesfr/xen-orchestra/issues/6038) [#6135](https://github.com/vatesfr/xen-orchestra/issues/6135) [#6024](https://github.com/vatesfr/xen-orchestra/issues/6024) [#6036](https://github.com/vatesfr/xen-orchestra/issues/6036) [Forum#6070](https://xcp-ng.org/forum/topic/6070) [Forum#6149](https://xcp-ng.org/forum/topic/6149) [Forum#6332](https://xcp-ng.org/forum/topic/6332) (PR [#6950](https://github.com/vatesfr/xen-orchestra/pull/6950))
- Synchronize VM description
- Fix duplicated VMs in Netbox after disconnecting one pool
- Migrating a VM from one pool to another keeps VM data added manually
- Fix largest IP prefix being picked instead of smallest
- Fix synchronization not working if some pools are unavailable
- Better error messages
### Bug fixes
@@ -49,6 +56,7 @@
- xo-server patch
- xo-server-transport-xmpp patch
- xo-server-audit patch
- xo-server-netbox major
- xo-web minor
<!--packages-end-->

View File

@@ -354,7 +354,7 @@ XO will try to find the right prefix for each IP address. If it can't find a pre
- Add a UUID custom field:
- Go to Other > Custom fields > Add
- Create a custom field called "uuid" (lower case!)
- Assign it to object types `virtualization > cluster` and `virtualization > virtual machine`
- Assign it to object types `virtualization > cluster`, `virtualization > virtual machine` and `virtualization > vminterface`
![](./assets/customfield.png)

View File

@@ -37,6 +37,7 @@
"devDependencies": {
"@babel/cli": "^7.13.16",
"@babel/core": "^7.14.0",
"@babel/plugin-proposal-export-default-from": "^7.18.10",
"@babel/preset-env": "^7.14.1",
"cross-env": "^7.0.3"
},

View File

@@ -0,0 +1,39 @@
const configurationSchema = {
description:
'Synchronize pools managed by Xen Orchestra with Netbox. Configuration steps: https://xen-orchestra.com/docs/advanced.html#netbox.',
type: 'object',
properties: {
endpoint: {
type: 'string',
title: 'Endpoint',
description: 'Netbox URI',
},
allowUnauthorized: {
type: 'boolean',
title: 'Unauthorized certificates',
description: 'Enable this if your Netbox instance uses a self-signed SSL certificate',
},
token: {
type: 'string',
title: 'Token',
description: 'Generate a token with write permissions from your Netbox interface',
},
pools: {
type: 'array',
title: 'Pools',
description: 'Pools to synchronize with Netbox',
items: {
type: 'string',
$type: 'pool',
},
},
syncInterval: {
type: 'number',
title: 'Interval',
description: 'Synchronization interval in hours - leave empty to disable auto-sync',
},
},
required: ['endpoint', 'token', 'pools'],
}
export { configurationSchema as default }

View File

@@ -0,0 +1,31 @@
import isEmpty from 'lodash/isEmpty'
import { compareNames } from './name-dedup'
/**
* Deeply compares 2 objects and returns an object representing the difference
* between the 2 objects. Returns undefined if the 2 objects are equal.
* In Netbox context: properly ignores differences found in names that could be
* due to name deduplication. e.g.: "foo" and "foo (2)" are considered equal.
* @param {any} newer
* @param {any} older
* @returns {Object|undefined} The patch that needs to be applied to older to get newer
*/
export default function diff(newer, older) {
if (typeof newer !== 'object') {
return newer === older ? undefined : newer
}
newer = { ...newer }
Object.keys(newer).forEach(key => {
if ((key === 'name' && compareNames(newer[key], older[key])) || diff(newer[key], older?.[key]) === undefined) {
delete newer[key]
}
})
if (isEmpty(newer)) {
return
}
return { ...newer, id: older.id }
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
import { NAME_MAX_LENGTH } from '.'
/**
* Generates the string "[name] ([index])" while also making sure it remains
* shorter than the max authorized length
* @param {string} name
* @param {number} index
* @returns {string}
*/
export function indexName(name, index) {
const suffix = ` (${index})`
return name.slice(0, NAME_MAX_LENGTH - suffix.length) + suffix
}
/**
* Compares name with the collection of usedNames and returns the next available
* name in the format "My Name (n)"
* @param {string} name
* @param {string[]} usedNames
* @returns {string}
*/
export function deduplicateName(name, usedNames) {
let index = 1
let uniqName = name
while (index < 1e3 && usedNames.includes(uniqName)) {
uniqName = indexName(name, index++, NAME_MAX_LENGTH)
}
if (index === 1e3) {
throw new Error(`Cannot deduplicate name ${name}`)
}
return uniqName
}
/**
* Checks if 2 names are identical or if their difference is only due to name
* deduplication
* @param {string} original
* @param {string} copy
* @returns {boolean}
*/
export function compareNames(original, copy) {
if (original === copy) {
return true
}
const match = copy.match(/.* \((\d+)\)$/)
return match !== null && indexName(original, match[1], NAME_MAX_LENGTH) === copy
}

View File

@@ -885,7 +885,7 @@
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
@@ -1023,13 +1023,13 @@
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/plugin-syntax-decorators" "^7.22.5"
"@babel/plugin-proposal-export-default-from@^7.0.0", "@babel/plugin-proposal-export-default-from@^7.12.13":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.22.5.tgz#825924eda1fad382c3de4db6fe1711b6fa03362f"
integrity sha512-UCe1X/hplyv6A5g2WnQ90tnHRvYL29dabCWww92lO7VdfMVTVReBTRrhiMrKQejHD9oVkdnRdwYuzUZkBVQisg==
"@babel/plugin-proposal-export-default-from@^7.0.0", "@babel/plugin-proposal-export-default-from@^7.12.13", "@babel/plugin-proposal-export-default-from@^7.18.10":
version "7.18.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz#091f4794dbce4027c03cf4ebc64d3fb96b75c206"
integrity sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-export-default-from" "^7.22.5"
"@babel/helper-plugin-utils" "^7.18.9"
"@babel/plugin-syntax-export-default-from" "^7.18.6"
"@babel/plugin-proposal-function-bind@^7.0.0", "@babel/plugin-proposal-function-bind@^7.12.13":
version "7.22.5"
@@ -1121,7 +1121,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
"@babel/plugin-syntax-export-default-from@^7.22.5":
"@babel/plugin-syntax-export-default-from@^7.18.6":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.22.5.tgz#ac3a24b362a04415a017ab96b9b4483d0e2a6e44"
integrity sha512-ODAqWWXB/yReh/jVQDag/3/tl6lgBueQkk/TcfW/59Oykm4c8a55XloX0CTk2k2VJiFWMgHby9xNX29IbCv9dQ==