Compare commits

...

131 Commits

Author SHA1 Message Date
Pierre Donias
b43cf27479 feat(xo-server): 5.26.0 2018-09-07 11:36:20 +02:00
Julien Fontanet
bcab6bb584 fix(xo-server/listReplicatedVms): dont use snapshot_time (#3394)
It is not always set on VMs imported from snapshots, add a new metadata in `other_config` and use the timestamp in the name_label a fallback.

Fixes #3391
2018-09-07 11:31:57 +02:00
Pierre Donias
8213601df6 chore(xo-web/VM,host,pool,SR): better UUID display (#3390) 2018-09-05 16:44:18 +02:00
Pierre Donias
e7ad577661 feat(xo-server,xo-web/VM): start on specific host (#3389)
Fixes #3191
2018-09-05 16:39:12 +02:00
Julien Fontanet
070213dd7f fix(xo-server/collection/redis): ignore undefined values (#3388)
Fixes #3385
2018-09-05 15:18:47 +02:00
Julien Fontanet
395cbb33ef chore: update dependencies 2018-09-05 09:42:10 +02:00
Julien Fontanet
a429a7aa35 feat(babel-config): same targets for all builds 2018-09-04 16:43:42 +02:00
Julien Fontanet
b70e09937b fix(babel-config): dont ignore pkg.browserslist entry 2018-09-04 16:41:28 +02:00
Pierre Donias
109ff4a4da feat(xo-web/backupNg): support restoring directories (#3384)
Fixes #1924
See #3300
2018-09-04 16:00:42 +02:00
Pierre Donias
0e185ab92a fix(xo-web/Backup NG): remove unsupported TAR file restore option (#3383) 2018-09-03 16:50:36 +02:00
Julien Fontanet
aefb76a4f6 fix(xo-server/file restore): support trailing slashes 2018-09-03 16:40:35 +02:00
Julien Fontanet
fe653dc7dd feat(xo-server): add job name in replicated VMs (#3379)
See support#891
2018-09-03 14:05:32 +02:00
Julien Fontanet
8b5c0e706c chore(xo-server/collection#first): returns undefined instead of null (#3382)
Fixes an issue introduced in 1407fb7bab in
`Remotes#getRemote()`.
2018-09-03 12:03:50 +02:00
Pierre Donias
d25584edf9 feat(xo-web/tasks): show finished tasks (#3377)
Fixes #3266
2018-08-31 17:19:03 +02:00
Pierre Donias
496ca2c716 fix(xo-web/XoItem): link should not open in new tab by default (#3376) 2018-08-31 14:40:00 +02:00
Julien Fontanet
4a09074a40 chore: update yarn.lock 2018-08-31 14:30:59 +02:00
Julien Fontanet
385fd80bb9 chore(xo-server-transport-xmpp): use Babel 7 2018-08-31 14:30:59 +02:00
Julien Fontanet
b6818abd0d chore(xo-server-transport-slack): use Babel 7 2018-08-31 14:30:59 +02:00
Julien Fontanet
45b5d10f1b chore(xo-server-transport-nagios): use Babel 7 2018-08-31 14:30:59 +02:00
Julien Fontanet
7866a265fe chore(xo-server-transport-email): use Babel 7 2018-08-31 14:30:59 +02:00
Julien Fontanet
774c0d09b1 chore(xo-server-auth-saml): use Babel 7 2018-08-31 14:30:59 +02:00
Julien Fontanet
df0029db3b chore(xo-server-auth-google): use Babel 7 2018-08-31 14:30:59 +02:00
Julien Fontanet
c1a5364448 chore(xo-server-auth-github): use Babel 7 2018-08-31 14:30:58 +02:00
Julien Fontanet
557ba1a4bc chore(xo-lib): use Babel 7 2018-08-31 14:30:58 +02:00
Julien Fontanet
bf932aada1 chore(xo-common): use Babel 7 2018-08-31 14:30:58 +02:00
Pierre Donias
2304deab3f fix(xo-web/vm/disks): individual actions 2018-08-31 14:18:14 +02:00
Julien Fontanet
ea9c0cfb10 fix(xo-web): export/import from Starter Edition 2018-08-31 13:14:31 +02:00
Pierre Donias
2fd5a6501b feat(xo-web/menu): hide Tasks entry for self users (#3373)
Fixes #3311
2018-08-30 15:44:30 +02:00
Pierre Donias
0ca5a32142 feat(xo-web/backup ng/restore): sort backups in select (#3374)
Fixes #3294
2018-08-30 13:38:02 +02:00
Julien Fontanet
f55f812d30 fix(linting): use legacy decorators 2018-08-30 11:53:27 +02:00
Julien Fontanet
98bbd53c28 fix(xo-server/log.delete): delSync does not exist
Fixes #3372
2018-08-29 18:21:48 +02:00
Julien Fontanet
1f0bfe2518 chore: update dependencies 2018-08-29 15:06:11 +02:00
Julien Fontanet
c5eae6e498 chore(xo-vmdk-to-vhd): remove unnecessary Babel runtime 2018-08-29 15:06:11 +02:00
Julien Fontanet
bfcdd29f10 chore(xo-collection): remove unnecessary Babel runtime 2018-08-29 15:06:11 +02:00
Julien Fontanet
8db0b59fe1 chore(fs): remove unnecessary Babel runtime 2018-08-29 15:06:10 +02:00
Julien Fontanet
6bc4c03b4c chore(vhd-cli): remove unnecessary Babel runtime 2018-08-29 15:06:10 +02:00
Julien Fontanet
4d63f93390 chore(vhd-lib): remove unnecessary Babel runtime 2018-08-29 15:06:10 +02:00
Pierre Donias
9722f2a5bd fix(xo-web/createVms): dont noop confirm modal rejection (#3371)
Fixes #3268
2018-08-29 13:14:07 +02:00
Pierre Donias
ee9c6817d6 fix(xo-web/new XOSAN): ignore detached local SRs (#3370) 2018-08-29 10:57:39 +02:00
Jon Sands
58564306ca (docu) fix backup grammar (#3369)
* (docu) fix backup grammar
2018-08-28 20:35:46 +02:00
Julien Fontanet
17c6f1762d chore: update dependencies 2018-08-28 20:29:29 +02:00
Julien Fontanet
17f13307bb fix(xo-server/listVmBackupsNg): dont fail on problematic remote (#3367)
Fixes #3365
2018-08-28 17:04:32 +02:00
Pierre Donias
a43c141ddd fix(xo-server/self): synchronize allocate and release together (#3368) 2018-08-28 16:54:17 +02:00
Pierre Donias
1e6da359cc fix(CHANGELOG): wrong xo-server version 2018-08-27 16:37:59 +02:00
Pierre Donias
626a9a777f feat(xo-web): 5.25.1 2018-08-27 16:35:04 +02:00
Pierre Donias
2768ad9e3b feat(xo-server): 5.25.2 2018-08-27 16:33:52 +02:00
Pierre Donias
3eec8f1a55 chore(CHANGELOG): 5.25.1 2018-08-27 16:28:36 +02:00
badrAZ
6148daa8b8 fix(CHANGELOG): move fix to correct release (#3357) 2018-08-27 16:17:54 +02:00
Julien Fontanet
338de7985f chore(docs/troubleshooting): must restart after memory setting 2018-08-27 15:59:33 +02:00
Julien Fontanet
df99528445 chore(CHANGELOG): improve import/export VDI entry 2018-08-27 14:41:10 +02:00
Julien Fontanet
890901366d fix(xo-server/{export,import}Vm): dont pass host (#3364)
Relies on redirection handling in `xen-api`.
2018-08-27 14:33:14 +02:00
Julien Fontanet
13271aa196 fix(CHANGELOG): move fix in next release 2018-08-27 11:45:59 +02:00
badrAZ
71aea20c7d fix(xo-server/jobs): fix race condition between end log and consolidation (#3238)
Fixes #3018
2018-08-27 11:44:50 +02:00
Julien Fontanet
b09da76b04 fix(xo-server/{export,import}Vdi): dont pass host (#3355)
Relies on redirection handling in `xen-api`.

Fixes #3354
2018-08-27 11:03:08 +02:00
Pierre Donias
ce35bbaeb4 fix(xo-server,xo-web): NFS remotes mount options (#3363)
Fixes #3361
2018-08-27 10:39:35 +02:00
Julien Fontanet
0034f0a1d3 feat(xo-server): 5.25.1 2018-08-23 13:47:52 +02:00
Julien Fontanet
e3391fa81f fix(xo-server/file restore): await addDirectory() 2018-08-23 13:47:30 +02:00
Pierre Donias
bacdc63c70 feat(xo-web): 5.25.0 2018-08-23 12:37:53 +02:00
Pierre Donias
b0285799c6 feat(xo-server): 5.25.0 2018-08-23 12:36:43 +02:00
Pierre Donias
f8b34c5d64 feat(@xen-orchestra/fs): 0.3.0 2018-08-23 12:33:21 +02:00
Pierre Donias
40f4a66bda feat(xo-server-backup-reports): 0.13.0 2018-08-23 12:30:56 +02:00
Pierre Donias
6125dae158 chore(CHANGELOG): 5.25.0 2018-08-23 12:26:45 +02:00
Pierre Donias
dfe4a934e9 feat(remotes): allow setting NFS mount options (#3353)
Fixes #1793
2018-08-23 11:34:56 +02:00
badrAZ
ecc33f46ab fix(xo-web/backup-ng/new): fix selected SRs issues (#3352)
Issue introduced in ec869ffd
2018-08-23 11:32:30 +02:00
Julien Fontanet
00d1985cf9 feat(xo-server/jobs): add job name to logs (#3351)
Fixes #3272
2018-08-23 11:29:42 +02:00
badrAZ
138aed8ae1 feat(xo-web/backup-ng/new): set default retention to 1 (#3290)
Fixes #3134
2018-08-23 10:18:29 +02:00
badrAZ
def9f947b7 feat(xo-web/new-vm): show error when getting coreOS default template fails (#3343)
Fixes #3227
2018-08-23 10:15:45 +02:00
Julien Fontanet
20dc4af4a4 fix(xo-server/importVdiContent): HOST → host 2018-08-22 18:07:57 +02:00
Julien Fontanet
4d2909567c feat(xo-server/snapshotVm): tag xo-disable-quiesce (#3350) 2018-08-22 15:15:05 +02:00
Julien Fontanet
92a93e4393 chore(xo-server): use xen-api 0.18.0 2018-08-21 18:27:45 +02:00
Julien Fontanet
389967d40c feat(xen-api): 0.18.0 2018-08-21 18:24:04 +02:00
Julien Fontanet
d66e8f29b7 feat(xen-api): add methods to mutate fields
- `setField()`
- `setFieldEntry()`
- `unsetFieldEntry()`
- `setFieldEntries()`
2018-08-21 18:15:38 +02:00
Julien Fontanet
3e9425bf79 feat(xen-api): add getRecordByUuid() 2018-08-21 17:49:37 +02:00
Julien Fontanet
41506e785e feat(xen-api/getRecord): add $id, $type and $ref props 2018-08-21 17:48:58 +02:00
Julien Fontanet
32d7ccfea5 feat(xen-api): expose null reference 2018-08-21 17:46:59 +02:00
Julien Fontanet
8a9f952ada feat(xen-api): support encoded credentials in URL 2018-08-21 17:46:18 +02:00
badrAZ
d15efae43f fix(xo-web/backup-ng/new): always show compression when full mode (#3345)
Fixes #3236
2018-08-21 10:12:36 +02:00
Julien Fontanet
627c06f4a8 fix(xo-acl-resolver): minor fixes
Due to 563969f2
2018-08-20 17:59:26 +02:00
Julien Fontanet
3e6201e93a chore(xo-server-auth-ldap): use Babel 7 2018-08-20 17:17:26 +02:00
Julien Fontanet
b941a649b5 chore(xo-server-usage-report): use Babel 7 2018-08-20 17:14:14 +02:00
Julien Fontanet
7b2b9ca618 chore(xo-server-load-balancer): use Babel 7 2018-08-20 17:12:33 +02:00
Julien Fontanet
404cf2b7e6 chore(xo-server-backup-reports): use Babel 7 2018-08-20 17:10:43 +02:00
Julien Fontanet
42698293de chore(xo-server-cloud): use Babel 7 2018-08-20 17:04:30 +02:00
Julien Fontanet
563969f2e9 chore(xo-acl-resolver): remove build step 2018-08-20 16:56:32 +02:00
Julien Fontanet
887e21daa5 chore: drop Node 4 compat for XO pkgs 2018-08-20 16:49:27 +02:00
badrAZ
b75a2a8dca feat(xo-web/backup-ng/new): move VMs' selection to dedicated card (#3338)
See #2711
2018-08-20 16:32:28 +02:00
badrAZ
31f32ba23c fix(xo-web/backup-ng/new): rename Export Ret. to Backup Ret. and Copy Ret. to Replication Ret. (#3337)
See #2711
2018-08-20 16:29:55 +02:00
badrAZ
936a4068d5 feat(xo-web/backup-ng/new): ignore replicated VMs by default in smart mode (#3312)
Fixes #2338
2018-08-20 16:29:10 +02:00
Julien Fontanet
266f287774 chore(xen-api): use Babel 7 2018-08-20 15:51:07 +02:00
badrAZ
5808485812 fix(CHANGELOG): missing release of backup-reports (#3341) 2018-08-20 13:51:46 +02:00
Olivier Lambert
b6dd83e32b feat: add local ext SR creation (#3335)
Fixes #3332
2018-08-19 17:12:36 +02:00
Julien Fontanet
2236bd71c4 chore: format with Prettier 2018-08-19 16:46:34 +02:00
badrAZ
ec869ffdd3 feat(xo-web/backup-ng/new): tip when thick-provisioned SR (#3333)
Fixes #3291
2018-08-17 15:31:06 +02:00
badrAZ
1aa4966a92 feat(xo-web/SelectTag): allow non existing tags (#3316) 2018-08-17 10:05:20 +02:00
badrAZ
235da199f9 chore(xo-web/backup-ng): remove useless computed (#3317) 2018-08-16 16:17:02 +02:00
badrAZ
4cad820271 fix(xo-web/select-objects): adding or removing an item from a select make the missing objects disappear (#3315) 2018-08-16 16:11:18 +02:00
Rajaa.BARHTAOUI
4a0b29e1f2 fix(xo-server/migrateVm): take VDIs of snapshot into account (#3298)
See #2557
2018-08-16 15:00:45 +02:00
badrAZ
1db9ca9e31 fix(xo-server/scheduling): default name should be an empty string (#3325) 2018-08-16 14:41:34 +02:00
Julien Fontanet
942ddfa742 feat(xo-server/remotes): sync on use (#3320)
Fixes #2852
2018-08-16 14:39:24 +02:00
Pierre Donias
355cddc044 feat(xo-web/VM): always show Convert button + better label (#3319)
Fixes #3201
2018-08-16 10:14:13 +02:00
Julien Fontanet
c23cccf2ce fix(xo-server/remotes): dont change enabled state on errors (#3318)
See #2852
2018-08-14 17:46:10 +02:00
Julien Fontanet
1407fb7bab chore(xo-server/remotes): merge _getRemote() and getRemote() 2018-08-14 17:10:18 +02:00
Julien Fontanet
4ba542b70f chore(xo-server/remotes): inline Remotes#create() 2018-08-14 17:10:18 +02:00
Julien Fontanet
76f75401ee chore(xo-server/remotes): store handler instances 2018-08-14 17:10:18 +02:00
Julien Fontanet
011cc7ad65 chore(xo-server/updateRemote): remove unused error param 2018-08-14 17:10:17 +02:00
Julien Fontanet
b966e6097f chore(xo-server/models/Remote): remove save() 2018-08-14 17:10:17 +02:00
Julien Fontanet
809e1a35cd chore(xo-server/models/Acl): inline create() and hash() 2018-08-14 17:10:17 +02:00
Julien Fontanet
8e7e1fccbe chore(xo-server/Model): remove unused validate() 2018-08-14 17:10:16 +02:00
Julien Fontanet
e86b30f205 chore(xo-server/Model): remove unused initialize() 2018-08-14 17:10:16 +02:00
Rajaa.BARHTAOUI
7dc0c4cf15 fix(xo-web/New VM): filter out SRs that are not in the template's pool (#3070)
Fixes #3068, fixes #3267
2018-08-14 16:51:32 +02:00
Rajaa.BARHTAOUI
561a9f140d feat(xo-web/host/logs): use SortedTable actions (#3313)
See #3179
2018-08-14 16:28:19 +02:00
badrAZ
ba56114e9f fix(xo-web/backup-ng/new): schedule's name overridden with undefined if it's not been edited (#3288)
Fixes #3286
2018-08-14 12:18:41 +02:00
Pierre Donias
11bd75d2fe feat(xo-web/tasks): remove empty message and let SortedTable handle it (#3305) 2018-08-13 17:21:18 +02:00
Julien Fontanet
ececbaf201 fix(xo-server/remotes): properly ignore async errors 2018-08-13 16:55:56 +02:00
Julien Fontanet
1985134d94 chore(xo-server/remotes): use Array#forEach() 2018-08-13 16:55:56 +02:00
Julien Fontanet
2cc59078b1 chore(xo-server/remotes): inline syncAllRemotes() and forgetAllRemotes() 2018-08-13 16:55:56 +02:00
Julien Fontanet
3d4d7db5da feat(xo-server/logs): double number of entries 2018-08-13 16:55:56 +02:00
Pierre Donias
dc40ceaafe feat(xo-web/SortedTable): always show filter input (#3296)
Fixes #3295
2018-08-13 16:44:31 +02:00
Rajaa.BARHTAOUI
0110e223ee feat(xo-web/Collapse): size prop (#3299) 2018-08-13 14:58:39 +02:00
Julien Fontanet
e7467dca8a feat(xo-server/backupNg): support restoring directories (#3300)
See #1924
2018-08-13 13:32:25 +02:00
Julien Fontanet
4a1e87b534 feat(xen-api/cli): add diff() helper 2018-08-13 11:53:56 +02:00
Olivier Lambert
5a32f904bc add no matching VMs error into troubleshooting for backup (#3297)
* add no matching VMs error into troubleshooting for backup
2018-08-12 10:43:13 +02:00
Julien Fontanet
14d023a9f5 feat(xo-server): 5.24.2 2018-08-10 16:50:43 +02:00
Julien Fontanet
2e5e81e93e feat(xen-api): 0.17.1 2018-08-10 16:50:11 +02:00
Julien Fontanet
c9cc106be6 feat(xo-server): 5.24.1 2018-08-10 12:57:31 +02:00
Julien Fontanet
29fc17f260 fix(xen-api/putResource): ensure taskRef is not sent on probing
This fix an issue introduced in ba6b9682

Fixes #3293.
2018-08-10 12:41:35 +02:00
Julien Fontanet
c7d16fd345 chore(vhd-lib): remove deprecated assert.equal 2018-08-10 11:37:47 +02:00
Julien Fontanet
ffe29b8957 feat(xo-server-cloud): 0.2.4 2018-08-10 11:23:38 +02:00
Julien Fontanet
be1bd9254d chore: update dependencies 2018-08-10 11:21:59 +02:00
Pierre Donias
6156649faa feat(xo-server-cloud): 0.2.3 2018-08-09 17:41:53 +02:00
Pierre Donias
2edddaa835 feat(xo-web): 5.24.0 2018-08-09 17:07:04 +02:00
176 changed files with 3363 additions and 3032 deletions

View File

@@ -10,6 +10,11 @@ module.exports = {
$Shape: true,
},
parser: 'babel-eslint',
parserOptions: {
ecmaFeatures: {
legacyDecorators: true,
},
},
rules: {
'comma-dangle': ['error', 'always-multiline'],
indent: 'off',

View File

@@ -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',
}
},

View File

@@ -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"
},

View File

@@ -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) {

View File

@@ -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",

View File

@@ -85,8 +85,8 @@ export const validChecksumOfReadStream = (
callback(
checksum !== expectedChecksum
? new Error(
`Bad checksum (${checksum}), expected: ${expectedChecksum}`
)
`Bad checksum (${checksum}), expected: ${expectedChecksum}`
)
: null
)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

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

View File

@@ -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.

View File

@@ -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:
![](./assets/quiesced1.png)

View File

@@ -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**

View File

@@ -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).

View File

@@ -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",

View File

@@ -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"

View File

@@ -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"
},

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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)

View File

@@ -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
}

View File

@@ -98,7 +98,7 @@ export default class Vhd {
Buffer.alloc(n),
start
)
assert.equal(bytesRead, n)
assert.strictEqual(bytesRead, n)
return buffer
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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}`
)
})
)

View File

@@ -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"
]
}
}

View File

@@ -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')

View File

@@ -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) {

View File

@@ -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()

View File

@@ -1,13 +0,0 @@
{
"comments": false,
"compact": true,
"presets": [
[
"env", {
"targets": {
"node": 4
}
}
]
]
}

View File

@@ -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_

View File

@@ -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"
}
}

View File

@@ -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"

View File

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

View File

@@ -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"

View File

@@ -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'

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -10,8 +10,7 @@ const xo = new Xo({
url: 'localhost:9000',
})
xo
.open()
xo.open()
.then(function () {
return xo
.call('acl.get', {})

View File

@@ -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"
]
}
}

View File

@@ -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",

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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
}
}
]
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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
}
}
]
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -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 () {

View File

@@ -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"

View File

@@ -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,

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -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)

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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
}
}
]
]
}
}

View File

@@ -0,0 +1,3 @@
module.exports = require('../../@xen-orchestra/babel-config')(
require('./package.json')
)

View File

@@ -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"
]
}
}

View File

@@ -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]}`
)
)
}

View File

@@ -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",

View File

@@ -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 },
}

View File

@@ -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 },
}

View File

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

View File

@@ -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'],
}

View File

@@ -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
}
}

View File

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

View File

@@ -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]

View File

@@ -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
})
)

View File

@@ -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')
}

View File

@@ -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 => {

View File

@@ -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)

View File

@@ -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',

View File

@@ -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')
})
})

View File

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

View File

@@ -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)
}

View File

@@ -64,6 +64,7 @@ export default {
data: data.data,
id,
jobId,
jobName: data.jobName,
scheduleId,
start: time,
status: runningJobs[jobId] === id ? 'pending' : 'interrupted',

View File

@@ -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,
})
)

View File

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

View File

@@ -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)

View File

@@ -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()
}
})()

View File

@@ -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) {

View File

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

View File

@@ -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)
},
})
}

View File

@@ -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))
}
})
})

View File

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

View File

@@ -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 (_) {}
}
}
}

View File

@@ -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) => {

View File

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