Compare commits
42 Commits
should-pro
...
feat_incre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4722a62a6a | ||
|
|
1f32557743 | ||
|
|
e95aae2129 | ||
|
|
9176171f20 | ||
|
|
d4f2249a4d | ||
|
|
e0b4069c17 | ||
|
|
6b25a21151 | ||
|
|
716dc45d85 | ||
|
|
57850230c8 | ||
|
|
362d597031 | ||
|
|
e89b84b37b | ||
|
|
ae6f6bf536 | ||
|
|
6f765bdd6f | ||
|
|
1982c6e6e6 | ||
|
|
527dceb43f | ||
|
|
f5a3d68d07 | ||
|
|
6c904fbc96 | ||
|
|
295036a1e3 | ||
|
|
5601d61b49 | ||
|
|
1c35c1a61a | ||
|
|
4143014466 | ||
|
|
90fea69b7e | ||
|
|
625663d619 | ||
|
|
403afc7aaf | ||
|
|
d295524c3c | ||
|
|
5eb4294e70 | ||
|
|
90598522a6 | ||
|
|
519fa1bcf8 | ||
|
|
7b0e5afe37 | ||
|
|
0b6b3a47a2 | ||
|
|
75db810508 | ||
|
|
2f52c564f5 | ||
|
|
011d582b80 | ||
|
|
32d21b2308 | ||
|
|
45971ca622 | ||
|
|
f3a09f2dad | ||
|
|
552a9c7b9f | ||
|
|
ed34d9cbc0 | ||
|
|
187ee99931 | ||
|
|
ff78dd8f7c | ||
|
|
b0eadb8ea4 | ||
|
|
a95754715a |
@@ -14,7 +14,7 @@
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
@@ -25,7 +25,7 @@
|
||||
"dependencies": {
|
||||
"@vates/multi-key-map": "^0.1.0",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"ensure-array": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"fuse-native": "^2.2.6",
|
||||
"lru-cache": "^7.14.0",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"vhd-lib": "^4.1.1"
|
||||
"vhd-lib": "^4.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vates/decorate-with": "^2.0.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"golike-defer": "^0.5.1",
|
||||
"object-hash": "^2.0.1"
|
||||
},
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"dependencies": {
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/backups": "^0.29.0",
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/backups": "^0.29.1",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"filenamify": "^4.1.0",
|
||||
"getopts": "^2.2.5",
|
||||
"lodash": "^4.17.15",
|
||||
"promise-toolbox":"^0.21.0"
|
||||
"promise-toolbox": "^0.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -27,7 +27,7 @@
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public"
|
||||
},
|
||||
"version": "0.7.8",
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"version": "0.29.0",
|
||||
"version": "0.29.1",
|
||||
"engines": {
|
||||
"node": ">=14.6"
|
||||
},
|
||||
@@ -21,13 +21,13 @@
|
||||
"@vates/cached-dns.lookup": "^1.0.0",
|
||||
"@vates/compose": "^2.1.0",
|
||||
"@vates/decorate-with": "^2.0.0",
|
||||
"@vates/disposable": "^0.1.2",
|
||||
"@vates/disposable": "^0.1.3",
|
||||
"@vates/fuse-vhd": "^1.0.0",
|
||||
"@vates/nbd-client": "*",
|
||||
"@vates/parse-duration": "^0.1.1",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"@xen-orchestra/template": "^0.1.0",
|
||||
"compare-versions": "^5.0.1",
|
||||
"d3-time-format": "^3.0.0",
|
||||
@@ -42,7 +42,7 @@
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"uuid": "^9.0.0",
|
||||
"vhd-lib": "^4.1.1",
|
||||
"vhd-lib": "^4.2.0",
|
||||
"yazl": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -52,7 +52,7 @@
|
||||
"tmp": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xen-orchestra/xapi": "^1.5.2"
|
||||
"@xen-orchestra/xapi": "^1.5.3"
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@xen-orchestra/fs",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "The File System for Xen Orchestra backups.",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/fs",
|
||||
@@ -30,7 +30,7 @@
|
||||
"@vates/decorate-with": "^2.0.0",
|
||||
"@vates/read-chunk": "^1.0.1",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"bind-property-descriptor": "^2.0.0",
|
||||
"decorator-synchronized": "^0.6.0",
|
||||
"execa": "^5.0.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@xen-orchestra/log",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"license": "ISC",
|
||||
"description": "Logging system with decoupled producers/consumer",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/log",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"engines": {
|
||||
"node": ">=15.6"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"@vates/event-listeners-manager": "^1.0.1",
|
||||
"@vates/parse-duration": "^0.1.1",
|
||||
"@xen-orchestra/emit-async": "^1.0.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"acme-client": "^5.0.0",
|
||||
"app-conf": "^2.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@xen-orchestra/proxy",
|
||||
"version": "0.26.4",
|
||||
"version": "0.26.5",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "XO Proxy used to remotely execute backup jobs",
|
||||
"keywords": [
|
||||
@@ -30,15 +30,15 @@
|
||||
"@vates/cached-dns.lookup": "^1.0.0",
|
||||
"@vates/compose": "^2.1.0",
|
||||
"@vates/decorate-with": "^2.0.0",
|
||||
"@vates/disposable": "^0.1.2",
|
||||
"@vates/disposable": "^0.1.3",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/backups": "^0.29.0",
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/backups": "^0.29.1",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"@xen-orchestra/mixin": "^0.1.0",
|
||||
"@xen-orchestra/mixins": "^0.8.1",
|
||||
"@xen-orchestra/mixins": "^0.8.2",
|
||||
"@xen-orchestra/self-signed": "^0.1.3",
|
||||
"@xen-orchestra/xapi": "^1.5.2",
|
||||
"@xen-orchestra/xapi": "^1.5.3",
|
||||
"ajv": "^8.0.3",
|
||||
"app-conf": "^2.3.0",
|
||||
"async-iterator-to-stream": "^1.1.0",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"pw": "^0.0.4",
|
||||
"xdg-basedir": "^4.0.0",
|
||||
"xo-lib": "^0.11.1",
|
||||
"xo-vmdk-to-vhd": "^2.4.3"
|
||||
"xo-vmdk-to-vhd": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xen-orchestra/xapi",
|
||||
"version": "1.5.2",
|
||||
"version": "1.5.3",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/xapi",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
@@ -23,14 +23,14 @@
|
||||
"dependencies": {
|
||||
"@vates/decorate-with": "^2.0.0",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"d3-time-format": "^3.0.0",
|
||||
"golike-defer": "^0.5.1",
|
||||
"http-request-plus": "^0.14.0",
|
||||
"json-rpc-protocol": "^0.13.2",
|
||||
"lodash": "^4.17.15",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"vhd-lib": "^4.1.1",
|
||||
"vhd-lib": "^4.2.0",
|
||||
"xo-common": "^0.8.0"
|
||||
},
|
||||
"private": false,
|
||||
|
||||
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,8 +1,58 @@
|
||||
# ChangeLog
|
||||
|
||||
## **5.77.0** (2022-11-30)
|
||||
|
||||
<img id="latest" src="https://badgen.net/badge/channel/latest/yellow" alt="Channel: latest" />
|
||||
|
||||
### Highlights
|
||||
|
||||
- [Proxies] Ability to register an existing proxy (PR [#6556](https://github.com/vatesfr/xen-orchestra/pull/6556))
|
||||
- [VM] [Warm migration](https://xen-orchestra.com/blog/warm-migration-with-xen-orchestra/) support (PRs [6549](https://github.com/vatesfr/xen-orchestra/pull/6549) & [6549](https://github.com/vatesfr/xen-orchestra/pull/6549))
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [Remotes] Prevent remote path from ending with `xo-vm-backups` as it's usually a mistake
|
||||
- [OVA export] Speed up OVA generation by 2. Generated file will be bigger (as big as uncompressed XVA) (PR [#6487](https://github.com/vatesfr/xen-orchestra/pull/6487))
|
||||
- [Settings/Users] Add `Remove` button to delete OTP of users from the admin panel [Forum#6521](https://xcp-ng.org/forum/topic/6521/remove-totp-on-a-user-account) (PR [#6541](https://github.com/vatesfr/xen-orchestra/pull/6541))
|
||||
- [Plugin/transport-nagios] XO now reports backed up VMs invidually with the VM name label used as _host_ and backup job name used as _service_
|
||||
- [VM/Advanced] Add warm migration button (PR [#6533](https://github.com/vatesfr/xen-orchestra/pull/6533))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [Dashboard/Health] Fix `Unknown SR` and `Unknown VDI` in Unhealthy VDIs (PR [#6519](https://github.com/vatesfr/xen-orchestra/pull/6519))
|
||||
- [Delta Backup] Can now recover VHD merge when failed at the begining
|
||||
- [Delta Backup] Fix `ENOENT` errors when merging a VHD directory on non-S3 remote
|
||||
- [Remote] Prevent the browser from auto-completing the encryption key field
|
||||
|
||||
### Released packages
|
||||
|
||||
- @xen-orchestra/log 0.5.0
|
||||
- @vates/disposable 0.1.3
|
||||
- @xen-orchestra/fs 3.3.0
|
||||
- vhd-lib 4.2.0
|
||||
- @xen-orchestra/audit-core 0.2.2
|
||||
- @xen-orchestra/backups 0.29.1
|
||||
- @xen-orchestra/backups-cli 1.0.0
|
||||
- @xen-orchestra/mixins 0.8.2
|
||||
- @xen-orchestra/xapi 1.5.3
|
||||
- @xen-orchestra/proxy 0.26.5
|
||||
- xo-vmdk-to-vhd 2.5.0
|
||||
- xo-cli 0.14.2
|
||||
- xo-server 5.107.1
|
||||
- xo-server-audit 0.10.2
|
||||
- xo-server-auth-ldap 0.10.6
|
||||
- xo-server-backup-reports 0.17.2
|
||||
- xo-server-load-balancer 0.7.2
|
||||
- xo-server-netbox 0.3.5
|
||||
- xo-server-sdn-controller 1.0.7
|
||||
- xo-server-transport-nagios 1.0.0
|
||||
- xo-server-usage-report 0.10.2
|
||||
- xo-server-web-hooks 0.3.2
|
||||
- xo-web 5.108.0
|
||||
|
||||
## **5.76.2** (2022-11-14)
|
||||
|
||||
<img id="latest" src="https://badgen.net/badge/channel/latest/yellow" alt="Channel: latest" />
|
||||
<img id="stable" src="https://badgen.net/badge/channel/stable/green" alt="Channel: stable" />
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -82,8 +132,6 @@
|
||||
|
||||
## **5.75.0** (2022-09-30)
|
||||
|
||||
<img id="stable" src="https://badgen.net/badge/channel/stable/green" alt="Channel: stable" />
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [Backup/Restore file] Implement File level restore for s3 and encrypted backups (PR [#6409](https://github.com/vatesfr/xen-orchestra/pull/6409))
|
||||
|
||||
@@ -7,20 +7,10 @@
|
||||
|
||||
> Users must be able to say: “Nice enhancement, I'm eager to test it”
|
||||
|
||||
- [Remotes] Prevent remote path from ending with `xo-vm-backups` as it's usually a mistake
|
||||
- [OVA export] Speed up OVA generation by 2. Generated file will be bigger (as big as uncompressed XVA) (PR [#6487](https://github.com/vatesfr/xen-orchestra/pull/6487))
|
||||
- [Settings/Users] Add `Remove` button to delete OTP of users from the admin panel [Forum#6521](https://xcp-ng.org/forum/topic/6521/remove-totp-on-a-user-account) (PR [#6541](https://github.com/vatesfr/xen-orchestra/pull/6541))
|
||||
- [Plugin/transport-nagios] XO now reports beckup VMs invidually with the VM name label used as *host* and backup job name used as *service*
|
||||
|
||||
### Bug fixes
|
||||
|
||||
> Users must be able to say: “I had this issue, happy to know it's fixed”
|
||||
|
||||
- [Dashboard/Health] Fix `Unknown SR` and `Unknown VDI` in Unhealthy VDIs (PR [#6519](https://github.com/vatesfr/xen-orchestra/pull/6519))
|
||||
- [Delta Backup] Can now recover VHD merge when failed at the begining
|
||||
- [Delta Backup] Fix `ENOENT` errors when merging a VHD directory on non-S3 remote
|
||||
- [Remote] Prevent the browser from auto-completing the encryption key field
|
||||
|
||||
### Packages to release
|
||||
|
||||
> When modifying a package, add it here with its release type.
|
||||
@@ -36,15 +26,4 @@
|
||||
> Keep this list alphabetically ordered to avoid merge conflicts
|
||||
|
||||
<!--packages-start-->
|
||||
|
||||
- @xen-orchestra/backups-cli major
|
||||
- @xen-orchestra/fs minor
|
||||
- @xen-orchestra/log minor
|
||||
- vhd-lib minor
|
||||
- xo-cli patch
|
||||
- xo-server minor
|
||||
- xo-server-transport-nagios major
|
||||
- xo-vmdk-to-vhd minor
|
||||
- xo-web minor
|
||||
|
||||
<!--packages-end-->
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/eslint-parser": "^7.13.8",
|
||||
"@babel/register": "^7.0.0",
|
||||
"@vates/async-each": "1.0.0",
|
||||
"@vates/async-each": "^1.0.0",
|
||||
"babel-jest": "^29.0.3",
|
||||
"benchmark": "^2.1.4",
|
||||
"deptree": "^1.0.0",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
@@ -84,7 +83,7 @@
|
||||
"build": "scripts/run-script.js --parallel --concurrency 2 build",
|
||||
"ci": "yarn && yarn build && yarn test-lint && yarn test-integration",
|
||||
"clean": "scripts/run-script.js --parallel clean",
|
||||
"dev": "scripts/run-script.js --parallel dev",
|
||||
"dev": "scripts/run-script.js --parallel --concurrency 0 --verbose dev",
|
||||
"dev-test": "jest --bail --watch \"^(?!.*\\.integ\\.spec\\.js$)\"",
|
||||
"docs:dev": "vuepress dev docs",
|
||||
"docs:build": "vuepress build docs",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"node": ">=10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"cli-progress": "^3.1.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"getopts": "^2.2.3",
|
||||
@@ -31,7 +31,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"uuid": "^9.0.0",
|
||||
"vhd-lib": "^4.1.1"
|
||||
"vhd-lib": "^4.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish"
|
||||
|
||||
@@ -8,7 +8,7 @@ const tmp = require('tmp')
|
||||
const { getSyncedHandler } = require('@xen-orchestra/fs')
|
||||
const { pFromCallback, Disposable } = require('promise-toolbox')
|
||||
|
||||
const { VhdFile, chainVhd, openVhd } = require('./index')
|
||||
const { VhdFile, chainVhd, openVhd, VhdAbstract } = require('./index')
|
||||
const { mergeVhdChain } = require('./merge')
|
||||
|
||||
const { checkFile, createRandomFile, convertFromRawToVhd } = require('./tests/utils')
|
||||
@@ -163,25 +163,29 @@ test('it can resume a simple merge ', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('it can resume a failed renaming ', async () => {
|
||||
test('it can resume a failed renaming', async () => {
|
||||
const mbOfFather = 8
|
||||
const mbOfChildren = 4
|
||||
const parentRandomFileName = `${tempDir}/randomfile`
|
||||
|
||||
const parentName = 'parentvhd.alias.vhd'
|
||||
const childName = 'childvhd.alias.vhd'
|
||||
|
||||
await createRandomFile(`${tempDir}/randomfile`, mbOfFather)
|
||||
await convertFromRawToVhd(`${tempDir}/randomfile`, `${tempDir}/parent.vhd`)
|
||||
const parentVhd = new VhdFile(handler, 'parent.vhd')
|
||||
await convertFromRawToVhd(`${tempDir}/randomfile`, `${tempDir}/parentdata.vhd`)
|
||||
VhdAbstract.createAlias(handler, parentName, 'parentdata.vhd')
|
||||
const parentVhd = new VhdFile(handler, 'parentdata.vhd')
|
||||
await parentVhd.readHeaderAndFooter()
|
||||
|
||||
await createRandomFile(`${tempDir}/small_randomfile`, mbOfChildren)
|
||||
await convertFromRawToVhd(`${tempDir}/small_randomfile`, `${tempDir}/child1.vhd`)
|
||||
await chainVhd(handler, 'parent.vhd', handler, 'child1.vhd', true)
|
||||
|
||||
const childVhd = new VhdFile(handler, 'child1.vhd')
|
||||
await convertFromRawToVhd(`${tempDir}/small_randomfile`, `${tempDir}/childdata.vhd`)
|
||||
await chainVhd(handler, 'parentdata.vhd', handler, 'childdata.vhd', true)
|
||||
VhdAbstract.createAlias(handler, childName, 'childdata.vhd')
|
||||
const childVhd = new VhdFile(handler, 'childdata.vhd')
|
||||
await childVhd.readHeaderAndFooter()
|
||||
|
||||
await handler.writeFile(
|
||||
'.parent.vhd.merge.json',
|
||||
`.${parentName}.merge.json`,
|
||||
JSON.stringify({
|
||||
parent: {
|
||||
header: parentVhd.header.checksum,
|
||||
@@ -192,15 +196,20 @@ test('it can resume a failed renaming ', async () => {
|
||||
step: 'cleanupVhds',
|
||||
})
|
||||
)
|
||||
// expect merge to succed
|
||||
await mergeVhdChain(handler, ['parent.vhd', 'child1.vhd'])
|
||||
// parent have been renamed
|
||||
expect(await fs.exists(`${tempDir}/parent.vhd`)).toBeFalsy()
|
||||
expect(await fs.exists(`${tempDir}/.parent.vhd.merge.json`)).toBeFalsy()
|
||||
// expect merge to succeed
|
||||
await mergeVhdChain(handler, [parentName, childName])
|
||||
|
||||
Disposable.use(openVhd(handler, 'child1.vhd'), async mergedVhd => {
|
||||
// parent have been renamed
|
||||
expect(await fs.exists(`${tempDir}/${parentName}`)).toBeFalsy()
|
||||
expect(await fs.exists(`${tempDir}/${childName}`)).toBeTruthy()
|
||||
expect(await fs.exists(`${tempDir}/.${parentName}.merge.json`)).toBeFalsy()
|
||||
// we shouldn't have moved the data, but the child data should have been merged into parent
|
||||
expect(await fs.exists(`${tempDir}/parentdata.vhd`)).toBeTruthy()
|
||||
expect(await fs.exists(`${tempDir}/childdata.vhd`)).toBeFalsy()
|
||||
|
||||
Disposable.use(openVhd(handler, childName), async mergedVhd => {
|
||||
await mergedVhd.readBlockAllocationTable()
|
||||
// the resume is at the step 'cleanupVhds' it should not have merged blocks and should still contians parent data
|
||||
// the resume is at the step 'cleanupVhds' it should not have merged blocks and should still contains parent data
|
||||
|
||||
let offset = 0
|
||||
const fd = await fs.open(parentRandomFileName, 'r')
|
||||
@@ -208,16 +217,14 @@ test('it can resume a failed renaming ', async () => {
|
||||
const blockContent = block.data
|
||||
const buffer = Buffer.alloc(blockContent.length)
|
||||
await fs.read(fd, buffer, 0, buffer.length, offset)
|
||||
|
||||
expect(buffer.equals(blockContent)).toEqual(true)
|
||||
offset += childVhd.header.blockSize
|
||||
}
|
||||
})
|
||||
|
||||
// merge succeed if renaming was already done
|
||||
|
||||
await handler.writeFile(
|
||||
'.parent.vhd.merge.json',
|
||||
`.${parentName}.merge.json`,
|
||||
JSON.stringify({
|
||||
parent: {
|
||||
header: parentVhd.header.checksum,
|
||||
@@ -228,10 +235,13 @@ test('it can resume a failed renaming ', async () => {
|
||||
step: 'cleanupVhds',
|
||||
})
|
||||
)
|
||||
await mergeVhdChain(handler, ['parent.vhd', 'child1.vhd'])
|
||||
expect(await fs.exists(`${tempDir}/parent.vhd`)).toBeFalsy()
|
||||
expect(await fs.exists(`${tempDir}/child1.vhd`)).toBeTruthy()
|
||||
expect(await fs.exists(`${tempDir}/.parent.vhd.merge.json`)).toBeFalsy()
|
||||
await mergeVhdChain(handler, [parentName, childName])
|
||||
expect(await fs.exists(`${tempDir}/${parentName}`)).toBeFalsy()
|
||||
expect(await fs.exists(`${tempDir}/${childName}`)).toBeTruthy()
|
||||
// we shouldn't have moved the data, but the child data should have been merged into parent
|
||||
expect(await fs.exists(`${tempDir}/parentdata.vhd`)).toBeTruthy()
|
||||
expect(await fs.exists(`${tempDir}/childdata.vhd`)).toBeFalsy()
|
||||
expect(await fs.exists(`${tempDir}/.${parentName}.merge.json`)).toBeFalsy()
|
||||
})
|
||||
|
||||
test('it can resume a multiple merge ', async () => {
|
||||
|
||||
@@ -18,6 +18,7 @@ const { VhdAbstract } = require('./Vhd/VhdAbstract')
|
||||
const { VhdDirectory } = require('./Vhd/VhdDirectory')
|
||||
const { VhdSynthetic } = require('./Vhd/VhdSynthetic')
|
||||
const { asyncMap } = require('@xen-orchestra/async-map')
|
||||
const { isVhdAlias, resolveVhdAlias } = require('./aliases')
|
||||
|
||||
const { warn } = createLogger('vhd-lib:merge')
|
||||
|
||||
@@ -253,10 +254,16 @@ class Merger {
|
||||
// in the case is an alias, renaming parent to mergeTargetChild will keep the real data
|
||||
// of mergeTargetChild in the data folder
|
||||
// mergeTargetChild is already in an incomplete state, its blocks have been transferred to parent
|
||||
await VhdAbstract.unlink(handler, mergeTargetChild)
|
||||
let oldTarget
|
||||
if (isVhdAlias(mergeTargetChild)) {
|
||||
oldTarget = await resolveVhdAlias(handler, mergeTargetChild)
|
||||
}
|
||||
|
||||
try {
|
||||
await handler.rename(parent, mergeTargetChild)
|
||||
if (oldTarget !== undefined) {
|
||||
await VhdAbstract.unlink(handler, oldTarget).catch(warn)
|
||||
}
|
||||
} catch (error) {
|
||||
// maybe the renaming was already successfull during merge
|
||||
if (error.code === 'ENOENT' && this.#isResuming) {
|
||||
@@ -265,6 +272,8 @@ class Merger {
|
||||
assert.strictEqual(vhd.header.checksum, this.#state.parent.header)
|
||||
})
|
||||
this.#logInfo(`the VHD parent was already renamed`, { parent, mergeTargetChild })
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "vhd-lib",
|
||||
"version": "4.1.1",
|
||||
"version": "4.2.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Primitives for VHD file handling",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/vhd-lib",
|
||||
@@ -18,8 +18,8 @@
|
||||
"@vates/async-each": "^1.0.0",
|
||||
"@vates/read-chunk": "^1.0.1",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"async-iterator-to-stream": "^1.0.2",
|
||||
"decorator-synchronized": "^0.6.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
@@ -31,7 +31,7 @@
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"execa": "^5.0.0",
|
||||
"get-stream": "^6.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"promise-toolbox": "^0.19.2",
|
||||
"readable-stream": "^3.1.1",
|
||||
"throttle": "^1.0.3",
|
||||
"vhd-lib": "^4.1.1"
|
||||
"vhd-lib": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "xo-cli",
|
||||
"version": "0.14.1",
|
||||
"version": "0.14.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Basic CLI for Xen-Orchestra",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-audit",
|
||||
"version": "0.10.1",
|
||||
"version": "0.10.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Audit plugin for XO-Server",
|
||||
"keywords": [
|
||||
@@ -44,9 +44,9 @@
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/audit-core": "^0.2.1",
|
||||
"@xen-orchestra/audit-core": "^0.2.2",
|
||||
"@xen-orchestra/cron": "^1.0.6",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"async-iterator-to-stream": "^1.1.0",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"readable-stream": "^4.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-auth-ldap",
|
||||
"version": "0.10.5",
|
||||
"version": "0.10.6",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "LDAP authentication plugin for XO-Server",
|
||||
"keywords": [
|
||||
@@ -31,7 +31,7 @@
|
||||
"node": ">=12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"ensure-array": "^1.0.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"inquirer": "^8.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-backup-reports",
|
||||
"version": "0.17.1",
|
||||
"version": "0.17.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Backup reports plugin for XO-Server",
|
||||
"keywords": [
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/defined": "^0.0.1",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"human-format": "^1.0.0",
|
||||
"lodash": "^4.13.1",
|
||||
"moment-timezone": "^0.5.13"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-load-balancer",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Load balancer for XO-Server",
|
||||
"keywords": [
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/cron": "^1.0.6",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"lodash": "^4.16.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-netbox",
|
||||
"version": "0.3.4",
|
||||
"version": "0.3.5",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Synchronizes pools managed by Xen Orchestra with Netbox",
|
||||
"keywords": [
|
||||
@@ -29,7 +29,7 @@
|
||||
"node": ">=14.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"semver": "^7.3.5"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vates/coalesce-calls": "^0.1.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"@xen-orchestra/openflow": "^0.1.2",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"lodash": "^4.17.11",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-transport-nagios",
|
||||
"version": "0.1.2",
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Send backup runs statuses to Nagios",
|
||||
"keywords": [
|
||||
@@ -28,7 +28,8 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"@xen-orchestra/template": "^0.1.0",
|
||||
"buffer-crc32": "^0.2.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,11 +2,24 @@ import crc32 from 'buffer-crc32'
|
||||
import net from 'net'
|
||||
import { Buffer } from 'buffer'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import { compileTemplate } from '@xen-orchestra/template'
|
||||
|
||||
const { debug, warn } = createLogger('xo:server:transport:nagios')
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const hostDescription = `Host name on Nagios.
|
||||
|
||||
Leave empty if the host name equals the vm name (the default configuration).
|
||||
|
||||
Otherwise, you could choose a custom name but the template \`{vm.name_label}\` must be included. For example: \`xo-backup-{vm.name_label}\`.`
|
||||
|
||||
const serviceDescription = `Service name on Nagios.
|
||||
|
||||
Leave empty if the host name equals the backup job name (the default configuration).
|
||||
|
||||
Otherwise, you could choose a custom name but the template \`{job.name}\` must e included. For example: \`{job.name}-Xen Orchestra\`.`
|
||||
|
||||
export const configurationSchema = {
|
||||
type: 'object',
|
||||
|
||||
@@ -23,6 +36,16 @@ export const configurationSchema = {
|
||||
type: 'string',
|
||||
description: 'The encryption key',
|
||||
},
|
||||
host: {
|
||||
default: '{vm.name_label}',
|
||||
description: hostDescription,
|
||||
type: 'string',
|
||||
},
|
||||
service: {
|
||||
default: '{job.name}',
|
||||
description: serviceDescription,
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ['server', 'port', 'key'],
|
||||
@@ -31,16 +54,18 @@ export const configurationSchema = {
|
||||
export const testSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
host: {
|
||||
description: 'Nagios host',
|
||||
VmNameLabel: {
|
||||
title: 'VM Name Label',
|
||||
description: 'Name of a VM',
|
||||
type: 'string',
|
||||
},
|
||||
service: {
|
||||
description: 'Nagios service',
|
||||
jobName: {
|
||||
title: 'Job Name',
|
||||
description: 'Name of a backup job',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['host', 'service'],
|
||||
required: ['VmNameLabel', 'jobName'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -96,9 +121,17 @@ class XoServerNagios {
|
||||
this._key = null
|
||||
}
|
||||
|
||||
configure(configuration) {
|
||||
configure({ host, service, ...configuration }) {
|
||||
this._conf = configuration
|
||||
this._key = Buffer.from(configuration.key, ENCODING)
|
||||
|
||||
const templateRules = {
|
||||
'{vm.name_label}': vmNameLabel => vmNameLabel,
|
||||
'{job.name}': (vmNameLabel, jobName) => jobName,
|
||||
}
|
||||
|
||||
this._getHost = compileTemplate(host, templateRules)
|
||||
this._getService = compileTemplate(service, templateRules)
|
||||
}
|
||||
|
||||
load() {
|
||||
@@ -109,21 +142,24 @@ class XoServerNagios {
|
||||
this._unset()
|
||||
}
|
||||
|
||||
test({ host, service }) {
|
||||
test({ VmNameLabel, jobName }) {
|
||||
return this._sendPassiveCheck(
|
||||
{
|
||||
message: 'The server-nagios plugin for Xen Orchestra server seems to be working fine, nicely done :)',
|
||||
status: OK,
|
||||
},
|
||||
host,
|
||||
service
|
||||
VmNameLabel,
|
||||
jobName
|
||||
)
|
||||
}
|
||||
|
||||
_sendPassiveCheck({ message, status }, host, service) {
|
||||
_sendPassiveCheck({ message, status }, vmNameLabel, jobName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._conf.host = host
|
||||
this._conf.service = service
|
||||
const conf = {
|
||||
...this._conf,
|
||||
host: this._getHost(vmNameLabel, jobName),
|
||||
service: this._getService(vmNameLabel, jobName),
|
||||
}
|
||||
|
||||
if (/\r|\n/.test(message)) {
|
||||
warn('the message must not contain a line break', { message })
|
||||
@@ -139,7 +175,7 @@ class XoServerNagios {
|
||||
|
||||
const client = new net.Socket()
|
||||
|
||||
client.connect(this._conf.port, this._conf.server, () => {
|
||||
client.connect(conf.port, conf.server, () => {
|
||||
debug('Successful connection')
|
||||
})
|
||||
|
||||
@@ -147,7 +183,7 @@ class XoServerNagios {
|
||||
const timestamp = data.readInt32BE(128)
|
||||
const iv = data.slice(0, 128) // initialization vector
|
||||
const packet = nscaPacketBuilder({
|
||||
...this._conf,
|
||||
...conf,
|
||||
iv,
|
||||
message,
|
||||
status,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-usage-report",
|
||||
"version": "0.10.1",
|
||||
"version": "0.10.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Report resources usage with their evolution",
|
||||
"keywords": [
|
||||
@@ -31,7 +31,7 @@
|
||||
"dependencies": {
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/cron": "^1.0.6",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"csv-stringify": "^6.0.0",
|
||||
"handlebars": "^4.0.6",
|
||||
"html-minifier": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-web-hooks",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Sends HTTP requests on XO-Server API calls",
|
||||
"keywords": [
|
||||
@@ -29,7 +29,7 @@
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/log": "^0.4.0"
|
||||
"@xen-orchestra/log": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.7.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "xo-server",
|
||||
"version": "5.106.1",
|
||||
"version": "5.107.1",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Server part of Xen-Orchestra",
|
||||
"keywords": [
|
||||
@@ -33,7 +33,7 @@
|
||||
"@vates/cached-dns.lookup": "^1.0.0",
|
||||
"@vates/compose": "^2.1.0",
|
||||
"@vates/decorate-with": "^2.0.0",
|
||||
"@vates/disposable": "^0.1.2",
|
||||
"@vates/disposable": "^0.1.3",
|
||||
"@vates/event-listeners-manager": "^1.0.1",
|
||||
"@vates/otp": "^1.0.0",
|
||||
"@vates/multi-key-map": "^0.1.0",
|
||||
@@ -41,17 +41,17 @@
|
||||
"@vates/predicates": "^1.1.0",
|
||||
"@vates/read-chunk": "^1.0.1",
|
||||
"@xen-orchestra/async-map": "^0.1.2",
|
||||
"@xen-orchestra/backups": "^0.29.0",
|
||||
"@xen-orchestra/backups": "^0.29.1",
|
||||
"@xen-orchestra/cron": "^1.0.6",
|
||||
"@xen-orchestra/defined": "^0.0.1",
|
||||
"@xen-orchestra/emit-async": "^1.0.0",
|
||||
"@xen-orchestra/fs": "^3.2.0",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/fs": "^3.3.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"@xen-orchestra/mixin": "^0.1.0",
|
||||
"@xen-orchestra/mixins": "^0.8.1",
|
||||
"@xen-orchestra/mixins": "^0.8.2",
|
||||
"@xen-orchestra/self-signed": "^0.1.3",
|
||||
"@xen-orchestra/template": "^0.1.0",
|
||||
"@xen-orchestra/xapi": "^1.5.2",
|
||||
"@xen-orchestra/xapi": "^1.5.3",
|
||||
"ajv": "^8.0.3",
|
||||
"app-conf": "^2.3.0",
|
||||
"async-iterator-to-stream": "^1.0.1",
|
||||
@@ -127,7 +127,7 @@
|
||||
"unzipper": "^0.10.5",
|
||||
"uuid": "^9.0.0",
|
||||
"value-matcher": "^0.2.0",
|
||||
"vhd-lib": "^4.1.1",
|
||||
"vhd-lib": "^4.2.0",
|
||||
"ws": "^8.2.3",
|
||||
"xdg-basedir": "^5.1.0",
|
||||
"xen-api": "^1.2.2",
|
||||
@@ -135,7 +135,7 @@
|
||||
"xo-collection": "^0.5.0",
|
||||
"xo-common": "^0.8.0",
|
||||
"xo-remote-parser": "^0.9.2",
|
||||
"xo-vmdk-to-vhd": "^2.4.3"
|
||||
"xo-vmdk-to-vhd": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import splitHost from 'split-host'
|
||||
|
||||
// https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/
|
||||
export function shouldProxy(host, { NO_PROXY, no_proxy = NO_PROXY } = process.env) {
|
||||
if (no_proxy == null) {
|
||||
return true
|
||||
}
|
||||
if (no_proxy === '*') {
|
||||
return false
|
||||
}
|
||||
|
||||
const { hostname } = splitHost(host)
|
||||
|
||||
for (let entry of no_proxy.split(',')) {
|
||||
entry = entry.trim()
|
||||
if (entry[0] === '.') {
|
||||
entry = entry.slice(1)
|
||||
}
|
||||
|
||||
entry = splitHost(entry.trim())
|
||||
|
||||
console.log(hostname, entry.hostname)
|
||||
if (hostname.endsWith(entry.hostname)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { shouldProxy } from './_shouldProxy.mjs'
|
||||
import t from 'tap'
|
||||
|
||||
const ensureArray = v => (v === undefined ? [] : Array.isArray(v) ? v : [v])
|
||||
|
||||
;[
|
||||
{
|
||||
no_proxy: null,
|
||||
ok: 'example.org',
|
||||
},
|
||||
{
|
||||
no_proxy: '*',
|
||||
nok: 'example.org',
|
||||
},
|
||||
{
|
||||
no_proxy: 'example.org, example.com',
|
||||
nok: ['example.org', 'example.org:1024', 'example.com'],
|
||||
ok: 'example.net',
|
||||
},
|
||||
|
||||
{
|
||||
no_proxy: ['example.org', '.example.org'],
|
||||
nok: ['example.org', 'example.org:1024', 'sub.example.org'],
|
||||
ok: 'example.com',
|
||||
},
|
||||
// {
|
||||
// no_proxy: 'example.org:1024',
|
||||
// nok: ['example.org:1024', 'sub.example.org:1024'],
|
||||
// ok: ['example.com', 'example.org'],
|
||||
// },
|
||||
{
|
||||
no_proxy: '[::1]',
|
||||
nok: ['[::1]', '[::1]:1024'],
|
||||
ok: ['[::2]', '[0::1]'],
|
||||
},
|
||||
].forEach(({ no_proxy: noProxies, ok, nok }) => {
|
||||
for (const no_proxy of ensureArray(noProxies)) {
|
||||
const opts = { no_proxy }
|
||||
t.test(String(no_proxy), function (t) {
|
||||
ok = ensureArray(ok)
|
||||
if (ok.length !== 0) {
|
||||
t.test('should proxy', t => {
|
||||
for (const host of ok) {
|
||||
t.equal(shouldProxy(host, opts), true, host)
|
||||
}
|
||||
t.end()
|
||||
})
|
||||
}
|
||||
nok = ensureArray(nok)
|
||||
if (nok.length !== 0) {
|
||||
t.test('should not proxy', t => {
|
||||
for (const host of nok) {
|
||||
t.equal(shouldProxy(host, opts), false, host)
|
||||
}
|
||||
t.end()
|
||||
})
|
||||
}
|
||||
t.end()
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1184,7 +1184,9 @@ export { export_ as export }
|
||||
async function handleVmImport(req, res, { data, srId, type, xapi }) {
|
||||
// Timeout seems to be broken in Node 4.
|
||||
// See https://github.com/nodejs/node/issues/3319
|
||||
req.setTimeout(43200000) // 12 hours
|
||||
req.setTimeout(24 * 60 * 60 * 1000, () => {
|
||||
log.warn('Import timeout reached', { data, srId, type })
|
||||
}) // 24 hours
|
||||
|
||||
// expect "multipart/form-data; boundary=something"
|
||||
const contentType = req.headers['content-type']
|
||||
|
||||
@@ -66,7 +66,7 @@ export default class MigrateVm {
|
||||
|
||||
// run the transfer again to transfer the changed parts
|
||||
// since the source is stopped, there won't be any new change after
|
||||
backup = this.#createWarmBackup(sourceVmId, srId)
|
||||
backup = this.#createWarmBackup(sourceVmId, srId, jobId)
|
||||
await backup.run()
|
||||
// find the destination Vm
|
||||
const targets = Object.keys(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Ajv from 'ajv'
|
||||
import cloneDeep from 'lodash/cloneDeep.js'
|
||||
import mapToArray from 'lodash/map.js'
|
||||
import noop from 'lodash/noop.js'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
@@ -153,22 +154,18 @@ export default class {
|
||||
}
|
||||
|
||||
const validate = this._ajv.compile(configurationSchema)
|
||||
|
||||
// deep clone the configuration to avoid modifying the parameter
|
||||
configuration = cloneDeep(configuration)
|
||||
|
||||
if (!validate(configuration)) {
|
||||
throw invalidParameters(validate.errors)
|
||||
}
|
||||
|
||||
// Sets the plugin configuration.
|
||||
await plugin.instance.configure(
|
||||
{
|
||||
// Shallow copy of the configuration object to avoid most of the
|
||||
// errors when the plugin is altering the configuration object
|
||||
// which is handed over to it.
|
||||
...configuration,
|
||||
},
|
||||
{
|
||||
loaded: plugin.loaded,
|
||||
}
|
||||
)
|
||||
await plugin.instance.configure(configuration, {
|
||||
loaded: plugin.loaded,
|
||||
})
|
||||
plugin.configured = true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "xo-vmdk-to-vhd",
|
||||
"version": "2.4.3",
|
||||
"version": "2.5.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "JS lib reading and writing .vmdk and .ova files",
|
||||
"keywords": [
|
||||
@@ -26,7 +26,7 @@
|
||||
"pako": "^2.0.4",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"tar-stream": "^2.2.0",
|
||||
"vhd-lib": "^4.1.1",
|
||||
"vhd-lib": "^4.2.0",
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "xo-web",
|
||||
"version": "5.107.0",
|
||||
"version": "5.108.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Web interface client for Xen-Orchestra",
|
||||
"keywords": [
|
||||
@@ -40,7 +40,7 @@
|
||||
"@nraynaud/novnc": "0.6.1",
|
||||
"@xen-orchestra/cron": "^1.0.6",
|
||||
"@xen-orchestra/defined": "^0.0.1",
|
||||
"@xen-orchestra/log": "^0.4.0",
|
||||
"@xen-orchestra/log": "^0.5.0",
|
||||
"@xen-orchestra/template": "^0.1.0",
|
||||
"ansi_up": "^4.0.3",
|
||||
"asap": "^2.0.6",
|
||||
@@ -138,7 +138,7 @@
|
||||
"xo-common": "^0.8.0",
|
||||
"xo-lib": "^0.11.1",
|
||||
"xo-remote-parser": "^0.9.2",
|
||||
"xo-vmdk-to-vhd": "^2.4.3"
|
||||
"xo-vmdk-to-vhd": "^2.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "GIT_HEAD=$(git rev-parse HEAD) NODE_ENV=production gulp build",
|
||||
|
||||
@@ -245,7 +245,7 @@ export default {
|
||||
// Original text: "Add your XCP-ng hosts or pools"
|
||||
homeWelcomeText: 'Añade tus hosts/pools de XenServer',
|
||||
|
||||
// Original text: 'Some XenServers have been registered but are not connected'
|
||||
// Original text: 'Some XCP-ng hosts have been registered but are not connected'
|
||||
homeConnectServerText: undefined,
|
||||
|
||||
// Original text: "Want some help?"
|
||||
|
||||
@@ -246,10 +246,10 @@ export default {
|
||||
homeWelcome: 'Bienvenue sur Xen Orchestra !',
|
||||
|
||||
// Original text: "Add your XCP-ng hosts or pools"
|
||||
homeWelcomeText: 'Ajouter vos serveurs ou pools XenServer',
|
||||
homeWelcomeText: 'Ajouter vos serveurs ou pools XCP-ng',
|
||||
|
||||
// Original text: "Some XenServers have been registered but are not connected"
|
||||
homeConnectServerText: "Des XenServers sont enregistrés mais aucun n'est connecté",
|
||||
// Original text: "Some XCP-ng hosts have been registered but are not connected"
|
||||
homeConnectServerText: "Des hôtes XCP-ng sont enregistrés mais aucun n'est connecté",
|
||||
|
||||
// Original text: "Want some help?"
|
||||
homeHelp: "Besoin d'aide ?",
|
||||
|
||||
@@ -228,10 +228,10 @@ export default {
|
||||
homeWelcome: 'Üdvözöljük a Felhőben!',
|
||||
|
||||
// Original text: "Add your XCP-ng hosts or pools"
|
||||
homeWelcomeText: 'Hozzáadása your XenServer kiszolgálók or pools',
|
||||
homeWelcomeText: 'Hozzáadása your XCP-ng kiszolgálók or pools',
|
||||
|
||||
// Original text: "Some XenServers have been registered but are not connected"
|
||||
homeConnectServerText: 'Some XenServers have been registered but are not Kapcsolódva',
|
||||
// Original text: "Some XCP-ng hosts have been registered but are not connected"
|
||||
homeConnectServerText: 'Some XCP-ng hosts have been registered but are not connected',
|
||||
|
||||
// Original text: "Want some help?"
|
||||
homeHelp: 'Segítségre van szüksége?',
|
||||
|
||||
@@ -516,10 +516,10 @@ export default {
|
||||
homeWelcome: 'Benvenuti in Xen Orchestra!',
|
||||
|
||||
// Original text: 'Add your XCP-ng hosts or pools'
|
||||
homeWelcomeText: 'Aggiungi i tuoi hosts o pools XenServer',
|
||||
homeWelcomeText: 'Aggiungi i tuoi hosts o pools XCP-ng',
|
||||
|
||||
// Original text: 'Some XenServers have been registered but are not connected'
|
||||
homeConnectServerText: 'Alcuni XenServers sono stati registrati ma non sono collegati',
|
||||
// Original text: 'Some XCP-ng hosts have been registered but are not connected'
|
||||
homeConnectServerText: 'Alcuni XCP-ng hosts sono stati registrati ma non sono collegati',
|
||||
|
||||
// Original text: 'Want some help?'
|
||||
homeHelp: 'Vuoi un aiuto?',
|
||||
|
||||
@@ -299,7 +299,7 @@ export default {
|
||||
// Original text: "Add your XCP-ng hosts or pools"
|
||||
homeWelcomeText: 'XenServer sunucu veya havuzunu ekle',
|
||||
|
||||
// Original text: "Some XenServers have been registered but are not connected"
|
||||
// Original text: "Some XCP-ng hosts have been registered but are not connected"
|
||||
homeConnectServerText: "Bazı XenServer'lar kayıtlı ama bağlı değil",
|
||||
|
||||
// Original text: "Want some help?"
|
||||
|
||||
@@ -7,6 +7,7 @@ const messages = {
|
||||
alpha: 'Alpha',
|
||||
creation: 'Creation',
|
||||
description: 'Description',
|
||||
deleteSourceVm: 'Delete source VM',
|
||||
expiration: 'Expiration',
|
||||
keyValue: '{key}: {value}',
|
||||
|
||||
@@ -108,8 +109,10 @@ const messages = {
|
||||
replaceExistingCertificate: 'Replace existing certificate',
|
||||
customFields: 'Custom fields',
|
||||
addCustomField: 'Add custom field',
|
||||
availableXoaPremium: 'Available in XOA Premium',
|
||||
editCustomField: 'Edit custom field',
|
||||
deleteCustomField: 'Delete custom field',
|
||||
onlyAvailableXoaUsers: 'Only available to XOA users',
|
||||
|
||||
// ----- Modals -----
|
||||
alertOk: 'OK',
|
||||
@@ -233,7 +236,7 @@ const messages = {
|
||||
homeFetchingData: 'Fetching data…',
|
||||
homeWelcome: 'Welcome to Xen Orchestra!',
|
||||
homeWelcomeText: 'Add your XCP-ng hosts or pools',
|
||||
homeConnectServerText: 'Some XenServers have been registered but are not connected',
|
||||
homeConnectServerText: 'Some XCP-ng hosts have been registered but are not connected',
|
||||
homeHelp: 'Want some help?',
|
||||
homeAddServer: 'Add server',
|
||||
homeConnectServer: 'Connect servers',
|
||||
@@ -770,8 +773,12 @@ const messages = {
|
||||
cloneVmLabel: 'Clone',
|
||||
cleanVm: 'Clean VM directory',
|
||||
fastCloneVmLabel: 'Fast clone',
|
||||
startMigratedVm: 'Start the migrated VM',
|
||||
vmConsoleLabel: 'Console',
|
||||
vmExportUrlValidity: 'The URL is valid once for a short period of time.',
|
||||
vmWarmMigration: 'Warm migration',
|
||||
vmWarmMigrationProcessInfo:
|
||||
'Warm migration process will first create a copy of the VM on the destination while the source VM is still running, then shutdown the source VM and send the changes that happened during the migration to the destination to minimize downtime.',
|
||||
backupLabel: 'Backup',
|
||||
|
||||
// ----- SR general tab -----
|
||||
@@ -1332,6 +1339,7 @@ const messages = {
|
||||
vmCoresPerSocketExceedsSocketsLimit: 'The selected value exceeds the sockets limit ({maxSockets, number})',
|
||||
vmHaDisabled: 'Disabled',
|
||||
vmMemoryLimitsLabel: 'Memory limits (min/max)',
|
||||
vmUuid: 'VM UUID',
|
||||
vmVgpu: 'vGPU',
|
||||
vmVgpus: 'GPUs',
|
||||
vmVgpuNone: 'None',
|
||||
@@ -2471,7 +2479,6 @@ const messages = {
|
||||
proxyUnknownVm: 'Unknown proxy VM.',
|
||||
|
||||
// ----- proxies -----
|
||||
deployProxyDisabled: 'Only available to XOA users',
|
||||
forgetProxyApplianceTitle: 'Forget prox{n, plural, one {y} other {ies}}',
|
||||
forgetProxyApplianceMessage: 'Are you sure you want to forget {n, number} prox{n, plural, one {y} other {ies}}?',
|
||||
forgetProxies: 'Forget proxy(ies)',
|
||||
@@ -2482,11 +2489,16 @@ const messages = {
|
||||
redeployProxy: 'Redeploy proxy',
|
||||
redeployProxyAction: 'Redeploy this proxy',
|
||||
redeployProxyWarning: 'This action will destroy the old proxy VM',
|
||||
registerProxy: 'Register a proxy',
|
||||
noProxiesAvailable: 'No proxies available',
|
||||
checkProxyHealth: 'Test your proxy',
|
||||
updateProxyApplianceSettings: 'Update appliance settings',
|
||||
urlNotFound: 'URL not found',
|
||||
proxyAuthToken: 'Authentication token',
|
||||
proxyConnectionFailedAfterRegistrationMessage: 'Unable to connect to this proxy. Do you want to forget it?',
|
||||
proxyCopyUrl: 'Copy proxy URL',
|
||||
proxyError: 'Proxy error',
|
||||
proxyOptionalVmUuid: 'VM UUID is optional but recommended.',
|
||||
proxyTestSuccess: 'Test passed for {name}',
|
||||
proxyTestSuccessMessage: 'The proxy appears to work correctly',
|
||||
proxyTestFailed: 'Test failed for {name}',
|
||||
@@ -2505,6 +2517,7 @@ const messages = {
|
||||
'The upgrade will interrupt {nJobs, number} running backup job{nJobs, plural, one {} other {s}}. Do you want to continue?',
|
||||
proxiesNeedUpgrade: 'Some proxies need to be upgraded.',
|
||||
upgradeNeededForProxies: 'Some proxies need to be upgraded. Click here to get more information.',
|
||||
xoProxyConcreteGuide: 'XO Proxy: a concrete guide',
|
||||
|
||||
// ----- Utils -----
|
||||
secondsFormat: '{seconds, plural, one {# second} other {# seconds}}',
|
||||
|
||||
@@ -26,8 +26,10 @@ import invoke from '../invoke'
|
||||
import Icon from '../icon'
|
||||
import logError from '../log-error'
|
||||
import NewAuthTokenModal from './new-auth-token-modal'
|
||||
import RegisterProxyModal from './register-proxy-modal'
|
||||
import renderXoItem, { renderXoItemFromId, Vm } from '../render-xo-item'
|
||||
import store from 'store'
|
||||
import WarmMigrationModal from './warm-migration-modal'
|
||||
import { alert, chooseAction, confirm } from '../modal'
|
||||
import { error, info, success } from '../notification'
|
||||
import { getObject } from 'selectors'
|
||||
@@ -1879,6 +1881,20 @@ export const shareVm = async (vm, resourceSet) =>
|
||||
}),
|
||||
}).then(() => editVm(vm, { share: true }), noop)
|
||||
|
||||
export const vmWarmMigration = async vm => {
|
||||
const { sr, deleteSourceVm, startDestinationVm } = await confirm({
|
||||
body: <WarmMigrationModal />,
|
||||
title: _('vmWarmMigration'),
|
||||
icon: 'vm-warm-migration',
|
||||
})
|
||||
return _call('vm.warmMigration', {
|
||||
deleteSourceVm,
|
||||
sr: resolveId(sr),
|
||||
startDestinationVm,
|
||||
vm: resolveId(vm),
|
||||
})
|
||||
}
|
||||
|
||||
// DISK ---------------------------------------------------------------
|
||||
|
||||
export const createDisk = (name, size, sr, { vm, bootable, mode, position }) =>
|
||||
@@ -3306,6 +3322,37 @@ export const deployProxyAppliance = (license, sr, { network, proxy, ...props } =
|
||||
...props,
|
||||
})::tap(subscribeProxies.forceRefresh)
|
||||
|
||||
export const registerProxy = async () => {
|
||||
const getStringOrUndefined = string => (string.trim() === '' ? undefined : string)
|
||||
|
||||
const { address, authenticationToken, name, vmUuid } = await confirm({
|
||||
body: <RegisterProxyModal />,
|
||||
icon: 'connect',
|
||||
title: _('registerProxy'),
|
||||
})
|
||||
|
||||
const proxyId = await registerProxyApplicance({
|
||||
address: getStringOrUndefined(address),
|
||||
authenticationToken: getStringOrUndefined(authenticationToken),
|
||||
name: getStringOrUndefined(name),
|
||||
vmUuid: getStringOrUndefined(vmUuid),
|
||||
})
|
||||
const _isProxyWorking = await isProxyWorking(proxyId).catch(err => {
|
||||
console.error('isProxyWorking error:', err)
|
||||
return false
|
||||
})
|
||||
if (!_isProxyWorking) {
|
||||
await confirm({
|
||||
body: _('proxyConnectionFailedAfterRegistrationMessage'),
|
||||
title: _('proxyError'),
|
||||
})
|
||||
await forgetProxyAppliances([proxyId])
|
||||
}
|
||||
}
|
||||
|
||||
export const registerProxyApplicance = proxyInfo =>
|
||||
_call('proxy.register', proxyInfo)::tap(subscribeProxies.forceRefresh)
|
||||
|
||||
export const editProxyAppliance = (proxy, { vm, ...props }) =>
|
||||
_call('proxy.update', {
|
||||
id: resolveId(proxy),
|
||||
@@ -3365,6 +3412,8 @@ export const checkProxyHealth = async proxy => {
|
||||
)
|
||||
}
|
||||
|
||||
export const isProxyWorking = async proxy => (await _call('proxy.checkHealth', { id: resolveId(proxy) })).success
|
||||
|
||||
// Audit plugin ---------------------------------------------------------
|
||||
|
||||
const METHOD_NOT_FOUND_CODE = -32601
|
||||
|
||||
68
packages/xo-web/src/common/xo/register-proxy-modal/index.js
Normal file
68
packages/xo-web/src/common/xo/register-proxy-modal/index.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import _ from 'intl'
|
||||
import Component from 'base-component'
|
||||
import Icon from 'icon'
|
||||
import React from 'react'
|
||||
import SingleLineRow from 'single-line-row'
|
||||
import { Col, Container } from 'grid'
|
||||
import { Input as DebounceInput } from 'debounce-input-decorator'
|
||||
|
||||
export default class RegisterProxyModal extends Component {
|
||||
state = {
|
||||
address: '',
|
||||
authenticationToken: '',
|
||||
name: '',
|
||||
vmUuid: '',
|
||||
}
|
||||
get value() {
|
||||
return this.state
|
||||
}
|
||||
|
||||
render() {
|
||||
const { address, authenticationToken, name, vmUuid } = this.state
|
||||
return (
|
||||
<Container>
|
||||
<a href='https://xen-orchestra.com/blog/xo-proxy-a-concrete-guide/' rel='noopener noreferrer'>
|
||||
<Icon icon='info' /> {_('xoProxyConcreteGuide')}
|
||||
</a>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('proxyAuthToken')}</Col>
|
||||
<Col size={6}>
|
||||
<DebounceInput
|
||||
className='form-control'
|
||||
onChange={this.linkState('authenticationToken')}
|
||||
value={authenticationToken}
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('name')}</Col>
|
||||
<Col size={6}>
|
||||
<DebounceInput className='form-control' onChange={this.linkState('name')} value={name} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('address')}</Col>
|
||||
<Col size={6}>
|
||||
<DebounceInput
|
||||
className='form-control'
|
||||
onChange={this.linkState('address')}
|
||||
placeholder='192.168.2.20[:4343]'
|
||||
value={address}
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('vmUuid')}</Col>
|
||||
<Col size={6}>
|
||||
<DebounceInput className='form-control' onChange={this.linkState('vmUuid')} value={vmUuid} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col className='text-info'>
|
||||
<Icon icon='info' /> {_('proxyOptionalVmUuid')}
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
48
packages/xo-web/src/common/xo/warm-migration-modal/index.js
Normal file
48
packages/xo-web/src/common/xo/warm-migration-modal/index.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import _ from 'intl'
|
||||
import Component from 'base-component'
|
||||
import Icon from 'icon'
|
||||
import React from 'react'
|
||||
import SingleLineRow from 'single-line-row'
|
||||
import { Col, Container } from 'grid'
|
||||
import { SelectSr } from 'select-objects'
|
||||
import { Toggle } from 'form'
|
||||
|
||||
export default class WarmMigrationModal extends Component {
|
||||
state = {
|
||||
deleteSourceVm: false,
|
||||
sr: undefined,
|
||||
startDestinationVm: false,
|
||||
}
|
||||
get value() {
|
||||
return this.state
|
||||
}
|
||||
|
||||
render() {
|
||||
const { deleteSourceVm, sr, startDestinationVm } = this.state
|
||||
return (
|
||||
<Container>
|
||||
<div className='text-info'>
|
||||
<Icon icon='info' /> <i>{_('vmWarmMigrationProcessInfo')}</i>
|
||||
</div>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('destinationSR')}</Col>
|
||||
<Col size={6}>
|
||||
<SelectSr onChange={this.linkState('sr')} value={sr} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('deleteSourceVm')}</Col>
|
||||
<Col size={6}>
|
||||
<Toggle onChange={this.toggleState('deleteSourceVm')} value={deleteSourceVm} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={6}>{_('startMigratedVm')}</Col>
|
||||
<Col size={6}>
|
||||
<Toggle onChange={this.toggleState('startDestinationVm')} value={startDestinationVm} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -488,6 +488,10 @@
|
||||
@extend .fa;
|
||||
@extend .fa-list-alt;
|
||||
}
|
||||
&-warm-migration {
|
||||
@extend .fa;
|
||||
@extend .fa-fire;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic states
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
forgetProxyAppliances,
|
||||
getLicenses,
|
||||
getProxyApplianceUpdaterState,
|
||||
registerProxy,
|
||||
subscribeProxies,
|
||||
upgradeProxyAppliance,
|
||||
EXPIRES_SOON_DELAY,
|
||||
@@ -322,10 +323,21 @@ const Proxies = decorate([
|
||||
handler={effects.deployProxy}
|
||||
icon='proxy'
|
||||
size='large'
|
||||
tooltip={state.isFromSource ? _('deployProxyDisabled') : undefined}
|
||||
tooltip={state.isFromSource ? _('onlyAvailableXoaUsers') : undefined}
|
||||
>
|
||||
{_('deployProxy')}
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
className='ml-1'
|
||||
btnStyle='success'
|
||||
disabled={state.isFromSource}
|
||||
handler={registerProxy}
|
||||
icon='connect'
|
||||
size='large'
|
||||
tooltip={state.isFromSource ? _('onlyAvailableXoaUsers') : undefined}
|
||||
>
|
||||
{_('registerProxy')}
|
||||
</ActionButton>
|
||||
</div>
|
||||
<NoObjects
|
||||
actions={ACTIONS}
|
||||
|
||||
@@ -45,11 +45,13 @@ import {
|
||||
subscribeResourceSets,
|
||||
subscribeUsers,
|
||||
suspendVm,
|
||||
vmWarmMigration,
|
||||
XEN_DEFAULT_CPU_CAP,
|
||||
XEN_DEFAULT_CPU_WEIGHT,
|
||||
XEN_VIDEORAM_VALUES,
|
||||
} from 'xo'
|
||||
import { createGetObject, createGetObjectsOfType, createSelector, isAdmin } from 'selectors'
|
||||
import { getXoaPlan, PREMIUM } from 'xoa-plans'
|
||||
import { SelectSuspendSr } from 'select-suspend-sr'
|
||||
|
||||
import BootOrder from './boot-order'
|
||||
@@ -450,6 +452,7 @@ export default class TabAdvanced extends Component {
|
||||
|
||||
render() {
|
||||
const { container, isAdmin, vgpus, vm, vmPool } = this.props
|
||||
const isWarmMigrationAvailable = getXoaPlan().value >= PREMIUM.value
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
@@ -484,6 +487,15 @@ export default class TabAdvanced extends Component {
|
||||
icon='vm-force-shutdown'
|
||||
labelId='forceShutdownVmLabel'
|
||||
/>
|
||||
<TabButton
|
||||
btnStyle='warning'
|
||||
disabled={!isWarmMigrationAvailable}
|
||||
handler={vmWarmMigration}
|
||||
handlerParam={vm}
|
||||
icon='vm-warm-migration'
|
||||
labelId='vmWarmMigration'
|
||||
tooltip={isWarmMigrationAvailable ? undefined : _('availableXoaPremium')}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{vm.power_state === 'Halted' && (
|
||||
|
||||
44
scripts/_computeDepOrder.js
Normal file
44
scripts/_computeDepOrder.js
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict'
|
||||
|
||||
function addPkgDepsToTree(deps, internalDeps) {
|
||||
if (deps !== undefined) {
|
||||
for (const depName of Object.keys(deps)) {
|
||||
const dep = this.pkgs[depName]
|
||||
if (dep !== undefined) {
|
||||
internalDeps.push(depName)
|
||||
addPkgToTree.call(this, dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPkgToTree(pkg) {
|
||||
const { name, package: pkgJson } = pkg
|
||||
|
||||
if (!(name in this.tree)) {
|
||||
const internalDeps = (this.tree[name] = [])
|
||||
|
||||
addPkgDepsToTree.call(this, pkgJson.dependencies, internalDeps)
|
||||
addPkgDepsToTree.call(this, pkgJson.devDependencies, internalDeps)
|
||||
addPkgDepsToTree.call(this, pkgJson.optionalDependencies, internalDeps)
|
||||
}
|
||||
}
|
||||
|
||||
function addPkgToResolution(name) {
|
||||
if (!this.resolution.has(name)) {
|
||||
this.tree[name].sort().forEach(addPkgToResolution, this)
|
||||
this.resolution.add(name)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string[]} package names in the order they should be released
|
||||
*/
|
||||
module.exports = function computeDepOrder(pkgsByName) {
|
||||
const tree = { __proto__: null }
|
||||
Object.values(pkgsByName).forEach(addPkgToTree, { pkgs: pkgsByName, tree })
|
||||
|
||||
const resolution = new Set()
|
||||
Object.keys(tree).sort().forEach(addPkgToResolution, { resolution, tree })
|
||||
return Array.from(resolution)
|
||||
}
|
||||
@@ -24,8 +24,7 @@ esac
|
||||
if [ $# -ge 2 ]
|
||||
then
|
||||
npm version "$2"
|
||||
|
||||
git add --patch
|
||||
git checkout HEAD :/yarn.lock
|
||||
fi
|
||||
# if version is not passed, simply update other packages
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
const DepTree = require('deptree')
|
||||
const fs = require('fs').promises
|
||||
const joinPath = require('path').join
|
||||
const semver = require('semver')
|
||||
@@ -10,6 +9,8 @@ const escapeRegExp = require('lodash/escapeRegExp')
|
||||
const invert = require('lodash/invert')
|
||||
const keyBy = require('lodash/keyBy')
|
||||
|
||||
const computeDepOrder = require('./_computeDepOrder.js')
|
||||
|
||||
const changelogConfig = {
|
||||
path: joinPath(__dirname, '../CHANGELOG.unreleased.md'),
|
||||
startTag: '<!--packages-start-->',
|
||||
@@ -19,14 +20,6 @@ const changelogConfig = {
|
||||
const RELEASE_WEIGHT = { PATCH: 1, MINOR: 2, MAJOR: 3 }
|
||||
const RELEASE_TYPE = invert(RELEASE_WEIGHT)
|
||||
|
||||
const releaseGraph = { __proto__: null }
|
||||
function addToGraph(name, depName) {
|
||||
const deps = releaseGraph[name] ?? (releaseGraph[name] = [])
|
||||
if (depName !== undefined) {
|
||||
deps.push(depName)
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Map<string, int>} A mapping of package names to their release weight */
|
||||
const packagesToRelease = new Map()
|
||||
|
||||
@@ -64,6 +57,7 @@ async function main(args, scriptName) {
|
||||
}
|
||||
|
||||
allPackages = keyBy(await getPackages(true), 'name')
|
||||
const releaseOrder = computeDepOrder(allPackages)
|
||||
|
||||
Object.entries(toRelease).forEach(([packageName, releaseType]) => {
|
||||
const rootPackage = allPackages[packageName]
|
||||
@@ -74,7 +68,6 @@ async function main(args, scriptName) {
|
||||
|
||||
const rootReleaseWeight = releaseTypeToWeight(releaseType)
|
||||
registerPackageToRelease(packageName, rootReleaseWeight)
|
||||
addToGraph(rootPackage.name)
|
||||
|
||||
handlePackageDependencies(rootPackage.name, getNextVersion(rootPackage.package.version, rootReleaseWeight))
|
||||
})
|
||||
@@ -82,20 +75,15 @@ async function main(args, scriptName) {
|
||||
const commandsToExecute = ['', 'Commands to execute:', '']
|
||||
const releasedPackages = ['', '### Released packages', '']
|
||||
|
||||
const tree = new DepTree()
|
||||
Object.keys(releaseGraph)
|
||||
.sort()
|
||||
.forEach(name => {
|
||||
tree.add(name, releaseGraph[name])
|
||||
})
|
||||
|
||||
tree.resolve().forEach(dependencyName => {
|
||||
const releaseWeight = packagesToRelease.get(dependencyName)
|
||||
const {
|
||||
package: { version },
|
||||
} = allPackages[dependencyName]
|
||||
commandsToExecute.push(`./scripts/bump-pkg ${dependencyName} ${RELEASE_TYPE[releaseWeight].toLocaleLowerCase()}`)
|
||||
releasedPackages.push(`- ${dependencyName} ${getNextVersion(version, releaseWeight)}`)
|
||||
releaseOrder.forEach(name => {
|
||||
if (packagesToRelease.has(name)) {
|
||||
const releaseWeight = packagesToRelease.get(name)
|
||||
const {
|
||||
package: { version },
|
||||
} = allPackages[name]
|
||||
commandsToExecute.push(`./scripts/bump-pkg ${name} ${RELEASE_TYPE[releaseWeight].toLocaleLowerCase()}`)
|
||||
releasedPackages.push(`- ${name} ${getNextVersion(version, releaseWeight)}`)
|
||||
}
|
||||
})
|
||||
|
||||
console.log(commandsToExecute.join('\n'))
|
||||
@@ -153,7 +141,6 @@ function handlePackageDependencies(packageName, packageNextVersion) {
|
||||
|
||||
if (releaseWeight !== undefined) {
|
||||
registerPackageToRelease(name, releaseWeight)
|
||||
addToGraph(name, packageName)
|
||||
handlePackageDependencies(name, getNextVersion(version, releaseWeight))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user