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:
@@ -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-->
|
||||
|
||||
@@ -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`
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
39
packages/xo-server-netbox/src/configuration-schema.js
Normal file
39
packages/xo-server-netbox/src/configuration-schema.js
Normal 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 }
|
||||
31
packages/xo-server-netbox/src/diff.js
Normal file
31
packages/xo-server-netbox/src/diff.js
Normal 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
50
packages/xo-server-netbox/src/name-dedup.js
Normal file
50
packages/xo-server-netbox/src/name-dedup.js
Normal 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
|
||||
}
|
||||
16
yarn.lock
16
yarn.lock
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user