Compare commits
131 Commits
xo-server-
...
xo-server-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b43cf27479 | ||
|
|
bcab6bb584 | ||
|
|
8213601df6 | ||
|
|
e7ad577661 | ||
|
|
070213dd7f | ||
|
|
395cbb33ef | ||
|
|
a429a7aa35 | ||
|
|
b70e09937b | ||
|
|
109ff4a4da | ||
|
|
0e185ab92a | ||
|
|
aefb76a4f6 | ||
|
|
fe653dc7dd | ||
|
|
8b5c0e706c | ||
|
|
d25584edf9 | ||
|
|
496ca2c716 | ||
|
|
4a09074a40 | ||
|
|
385fd80bb9 | ||
|
|
b6818abd0d | ||
|
|
45b5d10f1b | ||
|
|
7866a265fe | ||
|
|
774c0d09b1 | ||
|
|
df0029db3b | ||
|
|
c1a5364448 | ||
|
|
557ba1a4bc | ||
|
|
bf932aada1 | ||
|
|
2304deab3f | ||
|
|
ea9c0cfb10 | ||
|
|
2fd5a6501b | ||
|
|
0ca5a32142 | ||
|
|
f55f812d30 | ||
|
|
98bbd53c28 | ||
|
|
1f0bfe2518 | ||
|
|
c5eae6e498 | ||
|
|
bfcdd29f10 | ||
|
|
8db0b59fe1 | ||
|
|
6bc4c03b4c | ||
|
|
4d63f93390 | ||
|
|
9722f2a5bd | ||
|
|
ee9c6817d6 | ||
|
|
58564306ca | ||
|
|
17c6f1762d | ||
|
|
17f13307bb | ||
|
|
a43c141ddd | ||
|
|
1e6da359cc | ||
|
|
626a9a777f | ||
|
|
2768ad9e3b | ||
|
|
3eec8f1a55 | ||
|
|
6148daa8b8 | ||
|
|
338de7985f | ||
|
|
df99528445 | ||
|
|
890901366d | ||
|
|
13271aa196 | ||
|
|
71aea20c7d | ||
|
|
b09da76b04 | ||
|
|
ce35bbaeb4 | ||
|
|
0034f0a1d3 | ||
|
|
e3391fa81f | ||
|
|
bacdc63c70 | ||
|
|
b0285799c6 | ||
|
|
f8b34c5d64 | ||
|
|
40f4a66bda | ||
|
|
6125dae158 | ||
|
|
dfe4a934e9 | ||
|
|
ecc33f46ab | ||
|
|
00d1985cf9 | ||
|
|
138aed8ae1 | ||
|
|
def9f947b7 | ||
|
|
20dc4af4a4 | ||
|
|
4d2909567c | ||
|
|
92a93e4393 | ||
|
|
389967d40c | ||
|
|
d66e8f29b7 | ||
|
|
3e9425bf79 | ||
|
|
41506e785e | ||
|
|
32d7ccfea5 | ||
|
|
8a9f952ada | ||
|
|
d15efae43f | ||
|
|
627c06f4a8 | ||
|
|
3e6201e93a | ||
|
|
b941a649b5 | ||
|
|
7b2b9ca618 | ||
|
|
404cf2b7e6 | ||
|
|
42698293de | ||
|
|
563969f2e9 | ||
|
|
887e21daa5 | ||
|
|
b75a2a8dca | ||
|
|
31f32ba23c | ||
|
|
936a4068d5 | ||
|
|
266f287774 | ||
|
|
5808485812 | ||
|
|
b6dd83e32b | ||
|
|
2236bd71c4 | ||
|
|
ec869ffdd3 | ||
|
|
1aa4966a92 | ||
|
|
235da199f9 | ||
|
|
4cad820271 | ||
|
|
4a0b29e1f2 | ||
|
|
1db9ca9e31 | ||
|
|
942ddfa742 | ||
|
|
355cddc044 | ||
|
|
c23cccf2ce | ||
|
|
1407fb7bab | ||
|
|
4ba542b70f | ||
|
|
76f75401ee | ||
|
|
011cc7ad65 | ||
|
|
b966e6097f | ||
|
|
809e1a35cd | ||
|
|
8e7e1fccbe | ||
|
|
e86b30f205 | ||
|
|
7dc0c4cf15 | ||
|
|
561a9f140d | ||
|
|
ba56114e9f | ||
|
|
11bd75d2fe | ||
|
|
ececbaf201 | ||
|
|
1985134d94 | ||
|
|
2cc59078b1 | ||
|
|
3d4d7db5da | ||
|
|
dc40ceaafe | ||
|
|
0110e223ee | ||
|
|
e7467dca8a | ||
|
|
4a1e87b534 | ||
|
|
5a32f904bc | ||
|
|
14d023a9f5 | ||
|
|
2e5e81e93e | ||
|
|
c9cc106be6 | ||
|
|
29fc17f260 | ||
|
|
c7d16fd345 | ||
|
|
ffe29b8957 | ||
|
|
be1bd9254d | ||
|
|
6156649faa | ||
|
|
2edddaa835 |
@@ -10,6 +10,11 @@ module.exports = {
|
||||
$Shape: true,
|
||||
},
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
legacyDecorators: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
indent: 'off',
|
||||
|
||||
@@ -22,18 +22,16 @@ const configs = {
|
||||
// loose: true,
|
||||
|
||||
shippedProposals: true,
|
||||
targets: __PROD__
|
||||
? (() => {
|
||||
let node = (pkg.engines || {}).node
|
||||
if (node !== undefined) {
|
||||
const trimChars = '^=>~'
|
||||
while (trimChars.includes(node[0])) {
|
||||
node = node.slice(1)
|
||||
}
|
||||
return { node: node }
|
||||
}
|
||||
})()
|
||||
: { browsers: '', node: 'current' },
|
||||
targets: (() => {
|
||||
let node = (pkg.engines || {}).node
|
||||
if (node !== undefined) {
|
||||
const trimChars = '^=>~'
|
||||
while (trimChars.includes(node[0])) {
|
||||
node = node.slice(1)
|
||||
}
|
||||
}
|
||||
return { browsers: pkg.browserslist, node }
|
||||
})(),
|
||||
useBuiltIns: '@babel/polyfill' in (pkg.dependencies || {}) && 'usage',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
"moment-timezone": "^0.5.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
|
||||
@@ -44,7 +44,9 @@ class Schedule {
|
||||
this._createDate =
|
||||
zone.toLowerCase() === 'utc'
|
||||
? moment.utc
|
||||
: zone === 'local' ? moment : () => moment.tz(zone)
|
||||
: zone === 'local'
|
||||
? moment
|
||||
: () => moment.tz(zone)
|
||||
}
|
||||
|
||||
createJob (fn) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xen-orchestra/fs",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "The File System for Xen Orchestra backups.",
|
||||
"keywords": [],
|
||||
@@ -20,11 +20,10 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.0.0-beta.54",
|
||||
"@marsaud/smb2": "^0.9.0",
|
||||
"execa": "^0.10.0",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-stream": "^3.0.0",
|
||||
"get-stream": "^4.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.9.5",
|
||||
"through2": "^2.0.3",
|
||||
@@ -32,12 +31,11 @@
|
||||
"xo-remote-parser": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-function-bind": "7.0.0-beta.54",
|
||||
"@babel/plugin-transform-runtime": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"index-modules": "^0.3.0",
|
||||
|
||||
@@ -85,8 +85,8 @@ export const validChecksumOfReadStream = (
|
||||
callback(
|
||||
checksum !== expectedChecksum
|
||||
? new Error(
|
||||
`Bad checksum (${checksum}), expected: ${expectedChecksum}`
|
||||
)
|
||||
`Bad checksum (${checksum}), expected: ${expectedChecksum}`
|
||||
)
|
||||
: null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,14 +28,9 @@ export default class LocalHandler extends RemoteHandlerAbstract {
|
||||
|
||||
async _sync () {
|
||||
if (this._remote.enabled) {
|
||||
try {
|
||||
const path = this._getRealPath()
|
||||
await fs.ensureDir(path)
|
||||
await fs.access(path, fs.R_OK | fs.W_OK)
|
||||
} catch (exc) {
|
||||
this._remote.enabled = false
|
||||
this._remote.error = exc.message
|
||||
}
|
||||
const path = this._getRealPath()
|
||||
await fs.ensureDir(path)
|
||||
await fs.access(path, fs.R_OK | fs.W_OK)
|
||||
}
|
||||
return this._remote
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { forEach } from 'lodash'
|
||||
|
||||
import LocalHandler from './local'
|
||||
|
||||
const DEFAULT_NFS_OPTIONS = 'vers=3'
|
||||
|
||||
export default class NfsHandler extends LocalHandler {
|
||||
get type () {
|
||||
return 'nfs'
|
||||
@@ -52,12 +54,12 @@ export default class NfsHandler extends LocalHandler {
|
||||
|
||||
async _mount () {
|
||||
await fs.ensureDir(this._getRealPath())
|
||||
const { host, path, port } = this._remote
|
||||
const { host, path, port, options } = this._remote
|
||||
return execa('mount', [
|
||||
'-t',
|
||||
'nfs',
|
||||
'-o',
|
||||
'vers=3',
|
||||
DEFAULT_NFS_OPTIONS + (options !== undefined ? `,${options}` : ''),
|
||||
`${host}${port !== undefined ? ':' + port : ''}:${path}`,
|
||||
this._getRealPath(),
|
||||
])
|
||||
@@ -66,19 +68,9 @@ export default class NfsHandler extends LocalHandler {
|
||||
async _sync () {
|
||||
await this._loadRealMounts()
|
||||
if (this._matchesRealMount() && !this._remote.enabled) {
|
||||
try {
|
||||
await this._umount(this._remote)
|
||||
} catch (exc) {
|
||||
this._remote.enabled = true
|
||||
this._remote.error = exc.message
|
||||
}
|
||||
await this._umount(this._remote)
|
||||
} else if (!this._matchesRealMount() && this._remote.enabled) {
|
||||
try {
|
||||
await this._mount()
|
||||
} catch (exc) {
|
||||
this._remote.enabled = false
|
||||
this._remote.error = exc.message
|
||||
}
|
||||
await this._mount()
|
||||
}
|
||||
return this._remote
|
||||
}
|
||||
|
||||
@@ -71,13 +71,8 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
|
||||
async _sync () {
|
||||
if (this._remote.enabled) {
|
||||
try {
|
||||
// Check access (smb2 does not expose connect in public so far...)
|
||||
await this.list()
|
||||
} catch (error) {
|
||||
this._remote.enabled = false
|
||||
this._remote.error = error.message
|
||||
}
|
||||
// Check access (smb2 does not expose connect in public so far...)
|
||||
await this.list()
|
||||
}
|
||||
return this._remote
|
||||
}
|
||||
|
||||
69
CHANGELOG.md
69
CHANGELOG.md
@@ -4,10 +4,76 @@
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [Backup (file) restore] Order backups by date in selector [#3294](https://github.com/vatesfr/xen-orchestra/issues/3294) (PR [#3374](https://github.com/vatesfr/xen-orchestra/pull/3374))
|
||||
- [Self] Hide Tasks entry in menu for self users [#3311](https://github.com/vatesfr/xen-orchestra/issues/3311) (PR [#3373](https://github.com/vatesfr/xen-orchestra/pull/3373))
|
||||
- [Tasks] Show previous tasks [#3266](https://github.com/vatesfr/xen-orchestra/issues/3266) (PR [#3377](https://github.com/vatesfr/xen-orchestra/pull/3377))
|
||||
- [Backup NG] Add job name in names of replicated VMs (PR [#3379](https://github.com/vatesfr/xen-orchestra/pull/3379))
|
||||
- [Backup NG] Restore directories [#1924](https://github.com/vatesfr/xen-orchestra/issues/1924) (PR [#3384](https://github.com/vatesfr/xen-orchestra/pull/3384))
|
||||
- [VM] Start a VM on a specific host [#3191](https://github.com/vatesfr/xen-orchestra/issues/3191) (PR [#3389](https://github.com/vatesfr/xen-orchestra/pull/3389))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [Self] Fix Self Service quotas not being correctly updated when deleting multiple VMs at a time (PR [#3368](https://github.com/vatesfr/xen-orchestra/pull/3368))
|
||||
- [Backup NG] Don't fail listing backups when a remote is broken [#3365](https://github.com/vatesfr/xen-orchestra/issues/3365) (PR [#3367](https://github.com/vatesfr/xen-orchestra/pull/3367))
|
||||
- [New XOSAN] Fix error sometimes occurring when selecting the pool (PR [#3370](https://github.com/vatesfr/xen-orchestra/pull/3370))
|
||||
- [New VM] Selecting multiple VMs and clicking Create then Cancel used to redirect to Home [#3268](https://github.com/vatesfr/xen-orchestra/issues/3268) (PR [#3371](https://github.com/vatesfr/xen-orchestra/pull/3371))
|
||||
- [Remotes] `cannot read 'properties' of undefined` error (PR [#3382](https://github.com/vatesfr/xen-orchestra/pull/3382))
|
||||
- [Servers] Various issues when adding a new server [#3385](https://github.com/vatesfr/xen-orchestra/issues/3385) (PR [#3388](https://github.com/vatesfr/xen-orchestra/pull/3388))
|
||||
- [Backup NG] Always delete the correct old replications [#3391](https://github.com/vatesfr/xen-orchestra/issues/3391) (PR [#3394](https://github.com/vatesfr/xen-orchestra/pull/3394))
|
||||
|
||||
### Released packages
|
||||
|
||||
- xo-server v5.26.0
|
||||
- xo-web v5.26.0
|
||||
|
||||
## **5.25.2** (2018-08-27)
|
||||
|
||||
### Enhancements
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [Remotes] Fix "undefined" mount option issue [#3361](https://github.com/vatesfr/xen-orchestra/issues/3361) (PR [#3363](https://github.com/vatesfr/xen-orchestra/pull/3363))
|
||||
- [Continuous Replication] Don't try to import/export VDIs on halted host [#3354](https://github.com/vatesfr/xen-orchestra/issues/3354) (PR [#3355](https://github.com/vatesfr/xen-orchestra/pull/3355))
|
||||
- [Disaster Recovery] Don't try to import/export VMs on halted host (PR [#3364](https://github.com/vatesfr/xen-orchestra/pull/3364))
|
||||
- [Backup NG] A successful backup job reported as Interrupted [#3018](https://github.com/vatesfr/xen-orchestra/issues/3018) (PR [#3238](https://github.com/vatesfr/xen-orchestra/pull/3238))
|
||||
|
||||
### Released packages
|
||||
|
||||
- xo-server v5.25.2
|
||||
- xo-web v5.25.1
|
||||
|
||||
## **5.25.0** (2018-08-23)
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [Tables] Filter input now always shows up even if the table is empty [#3295](https://github.com/vatesfr/xen-orchestra/issues/3295) (PR [#3296](https://github.com/vatesfr/xen-orchestra/pull/3296))
|
||||
- [Tasks] The table is now still shown when there are no tasks (PR [#3305](https://github.com/vatesfr/xen-orchestra/pull/3305))
|
||||
- [Host / Logs] Homogenize action buttons in table and enable bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3313](https://github.com/vatesfr/xen-orchestra/pull/3313))
|
||||
- [VM/Advanced] Change "Convert" to "Convert to template" and always show the button [#3201](https://github.com/vatesfr/xen-orchestra/issues/3201) (PR [#3319](https://github.com/vatesfr/xen-orchestra/pull/3319))
|
||||
- [Backup NG form] Display a tip when doing a CR on a thick-provisioned SR [#3291](https://github.com/vatesfr/xen-orchestra/issues/3291) (PR [#3333](https://github.com/vatesfr/xen-orchestra/pull/3333))
|
||||
- [SR/new] Add local ext SR type [#3332](https://github.com/vatesfr/xen-orchestra/issues/3332) (PR [#3335](https://github.com/vatesfr/xen-orchestra/pull/3335))
|
||||
- [Backup reports] Send report for the interrupted backup jobs on the server startup [#2998](https://github.com/vatesfr/xen-orchestra/issues/#2998) (PR [3164](https://github.com/vatesfr/xen-orchestra/pull/3164) [3154](https://github.com/vatesfr/xen-orchestra/pull/3154))
|
||||
- [Backup NG form] Move VMs' selection to a dedicated card [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) (PR [#3338](https://github.com/vatesfr/xen-orchestra/pull/3338))
|
||||
- [Backup NG smart mode] Exclude replicated VMs [#2338](https://github.com/vatesfr/xen-orchestra/issues/2338) (PR [#3312](https://github.com/vatesfr/xen-orchestra/pull/3312))
|
||||
- [Backup NG form] Show the compression checkbox when the full mode is active [#3236](https://github.com/vatesfr/xen-orchestra/issues/3236) (PR [#3345](https://github.com/vatesfr/xen-orchestra/pull/3345))
|
||||
- [New VM] Display an error when the getting of the coreOS default template fails [#3227](https://github.com/vatesfr/xen-orchestra/issues/3227) (PR [#3343](https://github.com/vatesfr/xen-orchestra/pull/3343))
|
||||
- [Backup NG form] Set default retention to 1 [#3134](https://github.com/vatesfr/xen-orchestra/issues/3134) (PR [#3290](https://github.com/vatesfr/xen-orchestra/pull/3290))
|
||||
- [Backup NG] New logs are searchable by job name [#3272](https://github.com/vatesfr/xen-orchestra/issues/3272) (PR [#3351](https://github.com/vatesfr/xen-orchestra/pull/3351))
|
||||
- [Remotes] Add a field for NFS remotes to set mount options [#1793](https://github.com/vatesfr/xen-orchestra/issues/1793) (PR [#3353](https://github.com/vatesfr/xen-orchestra/pull/3353))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [Backup NG form] Fix schedule's name overridden with undefined if it's not been edited [#3286](https://github.com/vatesfr/xen-orchestra/issues/3286) (PR [#3288](https://github.com/vatesfr/xen-orchestra/pull/3288))
|
||||
- [Remotes] Don't change `enabled` state on errors (PR [#3318](https://github.com/vatesfr/xen-orchestra/pull/3318))
|
||||
- [Remotes] Auto-reconnect on use if necessary [#2852](https://github.com/vatesfr/xen-orchestra/issues/2852) (PR [#3320](https://github.com/vatesfr/xen-orchestra/pull/3320))
|
||||
- [XO items' select] Fix adding or removing a XO item from a select make the missing XO items disappear [#3322](https://github.com/vatesfr/xen-orchestra/issues/3322) (PR [#3315](https://github.com/vatesfr/xen-orchestra/pull/3315))
|
||||
- [New VM / Self] Filter out SRs that are not in the template's pool [#3068](https://github.com/vatesfr/xen-orchestra/issues/3068) (PR [#3070](https://github.com/vatesfr/xen-orchestra/pull/3070))
|
||||
- [New VM / Self] Fix 'unknown item' displayed in SR selector [#3267](https://github.com/vatesfr/xen-orchestra/issues/3267) (PR [#3070](https://github.com/vatesfr/xen-orchestra/pull/3070))
|
||||
|
||||
### Released packages
|
||||
|
||||
- xo-server-backup-reports v0.13.0
|
||||
- @xen-orchestra/fs 0.3.0
|
||||
- xo-server v5.25.0
|
||||
- xo-web v5.25.0
|
||||
|
||||
@@ -24,7 +90,6 @@
|
||||
- [Backup NG Overview] List the Backup NG job's modes [#3169](https://github.com/vatesfr/xen-orchestra/issues/3169) (PR [#3277](https://github.com/vatesfr/xen-orchestra/pull/3277))
|
||||
- [Backup NG form] Move "Use compression" checkbox in the advanced settings [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) (PR [#3281](https://github.com/vatesfr/xen-orchestra/pull/3281))
|
||||
- [Backup NG form] Ability to remove previous backups first before backup the VMs [#3212](https://github.com/vatesfr/xen-orchestra/issues/3212) (PR [#3260](https://github.com/vatesfr/xen-orchestra/pull/3260))
|
||||
- [Backup reports] Send report for the interrupted backup jobs on the server startup [#2998](https://github.com/vatesfr/xen-orchestra/issues/#2998) (PR [3164](https://github.com/vatesfr/xen-orchestra/pull/3164) [3154](https://github.com/vatesfr/xen-orchestra/pull/3154))
|
||||
- [Patching] Check date consistency before patching to avoid error on install [#3056](https://github.com/vatesfr/xen-orchestra/issues/3056)
|
||||
|
||||
### Bug fixes
|
||||
@@ -64,7 +129,7 @@
|
||||
|
||||
- xo-remote-parser v0.5.0
|
||||
- complex-matcher v0.4.0
|
||||
- xo-server-backup-reports v0.13.0
|
||||
- xo-server-backup-reports v0.12.3
|
||||
- xo-server v5.23.0
|
||||
- xo-web v5.23.0
|
||||
|
||||
|
||||
@@ -66,4 +66,10 @@ To solve it, you have to change a parameter in your VM. `xe vm-param-set has-ven
|
||||
|
||||
This message appears when you do not have enough free space on the target remote when running a backup to it.
|
||||
|
||||
To check your free space, enter your XOA and run `xoa check` to check free system space and `df -h` to check free space on your chosen remote storage.
|
||||
To check your free space, enter your XOA and run `xoa check` to check free system space and `df -h` to check free space on your chosen remote storage.
|
||||
|
||||
### Error: no VMs match this pattern
|
||||
|
||||
This is happening when you have a *smart backup job* that doesn't match any VMs. For example: you created a job to backup all running VMs. If no VMs are running on backup schedule, you'll have this message. This could also happen if you lost connection with your pool master (the VMs aren't visible anymore from Xen Orchestra).
|
||||
|
||||
Edit your job and try to see matching VMs or check if your pool is connected to XOA.
|
||||
@@ -31,11 +31,11 @@ You also have a filter to search anything related to these logs.
|
||||
|
||||
## Consistent backup (with quiesce snapshots)
|
||||
|
||||
All backup rely on snapshots. But what about data consistency? By default, Xen Orchestra will try to make a **quiesce snapshot** every time a snapshot is done (and fallback to normal snapshot if it's not possible).
|
||||
All backup types rely on snapshots. But what about data consistency? By default, Xen Orchestra will try to take a **quiesced snapshot** every time a snapshot is done (and fall back to normal snapshots if it's not possible).
|
||||
|
||||
All your Windows VMs can be protected (especially MS SQL or Exchange services) after you have installed Xen Tools in your VMs. A quiesce snapshots means the operating system will be notified and the cache will be flushed to disks. This way, your backups will always be consistent.
|
||||
Snapshots of Windows VMs can be quiesced (especially MS SQL or Exchange services) after you have installed Xen Tools in your VMs. However, [there is an extra step to install the VSS provider on windows](quiesce). A quiesced snapshot means the operating system will be notified and the cache will be flushed to disks. This way, your backups will always be consistent.
|
||||
|
||||
To see if you have quiesced snapshots for a VM, just go into its snapshot tab, the "info" icon means it is a quiesced snapshot:
|
||||
To see if you have quiesced snapshots for a VM, just go into its snapshot tab, then the "info" icon means it is a quiesced snapshot:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# File level restore
|
||||
|
||||
You can also restore individual files inside a VM. It works with all your existing delta backups.
|
||||
You can also restore specific files and directories inside a VM. It works with all your existing delta backups.
|
||||
|
||||
> You must use the latest XOA release. When you connect with the console, you should see `Build number: 16.12.20`. If you have this or higher (eg `17.*`), it means that's OK! Otherwise, please update your XOA.
|
||||
|
||||
> Restoring individual files from an SMB remote is not yet possible, but it's planned for the future!
|
||||
> Restoring individual files from an SMB remote is not possible yet, but it's planned for the future!
|
||||
|
||||
> File level restore **is only possible on delta backups**
|
||||
|
||||
|
||||
@@ -101,6 +101,13 @@ memory to xo-server itself:
|
||||
+ ExecStart=/usr/local/bin/node --max-old-space-size=8192 /usr/local/bin/xo-server
|
||||
```
|
||||
|
||||
The last step is to refresh and restart the service:
|
||||
|
||||
```
|
||||
$ systemctl daemon-reload
|
||||
$ systemctl restart xo-server
|
||||
```
|
||||
|
||||
### Behind a transparent proxy
|
||||
|
||||
If your are behind a transparent proxy, you'll probably have issues with the updater (SSL/TLS issues).
|
||||
|
||||
18
package.json
18
package.json
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/register": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/register": "7.0.0",
|
||||
"babel-core": "^7.0.0-0",
|
||||
"babel-eslint": "^8.1.2",
|
||||
"babel-eslint": "^9.0.0",
|
||||
"babel-jest": "^23.0.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"eslint": "^5.1.0",
|
||||
"eslint-config-standard": "12.0.0-alpha.0",
|
||||
"eslint-config-standard-jsx": "^5.0.0",
|
||||
"eslint-config-standard": "12.0.0",
|
||||
"eslint-config-standard-jsx": "^6.0.2",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-node": "^6.0.0",
|
||||
"eslint-plugin-promise": "^3.6.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.6.1",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"flow-bin": "^0.76.0",
|
||||
"flow-bin": "^0.80.0",
|
||||
"globby": "^8.0.0",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^23.0.1",
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.1",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
|
||||
@@ -26,20 +26,19 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/fs": "^0.2.1",
|
||||
"@xen-orchestra/fs": "^0.3.0",
|
||||
"cli-progress": "^2.0.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"struct-fu": "^1.2.0",
|
||||
"vhd-lib": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/plugin-transform-runtime": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"execa": "^0.10.0",
|
||||
"execa": "^1.0.0",
|
||||
"index-modules": "^0.3.0",
|
||||
"promise-toolbox": "^0.9.5",
|
||||
"rimraf": "^2.6.1",
|
||||
|
||||
@@ -14,7 +14,8 @@ export default async function main (args) {
|
||||
onProgress ({ done, total }) {
|
||||
if (bar === undefined) {
|
||||
bar = new Bar({
|
||||
format: 'merging [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}',
|
||||
format:
|
||||
'merging [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}',
|
||||
})
|
||||
bar.start(total, done)
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.0.0-beta.54",
|
||||
"async-iterator-to-stream": "^1.0.2",
|
||||
"from2": "^2.3.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
@@ -30,17 +29,16 @@
|
||||
"uuid": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/plugin-transform-runtime": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@xen-orchestra/fs": "^0.2.1",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@xen-orchestra/fs": "^0.3.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"execa": "^0.10.0",
|
||||
"execa": "^1.0.0",
|
||||
"fs-promise": "^2.0.0",
|
||||
"get-stream": "^3.0.0",
|
||||
"get-stream": "^4.0.0",
|
||||
"index-modules": "^0.3.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"tmp": "^0.0.33"
|
||||
|
||||
@@ -58,7 +58,7 @@ export default asyncIteratorToStream(async function * (
|
||||
|
||||
const maxTableEntries = Math.ceil(diskSize / VHD_BLOCK_SIZE_BYTES) + 1
|
||||
const tablePhysicalSizeBytes =
|
||||
Math.ceil(maxTableEntries * 4 / SECTOR_SIZE) * SECTOR_SIZE
|
||||
Math.ceil((maxTableEntries * 4) / SECTOR_SIZE) * SECTOR_SIZE
|
||||
|
||||
const batPosition = FOOTER_SIZE + HEADER_SIZE
|
||||
const firstBlockPosition = batPosition + tablePhysicalSizeBytes
|
||||
@@ -115,7 +115,7 @@ export default asyncIteratorToStream(async function * (
|
||||
}
|
||||
next.data.copy(
|
||||
currentBlockWithBitmap,
|
||||
bitmapSize + next.offsetBytes % VHD_BLOCK_SIZE_BYTES
|
||||
bitmapSize + (next.offsetBytes % VHD_BLOCK_SIZE_BYTES)
|
||||
)
|
||||
}
|
||||
yield * yieldAndTrack(currentBlockWithBitmap)
|
||||
|
||||
@@ -51,7 +51,10 @@ export default concurrency(2)(async function merge (
|
||||
|
||||
// finds first allocated block for the 2 following loops
|
||||
let firstBlock = 0
|
||||
while (firstBlock < maxTableEntries && !childVhd.containsBlock(firstBlock)) {
|
||||
while (
|
||||
firstBlock < maxTableEntries &&
|
||||
!childVhd.containsBlock(firstBlock)
|
||||
) {
|
||||
++firstBlock
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ export default class Vhd {
|
||||
Buffer.alloc(n),
|
||||
start
|
||||
)
|
||||
assert.equal(bytesRead, n)
|
||||
assert.strictEqual(bytesRead, n)
|
||||
return buffer
|
||||
}
|
||||
|
||||
|
||||
3
packages/xen-api/.babelrc.js
Normal file
3
packages/xen-api/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -30,14 +30,14 @@ exports.resolveRef = (xapi, type, refOrUuidOrNameLabel) =>
|
||||
isOpaqueRef(refOrUuidOrNameLabel)
|
||||
? refOrUuidOrNameLabel
|
||||
: xapi.call(`${type}.get_by_uuid`, refOrUuidOrNameLabel).catch(() =>
|
||||
xapi
|
||||
.call(`${type}.get_by_name_label`, refOrUuidOrNameLabel)
|
||||
.then(refs => {
|
||||
if (refs.length === 1) {
|
||||
return refs[0]
|
||||
}
|
||||
throw new Error(
|
||||
`no single match for ${type} with name label ${refOrUuidOrNameLabel}`
|
||||
)
|
||||
})
|
||||
)
|
||||
xapi
|
||||
.call(`${type}.get_by_name_label`, refOrUuidOrNameLabel)
|
||||
.then(refs => {
|
||||
if (refs.length === 1) {
|
||||
return refs[0]
|
||||
}
|
||||
throw new Error(
|
||||
`no single match for ${type} with name label ${refOrUuidOrNameLabel}`
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xen-api",
|
||||
"version": "0.17.0",
|
||||
"version": "0.18.0",
|
||||
"license": "ISC",
|
||||
"description": "Connector to the Xen API",
|
||||
"keywords": [
|
||||
@@ -29,16 +29,16 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"blocked": "^1.2.1",
|
||||
"debug": "^3.1.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"http-request-plus": "^0.5.0",
|
||||
"iterable-backoff": "^0.0.0",
|
||||
"jest-diff": "^23.5.0",
|
||||
"json-rpc-protocol": "^0.12.0",
|
||||
"kindof": "^2.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
@@ -51,12 +51,11 @@
|
||||
"xo-collection": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-function-bind": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -67,23 +66,5 @@
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
"transform-decorators-legacy",
|
||||
"transform-function-bind"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import 'babel-polyfill'
|
||||
|
||||
import blocked from 'blocked'
|
||||
import createDebug from 'debug'
|
||||
import diff from 'jest-diff'
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import execPromise from 'exec-promise'
|
||||
import minimist from 'minimist'
|
||||
@@ -81,16 +80,18 @@ const main = async args => {
|
||||
})
|
||||
repl.context.xapi = xapi
|
||||
|
||||
repl.context.diff = (a, b) => console.log('%s', diff(a, b))
|
||||
repl.context.find = predicate => find(xapi.objects.all, predicate)
|
||||
repl.context.findAll = predicate => filter(xapi.objects.all, predicate)
|
||||
|
||||
// Make the REPL waits for promise completion.
|
||||
repl.eval = (evaluate => (cmd, context, filename, cb) => {
|
||||
;fromCallback(cb => {
|
||||
evaluate.call(repl, cmd, context, filename, cb)
|
||||
})
|
||||
.then(value => (isArray(value) ? Promise.all(value) : value))
|
||||
::asCallback(cb)
|
||||
asCallback.call(
|
||||
fromCallback(cb => {
|
||||
evaluate.call(repl, cmd, context, filename, cb)
|
||||
}).then(value => (isArray(value) ? Promise.all(value) : value)),
|
||||
cb
|
||||
)
|
||||
})(repl.eval)
|
||||
|
||||
await eventToPromise(repl, 'exit')
|
||||
|
||||
@@ -122,7 +122,14 @@ const parseUrl = url => {
|
||||
}
|
||||
|
||||
const [, protocol = 'https:', username, password, hostname, port] = matches
|
||||
return { protocol, username, password, hostname, port }
|
||||
const parsedUrl = { protocol, hostname, port }
|
||||
if (username !== undefined) {
|
||||
parsedUrl.username = decodeURIComponent(username)
|
||||
}
|
||||
if (password !== undefined) {
|
||||
parsedUrl.password = decodeURIComponent(password)
|
||||
}
|
||||
return parsedUrl
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -136,12 +143,16 @@ const {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const NULL_REF = 'OpaqueRef:NULL'
|
||||
|
||||
const OPAQUE_REF_PREFIX = 'OpaqueRef:'
|
||||
export const isOpaqueRef = value =>
|
||||
typeof value === 'string' && startsWith(value, OPAQUE_REF_PREFIX)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const isGetAllRecordsMethod = RegExp.prototype.test.bind(/\.get_all_records$/)
|
||||
|
||||
const RE_READ_ONLY_METHOD = /^[^.]+\.get_/
|
||||
const isReadOnlyCall = (method, args) =>
|
||||
args.length === 1 &&
|
||||
@@ -249,7 +260,7 @@ export class Xapi extends EventEmitter {
|
||||
objects.getKey = getKey
|
||||
|
||||
this._objectsByRefs = createObject(null)
|
||||
this._objectsByRefs['OpaqueRef:NULL'] = undefined
|
||||
this._objectsByRefs[NULL_REF] = undefined
|
||||
|
||||
this._taskWatchers = Object.create(null)
|
||||
|
||||
@@ -417,7 +428,7 @@ export class Xapi extends EventEmitter {
|
||||
this._sessionCall('task.cancel', [taskRef]).catch(noop)
|
||||
})
|
||||
|
||||
return this.watchTask(taskRef)::lastly(() => {
|
||||
return lastly.call(this.watchTask(taskRef), () => {
|
||||
this._sessionCall('task.destroy', [taskRef]).catch(noop)
|
||||
})
|
||||
})
|
||||
@@ -485,8 +496,24 @@ export class Xapi extends EventEmitter {
|
||||
throw new Error('no object with UUID: ' + uuid)
|
||||
}
|
||||
|
||||
getRecord (type, ref) {
|
||||
return this._sessionCall(`${type}.get_record`, [ref])
|
||||
async getRecord (type, ref) {
|
||||
const record = await this._sessionCall(`${type}.get_record`, [ref])
|
||||
|
||||
// All custom properties are read-only and non enumerable.
|
||||
defineProperties(record, {
|
||||
$id: { value: record.uuid || ref },
|
||||
$ref: { value: ref },
|
||||
$type: { value: type },
|
||||
})
|
||||
|
||||
return record
|
||||
}
|
||||
|
||||
async getRecordByUuid (type, uuid) {
|
||||
return this.getRecord(
|
||||
type,
|
||||
await this._sessionCall(`${type}.get_by_uuid`, [uuid])
|
||||
)
|
||||
}
|
||||
|
||||
@cancelable
|
||||
@@ -561,7 +588,7 @@ export class Xapi extends EventEmitter {
|
||||
headers['content-length'] = '1125899906842624'
|
||||
}
|
||||
|
||||
const doRequest = override =>
|
||||
const doRequest = (...opts) =>
|
||||
httpRequest.put(
|
||||
$cancelToken,
|
||||
this._url,
|
||||
@@ -571,11 +598,12 @@ export class Xapi extends EventEmitter {
|
||||
{
|
||||
body,
|
||||
headers,
|
||||
query,
|
||||
pathname,
|
||||
maxRedirects: 0,
|
||||
rejectUnauthorized: !this._allowUnauthorized,
|
||||
},
|
||||
override,
|
||||
{ query }
|
||||
...opts
|
||||
)
|
||||
|
||||
// if a stream, sends a dummy request to probe for a
|
||||
@@ -586,8 +614,6 @@ export class Xapi extends EventEmitter {
|
||||
|
||||
// omit task_id because this request will fail on purpose
|
||||
query: 'task_id' in query ? omit(query, 'task_id') : query,
|
||||
|
||||
maxRedirects: 0,
|
||||
}).then(
|
||||
response => {
|
||||
response.req.abort()
|
||||
@@ -603,7 +629,8 @@ export class Xapi extends EventEmitter {
|
||||
statusCode,
|
||||
} = response
|
||||
if (statusCode === 302 && location !== undefined) {
|
||||
return doRequest(location)
|
||||
// ensure the original query is sent
|
||||
return doRequest(location, { query })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,6 +663,41 @@ export class Xapi extends EventEmitter {
|
||||
)
|
||||
}
|
||||
|
||||
setField ({ $type, $ref }, field, value) {
|
||||
return this.call(`${$type}.set_${field}`, $ref, value).then(noop)
|
||||
}
|
||||
|
||||
setFieldEntries (record, field, entries) {
|
||||
return Promise.all(
|
||||
Object.keys(entries).map(entry => {
|
||||
const value = entries[entry]
|
||||
if (value !== undefined) {
|
||||
return value === null
|
||||
? this.unsetFieldEntry(record, field, entry)
|
||||
: this.setFieldEntry(record, field, entry, value)
|
||||
}
|
||||
})
|
||||
).then(noop)
|
||||
}
|
||||
|
||||
async setFieldEntry ({ $type, $ref }, field, entry, value) {
|
||||
while (true) {
|
||||
try {
|
||||
await this.call(`${$type}.add_to_${field}`, $ref, entry, value)
|
||||
return
|
||||
} catch (error) {
|
||||
if (error == null || error.code !== 'MAP_DUPLICATE_KEY') {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
await this.unsetFieldEntry({ $type, $ref }, field, entry)
|
||||
}
|
||||
}
|
||||
|
||||
unsetFieldEntry ({ $type, $ref }, field, entry) {
|
||||
return this.call(`${$type}.remove_from_${field}`, $ref, entry)
|
||||
}
|
||||
|
||||
watchTask (ref) {
|
||||
const watchers = this._taskWatchers
|
||||
if (watchers === undefined) {
|
||||
@@ -695,7 +757,8 @@ export class Xapi extends EventEmitter {
|
||||
newArgs.push.apply(newArgs, args)
|
||||
}
|
||||
|
||||
return this._transportCall(method, newArgs)::pCatch(
|
||||
return pCatch.call(
|
||||
this._transportCall(method, newArgs),
|
||||
isSessionInvalid,
|
||||
() => {
|
||||
// XAPI is sometimes reinitialized and sessions are lost.
|
||||
@@ -844,12 +907,15 @@ export class Xapi extends EventEmitter {
|
||||
_watchEvents () {
|
||||
const loop = () =>
|
||||
this.status === CONNECTED &&
|
||||
this._sessionCall('event.from', [
|
||||
['*'],
|
||||
this._fromToken,
|
||||
EVENT_TIMEOUT + 0.1, // Force float.
|
||||
])
|
||||
::pTimeout(EVENT_TIMEOUT * 1.1e3) // 10% longer than the XenAPI timeout
|
||||
pTimeout
|
||||
.call(
|
||||
this._sessionCall('event.from', [
|
||||
['*'],
|
||||
this._fromToken,
|
||||
EVENT_TIMEOUT + 0.1, // Force float.
|
||||
]),
|
||||
EVENT_TIMEOUT * 1.1e3 // 10% longer than the XenAPI timeout
|
||||
)
|
||||
.then(onSuccess, onFailure)
|
||||
|
||||
const onSuccess = ({ events, token, valid_ref_counts: { task } }) => {
|
||||
@@ -894,7 +960,8 @@ export class Xapi extends EventEmitter {
|
||||
throw error
|
||||
}
|
||||
|
||||
return loop()::pCatch(
|
||||
return pCatch.call(
|
||||
loop(),
|
||||
isMethodUnknown,
|
||||
|
||||
// If the server failed, it is probably due to an excessively
|
||||
@@ -915,10 +982,7 @@ export class Xapi extends EventEmitter {
|
||||
return this._sessionCall('system.listMethods').then(methods => {
|
||||
// Uses introspection to determine the methods to use to get
|
||||
// all objects.
|
||||
const getAllRecordsMethods = filter(
|
||||
methods,
|
||||
::/\.get_all_records$/.test
|
||||
)
|
||||
const getAllRecordsMethods = filter(methods, isGetAllRecordsMethod)
|
||||
|
||||
return Promise.all(
|
||||
map(getAllRecordsMethods, method =>
|
||||
@@ -982,9 +1046,11 @@ Xapi.prototype._transportCall = reduce(
|
||||
function () {
|
||||
let iterator // lazily created
|
||||
const loop = () =>
|
||||
call
|
||||
.apply(this, arguments)
|
||||
::pCatch(isNetworkError, isXapiNetworkError, error => {
|
||||
pCatch.call(
|
||||
call.apply(this, arguments),
|
||||
isNetworkError,
|
||||
isXapiNetworkError,
|
||||
error => {
|
||||
if (iterator === undefined) {
|
||||
iterator = fibonacci()
|
||||
.clamp(undefined, 60)
|
||||
@@ -1010,17 +1076,19 @@ Xapi.prototype._transportCall = reduce(
|
||||
debug('%s: network error %s, aborting', this._humanId, error.code)
|
||||
|
||||
// mark as disconnected
|
||||
this.disconnect()::pCatch(noop)
|
||||
pCatch.call(this.disconnect(), noop)
|
||||
|
||||
throw error
|
||||
})
|
||||
}
|
||||
)
|
||||
return loop()
|
||||
},
|
||||
call =>
|
||||
function loop () {
|
||||
return call
|
||||
.apply(this, arguments)
|
||||
::pCatch(isHostSlave, ({ params: [master] }) => {
|
||||
return pCatch.call(
|
||||
call.apply(this, arguments),
|
||||
isHostSlave,
|
||||
({ params: [master] }) => {
|
||||
debug(
|
||||
'%s: host is slave, attempting to connect at %s',
|
||||
this._humanId,
|
||||
@@ -1035,7 +1103,8 @@ Xapi.prototype._transportCall = reduce(
|
||||
this._url = newUrl
|
||||
|
||||
return loop.apply(this, arguments)
|
||||
})
|
||||
}
|
||||
)
|
||||
},
|
||||
call =>
|
||||
function (method) {
|
||||
|
||||
@@ -23,9 +23,11 @@ xapi
|
||||
// Injects lots of events.
|
||||
.then(([poolRef]) => {
|
||||
const loop = () =>
|
||||
xapi
|
||||
.call('event.inject', 'pool', poolRef)
|
||||
::pDelay(10) // A small delay is required to avoid overloading the Xen API.
|
||||
pDelay
|
||||
.call(
|
||||
xapi.call('event.inject', 'pool', poolRef),
|
||||
10 // A small delay is required to avoid overloading the Xen API.
|
||||
)
|
||||
.then(loop)
|
||||
|
||||
return loop()
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"comments": false,
|
||||
"compact": true,
|
||||
"presets": [
|
||||
[
|
||||
"env", {
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
// These global variables are not a problem because the algorithm is
|
||||
// synchronous.
|
||||
let permissionsByObject
|
||||
@@ -103,7 +105,12 @@ function checkAuthorization (objectId, permission) {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export default (permissionsByObject_, getObject_, permissions, permission) => {
|
||||
module.exports = (
|
||||
permissionsByObject_,
|
||||
getObject_,
|
||||
permissions,
|
||||
permission
|
||||
) => {
|
||||
// Assign global variables.
|
||||
permissionsByObject = permissionsByObject_
|
||||
getObject = getObject_
|
||||
@@ -15,21 +15,11 @@
|
||||
"email": "julien.fontanet@vates.fr"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"bin": {},
|
||||
"files": [
|
||||
"dist/"
|
||||
"index.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-preset-env": "^1.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prepublishOnly": "yarn run build"
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.0.0-beta.54",
|
||||
"@babel/polyfill": "7.0.0",
|
||||
"bluebird": "^3.5.1",
|
||||
"chalk": "^2.2.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
@@ -49,10 +49,10 @@
|
||||
"xo-lib": "^0.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -308,8 +308,8 @@ async function listObjects (args) {
|
||||
const properties = getKeys(extractFlags(args))
|
||||
const filterProperties = properties.length
|
||||
? function (object) {
|
||||
return pick(object, properties)
|
||||
}
|
||||
return pick(object, properties)
|
||||
}
|
||||
: identity
|
||||
|
||||
const filter = args.length ? parseParameters(args) : undefined
|
||||
|
||||
@@ -22,19 +22,17 @@
|
||||
"*.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.0.0-beta.54",
|
||||
"kindof": "^2.0.0",
|
||||
"lodash": "^4.17.2",
|
||||
"make-error": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/plugin-transform-runtime": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"rimraf": "^2.6.1"
|
||||
|
||||
@@ -8,7 +8,10 @@ import isObject from './is-object'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const { create: createObject, prototype: { hasOwnProperty } } = Object
|
||||
const {
|
||||
create: createObject,
|
||||
prototype: { hasOwnProperty },
|
||||
} = Object
|
||||
|
||||
export const ACTION_ADD = 'add'
|
||||
export const ACTION_UPDATE = 'update'
|
||||
|
||||
3
packages/xo-common/.babelrc.js
Normal file
3
packages/xo-common/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -20,20 +20,21 @@
|
||||
"dist/",
|
||||
"*.js"
|
||||
],
|
||||
"browserslist": [
|
||||
"> 1%"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.18.0",
|
||||
"lodash": "^4.16.6",
|
||||
"make-error": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -44,22 +45,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": "> 1%",
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-lib/.babelrc.js
Normal file
3
packages/xo-lib/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -10,8 +10,7 @@ const xo = new Xo({
|
||||
url: 'localhost:9000',
|
||||
})
|
||||
|
||||
xo
|
||||
.open()
|
||||
xo.open()
|
||||
.then(function () {
|
||||
return xo
|
||||
.call('acl.get', {})
|
||||
|
||||
@@ -24,19 +24,22 @@
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"browserslist": [
|
||||
"> 2%"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonrpc-websocket-client": "^0.3.0",
|
||||
"jsonrpc-websocket-client": "^0.4.1",
|
||||
"lodash": "^4.17.2",
|
||||
"make-error": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -46,28 +49,5 @@
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"env": {
|
||||
"test": {
|
||||
"ignore": null
|
||||
}
|
||||
},
|
||||
"ignore": "*.spec.js",
|
||||
"plugins": [
|
||||
"lodash"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": "> 2%",
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"deep-freeze": "^0.0.1",
|
||||
|
||||
3
packages/xo-server-auth-github/.babelrc.js
Normal file
3
packages/xo-server-auth-github/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -26,35 +26,19 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.11.6",
|
||||
"passport-github": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-env": "^1.6.1"
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-auth-google/.babelrc.js
Normal file
3
packages/xo-server-auth-google/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -29,18 +29,16 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"passport-google-oauth20": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.3.3",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -50,28 +48,5 @@
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"env": {
|
||||
"test": {
|
||||
"ignore": null
|
||||
}
|
||||
},
|
||||
"ignore": "*.spec.js",
|
||||
"plugins": [
|
||||
"transform-runtime",
|
||||
"lodash"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-auth-ldap/.babelrc.js
Normal file
3
packages/xo-server-auth-ldap/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -30,10 +30,9 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.22.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"inquirer": "^6.0.0",
|
||||
@@ -42,11 +41,10 @@
|
||||
"promise-toolbox": "^0.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -56,22 +54,5 @@
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-auth-saml/.babelrc.js
Normal file
3
packages/xo-server-auth-saml/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -29,17 +29,16 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.11.6",
|
||||
"passport-saml": "^0.33.0"
|
||||
"passport-saml": "^0.35.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -50,21 +49,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-backup-reports/.babelrc.js
Normal file
3
packages/xo-server-backup-reports/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-backup-reports",
|
||||
"version": "0.12.3",
|
||||
"version": "0.13.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "Backup reports plugin for XO-Server",
|
||||
"keywords": [
|
||||
@@ -32,19 +32,18 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"human-format": "^0.10.0",
|
||||
"lodash": "^4.13.1",
|
||||
"moment-timezone": "^0.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -55,21 +54,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-cloud/.babelrc.js
Normal file
3
packages/xo-server-cloud/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-cloud",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.4",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"keywords": [
|
||||
@@ -28,19 +28,17 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.20.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"jsonrpc-websocket-client": "^0.3.0",
|
||||
"jsonrpc-websocket-client": "^0.4.1",
|
||||
"superagent": "^3.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.16.0",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.5.4"
|
||||
},
|
||||
@@ -51,21 +49,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-load-balancer/.babelrc.js
Normal file
3
packages/xo-server-load-balancer/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -27,39 +27,21 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/cron": "^1.0.3",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"lodash": "^4.16.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-stage-3": "^6.24.1"
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime",
|
||||
"lodash"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,10 @@ import { debug } from './utils'
|
||||
export default class DensityPlan extends Plan {
|
||||
_checkRessourcesThresholds (objects, averages) {
|
||||
const { low } = this._thresholds.memoryFree
|
||||
return filter(
|
||||
objects,
|
||||
object => {
|
||||
const { memory, memoryFree = memory } = averages[object.id]
|
||||
return memoryFree > low
|
||||
}
|
||||
)
|
||||
return filter(objects, object => {
|
||||
const { memory, memoryFree = memory } = averages[object.id]
|
||||
return memoryFree > low
|
||||
})
|
||||
}
|
||||
|
||||
async execute () {
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
"dependencies": {
|
||||
"@xen-orchestra/cron": "^1.0.3",
|
||||
"d3-time-format": "^2.1.1",
|
||||
"json5": "^1.0.0",
|
||||
"json5": "^2.0.1",
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -124,7 +124,7 @@ const SR_FUNCTIONS = {
|
||||
comparator: '>',
|
||||
createGetter: threshold => sr => {
|
||||
const getDisplayableValue = () =>
|
||||
sr.physical_utilisation * 100 / sr.physical_size
|
||||
(sr.physical_utilisation * 100) / sr.physical_size
|
||||
return {
|
||||
getDisplayableValue,
|
||||
shouldAlarm: () => getDisplayableValue() > threshold,
|
||||
|
||||
3
packages/xo-server-transport-email/.babelrc.js
Normal file
3
packages/xo-server-transport-email/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -28,7 +28,7 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"nodemailer": "^4.4.1",
|
||||
@@ -36,10 +36,10 @@
|
||||
"promise-toolbox": "^0.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -49,21 +49,5 @@
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-transport-nagios/.babelrc.js
Normal file
3
packages/xo-server-transport-nagios/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -28,17 +28,16 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.20.0",
|
||||
"buffer-crc32": "^0.2.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -49,21 +48,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,13 @@ class XoServerNagios {
|
||||
|
||||
const client = new net.Socket()
|
||||
|
||||
client.connect(this._conf.port, this._conf.server, () => {
|
||||
console.log('Successful connection')
|
||||
})
|
||||
client.connect(
|
||||
this._conf.port,
|
||||
this._conf.server,
|
||||
() => {
|
||||
console.log('Successful connection')
|
||||
}
|
||||
)
|
||||
|
||||
client.on('data', data => {
|
||||
const timestamp = data.readInt32BE(128)
|
||||
|
||||
3
packages/xo-server-transport-slack/.babelrc.js
Normal file
3
packages/xo-server-transport-slack/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -29,16 +29,16 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"promise-toolbox": "^0.9.5",
|
||||
"slack-node": "^0.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-preset-env": "^1.0.0",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.5.4"
|
||||
},
|
||||
@@ -49,18 +49,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-transport-xmpp/.babelrc.js
Normal file
3
packages/xo-server-transport-xmpp/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -29,17 +29,16 @@
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.11.6",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"node-xmpp-client": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -50,20 +49,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/xo-server-usage-report/.babelrc.js
Normal file
3
packages/xo-server-usage-report/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||
require('./package.json')
|
||||
)
|
||||
@@ -31,22 +31,20 @@
|
||||
"report.html.tpl"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xen-orchestra/cron": "^1.0.3",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"handlebars": "^4.0.6",
|
||||
"html-minifier": "^3.5.8",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
@@ -57,22 +55,5 @@
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ function computePercentage (curr, prev, options) {
|
||||
opt =>
|
||||
prev[opt] === 0 || prev[opt] === null
|
||||
? 'NONE'
|
||||
: `${(curr[opt] - prev[opt]) * 100 / prev[opt]}`
|
||||
: `${((curr[opt] - prev[opt]) * 100) / prev[opt]}`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server",
|
||||
"version": "5.24.0",
|
||||
"version": "5.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "Server part of Xen-Orchestra",
|
||||
"keywords": [
|
||||
@@ -31,12 +31,12 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.0.0-beta.54",
|
||||
"@babel/polyfill": "7.0.0",
|
||||
"@xen-orchestra/cron": "^1.0.3",
|
||||
"@xen-orchestra/fs": "^0.2.1",
|
||||
"@xen-orchestra/fs": "^0.3.0",
|
||||
"ajv": "^6.1.1",
|
||||
"app-conf": "^0.5.0",
|
||||
"archiver": "^2.1.0",
|
||||
"archiver": "^3.0.0",
|
||||
"async-iterator-to-stream": "^1.0.1",
|
||||
"base64url": "^3.0.0",
|
||||
"bind-property-descriptor": "^1.0.0",
|
||||
@@ -53,13 +53,13 @@
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"execa": "^0.10.0",
|
||||
"execa": "^1.0.0",
|
||||
"express": "^4.16.2",
|
||||
"express-session": "^1.15.6",
|
||||
"fatfs": "^0.10.4",
|
||||
"from2": "^2.3.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-stream": "^3.0.0",
|
||||
"get-stream": "^4.0.0",
|
||||
"golike-defer": "^0.4.1",
|
||||
"hashy": "^0.7.1",
|
||||
"helmet": "^3.9.0",
|
||||
@@ -72,7 +72,7 @@
|
||||
"jest-worker": "^23.0.0",
|
||||
"js-yaml": "^3.10.0",
|
||||
"json-rpc-peer": "^0.15.3",
|
||||
"json5": "^1.0.0",
|
||||
"json5": "^2.0.1",
|
||||
"julien-f-source-map-support": "0.1.0",
|
||||
"julien-f-unzip": "^0.2.1",
|
||||
"kindof": "^2.0.0",
|
||||
@@ -112,8 +112,8 @@
|
||||
"uuid": "^3.0.1",
|
||||
"value-matcher": "^0.2.0",
|
||||
"vhd-lib": "^0.3.0",
|
||||
"ws": "^5.0.0",
|
||||
"xen-api": "^0.17.0",
|
||||
"ws": "^6.0.0",
|
||||
"xen-api": "^0.18.0",
|
||||
"xml2js": "^0.4.19",
|
||||
"xo-acl-resolver": "^0.2.4",
|
||||
"xo-collection": "^0.4.1",
|
||||
@@ -123,17 +123,17 @@
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.54",
|
||||
"@babel/core": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-export-default-from": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-function-bind": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-pipeline-operator": "7.0.0-beta.54",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.0.0-beta.54",
|
||||
"@babel/preset-env": "7.0.0-beta.54",
|
||||
"@babel/preset-flow": "7.0.0-beta.54",
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.0.0",
|
||||
"@babel/plugin-proposal-pipeline-operator": "7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"index-modules": "^0.3.0",
|
||||
|
||||
@@ -35,8 +35,8 @@ list.params = {
|
||||
id: { type: 'string' },
|
||||
}
|
||||
|
||||
export async function create ({ name, url }) {
|
||||
return this.createRemote({ name, url })
|
||||
export async function create ({ name, url, options }) {
|
||||
return this.createRemote({ name, url, options })
|
||||
}
|
||||
|
||||
create.permission = 'admin'
|
||||
@@ -44,10 +44,11 @@ create.description = 'Creates a new fs remote point'
|
||||
create.params = {
|
||||
name: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
options: { type: 'string', optional: true },
|
||||
}
|
||||
|
||||
export async function set ({ id, name, url, enabled }) {
|
||||
await this.updateRemote(id, { name, url, enabled })
|
||||
export async function set ({ id, name, url, options, enabled }) {
|
||||
await this.updateRemote(id, { name, url, options, enabled })
|
||||
}
|
||||
|
||||
set.permission = 'admin'
|
||||
@@ -56,6 +57,7 @@ set.params = {
|
||||
id: { type: 'string' },
|
||||
name: { type: 'string', optional: true },
|
||||
url: { type: 'string', optional: true },
|
||||
options: { type: ['string', 'null'], optional: true },
|
||||
enabled: { type: 'boolean', optional: true },
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ set.params = {
|
||||
enabled: { type: 'boolean', optional: true },
|
||||
id: { type: 'string' },
|
||||
jobId: { type: 'string', optional: true },
|
||||
name: { type: 'string', optional: true },
|
||||
name: { type: ['string', 'null'], optional: true },
|
||||
timezone: { type: 'string', optional: true },
|
||||
}
|
||||
|
||||
|
||||
@@ -319,6 +319,46 @@ createLvm.resolve = {
|
||||
host: ['host', 'host', 'administrate'],
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Local ext SR
|
||||
|
||||
// This functions creates a local ext SR
|
||||
|
||||
export async function createExt ({ host, nameLabel, nameDescription, device }) {
|
||||
const xapi = this.getXapi(host)
|
||||
|
||||
const deviceConfig = {
|
||||
device,
|
||||
}
|
||||
|
||||
const srRef = await xapi.call(
|
||||
'SR.create',
|
||||
host._xapiRef,
|
||||
deviceConfig,
|
||||
'0',
|
||||
nameLabel,
|
||||
nameDescription,
|
||||
'ext', // SR ext
|
||||
'user', // recommended by Citrix
|
||||
false,
|
||||
{}
|
||||
)
|
||||
|
||||
const sr = await xapi.call('SR.get_record', srRef)
|
||||
return sr.uuid
|
||||
}
|
||||
|
||||
createExt.params = {
|
||||
host: { type: 'string' },
|
||||
nameLabel: { type: 'string' },
|
||||
nameDescription: { type: 'string' },
|
||||
device: { type: 'string' },
|
||||
}
|
||||
|
||||
createExt.resolve = {
|
||||
host: ['host', 'host', 'administrate'],
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// This function helps to detect all NFS shares (exports) on a NFS server
|
||||
// Return a table of exports with their paths and ACLs
|
||||
|
||||
@@ -456,7 +456,7 @@ export async function migrate ({
|
||||
if (mapVdisSrs) {
|
||||
mapVdisSrsXapi = {}
|
||||
forEach(mapVdisSrs, (srId, vdiId) => {
|
||||
const vdiXapiId = this.getObject(vdiId, 'VDI')._xapiId
|
||||
const vdiXapiId = this.getObject(vdiId, ['VDI', 'VDI-snapshot'])._xapiId
|
||||
mapVdisSrsXapi[vdiXapiId] = this.getObject(srId, 'SR')._xapiId
|
||||
return permissions.push([srId, 'administrate'])
|
||||
})
|
||||
@@ -994,16 +994,18 @@ rollingDrCopy.description =
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export function start ({ vm, force }) {
|
||||
return this.getXapi(vm).startVm(vm._xapiId, force)
|
||||
export function start ({ vm, force, host }) {
|
||||
return this.getXapi(vm).startVm(vm._xapiId, host?._xapiId, force)
|
||||
}
|
||||
|
||||
start.params = {
|
||||
force: { type: 'boolean', optional: true },
|
||||
host: { type: 'string', optional: true },
|
||||
id: { type: 'string' },
|
||||
}
|
||||
|
||||
start.resolve = {
|
||||
host: ['host', 'host', 'operate'],
|
||||
vm: ['id', 'VM', 'operate'],
|
||||
}
|
||||
|
||||
|
||||
@@ -38,19 +38,7 @@ export default class Collection extends EventEmitter {
|
||||
const { Model } = this
|
||||
map(
|
||||
models,
|
||||
model => {
|
||||
if (!(model instanceof Model)) {
|
||||
model = new Model(model)
|
||||
}
|
||||
|
||||
const error = model.validate()
|
||||
if (error) {
|
||||
// TODO: Better system inspired by Backbone.js
|
||||
throw error
|
||||
}
|
||||
|
||||
return model.properties
|
||||
},
|
||||
model => (model instanceof Model ? model.properties : model),
|
||||
models
|
||||
)
|
||||
|
||||
@@ -112,12 +100,6 @@ export default class Collection extends EventEmitter {
|
||||
throw new Error('a model without an id cannot be updated')
|
||||
}
|
||||
|
||||
const error = model.validate()
|
||||
if (error !== undefined) {
|
||||
// TODO: Better system inspired by Backbone.js.
|
||||
throw error
|
||||
}
|
||||
|
||||
return model.properties
|
||||
},
|
||||
models
|
||||
@@ -155,12 +137,12 @@ export default class Collection extends EventEmitter {
|
||||
|
||||
exists (properties) {
|
||||
/* jshint eqnull: true */
|
||||
return this.first(properties).then(model => model != null)
|
||||
return this.first(properties).then(model => model !== undefined)
|
||||
}
|
||||
|
||||
async _first (properties) {
|
||||
const models = await this.get(properties)
|
||||
|
||||
return models.length ? models[0] : null
|
||||
return models.length ? models[0] : undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,14 +83,14 @@ export default class Redis extends Collection {
|
||||
values == null
|
||||
? redis.srem(idsIndex, id) // entry no longer exists
|
||||
: asyncMap(indexes, index => {
|
||||
const value = values[index]
|
||||
if (value !== undefined) {
|
||||
return redis.sadd(
|
||||
`${prefix}_${index}:${String(value).toLowerCase()}`,
|
||||
id
|
||||
)
|
||||
}
|
||||
})
|
||||
const value = values[index]
|
||||
if (value !== undefined) {
|
||||
return redis.sadd(
|
||||
`${prefix}_${index}:${String(value).toLowerCase()}`,
|
||||
id
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -161,7 +161,9 @@ export default class Redis extends Collection {
|
||||
return
|
||||
}
|
||||
|
||||
params.push(name, value)
|
||||
if (value !== undefined) {
|
||||
params.push(name, value)
|
||||
}
|
||||
})
|
||||
|
||||
const key = `${prefix}:${id}`
|
||||
|
||||
@@ -15,14 +15,6 @@ export default class Model extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the model after construction.
|
||||
initialize () {}
|
||||
|
||||
// Validate the defined properties.
|
||||
//
|
||||
// Returns the error if any.
|
||||
validate (properties) {}
|
||||
|
||||
// Get a property.
|
||||
get (name, def) {
|
||||
const value = this.properties[name]
|
||||
|
||||
@@ -12,20 +12,6 @@ const DEFAULT_ACTION = 'admin'
|
||||
|
||||
export default class Acl extends Model {}
|
||||
|
||||
Acl.create = (subject, object, action) => {
|
||||
return Acl.hash(subject, object, action).then(
|
||||
hash =>
|
||||
new Acl({
|
||||
id: hash,
|
||||
subject,
|
||||
object,
|
||||
action,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
Acl.hash = (subject, object, action) => multiKeyHash(subject, object, action)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class Acls extends Collection {
|
||||
@@ -34,15 +20,25 @@ export class Acls extends Collection {
|
||||
}
|
||||
|
||||
create (subject, object, action) {
|
||||
return Acl.create(subject, object, action).then(acl => this.add(acl))
|
||||
return multiKeyHash(subject, object, action)
|
||||
.then(
|
||||
hash =>
|
||||
new Acl({
|
||||
id: hash,
|
||||
subject,
|
||||
object,
|
||||
action,
|
||||
})
|
||||
)
|
||||
.then(acl => this.add(acl))
|
||||
}
|
||||
|
||||
delete (subject, object, action) {
|
||||
return Acl.hash(subject, object, action).then(hash => this.remove(hash))
|
||||
return multiKeyHash(subject, object, action).then(hash => this.remove(hash))
|
||||
}
|
||||
|
||||
aclExists (subject, object, action) {
|
||||
return Acl.hash(subject, object, action).then(hash => this.exists(hash))
|
||||
return multiKeyHash(subject, object, action).then(hash => this.exists(hash))
|
||||
}
|
||||
|
||||
async get (properties) {
|
||||
@@ -61,10 +57,9 @@ export class Acls extends Collection {
|
||||
await this.remove(mapToArray(toUpdate, 'id'))
|
||||
|
||||
// Compute the new ids (new hashes).
|
||||
const { hash } = Acl
|
||||
await Promise.all(
|
||||
mapToArray(toUpdate, acl =>
|
||||
hash(acl.subject, acl.object, acl.action).then(id => {
|
||||
multiKeyHash(acl.subject, acl.object, acl.action).then(id => {
|
||||
acl.id = id
|
||||
})
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ export class PluginsMetadata extends Collection {
|
||||
|
||||
async merge (id, data) {
|
||||
const pluginMetadata = await this.first(id)
|
||||
if (!pluginMetadata) {
|
||||
if (pluginMetadata === undefined) {
|
||||
throw new Error('no such plugin metadata')
|
||||
}
|
||||
|
||||
|
||||
@@ -11,21 +11,6 @@ export class Remotes extends Collection {
|
||||
return Remote
|
||||
}
|
||||
|
||||
create (name, url) {
|
||||
return this.add(
|
||||
new Remote({
|
||||
name,
|
||||
url,
|
||||
enabled: false,
|
||||
error: '',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async save (remote) {
|
||||
return /* await */ this.update(remote)
|
||||
}
|
||||
|
||||
async get (properties) {
|
||||
const remotes = await super.get(properties)
|
||||
forEach(remotes, remote => {
|
||||
|
||||
@@ -58,16 +58,15 @@ export default function proxyConsole (ws, vmConsole, sessionId) {
|
||||
ws.close()
|
||||
})
|
||||
|
||||
ws
|
||||
.on('error', error => {
|
||||
closed = true
|
||||
debug(
|
||||
'error from the XO client: %s',
|
||||
error.stack || error.message || error
|
||||
)
|
||||
ws.on('error', error => {
|
||||
closed = true
|
||||
debug(
|
||||
'error from the XO client: %s',
|
||||
error.stack || error.message || error
|
||||
)
|
||||
|
||||
socket.end()
|
||||
})
|
||||
socket.end()
|
||||
})
|
||||
.on('message', data => {
|
||||
if (!closed) {
|
||||
socket.write(data)
|
||||
|
||||
@@ -13,6 +13,10 @@ export default {
|
||||
type: 'string',
|
||||
description: 'identifier of this job',
|
||||
},
|
||||
jobName: {
|
||||
type: 'string',
|
||||
description: 'name of this job when it was started',
|
||||
},
|
||||
scheduleId: {
|
||||
type: 'string',
|
||||
description: 'identifier of the schedule which ran the job',
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('formatXml()', function () {
|
||||
|
||||
describe('generateToken()', () => {
|
||||
it('generates a string', async () => {
|
||||
expect(typeof await generateToken()).toBe('string')
|
||||
expect(typeof (await generateToken())).toBe('string')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
every,
|
||||
find,
|
||||
filter,
|
||||
flatMap,
|
||||
flatten,
|
||||
groupBy,
|
||||
includes,
|
||||
@@ -737,10 +738,8 @@ export default class Xapi extends XapiBase {
|
||||
async exportVm ($cancelToken, vmId, { compress = true } = {}) {
|
||||
const vm = this.getObject(vmId)
|
||||
|
||||
let host
|
||||
let snapshotRef
|
||||
if (isVmRunning(vm)) {
|
||||
host = vm.$resident_on
|
||||
snapshotRef = (await this._snapshotVm(
|
||||
$cancelToken,
|
||||
vm,
|
||||
@@ -749,7 +748,6 @@ export default class Xapi extends XapiBase {
|
||||
}
|
||||
|
||||
const promise = this.getResource($cancelToken, '/export/', {
|
||||
host,
|
||||
query: {
|
||||
ref: snapshotRef || vm.$ref,
|
||||
use_compression: compress ? 'true' : 'false',
|
||||
@@ -1149,7 +1147,8 @@ export default class Xapi extends XapiBase {
|
||||
// VDIs/SRs mapping
|
||||
const vdis = {}
|
||||
const defaultSr = host.$pool.$default_SR
|
||||
for (const vbd of vm.$VBDs) {
|
||||
const vbds = flatMap(vm.$snapshots, '$VBDs').concat(vm.$VBDs)
|
||||
for (const vbd of vbds) {
|
||||
const vdi = vbd.$VDI
|
||||
if (vbd.type === 'Disk') {
|
||||
vdis[vdi.$ref] =
|
||||
@@ -1299,9 +1298,7 @@ export default class Xapi extends XapiBase {
|
||||
const taskRef = await this.createTask('VM import')
|
||||
const query = {}
|
||||
|
||||
let host
|
||||
if (sr != null) {
|
||||
host = sr.$PBDs[0].$host
|
||||
query.sr_id = sr.$ref
|
||||
}
|
||||
|
||||
@@ -1317,7 +1314,6 @@ export default class Xapi extends XapiBase {
|
||||
}
|
||||
|
||||
const vmRef = await this.putResource($cancelToken, stream, '/import/', {
|
||||
host,
|
||||
query,
|
||||
task: taskRef,
|
||||
}).then(extractOpaqueRef)
|
||||
@@ -1487,25 +1483,31 @@ export default class Xapi extends XapiBase {
|
||||
)
|
||||
|
||||
let ref
|
||||
try {
|
||||
ref = await this.callAsync(
|
||||
$cancelToken,
|
||||
'VM.snapshot_with_quiesce',
|
||||
vm.$ref,
|
||||
nameLabel
|
||||
).then(extractOpaqueRef)
|
||||
this.addTag(ref, 'quiesce')::ignoreErrors()
|
||||
} catch (error) {
|
||||
const { code } = error
|
||||
if (
|
||||
code !== 'VM_SNAPSHOT_WITH_QUIESCE_NOT_SUPPORTED' &&
|
||||
// quiesce only work on a running VM
|
||||
code !== 'VM_BAD_POWER_STATE' &&
|
||||
// quiesce failed, fallback on standard snapshot
|
||||
// TODO: emit warning
|
||||
code !== 'VM_SNAPSHOT_WITH_QUIESCE_FAILED'
|
||||
) {
|
||||
throw error
|
||||
do {
|
||||
if (!vm.tags.includes('xo-disable-quiesce')) {
|
||||
try {
|
||||
ref = await this.callAsync(
|
||||
$cancelToken,
|
||||
'VM.snapshot_with_quiesce',
|
||||
vm.$ref,
|
||||
nameLabel
|
||||
).then(extractOpaqueRef)
|
||||
this.addTag(ref, 'quiesce')::ignoreErrors()
|
||||
|
||||
break
|
||||
} catch (error) {
|
||||
const { code } = error
|
||||
if (
|
||||
code !== 'VM_SNAPSHOT_WITH_QUIESCE_NOT_SUPPORTED' &&
|
||||
// quiesce only work on a running VM
|
||||
code !== 'VM_BAD_POWER_STATE' &&
|
||||
// quiesce failed, fallback on standard snapshot
|
||||
// TODO: emit warning
|
||||
code !== 'VM_SNAPSHOT_WITH_QUIESCE_FAILED'
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
ref = await this.callAsync(
|
||||
$cancelToken,
|
||||
@@ -1513,7 +1515,8 @@ export default class Xapi extends XapiBase {
|
||||
vm.$ref,
|
||||
nameLabel
|
||||
).then(extractOpaqueRef)
|
||||
}
|
||||
} while (false)
|
||||
|
||||
// Convert the template to a VM and wait to have receive the up-
|
||||
// to-date object.
|
||||
const [, snapshot] = await Promise.all([
|
||||
@@ -1534,7 +1537,7 @@ export default class Xapi extends XapiBase {
|
||||
await this._updateObjectMapProperty(vm, 'VCPUs_params', { weight })
|
||||
}
|
||||
|
||||
async _startVm (vm, force) {
|
||||
async _startVm (vm, host, force) {
|
||||
debug(`Starting VM ${vm.name_label}`)
|
||||
|
||||
if (force) {
|
||||
@@ -1543,17 +1546,25 @@ export default class Xapi extends XapiBase {
|
||||
})
|
||||
}
|
||||
|
||||
return this.call(
|
||||
'VM.start',
|
||||
vm.$ref,
|
||||
false, // Start paused?
|
||||
false // Skip pre-boot checks?
|
||||
)
|
||||
return host === undefined
|
||||
? this.call(
|
||||
'VM.start',
|
||||
vm.$ref,
|
||||
false, // Start paused?
|
||||
false // Skip pre-boot checks?
|
||||
)
|
||||
: this.call(
|
||||
'VM.start_on',
|
||||
vm.$ref,
|
||||
host.$ref,
|
||||
false,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
async startVm (vmId, force) {
|
||||
async startVm (vmId, hostId, force) {
|
||||
try {
|
||||
await this._startVm(this.getObject(vmId), force)
|
||||
await this._startVm(this.getObject(vmId), hostId && this.getObject(hostId), force)
|
||||
} catch (e) {
|
||||
if (e.code === 'OPERATION_BLOCKED') {
|
||||
throw forbiddenOperation('Start', e.params[1])
|
||||
@@ -1931,9 +1942,6 @@ export default class Xapi extends XapiBase {
|
||||
@concurrency(12, stream => stream.then(stream => fromEvent(stream, 'end')))
|
||||
@cancelable
|
||||
_exportVdi ($cancelToken, vdi, base, format = VDI_FORMAT_VHD) {
|
||||
const sr = vdi.$SR
|
||||
const host = sr.$PBDs[0].$host
|
||||
|
||||
const query = {
|
||||
format,
|
||||
vdi: vdi.$ref,
|
||||
@@ -1949,13 +1957,12 @@ export default class Xapi extends XapiBase {
|
||||
)
|
||||
|
||||
return this.getResource($cancelToken, '/export_raw_vdi/', {
|
||||
host,
|
||||
query,
|
||||
task: this.createTask('VDI Export', vdi.name_label),
|
||||
}).catch(error => {
|
||||
// augment the error with as much relevant info as possible
|
||||
error.host = host
|
||||
error.SR = sr
|
||||
error.pool_master = vdi.$pool.$master
|
||||
error.SR = vdi.$SR
|
||||
error.VDI = vdi
|
||||
|
||||
throw error
|
||||
@@ -1970,19 +1977,10 @@ export default class Xapi extends XapiBase {
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
async _importVdiContent (vdi, body, format = VDI_FORMAT_VHD) {
|
||||
const sr = vdi.$SR
|
||||
const pbd = find(sr.$PBDs, 'currently_attached')
|
||||
if (pbd === undefined) {
|
||||
throw new Error('no valid PBDs found')
|
||||
}
|
||||
|
||||
const host = pbd.$HOST
|
||||
|
||||
await Promise.all([
|
||||
body.task,
|
||||
body.checksumVerified,
|
||||
this.putResource(body, '/import_raw_vdi/', {
|
||||
host,
|
||||
query: {
|
||||
format,
|
||||
vdi: vdi.$ref,
|
||||
@@ -1991,8 +1989,8 @@ export default class Xapi extends XapiBase {
|
||||
}),
|
||||
]).catch(error => {
|
||||
// augment the error with as much relevant info as possible
|
||||
error.host = host
|
||||
error.SR = sr
|
||||
error.pool_master = vdi.$pool.$master
|
||||
error.SR = vdi.$SR
|
||||
error.VDI = vdi
|
||||
|
||||
throw error
|
||||
|
||||
@@ -165,14 +165,14 @@ export default class {
|
||||
}
|
||||
|
||||
async deleteAuthenticationToken (id) {
|
||||
if (!await this._tokens.remove(id)) {
|
||||
if (!(await this._tokens.remove(id))) {
|
||||
throw noSuchAuthenticationToken(id)
|
||||
}
|
||||
}
|
||||
|
||||
async getAuthenticationToken (id) {
|
||||
let token = await this._tokens.first(id)
|
||||
if (!token) {
|
||||
if (token === undefined) {
|
||||
throw noSuchAuthenticationToken(id)
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ export default {
|
||||
data: data.data,
|
||||
id,
|
||||
jobId,
|
||||
jobName: data.jobName,
|
||||
scheduleId,
|
||||
start: time,
|
||||
status: runningJobs[jobId] === id ? 'pending' : 'interrupted',
|
||||
|
||||
@@ -104,6 +104,16 @@ type Metadata = MetadataDelta | MetadataFull
|
||||
const compareSnapshotTime = (a: Vm, b: Vm): number =>
|
||||
a.snapshot_time < b.snapshot_time ? -1 : 1
|
||||
|
||||
const getReplicatedVmDatetime = (vm: Vm) => {
|
||||
const {
|
||||
'xo:backup:datetime': datetime = vm.name_label.slice(-17, -1),
|
||||
} = vm.other_config
|
||||
return datetime
|
||||
}
|
||||
|
||||
const compareReplicatedVmDatetime = (a: Vm, b: Vm): number =>
|
||||
getReplicatedVmDatetime(a) < getReplicatedVmDatetime(b) ? -1 : 1
|
||||
|
||||
const compareTimestamp = (a: Metadata, b: Metadata): number =>
|
||||
a.timestamp - b.timestamp
|
||||
|
||||
@@ -183,9 +193,7 @@ const listReplicatedVms = (
|
||||
}
|
||||
}
|
||||
|
||||
// the replicated VMs have been created from a snapshot, therefore we can use
|
||||
// `snapshot_time` as the creation time
|
||||
return values(vms).sort(compareSnapshotTime)
|
||||
return values(vms).sort(compareReplicatedVmDatetime)
|
||||
}
|
||||
|
||||
const importers: $Dict<
|
||||
@@ -397,7 +405,7 @@ const wrapTaskFn = <T>(
|
||||
//
|
||||
// Attributes of created VMs:
|
||||
//
|
||||
// - name: `${original name} (${safeDateFormat(backup timestamp)})`
|
||||
// - name: `${original name} - ${job name} - (${safeDateFormat(backup timestamp)})`
|
||||
// - tag:
|
||||
// - copy in delta mode: `Continuous Replication`
|
||||
// - copy in full mode: `Disaster Recovery`
|
||||
@@ -637,46 +645,50 @@ export default class BackupNg {
|
||||
const app = this._app
|
||||
await Promise.all(
|
||||
remotes.map(async remoteId => {
|
||||
const handler = await app.getRemoteHandler(remoteId)
|
||||
try {
|
||||
const handler = await app.getRemoteHandler(remoteId)
|
||||
|
||||
const entries = (await handler.list(BACKUP_DIR).catch(error => {
|
||||
if (error == null || error.code !== 'ENOENT') {
|
||||
throw error
|
||||
}
|
||||
return []
|
||||
})).filter(name => name !== 'index.json')
|
||||
|
||||
const backupsByVm = (backupsByVmByRemote[remoteId] = {})
|
||||
await Promise.all(
|
||||
entries.map(async vmUuid => {
|
||||
// $FlowFixMe don't know what is the problem (JFT)
|
||||
const backups = await this._listVmBackups(handler, vmUuid)
|
||||
|
||||
if (backups.length === 0) {
|
||||
return
|
||||
const entries = (await handler.list(BACKUP_DIR).catch(error => {
|
||||
if (error == null || error.code !== 'ENOENT') {
|
||||
throw error
|
||||
}
|
||||
return []
|
||||
})).filter(name => name !== 'index.json')
|
||||
|
||||
// inject an id usable by importVmBackupNg()
|
||||
backups.forEach(backup => {
|
||||
backup.id = `${remoteId}/${backup._filename}`
|
||||
const backupsByVm = (backupsByVmByRemote[remoteId] = {})
|
||||
await Promise.all(
|
||||
entries.map(async vmUuid => {
|
||||
// $FlowFixMe don't know what is the problem (JFT)
|
||||
const backups = await this._listVmBackups(handler, vmUuid)
|
||||
|
||||
const { vdis, vhds } = backup
|
||||
backup.disks =
|
||||
vhds === undefined
|
||||
? []
|
||||
: Object.keys(vhds).map(vdiId => {
|
||||
const vdi = vdis[vdiId]
|
||||
return {
|
||||
id: `${dirname(backup._filename)}/${vhds[vdiId]}`,
|
||||
name: vdi.name_label,
|
||||
uuid: vdi.uuid,
|
||||
}
|
||||
})
|
||||
if (backups.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// inject an id usable by importVmBackupNg()
|
||||
backups.forEach(backup => {
|
||||
backup.id = `${remoteId}/${backup._filename}`
|
||||
|
||||
const { vdis, vhds } = backup
|
||||
backup.disks =
|
||||
vhds === undefined
|
||||
? []
|
||||
: Object.keys(vhds).map(vdiId => {
|
||||
const vdi = vdis[vdiId]
|
||||
return {
|
||||
id: `${dirname(backup._filename)}/${vhds[vdiId]}`,
|
||||
name: vdi.name_label,
|
||||
uuid: vdi.uuid,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
backupsByVm[vmUuid] = backups
|
||||
})
|
||||
|
||||
backupsByVm[vmUuid] = backups
|
||||
})
|
||||
)
|
||||
)
|
||||
} catch (error) {
|
||||
console.warn('[Warn] listVmBackups for remote %s:', remoteId, error)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
@@ -758,6 +770,7 @@ export default class BackupNg {
|
||||
parentId: taskId,
|
||||
},
|
||||
xapi._updateObjectMapProperty(vm, 'other_config', {
|
||||
'xo:backup:datetime': null,
|
||||
'xo:backup:job': null,
|
||||
'xo:backup:schedule': null,
|
||||
'xo:backup:vm': null,
|
||||
@@ -869,6 +882,7 @@ export default class BackupNg {
|
||||
parentId: taskId,
|
||||
},
|
||||
xapi._updateObjectMapProperty(snapshot, 'other_config', {
|
||||
'xo:backup:datetime': snapshot.snapshot_time,
|
||||
'xo:backup:job': jobId,
|
||||
'xo:backup:schedule': scheduleId,
|
||||
'xo:backup:vm': vmUuid,
|
||||
@@ -1044,9 +1058,9 @@ export default class BackupNg {
|
||||
},
|
||||
xapi._importVm($cancelToken, fork, sr, vm =>
|
||||
xapi._setObjectProperties(vm, {
|
||||
nameLabel: `${metadata.vm.name_label} (${safeDateFormat(
|
||||
metadata.timestamp
|
||||
)})`,
|
||||
nameLabel: `${metadata.vm.name_label} - ${
|
||||
job.name
|
||||
} - (${safeDateFormat(metadata.timestamp)})`,
|
||||
})
|
||||
)
|
||||
)
|
||||
@@ -1374,9 +1388,9 @@ export default class BackupNg {
|
||||
},
|
||||
xapi.importDeltaVm(fork, {
|
||||
disableStartAfterImport: false, // we'll take care of that
|
||||
name_label: `${metadata.vm.name_label} (${safeDateFormat(
|
||||
metadata.timestamp
|
||||
)})`,
|
||||
name_label: `${metadata.vm.name_label} - ${
|
||||
job.name
|
||||
} - (${safeDateFormat(metadata.timestamp)})`,
|
||||
srId,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -63,7 +63,7 @@ export default class {
|
||||
|
||||
async getCloudConfig (id: string): Promise<CloudConfig> {
|
||||
const cloudConfig = await this._db.first(id)
|
||||
if (cloudConfig === null) {
|
||||
if (cloudConfig === undefined) {
|
||||
throw noSuchObject(id, 'cloud config')
|
||||
}
|
||||
return cloudConfig.properties
|
||||
|
||||
@@ -34,12 +34,30 @@ const PARTITION_TYPE_NAMES = {
|
||||
|
||||
const RE_VHDI = /^vhdi(\d+)$/
|
||||
|
||||
async function addDirectory (zip, realPath, metadataPath) {
|
||||
try {
|
||||
const files = await readdir(realPath)
|
||||
await Promise.all(
|
||||
files.map(file =>
|
||||
addDirectory(zip, realPath + '/' + file, metadataPath + '/' + file)
|
||||
)
|
||||
)
|
||||
} catch (error) {
|
||||
if (error == null || error.code !== 'ENOTDIR') {
|
||||
throw error
|
||||
}
|
||||
zip.addFile(realPath, metadataPath)
|
||||
}
|
||||
}
|
||||
|
||||
const parsePartxLine = createPairsParser({
|
||||
keyTransform: key => (key === 'UUID' ? 'id' : key.toLowerCase()),
|
||||
valueTransform: (value, key) =>
|
||||
key === 'start' || key === 'size'
|
||||
? +value
|
||||
: key === 'type' ? PARTITION_TYPE_NAMES[+value] || value : value,
|
||||
: key === 'type'
|
||||
? PARTITION_TYPE_NAMES[+value] || value
|
||||
: value,
|
||||
})
|
||||
|
||||
const listLvmLogicalVolumes = defer(
|
||||
@@ -162,9 +180,15 @@ export default class BackupNgFileRestore {
|
||||
$defer.onFailure(partition.unmount)
|
||||
|
||||
const zip = new ZipFile()
|
||||
paths.forEach(file => {
|
||||
zip.addFile(resolveSubpath(partition.path, file), normalize('./' + file))
|
||||
})
|
||||
await Promise.all(
|
||||
paths.map(file =>
|
||||
addDirectory(
|
||||
zip,
|
||||
resolveSubpath(partition.path, file),
|
||||
normalize('./' + file).replace(/\/+$/, '')
|
||||
)
|
||||
)
|
||||
)
|
||||
zip.end()
|
||||
return zip.outputStream.on('end', () =>
|
||||
partition.unmount().then(disk.unmount)
|
||||
|
||||
@@ -175,8 +175,8 @@ export default class IpPools {
|
||||
Promise.all(mapToArray(updatedIpPools, ipPool => this._save(ipPool)))
|
||||
return resourseSetId
|
||||
? this._xo
|
||||
.allocateLimitsInResourceSet(limits, resourseSetId)
|
||||
.then(saveIpPools)
|
||||
.allocateLimitsInResourceSet(limits, resourseSetId)
|
||||
.then(saveIpPools)
|
||||
: saveIpPools()
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -200,7 +200,10 @@ export default class Jobs {
|
||||
|
||||
async getJob (id: string, type?: string): Promise<Job> {
|
||||
let job = await this._jobs.first(id)
|
||||
if (job === null || (type !== undefined && job.properties.type !== type)) {
|
||||
if (
|
||||
job === undefined ||
|
||||
(type !== undefined && job.properties.type !== type)
|
||||
) {
|
||||
throw noSuchObject(id, 'job')
|
||||
}
|
||||
|
||||
@@ -272,6 +275,7 @@ export default class Jobs {
|
||||
event: 'job.start',
|
||||
userId: job.userId,
|
||||
jobId: id,
|
||||
jobName: job.name,
|
||||
scheduleId: schedule?.id,
|
||||
// $FlowFixMe only defined for CallJob
|
||||
key: job.key,
|
||||
@@ -303,10 +307,14 @@ export default class Jobs {
|
||||
schedule,
|
||||
session,
|
||||
})
|
||||
logger.notice(`Execution terminated for ${job.id}.`, {
|
||||
event: 'job.end',
|
||||
runJobId,
|
||||
})
|
||||
await logger.notice(
|
||||
`Execution terminated for ${job.id}.`,
|
||||
{
|
||||
event: 'job.end',
|
||||
runJobId,
|
||||
},
|
||||
true
|
||||
)
|
||||
|
||||
app.emit('job:terminated', status, job, schedule, runJobId)
|
||||
} catch (error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ export default class Logs {
|
||||
app.on('clean', () => this._gc())
|
||||
}
|
||||
|
||||
async _gc (keep = 1e4) {
|
||||
async _gc (keep = 2e4) {
|
||||
const db = await this._app.getStore('logs')
|
||||
|
||||
let count = 1
|
||||
|
||||
@@ -15,8 +15,8 @@ const LEVELS = [
|
||||
// Create high level log methods.
|
||||
for (const level of LEVELS) {
|
||||
Object.defineProperty(AbstractLogger.prototype, level, {
|
||||
value (message, data) {
|
||||
return this._add(level, message, data)
|
||||
value (message, data, returnPromise) {
|
||||
return this._add(level, message, data, returnPromise)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import highland from 'highland'
|
||||
import { ignoreErrors } from 'promise-toolbox'
|
||||
|
||||
import AbstractLogger from './abstract'
|
||||
|
||||
@@ -24,7 +25,7 @@ export default class LevelDbLogger extends AbstractLogger {
|
||||
this._namespace = namespace
|
||||
}
|
||||
|
||||
_add (level, message, data) {
|
||||
_add (level, message, data, returnPromise = false) {
|
||||
const time = Date.now()
|
||||
|
||||
const log = {
|
||||
@@ -36,7 +37,14 @@ export default class LevelDbLogger extends AbstractLogger {
|
||||
}
|
||||
|
||||
const key = generateUniqueKey(time)
|
||||
this._db.putSync(key, log)
|
||||
|
||||
const promise = this._db.put(key, log)
|
||||
|
||||
if (returnPromise) {
|
||||
return promise.then(() => key)
|
||||
}
|
||||
|
||||
;promise::ignoreErrors()
|
||||
return key
|
||||
}
|
||||
|
||||
@@ -50,7 +58,7 @@ export default class LevelDbLogger extends AbstractLogger {
|
||||
forEach(Array.isArray(id) ? id : [id], id => {
|
||||
this._db.get(id).then(value => {
|
||||
if (value.namespace === this._namespace) {
|
||||
this._db.delSync(id, noop)
|
||||
ignoreErrors.call(this._db.del(id, noop))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class {
|
||||
|
||||
async _getPluginMetadata (id) {
|
||||
const metadata = await this._pluginsMetadata.first(id)
|
||||
return metadata ? metadata.properties : null
|
||||
return metadata?.properties
|
||||
}
|
||||
|
||||
async registerPlugin (
|
||||
@@ -70,7 +70,7 @@ export default class {
|
||||
const metadata = await this._getPluginMetadata(id)
|
||||
let autoload = true
|
||||
let configuration
|
||||
if (metadata) {
|
||||
if (metadata !== undefined) {
|
||||
;({ autoload, configuration } = metadata)
|
||||
} else {
|
||||
console.log(`[NOTICE] register plugin ${name} for the first time`)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import synchronized from 'decorator-synchronized'
|
||||
import { getHandler } from '@xen-orchestra/fs'
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
import { ignoreErrors } from 'promise-toolbox'
|
||||
|
||||
import { forEach, mapToArray } from '../utils'
|
||||
import patch from '../patch'
|
||||
import { mapToArray } from '../utils'
|
||||
import { Remotes } from '../models/remote'
|
||||
|
||||
// ===================================================================
|
||||
@@ -13,6 +16,7 @@ export default class {
|
||||
prefix: 'xo:remote',
|
||||
indexes: ['enabled'],
|
||||
})
|
||||
this._handlers = { __proto__: null }
|
||||
|
||||
xo.on('clean', () => this._remotes.rebuildIndexes())
|
||||
xo.on('start', async () => {
|
||||
@@ -20,12 +24,24 @@ export default class {
|
||||
'remotes',
|
||||
() => this._remotes.get(),
|
||||
remotes =>
|
||||
Promise.all(mapToArray(remotes, remote => this._remotes.save(remote)))
|
||||
Promise.all(
|
||||
mapToArray(remotes, remote => this._remotes.update(remote))
|
||||
)
|
||||
)
|
||||
|
||||
await this.syncAllRemotes()
|
||||
const remotes = await this.getAllRemotes()
|
||||
remotes.forEach(remote => {
|
||||
ignoreErrors.call(this.updateRemote(remote.id, {}))
|
||||
})
|
||||
})
|
||||
xo.on('stop', async () => {
|
||||
const handlers = this._handlers
|
||||
for (const id in handlers) {
|
||||
try {
|
||||
await handlers[id].forget()
|
||||
} catch (_) {}
|
||||
}
|
||||
})
|
||||
xo.on('stop', () => this.forgetAllRemotes())
|
||||
}
|
||||
|
||||
async getRemoteHandler (remote, ignoreDisabled) {
|
||||
@@ -37,7 +53,22 @@ export default class {
|
||||
throw new Error('remote is disabled')
|
||||
}
|
||||
|
||||
return getHandler(remote)
|
||||
const { id } = remote
|
||||
const handlers = this._handlers
|
||||
let handler = handlers[id]
|
||||
if (handler === undefined) {
|
||||
handler = handlers[id] = getHandler(remote)
|
||||
}
|
||||
|
||||
try {
|
||||
await handler.sync()
|
||||
ignoreErrors.call(this._updateRemote(id, { error: '' }))
|
||||
} catch (error) {
|
||||
ignoreErrors.call(this._updateRemote(id, { error: error.message }))
|
||||
throw error
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
async testRemote (remote) {
|
||||
@@ -49,42 +80,49 @@ export default class {
|
||||
return this._remotes.get()
|
||||
}
|
||||
|
||||
async _getRemote (id) {
|
||||
async getRemote (id) {
|
||||
const remote = await this._remotes.first(id)
|
||||
if (!remote) {
|
||||
if (remote === undefined) {
|
||||
throw noSuchObject(id, 'remote')
|
||||
}
|
||||
|
||||
return remote
|
||||
return remote.properties
|
||||
}
|
||||
|
||||
async getRemote (id) {
|
||||
return (await this._getRemote(id)).properties
|
||||
}
|
||||
|
||||
async createRemote ({ name, url }) {
|
||||
const remote = await this._remotes.create(name, url)
|
||||
async createRemote ({ name, url, options }) {
|
||||
const params = {
|
||||
name,
|
||||
url,
|
||||
enabled: false,
|
||||
error: '',
|
||||
}
|
||||
if (options !== undefined) {
|
||||
params.options = options
|
||||
}
|
||||
const remote = await this._remotes.add(params)
|
||||
return /* await */ this.updateRemote(remote.get('id'), { enabled: true })
|
||||
}
|
||||
|
||||
async updateRemote (id, { name, url, enabled, error }) {
|
||||
const remote = await this._getRemote(id)
|
||||
this._updateRemote(remote, { name, url, enabled, error })
|
||||
const handler = await this.getRemoteHandler(remote.properties, true)
|
||||
const props = await handler.sync()
|
||||
this._updateRemote(remote, props)
|
||||
return (await this._remotes.save(remote)).properties
|
||||
updateRemote (id, { name, url, options, enabled }) {
|
||||
const handlers = this._handlers
|
||||
const handler = handlers[id]
|
||||
if (handler !== undefined) {
|
||||
delete this._handlers[id]
|
||||
ignoreErrors.call(handler.forget())
|
||||
}
|
||||
|
||||
return this._updateRemote(id, {
|
||||
name,
|
||||
url,
|
||||
options,
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
||||
_updateRemote (remote, { name, url, enabled, error }) {
|
||||
if (name) remote.set('name', name)
|
||||
if (url) remote.set('url', url)
|
||||
if (enabled !== undefined) remote.set('enabled', enabled)
|
||||
if (error) {
|
||||
remote.set('error', error)
|
||||
} else {
|
||||
remote.set('error', '')
|
||||
}
|
||||
@synchronized()
|
||||
async _updateRemote (id, props) {
|
||||
const remote = await this.getRemote(id)
|
||||
patch(remote, props)
|
||||
return (await this._remotes.update(remote)).properties
|
||||
}
|
||||
|
||||
async removeRemote (id) {
|
||||
@@ -92,22 +130,4 @@ export default class {
|
||||
await handler.forget()
|
||||
await this._remotes.remove(id)
|
||||
}
|
||||
|
||||
// TODO: Should it be private?
|
||||
async syncAllRemotes () {
|
||||
const remotes = await this.getAllRemotes()
|
||||
forEach(remotes, remote => {
|
||||
this.updateRemote(remote.id, {})
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Should it be private?
|
||||
async forgetAllRemotes () {
|
||||
const remotes = await this.getAllRemotes()
|
||||
for (const remote of remotes) {
|
||||
try {
|
||||
;(await this.getRemoteHandler(remote, true)).forget()
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const synchronizedResourceSets = synchronized()
|
||||
|
||||
const VM_RESOURCES = {
|
||||
cpus: true,
|
||||
disk: true,
|
||||
@@ -61,15 +63,15 @@ const normalize = set => ({
|
||||
ipPools: set.ipPools || [],
|
||||
limits: set.limits
|
||||
? map(
|
||||
set.limits,
|
||||
limit =>
|
||||
isObject(limit)
|
||||
? limit
|
||||
: {
|
||||
available: limit,
|
||||
total: limit,
|
||||
}
|
||||
)
|
||||
set.limits,
|
||||
limit =>
|
||||
isObject(limit)
|
||||
? limit
|
||||
: {
|
||||
available: limit,
|
||||
total: limit,
|
||||
}
|
||||
)
|
||||
: {},
|
||||
name: set.name || '',
|
||||
objects: set.objects || [],
|
||||
@@ -286,7 +288,7 @@ export default class {
|
||||
await this._save(set)
|
||||
}
|
||||
|
||||
@synchronized
|
||||
@synchronizedResourceSets
|
||||
async allocateLimitsInResourceSet (limits, setId) {
|
||||
const set = await this.getResourceSet(setId)
|
||||
forEach(limits, (quantity, id) => {
|
||||
@@ -302,7 +304,7 @@ export default class {
|
||||
await this._save(set)
|
||||
}
|
||||
|
||||
@synchronized
|
||||
@synchronizedResourceSets
|
||||
async releaseLimitsInResourceSet (limits, setId) {
|
||||
const set = await this.getResourceSet(setId)
|
||||
forEach(limits, (quantity, id) => {
|
||||
|
||||
@@ -96,7 +96,7 @@ export default class Scheduling {
|
||||
cron,
|
||||
enabled,
|
||||
jobId,
|
||||
name,
|
||||
name = '',
|
||||
timezone,
|
||||
userId,
|
||||
}: $Diff<Schedule, {| id: string |}>) {
|
||||
@@ -114,7 +114,7 @@ export default class Scheduling {
|
||||
|
||||
async getSchedule (id: string): Promise<Schedule> {
|
||||
const schedule = await this._db.first(id)
|
||||
if (schedule === null) {
|
||||
if (schedule === undefined) {
|
||||
throw noSuchObject(id, 'schedule')
|
||||
}
|
||||
return schedule.properties
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user