Compare commits

...

728 Commits

Author SHA1 Message Date
Pierre Donias
120e01897d feat(fs): 0.5.0 2018-12-18 13:55:16 +01:00
badrAZ
06755cb6b6 feat(xo-web/backup-ng/logs): merge status and display log details (#3800)
Fixes #3797
2018-12-18 11:00:47 +01:00
Julien Fontanet
27409f4fd5 fix(xo-server/worker): sync the remote handlers
Fixes #3739
2018-12-17 18:00:02 +01:00
Julien Fontanet
82253509d0 fix(xo-server/backup NG): fix issues due to fs changes 2018-12-17 17:59:54 +01:00
Julien Fontanet
c450685ddd chore(fs/{sync,forget}): add comments 2018-12-17 17:59:54 +01:00
badrAZ
9a79088e8a chore(xo-web/backup-ng): use btnStyle (#3798) 2018-12-17 15:43:39 +01:00
Julien Fontanet
83760157ad chore(xen-api): silence Bluebird warning 2018-12-17 15:32:52 +01:00
Julien Fontanet
985aa2225e fix(fs/local#create{Read,Write}Stream): wait for file to be opened
This also fix `createOutputStream` when directories are missing.
2018-12-17 15:25:35 +01:00
badrAZ
0ad340d971 feat(xo-web/backup-ng/overview): ability to copy schedule/job id to clipboard (#3791)
Fixes #3753
2018-12-17 10:20:51 +01:00
Rajaa.BARHTAOUI
97726dce12 fix(xo-web/self): all objects are missing (#3096)
Fixes #2689
2018-12-14 16:24:10 +01:00
Julien Fontanet
342320b481 feat(xo-server/xen-servers): add metadata to PoolAlreadyConnected (#3792) 2018-12-14 15:09:43 +01:00
badrAZ
1bfcbf49b9 feat(xo-web/backup-ng/new): add a link to the documentation (#3790)
Fixes #3789
2018-12-13 15:49:37 +01:00
Julien Fontanet
9d1eb8182b fix(xo-server/remote.list): add path to Handler#list()
Fixes #3768

Introduced in 043b381733
2018-12-13 15:19:04 +01:00
Julien Fontanet
18a6c57f02 chore(CHANGELOG): add fs to the list of packages 2018-12-13 14:53:59 +01:00
badrAZ
d3b6d1a97f fix(xo-server/servers): always remove pool→connection association (#3782)
* fix(xo-server/servers): delete correctly the connection from the cashe

* useless findKey

* CHANGELOG

* fix error

* code improvement

* code improvement

* code improvement

* Update CHANGELOG.md

* fix errors

* Update xen-servers.js

* missing entry in CHANGELOG
2018-12-13 12:21:02 +01:00
Julien Fontanet
ece881c02c fix(fs#unlink): remove timeout
This is not an `O(1)` operation.
2018-12-13 11:07:28 +01:00
badrAZ
631e8ce52d feat(xo-web/backup-ng/overview): display last run status (#3779)
Fixes #3769
2018-12-12 16:27:34 +01:00
badrAZ
cb5d3b9750 feat(xo-server/backup-ng): disable VM HA on DR/CR (#3755)
Fixes #2359
2018-12-12 16:04:03 +01:00
Julien Fontanet
995e6664f9 fix(fs/smb#create{Read,Write}Stream): works with opened files (#3785) 2018-12-12 15:38:54 +01:00
Julien Fontanet
1f497aa4df chore(fs): update @marsaud/smb2 to v0.13 2018-12-12 15:12:58 +01:00
Julien Fontanet
184dbc5516 chore(fs/smb): use a single client/connection (#3775) 2018-12-11 18:47:46 +01:00
Julien Fontanet
a1f25a4e3e chore(docs/supported-versions): add XS and XCP-ng 7.6 2018-12-11 10:46:13 +01:00
Julien Fontanet
cc4ab94428 chore: reformat with Prettier 2018-12-11 10:45:00 +01:00
Rajaa.BARHTAOUI
48727740c4 feat(xo-web/new-xosan): use SortedTable (#3691)
See #2416
2018-12-10 16:15:04 +01:00
Julien Fontanet
cc26e378e5 chore(fs/{createOutputStream,outputFile}): move dir logic in abstract 2018-12-07 17:12:12 +01:00
Julien Fontanet
28579258b3 feat(docs/sources): add git checkout before pull
Related to #3764.
2018-12-07 14:54:26 +01:00
Julien Fontanet
70b9b67f67 chore(fs/test): add TEST_DATA_LEN constant 2018-12-07 11:14:02 +01:00
Julien Fontanet
224b053eb1 chore(fs/test): test read() 2018-12-07 11:12:00 +01:00
Julien Fontanet
39bce978bc chore(fs/test): testWithFileDescriptor helper 2018-12-07 10:57:14 +01:00
Julien Fontanet
9435bd5493 chore(fs#openFile): flags are not optional 2018-12-07 10:45:56 +01:00
badrAZ
2284b3ef0a feat(xo-server): obfuscate token param (#3761) 2018-12-07 10:35:54 +01:00
Julien Fontanet
99dc64e8bb fix: occured → occurred (2) 2018-12-06 18:53:35 +01:00
Julien Fontanet
e47525b60b chore(fs/test): test list prependDir option 2018-12-06 18:53:09 +01:00
Julien Fontanet
10d4782ee2 feat(fs/mk{dir,tree}): new methods to create dirs (#3763) 2018-12-06 18:50:39 +01:00
Julien Fontanet
11cff2c065 feat(fs): prefix get/set → addPrefix method (#3766)
This method returns a new object which will transparently prefix all paths.

It is better than previous API because:

- the same handler can be reused and will not be invalidated when changing the prefix
- Abstract and implementations can call high-level methods without worrying about doubling the prefix
2018-12-06 17:22:37 +01:00
Julien Fontanet
11f742b020 fix: occured → occurred 2018-12-06 17:18:08 +01:00
Julien Fontanet
2353552e11 chore(fs/abstract): use path.posix
All paths in this lib are Posix paths and should be treated as such, even on Windows.
2018-12-06 13:59:51 +01:00
Rajaa.BARHTAOUI
d6012d8639 feat(xo-web/VM): add tooltip for VM status icon (#3765)
Fixes #3749
2018-12-06 11:48:21 +01:00
Julien Fontanet
7089ee778a fix(xo-server/migrateLegacyBackupJob): dont migrate when ids contain : 2018-12-05 17:49:07 +01:00
Julien Fontanet
629931782e feat(fs/{rmdir,unlink}): dont throw on missing entries 2018-12-05 17:20:51 +01:00
Julien Fontanet
1b48c626f4 fix(fs/smb): create{Read,Output}Stream and getSize with opened files 2018-12-05 16:31:38 +01:00
Julien Fontanet
ba35f51459 chore(fs/test): test createOutputStream 2018-12-05 16:31:38 +01:00
Julien Fontanet
ee5f3fc68d chore(fs/test): test createReadStream and getSize on opened files 2018-12-05 16:31:39 +01:00
Julien Fontanet
7be671f0f7 chore(fs/test): ensure test dir exists 2018-12-05 16:26:38 +01:00
Julien Fontanet
48c3748c28 feat(fs/abstract#rmdir): implement timeout 2018-12-05 15:37:21 +01:00
Julien Fontanet
3814a261d6 fix(fs/abstract#kResolve): edge case with / path 2018-12-05 15:35:24 +01:00
Julien Fontanet
81b82ce06b fix(fs/smb#_rmdir): error code on non-empty dir 2018-12-05 15:35:24 +01:00
Julien Fontanet
20c3f76278 feat(fs): split rmdir into rmdir and rmtree 2018-12-05 15:35:17 +01:00
Julien Fontanet
2c93b69144 chore(fs): simplify sync/forget
- These methods are now optional
- sync no longer returns anything
2018-12-05 14:39:12 +01:00
Julien Fontanet
043b381733 chore(fs/abstract#list): remove default value 2018-12-05 14:30:55 +01:00
Julien Fontanet
ff014df231 chore(fs): remove unnecessary default params 2018-12-05 14:29:35 +01:00
Julien Fontanet
055d1e81da chore(fs): sort methods 2018-12-05 14:27:20 +01:00
Julien Fontanet
b60678e79f fix(fs/abstract#test): resolve path 2018-12-05 14:06:53 +01:00
Julien Fontanet
fd93dfbc18 feat(fs): prefix support (#3759)
It is a prefix that will be used as root for all other paths.
2018-12-05 13:51:38 +01:00
Julien Fontanet
d74a5d73f0 chore(fs/abstract#test): Buffer#compare → Buffer#equals 2018-12-05 13:49:33 +01:00
Julien Fontanet
16a6d395c8 chore(fs/abstract): only call low-level methods 2018-12-05 13:48:42 +01:00
badrAZ
d6654807fa fix(xo-server/logs): sensitive params not hidden (#3760) 2018-12-05 13:41:32 +01:00
Julien Fontanet
e25ff221ba fix(fs/abstract#_outputFile): missing callback 2018-12-05 11:05:01 +01:00
Julien Fontanet
7df965ccd7 chore(fs): remove unnecessary tests 2018-12-05 11:00:55 +01:00
Julien Fontanet
f6b73b8303 chore(fs/abstract#_outputFile): use readable-stream/finished 2018-12-05 11:00:12 +01:00
Julien Fontanet
d617214c62 chore: update dependencies 2018-12-05 10:39:38 +01:00
Julien Fontanet
e85744cec0 chore(fs/smb): preserve stack on wrapped errors 2018-12-05 10:27:13 +01:00
Julien Fontanet
da74555e02 feat(xo-remote-parser/smb): handle missing path 2018-12-04 17:48:38 +01:00
Enishowk
4a9f489f20 fix(changelog): XOSAN line in wrong place (#3756) 2018-12-04 17:46:01 +01:00
Enishowk
18b17bda7c feat(@xen-orchestra/fs): add unit tests (#3736) 2018-12-04 16:36:10 +01:00
badrAZ
7faff824ff feat(xo-server/xen-servers): auto-connect to ejected host (#3738)
See #2238
2018-12-04 13:28:52 +01:00
Pierre Donias
e08d03687e feat(xo-web/render-xo-item/SR): minor improvements (#3752)
- Space left before container
- Hide context to self users
- Don't use text-muted on links
2018-12-04 11:35:57 +01:00
Rajaa.BARHTAOUI
75f1d80a86 feat(xo-web): contextualize SR (#3751)
See  #3021
2018-12-04 10:09:31 +01:00
Julien Fontanet
3967bfa099 chore(xo-server/config/remoteOptions.timeout): add comment 2018-12-03 17:50:34 +01:00
Rajaa.BARHTAOUI
6f6f463592 feat(xo-web/health/Orphan VDIs): display SR container (#3747)
See #3021
2018-12-03 16:56:34 +01:00
Rajaa.BARHTAOUI
8a760823b8 feat(xo-web/render-xo-item): contextualize VDI (#3285)
See #3021
2018-12-03 15:27:58 +01:00
Enishowk
8cd66af3f8 feat(backup-ng): add XOSAN in excluded tags by default (#3563)
Fixes #2128
2018-12-03 15:04:38 +01:00
Julien Fontanet
8569dbf985 chore: String#substring() → String#slice()
- homogeneity
- [faster in v8](https://bugs.chromium.org/p/v8/issues/detail?id=6730)
2018-12-03 14:59:59 +01:00
Julien Fontanet
9a03a70a3d fix(ESLint): disable react/jsx-indent rule 2018-12-03 14:59:48 +01:00
Julien Fontanet
42badbb08e fix(lint-staged): ESLint should not format 2018-12-03 14:54:41 +01:00
Julien Fontanet
956bdf0e03 feat(fs/smb#_writeFile): support flags 2018-12-03 14:39:24 +01:00
Julien Fontanet
91ff02d5c3 fix(fs/smb): always normalize errors 2018-12-03 14:38:51 +01:00
Julien Fontanet
84d88cf2b9 fix(fs/local#_{output,read}File): correctly handle flags 2018-12-03 14:35:52 +01:00
Julien Fontanet
fca2693730 feat(fs/{output,read}File): only support flags option 2018-12-03 14:35:17 +01:00
Julien Fontanet
d7ac1b9659 feat(fs/smb): implement rmdir 2018-12-03 13:11:31 +01:00
Julien Fontanet
ddb1a8ff51 fix(fs/smb): ensure connection is always closed (#3733) 2018-12-03 11:41:27 +01:00
Pierre Donias
56a2f8858b feat(xo-web/render-xo-item): simpler item components (#3547)
See #2605
2018-12-03 11:11:55 +01:00
Pierre Donias
55d7a1def0 fix(xo-web/backup-ng): canDeltaBackup import (#3745) 2018-12-03 10:34:46 +01:00
Pierre Donias
7b354f364c fix(xo-acl-resolver): add PBD resolution rule (#3742)
Fixes #2204
2018-12-03 09:32:18 +01:00
Pierre Donias
12dd40d330 fix(xo-web): missingPaths → missingPatches (#3741) 2018-11-30 15:12:10 +01:00
badrAZ
205f09a633 feat(xo-web/settings/users): display user groups (#3740)
Fixes #3719
2018-11-30 14:34:44 +01:00
badrAZ
f7dcccd8af chore(xo-web/backup-ng/new): improve setting pools implementation (#3715)
See #2578
2018-11-30 11:00:05 +01:00
Julien Fontanet
75592023f2 fix(xo-server/file restore): correctly use _getFilePath
Fixes #3739
2018-11-30 08:24:52 +01:00
Pierre Donias
a794a61c9b feat(xo-server): 5.31.1 2018-11-29 17:10:51 +01:00
Pierre Donias
804da115c9 chore(xo-server): use app-conf@0.6.1 2018-11-29 17:10:20 +01:00
Julien Fontanet
89df4f771b fix(fs/local#_rmdir): correctly prefix path 2018-11-29 15:35:58 +01:00
Pierre Donias
db3c5cfcb8 chore(CHANGELOG): 5.29.0 2018-11-29 14:22:08 +01:00
Pierre Donias
19d191a472 feat(xo-web): 5.31.0 2018-11-29 14:15:55 +01:00
Pierre Donias
d906fec236 feat(xo-server): 5.31.0 2018-11-29 14:15:18 +01:00
Pierre Donias
552482275d feat(xo-server-usage-report): 0.7.1 2018-11-29 14:13:54 +01:00
Pierre Donias
f06d40cf95 feat(xo-server-perf-alert): 0.2.0 2018-11-29 14:13:03 +01:00
Pierre Donias
cf3f1a1705 feat(xen-api): 0.22.0 2018-11-29 14:10:42 +01:00
Pierre Donias
08583c06ef feat({xo-server,xo-web}/VM): pause/unpause (#3731)
Fixes #3727
2018-11-29 11:51:42 +01:00
Julien Fontanet
5271a5c984 chore(xo-web/addSubscriptions): dont use deprecated componentWillMount (#3734) 2018-11-29 11:17:46 +01:00
Julien Fontanet
e69610643b feat(xo-server/xosan): use XOSAN as VM tag (#3735)
It previously was `XOSAN-${pool.name_label}` which is unnecessary, not easy to use for filtering and problematic for #2128.
2018-11-28 22:27:45 +01:00
Julien Fontanet
ef61e4fe6d chore(xo-server/api/xosan): reformat code 2018-11-28 22:10:12 +01:00
Julien Fontanet
4f776e1370 chore(fs): normalize paths in Abstract (#3732) 2018-11-28 21:44:49 +01:00
Julien Fontanet
aa72708996 chore: reformat some files 2018-11-28 19:10:51 +01:00
Enishowk
8751180634 feat(fs): rmdir method (#3730) 2018-11-28 17:42:32 +01:00
Julien Fontanet
2e327be49d chore(xo-web): reformat some files 2018-11-28 17:40:56 +01:00
badrAZ
f06a937c9c fix(xo-server/mergeXenPools): pool id → server id (#3728)
See #2238
2018-11-28 15:56:30 +01:00
Julien Fontanet
e65b3200cd chore(fs): fix some Flow types 2018-11-28 13:39:38 +01:00
Julien Fontanet
30d3701ab1 chore(fs): reformat code 2018-11-28 13:34:12 +01:00
badrAZ
05fa76dad3 feat(xo-server/xen-servers): prevent server connection when pool already connected (#3724)
See #2238
2018-11-28 10:42:55 +01:00
Julien Fontanet
4020081492 feat(xo-server/api): use per message deflate (#3720)
Related to #3699
2018-11-27 17:51:26 +01:00
Pierre Donias
2fbd4a62b2 chore(prettier): use jsxSingleQuote 2018-11-27 16:03:45 +01:00
Julien Fontanet
b773f5e821 fix(xo-server/xen-servers): pool.id → pool.$id (#3723)
Correctly unregister a Xapi connection from its pool.
2018-11-27 10:24:52 +01:00
Julien Fontanet
76c5ced1dd Update xen-servers.js 2018-11-27 10:23:46 +01:00
badrAZ
197768875b fix error 2018-11-27 10:21:39 +01:00
badrAZ
f0483862a5 fix(xo-server/xen-servers): a xapi object doesn't have an id 2018-11-27 10:12:47 +01:00
Julien Fontanet
ac46d3a5a2 chore: update dependencies
Fixes #3722
2018-11-27 09:26:39 +01:00
Julien Fontanet
2da576a1f8 fix(xo-web): avoid using TextDecoder (#3718)
This is an experimental API and not really necessary here.
2018-11-23 16:54:50 +01:00
Julien Fontanet
2e1ac27cf5 chore: dont format with ESLint 2018-11-23 16:38:24 +01:00
Julien Fontanet
258404affc fix(xo-web/settings/remotes): dont wait for test on create/edit 2018-11-23 16:03:00 +01:00
badrAZ
5121d9d1d7 feat(xo-web/logs): ability to filter logs by the VM name (#3711)
Fixes #2947

To be able to filter logs by the VM name, a property in the top level is needed. `vmNames` is an array which contains all found VMs name. It can be `undefined` if none VM is found.
2018-11-23 14:10:26 +01:00
Pierre Donias
f2a38c5ddd fix(xo-web/xoa/licenses): tip: XOA license mgmt not supported (#3714)
Fixes #3713
2018-11-21 13:12:29 +01:00
badrAZ
97a77b1a33 fix(xo-web/backup-ng-logs): differentiate not fetched yet from empty (#3712)
**NoObjects:**

The `NoObjects` is a wrapper which shows a spinner if a collection is `undefined` and a default message if it's empty.

**The Issue:**

In the line `323`, `groupBy` always returns an object even if `logs` is `undefined`.
2018-11-21 10:31:14 +01:00
Rajaa.BARHTAOUI
88ca41231f feat(xo-web/home/VMs): display pool's name (#3709)
Fixes #2226
2018-11-20 15:59:37 +01:00
badrAZ
9a8f84ccb5 chore(xo-server-perf-alert): add documentation (#3710)
This PR completes the merged PR #3675
2018-11-20 14:18:08 +01:00
Julien Fontanet
dd50fc37fe feat(xen-api): callTimeout option (#3707) 2018-11-20 10:12:58 +01:00
badrAZ
cafcadb286 feat(xo-server-perf-alert): notify if value is below threshold (#3675)
Fixes #3612
2018-11-20 10:07:42 +01:00
Pierre Donias
db3d6bba79 fix(xo-web/acls): action should be reset to undefined (#3704) 2018-11-19 16:14:25 +01:00
Julien Fontanet
11a0fc2a22 chore: update dependencies 2018-11-19 16:09:55 +01:00
badrAZ
1e0a8a5034 fix(CHANGELOG): missing entry (#3703) 2018-11-19 14:28:48 +01:00
Pierre Donias
34ef3e5998 fix(xo-web/xoa): tab icons (#3702) 2018-11-19 13:59:06 +01:00
Enishowk
e73fcc450d feat(xo-server/vm.set): use set_domain_type when available (#3700) 2018-11-19 09:39:16 +01:00
Julien Fontanet
2946eaa156 feat(xo-server): 5.30.1 2018-11-16 18:11:39 +01:00
Julien Fontanet
6dcae9a7d7 fix(xo-server/deleteVm): pass array to Promise.all 2018-11-16 18:10:47 +01:00
Pierre Donias
abeb36f06c chore(CHANGELOG): 5.28.2 2018-11-16 17:53:21 +01:00
Pierre Donias
41139578ba feat(xo-web): 0.30.0 2018-11-16 17:50:25 +01:00
Pierre Donias
cda7621b5d feat(xo-server): 5.30.0 2018-11-16 17:47:58 +01:00
Pierre Donias
b75dd2d424 feat(xo-acl-resolver): 0.4.0 2018-11-16 17:42:41 +01:00
Pierre Donias
273f208722 feat(xo-common): 0.2.0 2018-11-16 17:41:08 +01:00
Pierre Donias
c01e8e892e feat(xen-api): 0.21.0 2018-11-16 17:39:16 +01:00
Julien Fontanet
9dfd81c28f feat(xo-web/subscriptions): keep cached data for 10m (#3701)
Related to #3699

This should improve user experience when changing pages.
2018-11-16 17:37:33 +01:00
Pierre Donias
5dd26ebe33 fix(CHANGELOG): missing entries and links 2018-11-16 16:58:48 +01:00
Julien Fontanet
4c0fe3c14f fix(xo-server/importVdiContent): only throws when compiled with yarn dev
Related to #3678
2018-11-16 14:19:34 +01:00
badrAZ
2353581da8 chore(xo-web/backup-ng): improve setting power state implementation (#3684) 2018-11-16 14:05:58 +01:00
Julien Fontanet
2934b23d2f fix(xo-server/vm.delete): fix template handling (#3695)
Fixes #3498

It seems a recent version of XenServer forbids from destroying VM templates directly, we need to set `is_a_template` to `false` before calling `VM.destroy`.
2018-11-16 11:04:56 +01:00
Julien Fontanet
82e4197237 fix(xo-web/deleteVm): dont ask to force if error (#3696) 2018-11-16 10:39:09 +01:00
Julien Fontanet
a23189f132 fix(xo-server): update VM object when {,guest_}metrics have changed (#3694)
Fixes #3533
2018-11-16 10:32:13 +01:00
Julien Fontanet
47fa1ec81e fix(xo-web/settings/remotes): missing form id (#3697) 2018-11-16 09:35:14 +01:00
Julien Fontanet
4b468663f3 feat(xo-server): ability to set XAPI options in config (#3692) 2018-11-15 16:00:52 +01:00
Nicolas Raynaud
6628dc777d fix(xo-server/xosan.createSR): fix network detection (#3689)
Fixes #3688
2018-11-15 11:10:50 +01:00
Julien Fontanet
3ef3ae0166 fix(xo-server/remote.get{,All}): obfuscate sensitive values (#3687)
Fixes #3682
2018-11-14 14:39:20 +01:00
Julien Fontanet
bc6dbe2771 chore(xen-api/README): add 7.{4..6} compatibility 2018-11-14 14:18:14 +01:00
badrAZ
5651160d1c fix(xo-web/settings/remotes): fix incorrect input type (#3683)
Fixes #3681
2018-11-14 11:21:38 +01:00
Julien Fontanet
6da2669c6f fix(fs/smb#list): throws ENOTDIR instead of SMB error (#3685)
Fixes support#1014

This is more in line of other handlers and fix an issue in Backup NG where listing VM backups would fail when there are files in `xo-vm-backups/`.
2018-11-14 10:11:52 +01:00
Julien Fontanet
8094b5097f feat(xo-server/reaclette-utils/generateId): used as a computed for generated ID (#3680) 2018-11-13 22:58:37 +01:00
Pierre Donias
bdb0547b86 fix(xo-server/vm.create): merge double limit allocation (#3667)
This prevents unwanted error if the limits are exceeded in a transitory fashion.
2018-11-13 17:18:56 +01:00
Pierre Donias
ea08fbbfba fix(xo-server-cloud): missing protocol (#3686) 2018-11-13 17:01:03 +01:00
Julien Fontanet
b4cbd8b2b5 chore(fs): ignore enabled option
It makes no sense for this library to handled disabled remote.
2018-11-13 15:59:16 +01:00
Julien Fontanet
f8fbb6b7d3 feat(xen-api): add call params to errors (#3679)
It also moves `.method` to `.call.method`, but it appears it was not currently used in our code.
2018-11-13 12:03:05 +01:00
Julien Fontanet
c8da9fec0a chore: minor Flow fixes 2018-11-13 11:24:23 +01:00
Julien Fontanet
79fb3ec8bd fix(xo-server/plugin.get): hide sensitive config values (#3671) 2018-11-13 11:09:58 +01:00
Julien Fontanet
2243966ce1 feat(xo-server/checkPermissions): similar to hasPermissions but throws 2018-11-13 10:46:38 +01:00
Julien Fontanet
ca7d520997 feat(xo-acl-resolver/assert): throw error if unauthorized 2018-11-13 10:46:38 +01:00
Julien Fontanet
df44487363 chore(xo-web/backup-ng/log): disable bug reporting
Temporary measure because it is currently broken due to the size of the log in the HTTP request.
2018-11-13 10:13:20 +01:00
Julien Fontanet
b39eb0f60d chore(xo-server/api): add expected permission to unauthorized error 2018-11-12 17:34:15 +01:00
badrAZ
a3dcdc4fd5 chore(xo-server-perf-alert): unused property (#3672) 2018-11-12 15:38:58 +01:00
Julien Fontanet
2daac73c17 chore(xo-common/api-errors/unauthorized): add metadata
```ts
let data: {
  permission?: string,
  object: {
    id?: string,
    type?: string,
  },
}
```
2018-11-12 15:30:28 +01:00
Julien Fontanet
23eb3c3094 chore(xo-server/resourceSet.get{,All}): explicit permission check 2018-11-12 15:25:51 +01:00
Julien Fontanet
776d0f9e4a chore(xo-server/ipPool.getAll): explicit permission check 2018-11-12 15:25:51 +01:00
Enishowk
54bdcc6dd2 feat(xo-web/VM): switch virtualization mode (#3669)
Fixes #2372
2018-11-12 13:57:23 +01:00
Julien Fontanet
38084c8199 chore(xo-common/api-errors/unauthorized): remove "unauthorized" in message
It confuses users and the message is still relevant without in case of unauthorized users.
2018-11-12 13:50:33 +01:00
badrAZ
4525ee7491 fix(xo-server-usage-report): gracefully handle fetching stats error (#3656)
Fixes #3600
2018-11-09 16:10:54 +01:00
badrAZ
66a476bd21 feat(xo-web/backup-ng): warning & omitting VMs/pools on XS < 6.5 (#3668)
Fixes #3540
2018-11-09 15:02:29 +01:00
Pierre Donias
be6cc12632 fix(xo-server/vm.create): revert 37a906a2 (#3666)
Fixes #3658, fixes support#1064
2018-11-08 17:31:07 +01:00
Enishowk
673475dcb2 fix(xo-web/vm): restore display of pvhvm virtualization mode (#3662)
Fixes #3576
2018-11-08 17:05:46 +01:00
badrAZ
7dc1a80a83 fix(xo-server/xapi-stats/getVmStats): avoid sync exceptions (#3661) 2018-11-08 16:31:40 +01:00
Julien Fontanet
d49294849f chore(xo-server/vm.import): remove host parameter (#3663) 2018-11-08 16:30:50 +01:00
Rajaa.BARHTAOUI
6b394302c1 feat(xo-web/sortedTable): mutualize actions (#3594) 2018-11-08 14:40:54 +01:00
Julien Fontanet
00e1601f85 chore(xo-server): use VM.domain_type when available (#3664)
And fallback to our previous detection when unavailable.

Starting from this commit, `virtualizationMode` will no longer contain `'pvhvm'`, this must be computed UI side by using both `virtualizationMode` and `xenTools` properties.
2018-11-08 14:37:47 +01:00
Rajaa.BARHTAOUI
b75e746586 fix(xo-web/StrongConfirm): input loses focus (#3649) 2018-11-08 11:38:17 +01:00
Enishowk
32a9fa9bb0 feat(xo-web/migration): auto-select host/SR when there's only one (#3654)
Fixes #3502
2018-11-08 11:30:38 +01:00
badrAZ
79d68dece4 fix(xo-server/xapi-stats): re-fetch host stats if VM missing (#3660) 2018-11-08 11:28:17 +01:00
Julien Fontanet
1701e1d4ba feat(xo-server/_importVdiContent): require length in dev mode 2018-11-08 10:45:11 +01:00
Julien Fontanet
497b3eb296 feat(xo-server/disk.import): extract length from header 2018-11-08 10:44:35 +01:00
Julien Fontanet
ecfafa0fea chore: npm → yarn 2018-11-07 22:24:46 +01:00
Julien Fontanet
def66d8218 chore(xapi-explore-sr): remove Jest config/scripts 2018-11-07 22:23:17 +01:00
Julien Fontanet
eeb08abec2 chore(xapi-explore-sr): use Babel 7 2018-11-07 22:18:58 +01:00
Julien Fontanet
90923c657d chore: re-format code 2018-11-07 18:37:23 +01:00
Julien Fontanet
4ff6eeb424 chore: update dependencies 2018-11-07 18:15:57 +01:00
Julien Fontanet
2d98fb40f1 feat(xo-server/_assertConsistentHostServerTime): display delta 2018-11-07 17:06:17 +01:00
Julien Fontanet
256a58ded2 feat(xo-server/_assertConsistentHostServerTime): threshold 2s → 30s 2018-11-07 17:00:29 +01:00
badrAZ
bf3b31a9ef fix(xo-web/logs): properly display restore failures (#3648) 2018-11-07 15:20:25 +01:00
badrAZ
7fc8d59605 feat(xo-server/backup-ng): warnings for missing VMs (#3647) 2018-11-07 13:12:26 +01:00
Julien Fontanet
1a39b2113a fix(docs): XO requires Node 8 2018-11-07 10:53:02 +01:00
badrAZ
cb9f3fbb2c chore(xo-server/backup-ng): restore tasks documentation (#3652) 2018-11-07 10:44:33 +01:00
badrAZ
487f413cdd feat(xo-web/backup): move "restore/file-restore" to Backup NG view (#3610)
Fixes #3499
2018-11-07 10:22:31 +01:00
Pierre Donias
f847969206 fix(xo-server/patching): correctly ignore upgrade patches (#3651)
Possibly related to support#1009
2018-11-07 10:10:36 +01:00
Julien Fontanet
5d9aad44c2 Merge branch 'xapi-explore-sr/master' 2018-11-06 18:26:44 +01:00
Julien Fontanet
ba2027e6d7 feat(xapi-explore-sr): move all files to packages/xapi-explore-sr 2018-11-06 18:19:39 +01:00
Julien Fontanet
087da9376f Merge branch 'xo-import-servers-csv/master' 2018-11-06 18:11:58 +01:00
Julien Fontanet
218e3b46e0 feat(xo-import-servers-csv): move all files to packages/xo-import-servers-csv 2018-11-06 17:55:20 +01:00
Rajaa.BARHTAOUI
f9921e354e feat(xo-web/StrongConfirm): press Enter to validate (#2890)
Fixes #2735
2018-11-06 15:29:01 +01:00
badrAZ
341148a7d3 fix(xo-web/logs): fix restarting VMs with concurrency issue (#3634)
Fixes #3603
2018-11-06 13:45:30 +01:00
Julien Fontanet
7216165f1e chore: update dependencies 2018-11-06 13:27:33 +01:00
Julien Fontanet
a9557af04b fix(CHANGELOG): fix versions 2018-11-05 16:48:09 +01:00
Julien Fontanet
abb80270ad feat(xo-web): 5.29.3 2018-11-05 16:47:23 +01:00
Julien Fontanet
72e93384a5 feat(xo-server): 5.29.4 2018-11-05 16:46:54 +01:00
Julien Fontanet
663b1b76ec fix(CHANGELOG): move entry to correct release 2018-11-05 16:46:18 +01:00
Julien Fontanet
24b8c671fa fix(xo-server/mergeVhd): use remote options 2018-11-05 16:41:37 +01:00
Julien Fontanet
986fec1cd3 feat(xo-server): pass config to workers 2018-11-05 16:41:37 +01:00
badrAZ
f6c2cbc5cf chore(xo-web/backup): devs can create legacy backups (#3645)
Fixes #3624
2018-11-05 14:40:42 +01:00
Pierre Donias
289ed89a78 fix(xo-server/vm.create): dont extract cpus & memoryMax from params (#3646)
Fixes #3644
2018-11-05 14:34:58 +01:00
Enishowk
73de421d47 feat(xo-web/vm/advanced): add nested virt toggle (#3625)
Fixes #3619
2018-11-05 14:17:19 +01:00
badrAZ
dc1eb82295 chore(xo-server/backup-ng): tasks' documentation (#3640) 2018-11-05 14:16:36 +01:00
Enishowk
6629c12166 fix(xo-web/form/Toggle): dont emit onChange if disabled (#3643)
fix(xo-web/form/Toggle): dont emit onChange if disabled
2018-11-05 14:09:13 +01:00
badrAZ
ec5bc1db95 fix(xo-web/backup-ng-logs): incorrect started jobs filter (#3641)
Fixes #3636
2018-11-05 12:09:52 +01:00
Julien Fontanet
ac2c40c842 fix(xo-server/vm.*): ensure force params are optional 2018-11-05 10:42:28 +01:00
Julien Fontanet
61bf669252 feat(fs/nfs): ensure mount error not hidden 2018-11-05 10:11:57 +01:00
Julien Fontanet
4105c53155 chore(CHANGELOG): 5.28.1 2018-11-05 09:59:36 +01:00
Julien Fontanet
aeab2b2a08 feat(xo-web): 5.29.2 2018-11-05 09:57:50 +01:00
Julien Fontanet
95e33ee612 feat(xo-server): 5.29.3 2018-11-05 09:57:01 +01:00
Julien Fontanet
093bda7039 feat(fs): 0.4.1 2018-11-05 09:53:43 +01:00
Julien Fontanet
4e35b19ac5 fix(xo-web/xoa/update): fix re-registration 2018-11-05 09:46:22 +01:00
Julien Fontanet
244d8a51e8 chore(xo-web/xoa/update): dont hide error 2018-11-05 09:42:59 +01:00
Julien Fontanet
9d6cc77cc8 chore(changelog): add timeout entry 2018-11-03 17:45:50 +01:00
Julien Fontanet
d5e0150880 chore: update promise-toolbox to v0.11
`timeout()` now provides better stack traces and support 0 delay to
disable the timeout.
2018-11-03 17:45:50 +01:00
Julien Fontanet
5cf29a98b3 feat(fs): configurable timeout 2018-11-03 17:45:50 +01:00
Julien Fontanet
165c2262c0 fix(nfs): default timeout 10s → 10m 2018-11-03 17:45:50 +01:00
badrAZ
74f5d2e0cd fix(xo-server/backup-ng): "vms" should be a dictionary (#3635) 2018-11-02 16:29:44 +01:00
badrAZ
2d93456f52 feat(xo-server/backup-ng): log scheduled VMs if concurrency above 0 (#3633)
See #3603
2018-11-02 14:56:46 +01:00
Julien Fontanet
fd401ca335 fix(fs/nfs): opts param is optional 2018-11-02 10:09:19 +01:00
Julien Fontanet
97ba93a9ad chore(fs): configurable mounts dir (#3413) 2018-11-02 09:37:35 +01:00
Pierre Donias
0788c25710 feat(xo-web): 5.29.1 2018-10-31 17:20:15 +01:00
Julien Fontanet
82bba951db feat(xo-web/xoa-updater/logs): from old to new
Fixes #2708
2018-10-31 17:18:53 +01:00
Julien Fontanet
6efd611b80 feat(xo-web/xoa/update): use pre for updater logs 2018-10-31 17:18:53 +01:00
Julien Fontanet
b7d43b42b9 fix(xo-web/xoa/update): toggleState not compatible with ActionButton 2018-10-31 17:18:52 +01:00
Julien Fontanet
801b71d9ae fix(xo-web/xoa/update): typo 2018-10-31 17:18:52 +01:00
Pierre Donias
0ff7c2188a feat(xo-server): 5.29.2 2018-10-31 16:56:23 +01:00
Julien Fontanet
bc1667440f feat(log): 0.1.4 2018-10-31 16:55:11 +01:00
Julien Fontanet
227b464a8e fix(log): paths in transports/* 2018-10-31 16:54:35 +01:00
Pierre Donias
f6c43650b4 feat(xo-server): 5.29.1 2018-10-31 16:43:29 +01:00
Pierre Donias
597689fde0 chore(xo-server): use log 0.1.3 2018-10-31 16:42:03 +01:00
Julien Fontanet
da6b71fde8 feat(log): 0.1.3 2018-10-31 16:39:20 +01:00
Julien Fontanet
5f2590c858 fix(log): remove transports symlink
Issue with publication.
2018-10-31 16:38:23 +01:00
Julien Fontanet
37b0867151 feat(log): 0.1.1 2018-10-31 16:31:22 +01:00
Julien Fontanet
85031cfb9d fix(log): publish configure.js and transports 2018-10-31 16:30:55 +01:00
Pierre Donias
a13f86fb7c chore(CHANGELOG): 5.28.0 2018-10-31 16:03:35 +01:00
Pierre Donias
7cbc5e642f feat(xo-web): 5.29.0 2018-10-31 15:59:53 +01:00
Pierre Donias
48d4abc259 feat(xo-server): 5.29.0 2018-10-31 15:58:52 +01:00
Pierre Donias
c805f3b1a7 feat(xo-server-usage-report): 0.7.0 2018-10-31 15:55:26 +01:00
Pierre Donias
40212582a9 feat(xen-api): 0.20.0 2018-10-31 15:53:40 +01:00
Pierre Donias
2c875928de feat(vhd-lib): 0.4.0 2018-10-31 15:51:49 +01:00
Pierre Donias
c24eb9778e feat(complex-matcher): 0.5.0 2018-10-31 15:48:31 +01:00
Pierre Donias
eb079b1360 feat(fs): 0.4.0 2018-10-31 15:46:49 +01:00
Pierre Donias
120a519303 chore(@xen-orchestra/log): packages should not be private 2018-10-31 15:32:17 +01:00
Pierre Donias
95def95678 feat(log): 0.1.0 2018-10-31 15:26:58 +01:00
Pierre Donias
8274a00f91 feat(xo-common): 0.1.2 2018-10-31 15:21:09 +01:00
Pierre Donias
3c6c4976cd feat(xo-server-backup-reports): 0.15.0 2018-10-31 15:19:21 +01:00
Julien Fontanet
0fd35b1679 feat(xo-web/xoa): product version and pkgs list (#3621)
Fixes #3560
2018-10-31 10:49:10 +01:00
Enishowk
3c931604be fix(xo-web/backup-ng/utils/FormFeedback): fix incorrect prop type (#3604)
See #2578
2018-10-31 10:14:41 +01:00
Julien Fontanet
02dddbd662 chore(xo-web/xoa/update): rewrite (#3620) 2018-10-31 09:46:44 +01:00
Julien Fontanet
675763d039 chore(xo-web): mutualize chain decoration 2018-10-30 18:40:21 +01:00
Julien Fontanet
63acc7ef32 fix(xo-server/bin): import → require 2018-10-30 17:45:02 +01:00
Enishowk
cbd78bdfef chore(xo-server): use @xen-orchestra/log to handle global errors (#3618)
Fixes #3616
2018-10-30 17:38:24 +01:00
Julien Fontanet
a09a2ed6c3 chore(log/configure): explicit test 2018-10-30 17:14:44 +01:00
Enishowk
7d18a6d8a9 fix(xo-web/Home/NoObjects): only subscribe to servers if admin (#3613)
Fixes #2335
2018-10-30 15:58:06 +01:00
Julien Fontanet
65a5984d4c fix(cron): prevent exceptions from breaking the job (#3617)
Fixes support#1038
2018-10-30 15:26:46 +01:00
badrAZ
d5f519bf5a feat(backup-ng): display logs for backup restoration (#3609)
Fixes #2511
2018-10-30 15:26:26 +01:00
badrAZ
bede39c8f3 fix(xo-server-usage-report): handle SR without container (#3614)
This is a work-around for an XO bug.

Related to #3600
2018-10-30 14:07:50 +01:00
badrAZ
a9e3682776 fix(xo-server-usage-report): all SRs display issue (#3615)
Introduced by #3508
2018-10-30 13:54:57 +01:00
Enishowk
87c3c8732f fix(xo-server/jobs): emit job:terminated even if job throws (#3593)
Fixes #3458
2018-10-30 13:39:49 +01:00
Enishowk
0011bfea8c feat(travis/tests): run tests on files that differs from master (#3599)
Fixes #2703
2018-10-30 11:57:26 +01:00
Pierre Donias
e047649c3b fix(xo-web/patches): ignore CDs with detached VBD (#3611)
Fixes support#1032
2018-10-30 09:53:11 +01:00
badrAZ
de397b63c5 feat(xo-web/sorted-table): "valuePath" property (#3607)
Fixes #3606
2018-10-30 09:42:13 +01:00
Julien Fontanet
75b7726fca chore: format YAML with Prettier 2018-10-29 13:29:30 +01:00
Julien Fontanet
d83a2366c2 chore(ci): dont test on Node 6 2018-10-26 16:47:33 +02:00
Julien Fontanet
2d4d653c55 chore: re-format code 2018-10-26 16:40:00 +02:00
badrAZ
c7a1d55f6f chore: rename freactal to reaclette (#3601) 2018-10-26 15:24:50 +02:00
Enishowk
46b5c5ccd1 feat(xo-server): Replace debug with @xen-orchestra/log for basic logging (#3579)
Fixes #3555
2018-10-26 14:14:17 +02:00
Jon Sands
4607417e7a chore(docs/backup troubleshooting): Parse Error with Delta Backup (#3588)
Also add note about OVA failure with xo-cli.
2018-10-25 23:52:48 +02:00
Julien Fontanet
76887c7e25 chore(xo-web/self/_getIpPoolPredicate): dont use include
`O(n)` → `O(1)`
2018-10-25 16:02:28 +02:00
Julien Fontanet
ab7cae5816 fix(xo-web/self): fix IP pool predicate selector (#3598) 2018-10-25 15:58:23 +02:00
badrAZ
b1ce389ad8 fix(xo-web/backup-ng): don't submit when retention is undefined (#3487) 2018-10-25 15:44:35 +02:00
badrAZ
52aa5ff780 feat(xo-server-backup-reports): support task warnings (#3596) 2018-10-25 12:22:35 +02:00
badrAZ
30372e511e feat(xo-server/backup-ng): consolidation of the restore logs (#3537) 2018-10-25 12:21:28 +02:00
Rajaa.BARHTAOUI
dc15a6282a feat(xo-web/sr/hosts, xo-web/host/srs): use SortedTable actions (#3539)
See #3179
2018-10-25 10:47:21 +02:00
badrAZ
97dcc204ef feat(xo-web/backup-ng/logs): support task warnings (#3591)
Fixes #3589
2018-10-25 09:31:23 +02:00
Julien Fontanet
ecda3e0174 feat(lint-staged): add formatting changes before testing (#3595)
So there won't be stylistic diffs between files and index if the test fails.
2018-10-25 09:16:21 +02:00
Rajaa.BARHTAOUI
8342bb2bc8 fix(xo-web/SortedTable): broken individual actions
Introduced by cdced7cdc1
2018-10-24 16:53:19 +02:00
badrAZ
51a137c4e5 feat(xo-server/schemas/log): task warning (#3590)
See #3589
2018-10-24 10:58:56 +02:00
Enishowk
a26ced5de9 chore(xo-web/backup-ng): rework the Schedule view (#3586)
Fixes #3491
2018-10-24 10:13:09 +02:00
badrAZ
85f0c69c03 chore(xo-server): use reaclette instead of @julien-f/freactal (#3587) 2018-10-23 17:57:00 +02:00
Julien Fontanet
3aac757ef5 feat(xo-web/selectors): cache ACLs resolution (#3584)
Fixes #3578
2018-10-23 14:50:09 +02:00
badrAZ
91541d0ba4 feat(xo-web/log): disable filters with no entries (#3442)
Fixes #3438
2018-10-23 12:01:26 +02:00
badrAZ
dfd66a56c3 feat(xo-server-usage-report): ability to send daily (#3582)
Fixes #3544
2018-10-23 09:20:11 +02:00
badrAZ
60f9393d29 fix(xo-web/scheduling): fix unselecting single value (#3577)
Fixes #3574
2018-10-22 23:36:44 +02:00
Rajaa.BARHTAOUI
cdced7cdc1 feat(xo-web/SortedTable): sort action by level (#3545)
Fixes #3168
2018-10-22 13:53:12 +02:00
Julien Fontanet
69709009ed fix(xo-web/backup-ng/new): fix timeout unit (#3575)
Fixes issue introduced in aa5b3dc42
2018-10-20 16:10:36 +02:00
Julien Fontanet
bf14560709 feat(xo-web/form/range): handle undefined value 2018-10-19 16:30:54 +02:00
Julien Fontanet
775b629ee9 fix(xo-web/form/range): fix required prop type 2018-10-19 16:30:54 +02:00
Julien Fontanet
ec9717dafb chore(xo-web/scheduling): compute TimePicker max step (#3568) 2018-10-19 16:22:25 +02:00
Julien Fontanet
0cd84ee250 chore(xo-server): move LVM start hook to file restore NG
Otherwise, I have a feeling that we may forget to migrate this code when
legacy backup code will be retired.
2018-10-19 11:10:36 +02:00
Pierre Donias
b3681e7c39 fix(xo-server/recomputeResourceSetsLimits): omit CR and DR VMs (#3561)
Fixes #3064
2018-10-19 09:58:39 +02:00
Pierre Donias
a7a7597d9a feat(xo-web/backup-ng/restore): show job in backup select (#3564)
Fixes #3264
2018-10-19 09:48:55 +02:00
badrAZ
bed3da81e1 feat(xo-web/scheduling): merge selection and interval tabs (#3519)
Fixes #1902
2018-10-19 09:20:51 +02:00
Pierre Donias
c43dc31a55 fix(CHANGELOG): missing PR links (#3562) 2018-10-18 18:50:19 +02:00
Enishowk
c5a21922d1 chore(backup-ng): collapse advanced settings by default (#3559)
Fixes #3551
2018-10-18 14:28:35 +02:00
Pierre Donias
2ae660a46b fix(xo-web/settings/acls): freactal to fix lifecycle issues (#3548) 2018-10-18 13:47:48 +02:00
Julien Fontanet
f6fcae4489 chore(xo-server/utils): remove unused pDebug 2018-10-18 10:53:55 +02:00
Enishowk
e0a3b8ace8 feat(xo-web/backup-ng/overview): show advanced settings with non-default values (#3554)
Fixes #3549
2018-10-18 10:46:15 +02:00
badrAZ
b67231c56b feat(xo-server/backup-ng): report missing VMs (#3522)
Fixes #3434
2018-10-18 10:23:56 +02:00
Enishowk
aa5b3dc426 chore(xo-web/backup-ng): timeout is in hours (#3553)
Fixes #3550
2018-10-17 19:24:47 +02:00
Enishowk
1a528adfbb feat(complex-matcher) : raw numbers can match strings (#3552)
Fixes #2906
2018-10-17 18:24:49 +02:00
Enishowk
64d295ee3f chore(xo-web): remove unnecessary prop-types-decorator (#3542) 2018-10-17 16:30:57 +02:00
Julien Fontanet
b940ade902 fix(xo-common/api-errors): predicate is optional (#3543) 2018-10-17 15:49:27 +02:00
Pierre Donias
37a906a233 fix(xo-server/VM/create): compute quotas from user inputs (#3546)
Fixes #2683
2018-10-17 14:55:48 +02:00
Rajaa.BARHTAOUI
e76603ce7e feat(xo-web/settings/acls): use SortedTable actions (#3536)
See #3179
2018-10-17 14:07:42 +02:00
Nicolas Raynaud
aca9aa0a7a feat(vhd-lib/createReadableSparseStream): expose stream.length (#3526) 2018-10-16 17:16:59 +02:00
Pierre Donias
6d20ef5d51 fix(xo-server/disk.create): handle resource set not found (#3530)
Fixes #2814
2018-10-16 16:58:13 +02:00
Pierre Donias
4d18ab1ae0 fix(xo-web/new VM): missing cloud configs in some cases (#3535)
Fixes #3532
2018-10-16 14:14:46 +02:00
Julien Fontanet
fa5c707fbc fix(xo-server/worker): use Bluebird as Promise (#3538)
Similar to 6c6dfa9ac4 but for the worker processes.
2018-10-16 11:53:41 +02:00
Rajaa.BARHTAOUI
37b9d8ec10 feat(xo-web/settings/logs): use SortedTable actions (#3528)
See #3179
2018-10-16 11:52:05 +02:00
Enishowk
61db0269a2 feat(fs): add timeouts to atomic operations (#3534)
Fixes #3467
2018-10-16 10:38:46 +02:00
Rajaa.BARHTAOUI
a8ad13f60e feat(xo-web/settings/users): use SortedTable actions (#3531)
See #3179
2018-10-16 10:31:29 +02:00
badrAZ
f14dd04ea7 feat(xo-server/backup-ng): log the restore tasks (#3452) 2018-10-15 18:08:46 +02:00
badrAZ
0add8cd5a3 fix(CHANGELOG): missing PR/issue link (#3529) 2018-10-15 14:55:49 +02:00
Pierre Donias
16cc539a57 fix(xo-web/pool/patches): missing withRef (#3527)
Fixes #3523
2018-10-15 09:49:31 +02:00
Julien Fontanet
5ba25a34cb chore(xo-server/jobs): move plugins:registered listener out of start 2018-10-12 14:02:46 +02:00
Julien Fontanet
61de65fc21 chore(xo-server/jobs): handle failing getLogger 2018-10-12 14:01:54 +02:00
Julien Fontanet
5195539a95 chore(xo-server): remove testing on disabled remotes 2018-10-12 13:40:59 +02:00
Julien Fontanet
ce93fb0e4c fix(xo-server): fix removing broken remotes (#3521)
Fixes #3327
2018-10-12 11:36:40 +02:00
badrAZ
3cb58ed700 fix(xo-web/backup-ng): deleted remote issue (#3520) 2018-10-12 11:32:41 +02:00
Julien Fontanet
bb48c960fe feat(vhd-lib/createSyntheticStream): add length property to stream (#3517)
Expose the file size on the VHD stream, necessary to be compatible with a future version of vhd-tool (f978789dc4).
2018-10-11 17:57:31 +02:00
Nicolas Raynaud
286a0031dd VHD: add test on stream length 2018-10-11 08:40:00 -07:00
Nicolas Raynaud
dcbd7e1113 remove intellij file 2018-10-11 08:36:44 -07:00
Nicolas Raynaud
0a43454c8a VHD: add test on stream length 2018-10-11 08:23:45 -07:00
Julien Fontanet
f5f1491e47 fix(changelog): packages by publish order
Or inverse dependebcy order.
2018-10-11 16:50:49 +02:00
Julien Fontanet
e935ae567f async createSyntheticStream is a breaking change 2018-10-11 16:50:04 +02:00
Julien Fontanet
3f08f099fe fix sync uses of createSyntheticStream 2018-10-11 16:49:29 +02:00
Nicolas Raynaud
18a5ba0029 VHD: remove unnecessary rounding 2018-10-11 07:17:15 -07:00
Nicolas Raynaud
c426d0328f VHD: revert version increment 2018-10-11 07:15:50 -07:00
Nicolas Raynaud
91b2456c15 VHD: expose file length on the stream object 2018-10-10 18:36:54 -07:00
Nicolas Raynaud
585aa74e0c Merge branch 'master' into nr-vhd-stream-length
# Conflicts:
#	CHANGELOG.md
2018-10-10 18:35:20 -07:00
Nicolas Raynaud
eefaec5abd VHD: expose file length on the stream object 2018-10-10 16:35:46 -07:00
Nicolas Raynaud
c7a5eebff6 VHD: expose file length on the stream object 2018-10-10 16:28:59 -07:00
badrAZ
f077528936 feat(xo-server-usage-report): add top 3 IOPS VM usage (#3463)
Fixes #3308
2018-10-10 16:40:26 +02:00
badrAZ
39728974b1 feat(xo-server-backup-reports): add job and run ID (#3516)
Fixes #3488
2018-10-10 16:00:27 +02:00
badrAZ
e14585895b fix(xo-server-usage-report): handle fetching missing patches failure (#3515)
Fixes #3510
2018-10-10 15:39:06 +02:00
badrAZ
0999042718 feat(xo-web/backup-ng/new): link to plugins setting (#3514)
Fixes #3457
2018-10-10 14:45:28 +02:00
badrAZ
4e2e669533 feat(xo-web/new-vm): display warning when memory < static_min (#3513)
Fixes #3496
2018-10-10 14:16:37 +02:00
badrAZ
de266ae6a8 chore(xo-server-usage-report): rename top VMs/hosts header (#3511) 2018-10-10 11:27:22 +02:00
badrAZ
d7cd87a6e4 feat(xo-server-usage-report): add top 3 SRs by IOPS (#3508)
Fixes #3306
2018-10-10 11:26:33 +02:00
Julien Fontanet
c5aabbadc2 feat(xo-web/self): order resource sets by name (#3507)
Fixes support#984.
2018-10-09 16:38:06 +02:00
badrAZ
36a5e3c2ab feat(xo-server-usage-report): add VM IOPS read/write/total (#3455)
Fixes #3309
2018-10-08 17:18:22 +02:00
badrAZ
f475261b9a chore(xo-server-usage-report): improve implementation (#3472) 2018-10-08 16:51:56 +02:00
Julien Fontanet
62dce8f92a chore(package): update dependencies 2018-10-08 13:50:50 +02:00
Julien Fontanet
e6d90d2154 feat(xen-api): _wrapRecord() (#3448)
Objectives:

- reduced memory usage and perf enhancement due to:
   - mutualization of fields and methods via prototype
   - mutualization of hidden classes
- easier manipulation via helper methods:
   - `await pool.set_name_label('my pool')`
   - `await pool.update_other_config({ foo: 'bar', baz: null })`
2018-10-08 10:58:03 +02:00
badrAZ
b5d823ec1a fix(xo-server-usage-report): dont show evolution if 0 (#3471) 2018-10-08 10:27:15 +02:00
badrAZ
a786c68e8b fix(xo-server-usage-report): fix HTML (#3473) 2018-10-05 16:37:14 +02:00
Pierre Donias
e6fa00c4d8 chore(CHANGELOG): 5.28.0 2018-10-05 14:03:50 +02:00
Pierre Donias
5721fac793 feat(xo-web): 5.28.0 2018-10-05 14:01:32 +02:00
Pierre Donias
674ed4384a feat(xo-server): 5.28.0 2018-10-05 14:00:27 +02:00
Pierre Donias
1d7d83f8c6 feat(xo-acl-resolver): 0.3.0 2018-10-05 13:51:18 +02:00
Pierre Donias
f812cc2729 feat(xo-server-usage-report): 0.6.0 2018-10-05 13:49:36 +02:00
Pierre Donias
abc50a5e84 feat(xo-vmdk-to-vhd): 0.1.5 2018-10-05 13:47:44 +02:00
Pierre Donias
e94cae3044 feat(vhd-lib): 0.3.2 2018-10-05 13:45:06 +02:00
Pierre Donias
0b35a35576 feat(xo-server/vm.clone): add admin ACL and allocate Self resources (#3493)
Fixes #3139
2018-10-05 12:07:28 +02:00
badrAZ
0253c63db3 feat(acls): allow VM operators to create/delete snapshots (#3482)
Fixes #3443
2018-10-05 11:48:42 +02:00
Julien Fontanet
0d3b2bc814 fix(vhd-lib,xo-vmdk-to-vhd): add polyfill for Symbol.asyncIterator (#3492)
Fixes part of #3468.
Fixes tests on CI.
2018-10-05 10:44:03 +02:00
Julien Fontanet
65307e5bc7 fix(xo-server/xapi#exportVm): destroy snapshot on getResource failure 2018-10-05 09:51:48 +02:00
Julien Fontanet
90de47d708 chore(xo-server/xapi#exportVm): minor code improvement 2018-10-05 09:51:48 +02:00
Julien Fontanet
72a4179c03 chore(xo-server/xapi): add metadata to {export,import}Vm errors 2018-10-05 09:51:48 +02:00
badrAZ
4eee195d21 fix(CHANGELOG): add entries related to #3475 (#3489) 2018-10-04 16:56:17 +02:00
badrAZ
9488711406 feat(xo-server-usage-report): top 3 used SRs instead of big SRs (#3475)
Fixes #3307
2018-10-04 16:08:18 +02:00
Julien Fontanet
4cf04aca72 feat(cr-seed-cli): 0.2.0 2018-10-04 15:09:22 +02:00
Julien Fontanet
410d6762bf fix(cr-seed-cli): set xo:backup:exported on snapshot
Related to bdefd0bcd
2018-10-04 15:08:16 +02:00
Julien Fontanet
0a95426e63 fix(xo-server/delta NG): detection of removed disks
Related to eb9655125
2018-10-04 14:53:33 +02:00
Julien Fontanet
4ec4970d49 chore(async-map): build for tests 2018-10-03 21:27:56 +02:00
Julien Fontanet
e57ae0a8ce chore(xo-server): add support for ?? 2018-10-03 16:00:24 +02:00
badrAZ
1e7852369f feat(xo-server/xapi-stats): expose VM IOPS metric (#3454) 2018-10-03 15:50:35 +02:00
Julien Fontanet
bdefd0bcd8 feat(xo-server/delta NG): mark successfully exported snapshots (#3485)
Allows runs after failure/interruption to be deltas.
Also, allows some schedules to only do rolling snapshots without breaking the exports.

Thanks a lot to @Samuel-BF for PR #3466.
2018-10-03 15:36:08 +02:00
badrAZ
fa88e1789c fix(xo-web/backup-ng/new): fix retention default value (#3486) 2018-10-03 15:26:48 +02:00
Pierre Donias
df90094cae feat(xo-web/pool/patches): extra modal before bulk install (#3484)
Fixes #3252
2018-10-03 15:15:04 +02:00
Pierre Donias
efdbc18a0a feat(xo-web/home/pool): show # of *unique* available patches (#3483)
Fixes #3321
2018-10-03 09:17:35 +02:00
Julien Fontanet
fc1dd3ce09 chore(xo-cli): got → http-request-plus 2018-10-02 20:19:43 +02:00
Julien Fontanet
10aff53d2c chore(xo-server-cloud): superagent → http-request-plus 2018-10-02 19:57:27 +02:00
Pierre Donias
85c3d64c04 feat(xo-web/host/network): private networks (#3481)
Fixes #3387
2018-10-02 14:42:24 +02:00
badrAZ
5a71ab53be feat(xo-acl-resolver): allow ACLs on VM snapshots (#3480)
Related to #3443
2018-10-02 13:33:26 +02:00
Julien Fontanet
d022b40732 chore: update dependencies 2018-10-02 12:01:14 +02:00
Pierre Donias
e105c0aad1 feat(xo-web/host/networks): remove "Add network" button (#3478)
Fixes #3386
2018-10-02 11:38:28 +02:00
Julien Fontanet
eb9655125c fix(xo-server/delta NG): handle removed disks (#3479) 2018-10-02 11:31:11 +02:00
Julien Fontanet
a10fea2823 chore(xo-server/xapi/utils): remove NULL_REF in favor of xen-api 2018-10-02 11:07:19 +02:00
badrAZ
0c05d89d3f fix(xo-web/VM/snapshot): allow VM admin to access snapshot tab (#3477) 2018-10-02 10:52:36 +02:00
Pierre Donias
d600d4cc28 chore(CHANGELOG): 5.27.1 2018-09-28 17:08:42 +02:00
Pierre Donias
4f0e5317ed feat(xo-web): 5.27.1 2018-09-28 17:05:40 +02:00
Pierre Donias
fce7c7fd49 feat(xo-server): 5.27.2 2018-09-28 17:04:48 +02:00
Pierre Donias
929ca767ca feat(xo-vmdk-to-vhd): 0.1.4 2018-09-28 17:01:43 +02:00
Pierre Donias
342c1bc6fa feat(vhd-lib): 0.3.1 2018-09-28 16:58:46 +02:00
Pierre Donias
317e301067 feat(fs): 0.3.1 2018-09-28 16:56:01 +02:00
Julien Fontanet
ac5741a341 fix(xo-server/deleteVm): build disks list before deleting VM (#3465)
Otherwise there is a race condition and VBD records might have been removed from cache before listing the VM disks due to destroying the VM.
2018-09-28 16:48:21 +02:00
badrAZ
3f1fb7092b fix(xo-web/modal#form): prevent default behavior of submit (#3462) 2018-09-28 11:42:47 +02:00
Julien Fontanet
7298a8b8f0 fix(fs/NfsHandler): race conds on sync() (#3460)
Fixes #3380
2018-09-28 11:40:12 +02:00
Julien Fontanet
856924c970 chore(log): remove @babel-polyfill dep 2018-09-26 17:41:43 +02:00
Julien Fontanet
9a285d280f chore(fs/NfsHandler): merge _loadRealMounts and _matchesRealMount in _sync 2018-09-26 17:00:47 +02:00
Julien Fontanet
9c3fc56d4a chore(fs/NfsHandler#_umount): remove unused remote param 2018-09-26 17:00:47 +02:00
Julien Fontanet
aaaee45eeb chore(xo-server): remove @babel/polyfill (#3453)
Fixes #2580
Fixes #2820
2018-09-26 14:22:02 +02:00
Nicolas Raynaud
ac2ab21826 fix(vhd-lib): fix geometry computation for more than 2^28 sectors (#3451)
Fixes https://gitlab.com/vates/xoa-support/issues/942
It was a bug in the disk geometry computation.
2018-09-25 14:51:17 +02:00
badrAZ
b22514646e feat(xo-web/backup-ng/overview): display the schedule's name (#3445)
Fixes #3444
2018-09-24 13:43:49 +02:00
Julien Fontanet
c7ab1ddb0c chore: update dependencies 2018-09-24 13:39:31 +02:00
Pierre Donias
7ddc595a1c fix(xo-web/file-restore): incorrect line-through on similar filenames (#3447) 2018-09-24 11:09:45 +02:00
Pierre Donias
4147800266 fix(xo-web/file-restore): ensure folder path trailing slash (#3446) 2018-09-24 10:52:48 +02:00
Pierre Donias
99e6d54647 chore(CHANGELOG): 5.27.0 2018-09-24 10:01:39 +02:00
Julien Fontanet
dac5901c6b feat(xo-server): 5.27.1 2018-09-22 14:01:24 +02:00
Julien Fontanet
308928a7d4 feat(xen-api): 0.19.0 2018-09-22 14:00:55 +02:00
Julien Fontanet
e6e3d2cd52 chore: update http-request-plus to 0.6.0 2018-09-22 13:06:30 +02:00
Pierre Donias
2e01de7ff8 feat(xo-web): 5.27.0 2018-09-21 18:02:57 +02:00
Pierre Donias
90f94da4e4 feat(xo-server): 5.27.0 2018-09-21 18:01:27 +02:00
Pierre Donias
29b5acef3f chore(xo): packages should not be private 2018-09-21 17:50:49 +02:00
Pierre Donias
599b094b50 feat(xo-server-backup-reports): 0.14.0 2018-09-21 17:43:41 +02:00
Julien Fontanet
7c6e423d24 fix(Backup NG): remove all unnecessary snapshots (#3439)
Even those from other schedules.

Fixes #3132
2018-09-21 17:37:49 +02:00
badrAZ
82956af785 fix(xo-web/logs): fix @xen-orchestra/log import (#3441) 2018-09-21 15:36:54 +02:00
badrAZ
6ca09dc9fe feat(xo-web/vm/tab-advanced): ability to set the NIC type (#3440)
Fixes #3423
2018-09-21 15:30:23 +02:00
badrAZ
e0ecbab841 feat(xo-server/vm): ability to change NIC type (#3437)
See #3423
2018-09-21 14:42:43 +02:00
Julien Fontanet
83d1a5ff13 feat(defined): helpers to deal with undefined (#3436)
Extracted from xo-web.
2018-09-21 11:56:33 +02:00
badrAZ
a71740d49a feat(backup-ng): ability to restart all failed VMs (#3420)
Fixes #3339
2018-09-20 17:05:57 +02:00
Julien Fontanet
0215c19d1d feat(xo-server/proxy-console): work around missing host (#3435)
Fixes #3432
2018-09-20 14:47:39 +02:00
Julien Fontanet
ea1c3ab54a fix(xo-server/delta NG): dont start export before all targets ready (#3427)
Fixes #3424
2018-09-20 14:38:45 +02:00
badrAZ
b98b618be8 fix(xo-server-backup-reports): handle log not found (#3430) 2018-09-20 14:34:29 +02:00
Julien Fontanet
5e363761a2 fix(xo-server/proxy-console): pass hostname to net.connect 2018-09-20 10:25:47 +02:00
Julien Fontanet
62d48bd59d fix(async-map): apply → call 😖
Fixes #3431
2018-09-20 10:04:09 +02:00
badrAZ
a0049bae8d fix(xo-web/backup-ng): make log value dynamic in copy/report buttons (#3360)
Fixes #3273
2018-09-18 15:15:59 +02:00
Julien Fontanet
18660cb0e1 fix(async-map): accept objects as collection 2018-09-18 12:10:44 +02:00
Julien Fontanet
e3c6c1c1ca chore(async-map): use lodash/map import 2018-09-18 12:10:43 +02:00
badrAZ
56114a7d18 feat(xo-server-backup-report): ability to test the plugin (#3421) 2018-09-17 17:23:45 +02:00
Julien Fontanet
0fe70b1a91 feat(log): new lib to help logging (#3414)
Fixes #2414
Related to #1669
2018-09-17 17:02:27 +02:00
Julien Fontanet
527eb0b1e6 feat(async-map): better Promise.all + map (#3422) 2018-09-17 16:37:49 +02:00
Julien Fontanet
cfd6fd722a feat(mixin): split a class in independant parts (#3415) 2018-09-17 16:33:21 +02:00
badrAZ
42591bd4bd feat(xo-web/VM): display the PVHVM status (#3418)
Fixes #3014
2018-09-17 11:06:19 +02:00
Julien Fontanet
8689cff26b feat(emit-async): handle async listeners (#3416)
Extracted from xo-server.
2018-09-17 10:42:22 +02:00
badrAZ
b4e4d32255 feat(xo-web/backup-ng/overview): display transferred and merged data size (#3408) 2018-09-14 17:21:06 +02:00
Julien Fontanet
18c88ba770 feat(cr-seed-cli): CLI to help CR seeding (#3346) 2018-09-14 17:04:55 +02:00
badrAZ
d212168f59 feat(xo-web/backup-ng/new): use a modal for creating/editing a schedule (#3359)
Fixes #3138
2018-09-14 15:53:28 +02:00
Julien Fontanet
3625477187 fix(xo-web/xoa-updater): wait trial request before checking state (#3412)
Fixes #3407
2018-09-14 12:26:41 +02:00
Pierre Donias
9be8525439 await xoaState 2018-09-14 12:04:19 +02:00
Pierre Donias
6e013a0dc5 PR 2018-09-14 11:54:39 +02:00
Pierre Donias
06d555d4f9 fix(xo-web/xoa-updater): wait for trial request before checking trial state
Fixes #3407
2018-09-14 11:51:43 +02:00
Julien Fontanet
63fe0f2c88 fix(xo-server/importDeltaVm): dont fail on empty VBDs (#3410) 2018-09-14 10:37:09 +02:00
Julien Fontanet
2a276dfb99 changelog 2018-09-14 10:36:49 +02:00
Julien Fontanet
1f009ca954 fix(xo-server/importDeltaVm): dont fail on empty VBDs 2018-09-13 17:27:11 +02:00
badrAZ
5cf1ba41f3 feat(xo-web/modal#form): support an additional icon to the title (#3409) 2018-09-13 16:58:03 +02:00
badrAZ
1ef205cb74 feat(xo-web/backup-ng/new): tip for creating vms on a thin-provisioned storage (#3402)
Fixes #3334
2018-09-13 16:23:51 +02:00
Julien Fontanet
1e59af3ab2 fix(xo-server/deleteVm): dont delete VDIs/VIFs if destroy is blocked (#3406) 2018-09-13 11:44:17 +02:00
badrAZ
bb88b420c1 chore(xo-web/backup-ng/new): make "setSchedule" support multiple params (#3404) 2018-09-12 13:53:37 +02:00
badrAZ
e8475d144c chore(xo-web/backup-ng/new): improve new schedule implementation (#3348) 2018-09-12 13:43:02 +02:00
badrAZ
dedac62269 feat(xo-web): implementation of the modal dedicated to forms (#3358) 2018-09-11 17:31:20 +02:00
badrAZ
f8fdd888c4 fix(xo-web/remotes): error appears twice on testing (#3399) 2018-09-11 17:27:10 +02:00
Julien Fontanet
d8bd30e355 feat(xo-web/remotes): use WORKGROUP as default SMB domain (#3398) 2018-09-11 10:46:52 +02:00
badrAZ
4fec816274 feat(xo-web/settings/remotes): test on creation/edition (#3397)
See #3323
2018-09-11 10:09:16 +02:00
badrAZ
1211447e81 fix(xo-web/settings/remotes): rename connect(ed)/disconnect(ed) to enable(d)/disable(d) (#3396)
See #3323
2018-09-10 17:15:33 +02:00
Julien Fontanet
ed78f4c5ee fix(xo-server/backup NG): third time is the charm (sigh) 2018-09-10 10:59:56 +02:00
Julien Fontanet
4a2e9d4c88 fix(xo-server/backup NG): CancelToken.race → .source 2018-09-10 10:51:05 +02:00
Julien Fontanet
1807917204 fix(xo-server/backup NG): CancelToken#fork() no longer exists
Issue introduced in b3004a38a
2018-09-10 10:47:53 +02:00
Julien Fontanet
b3004a38aa chore: update promise-toolbox to v0.10 (#3392) 2018-09-07 14:34:47 +02:00
Pierre Donias
0ad6c073ee chore(CHANGELOG): 5.26.0 2018-09-07 11:39:26 +02:00
Pierre Donias
0abaadb391 feat(xo-web): 5.26.0 2018-09-07 11:37:12 +02:00
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
Pierre Donias
d00fff3d4c feat(xo-server): 5.24.0 2018-08-09 17:05:10 +02:00
Pierre Donias
c0b84ae848 feat(@xen-orchestra/fs): 0.2.1 2018-08-09 16:59:43 +02:00
Pierre Donias
f68d1539aa feat(xen-api): 0.17.0 2018-08-09 16:55:25 +02:00
Pierre Donias
455e48596c chore(CHANGELOG): 5.24.0 2018-08-09 16:50:54 +02:00
badrAZ
ba6b968256 fix(xen-api/putResource): dont loose task ref on redirect (#3243)
Fixes #3245
2018-08-09 16:44:52 +02:00
Pierre Donias
8fbf2786fd feat(xo-server/patching): check date consistency before patching (#3292)
Fixes #3056
2018-08-09 16:10:13 +02:00
badrAZ
06d38808be feat(xo-server/jobs): send report for interrupted jobs on server start (#3164)
Fixes #2998
2018-08-09 15:45:28 +02:00
Olivier Lambert
49565a672b doc improvements and text fixes (#3289) 2018-08-08 17:05:15 +02:00
marcpezin
2d980f3d50 Supported version list (#3279)
* XCP/XS Supported version list
2018-08-08 14:58:56 +02:00
badrAZ
e29ba3e0cf feat(xo-web/backup-ng/new): remove previous backups first (#3260)
Fixes #3212
2018-08-08 14:32:28 +02:00
badrAZ
dff342d2bd feat(xo-web/backup-ng): rename "Export ret." to "Backup ret." and "Copy ret." to "Replication ret." (#3283)
See #2711
2018-08-08 14:11:22 +02:00
badrAZ
d38ad3a17f feat(xo-web/backup-ng/new): move compression in the advanced settings (#3281)
See #2711
2018-08-08 11:45:34 +02:00
Pierre Donias
ce21da4146 fix(xo-web/VM): missing data prop on Copiable (#3282) 2018-08-08 11:42:45 +02:00
badrAZ
09a381806b fix(xo-web/backup-ng/new): the compression default value should be false (#3280)
See #2711
2018-08-08 11:03:29 +02:00
badrAZ
0ca7b8539f chore(xo-web/backup-ng/new): better "settings" management (#3259) 2018-08-08 10:36:33 +02:00
Julien Fontanet
cc90d41be4 fix(fs/SMB): rename should replace existing files (#3278)
Fixes #3224
2018-08-08 10:32:08 +02:00
badrAZ
a7dd83772e feat(xo-web/backup-ng/overview): list the backup job's modes (#3277)
Fixes #3169
2018-08-07 17:22:12 +02:00
Rajaa.BARHTAOUI
eea52b5166 feat(xo-web/health/alarms): use SortedTable actions (#3271)
See #3179
2018-08-07 17:20:59 +02:00
Rajaa.BARHTAOUI
4c20e89a8b feat(xo-web/health/orphanedSnapshotVdis): use SortedTable actions (#3270)
See #3179
2018-08-07 16:53:57 +02:00
badrAZ
0d88a9f8f4 fix(xo-web/backup-ng/new): job's global settings can be undefined (#3275)
Introduced by the PR #3237
2018-08-07 16:15:02 +02:00
Rajaa.BARHTAOUI
7ad4fc0e6c feat(xo-web/health/orphanedVms): use SortedTable actions (#3274)
See #3179
2018-08-07 15:08:53 +02:00
badrAZ
c7c205d52f feat(xo-web/vm): move the copy clipboard of the VM's UUID to the header (#3248)
Fixes #3221
2018-08-07 14:17:47 +02:00
badrAZ
cdc43a6cde feat(xo-web/backup-ng/new): make the smart mode's toggle more visible (#3263)
See #2711
2018-08-06 11:50:01 +02:00
badrAZ
a58a9a9e6a feat(xo-web/ActionButton): ability to set the icon's color (#3258)
See PR: #3255
2018-08-06 11:11:58 +02:00
badrAZ
9d2f15cf33 chore(xo-web/backup-ng/new): better globalSettings management (#3237) 2018-08-06 11:06:32 +02:00
badrAZ
2d596af9a5 fix(xo-webpool/tab-advanced): GPU groups not filtered by pool (#3253)
Fixes #3176
2018-08-06 10:57:16 +02:00
badrAZ
f70a25e82f feat(xo-web/settings/remotes): make SMB subfolder field optional (#3250)
Fixes #3249
2018-08-06 10:35:39 +02:00
Pierre Donias
33859303bd Revert "fix(xen-api/getResource): softer backpressure to prevent XS75 error (#3234)"
This reverts commit b9f3313903.
2018-08-06 09:59:50 +02:00
bchool
1ed0c01add Extra packages XO needs for FLR on Debian (#3240)
This resolves #3239
2018-07-28 17:56:55 +02:00
Pierre Donias
dfcd41992a feat(xo-web): 5.23.0 2018-07-26 16:20:37 +02:00
Pierre Donias
26cb388c64 feat(xo-server): 5.23.0 2018-07-26 16:19:25 +02:00
Pierre Donias
b2be6e5d5b feat(xo-server-backup-reports): 0.12.3 2018-07-26 16:16:10 +02:00
Pierre Donias
2fec62968e feat(complex-matcher): 0.4.0 2018-07-26 16:11:39 +02:00
Pierre Donias
b15a6963e4 chore(@xen-orchestra/fs): use xo-remote-parser@0.5.0 2018-07-26 16:06:00 +02:00
Pierre Donias
f7b4f78c8d feat(xo-remote-parser): 0.5.0 2018-07-26 16:01:16 +02:00
Pierre Donias
9bebfe71ac feat(xen-api): 0.16.12 2018-07-26 14:10:59 +02:00
Pierre Donias
a7b3e8e59c chore(CHANGELOG): 5.23.0 2018-07-26 13:59:07 +02:00
Pierre Donias
b9f3313903 fix(xen-api/getResource): softer backpressure to prevent XS75 error (#3234)
Fixes #3205

https://bugs.xenserver.org/browse/XSO-873
2018-07-26 13:53:00 +02:00
Pierre Donias
9c4bd0d0dd feat(xo-web/backup NG): remove delete button on logs (#3235)
Fixes #3153
2018-07-26 11:49:59 +02:00
badrAZ
ac2e490569 feat(xo-web/settings/remotes): ability to edit/delete a remote with an invalid URL (#3226)
Fixes #3182
2018-07-26 11:29:33 +02:00
Jon Sands
114cf7ac7d (Documentation) Add password recovery step for web UI (#3233)
* fix XOSAN links

* Fix default SR command + cleanup

* add more coalesce debug info

* remove extra space

* add password recovery step
2018-07-25 23:45:08 +02:00
badrAZ
255e0c61d5 feat(xo-web): implementation of the pool item component (#3229)
See #2605
2018-07-25 14:57:43 +02:00
badrAZ
288868fe91 feat(xo-web/backup-ng/new): ability to set a job's timeout (#3222)
Fixes #2978
2018-07-25 14:43:35 +02:00
badrAZ
aba7b730f5 fix(xo-web/backup-ng/new): ability to edit a schedule's state (#3228)
Fixes #3223
2018-07-25 14:36:44 +02:00
badrAZ
b95fc86667 feat(xo-web/setting/remotes): ability to change the type of a remote (#3207) 2018-07-25 14:24:25 +02:00
badrAZ
0ed1df3af6 feat(xo-web/backup-ng/new): ability to edit a schedule's name (#3143)
See #2711
Fixes #3071
2018-07-25 14:08:02 +02:00
Jon Sands
eff38b9aee (Documentation) add more coalesce debug info (#3230)
* fix XOSAN links

* Fix default SR command + cleanup

* add more coalesce debug info

* remove extra space
2018-07-24 23:45:49 +02:00
Pierre Donias
53477be12d feat(xo-web/VM,SR): import VDI content (#3216)
See #2432
2018-07-23 09:24:44 +02:00
Julien Fontanet
d99555a4a8 feat(xo-server/backupNg): remove incomplete XVAs (#3215)
Fixes #3159
2018-07-20 15:29:38 +02:00
Julien Fontanet
f00be23e02 chore(package): update eslint-config-standard to 12.0.0-alpha.0 2018-07-20 14:57:13 +02:00
Julien Fontanet
ee6025f3ad fix(xo-server/backupNg/migration): migrate reportWhen (#3206)
Fixes #3180
2018-07-20 14:03:07 +02:00
Julien Fontanet
990c0e570d feat(xo-server/backup NG): disable vmTimeout 2018-07-20 13:58:45 +02:00
Julien Fontanet
b07b4e3744 Revert "fix(xo-server/jobs): serialize all fields"
This reverts commit d51527b2d8.

Causing too much issues, a new approach is developed in #3209.
2018-07-19 18:39:36 +02:00
Julien Fontanet
6d25e11f72 feat(xo-server/disk.exportContent): print stream errors 2018-07-19 17:35:55 +02:00
Julien Fontanet
16c250e122 chore(xo-server/backupNg/migration): use copyRetention for replication (#3204) 2018-07-19 14:48:17 +02:00
Julien Fontanet
e6a10e1ac2 feat(complex-matcher): update docs and changelog 2018-07-19 12:03:00 +02:00
Julien Fontanet
829beb84e2 feat(complex-matcher): regexp support (#3199) 2018-07-19 12:01:57 +02:00
badrAZ
c3066921ab chore(xo-web/settings/remotes): avoid creating functions in the render (#3203) 2018-07-19 11:28:00 +02:00
badrAZ
ca424f166b chore(xo-web/settings/remotes): pass formatMessage to SortedTable (#3202)
Instead of creating new components.
2018-07-19 10:25:41 +02:00
Julien Fontanet
4192abf3c2 fix(tests): workaround a Babel bug for Node 6 2018-07-19 10:21:39 +02:00
Julien Fontanet
d51527b2d8 fix(xo-server/jobs): serialize all fields
Otherwise some strings containing valid JSON syntax are incorrectly
transformed.
2018-07-19 10:15:45 +02:00
badrAZ
a1c829e393 chore(xo-web/settings/remotes): use groupBy (#3200) 2018-07-19 09:46:23 +02:00
Julien Fontanet
7dfb1635c2 feat(complex-matcher): add glob pattern support (#3198)
Fixes #3190
2018-07-18 15:06:33 +02:00
Julien Fontanet
e58d56a656 chore(complex-matcher/StringNode): bind lcValue to match 2018-07-18 12:37:33 +02:00
Pierre Donias
93037ee44f fix(xo-server/api): disk.importContent description, params & resolve (#3197) 2018-07-18 11:29:20 +02:00
Julien Fontanet
ecd8265098 chore(xo-web): downgrade modular-cssify to 12
Work around an issue with 14 which breaks in watch mode
2018-07-17 18:19:03 +02:00
Pierre Donias
4c73cfa423 feat(xo-web/VM,SR): export VDI content (#3194)
See #2432
2018-07-17 13:38:23 +02:00
Julien Fontanet
19b3c207c7 feat(xo-server/api): disk.importContent (#3193)
See #2432
2018-07-17 12:04:35 +02:00
Julien Fontanet
58d4408909 fix(xo-server/api/disk): coding style 2018-07-17 11:22:00 +02:00
Julien Fontanet
4925e678ed chore: update dependencies 2018-07-17 11:21:45 +02:00
Julien Fontanet
7d14705dcf feat(xo-server/api): disk.exportContent (#3192)
See #2432
2018-07-17 11:08:25 +02:00
Julien Fontanet
5d0040acd1 chore(xo-server/api/vm): remove unnecessary exports 2018-07-17 10:35:38 +02:00
Julien Fontanet
8aa6c6551b feat(vhd-cli/info): support incomplete file 2018-07-17 10:35:38 +02:00
badrAZ
c9f08a2f8f fix(xo-server-backup-reports): dont bail on successful VM with reportWhen failure (#3185)
Fixes #3181
2018-07-16 11:30:25 +02:00
Pierre Donias
70b17a6fa9 feat(xo-web): 5.22.1 2018-07-13 13:55:10 +02:00
Pierre Donias
c1a118f2a1 chore(CHANGELOG): 5.22.1 2018-07-13 13:52:47 +02:00
badrAZ
fa3eb1cdf5 fix(xo-web/select-objects#SelectRemote): gracefully ignore remotes with invalid URL (#3178) 2018-07-13 11:09:52 +02:00
Jon Sands
16a6902bd2 (Docu) Fix default SR command + cleanup (#3177)
* fix XOSAN links

* Fix default SR command + cleanup
2018-07-13 09:52:01 +02:00
Pierre Donias
296e30a40f feat(xo-web): 5.22.0 2018-07-12 15:25:44 +02:00
Julien Fontanet
873db3bf26 0.2.1 2018-04-13 11:32:47 +02:00
Julien Fontanet
c795887a35 fix: display unmanaged snapshots as unmanaged 2018-04-13 11:32:33 +02:00
Julien Fontanet
23824bafe8 1.1.0 2018-04-09 16:07:31 +02:00
Julien Fontanet
5cca58f2b3 feat(README): add documentation 2018-04-09 16:03:25 +02:00
Julien Fontanet
d05c9b6133 chore: use console.error to display errors 2018-04-09 16:03:04 +02:00
Julien Fontanet
39a84a1ac0 feat: support allowUnauthorized, autoConnect and label 2018-04-09 16:02:46 +02:00
Julien Fontanet
b1c851c9d6 chore(package): prepublishOnly script 2018-04-09 16:02:17 +02:00
Julien Fontanet
6280a9365c chore(package): update dependencies 2018-04-09 16:02:04 +02:00
Julien Fontanet
2741dacd64 0.2.0 2018-04-09 14:01:47 +02:00
Julien Fontanet
4c2c2390bd chore(package): prepublish → prepublishOnly 2018-04-09 14:01:18 +02:00
Julien Fontanet
635b8ce5f0 chore(package): update dependencies 2018-04-09 14:00:17 +02:00
Julien Fontanet
efc13cc456 fix: display VDI with missing parent
Consider them parentless even though they are simply unknown.
2018-04-09 11:34:56 +02:00
Julien Fontanet
078f319fe1 0.1.1 2017-06-07 11:25:44 +02:00
Julien Fontanet
0f0e785871 fix: ensure vdi.physical_utilisation is a number 2017-06-07 11:25:39 +02:00
Julien Fontanet
4e4c85121c 0.1.0 2017-05-11 15:27:15 +02:00
Julien Fontanet
019d6f4cb6 feat: display VDI size 2017-05-11 15:27:06 +02:00
Julien Fontanet
725b0342d1 fix: Xen → XenServer 2017-05-11 15:22:49 +02:00
Julien Fontanet
c93ccb8111 feat: handle -h and --help flags 2017-05-11 15:22:12 +02:00
Julien Fontanet
670befdaf6 chore(package): update all dependencies 2017-05-11 15:19:08 +02:00
Julien Fontanet
55eefd865f 0.0.4 2017-03-30 16:51:27 +02:00
Julien Fontanet
43e5d610e3 fix(package): jest config testPathDirs → roots 2017-03-30 16:51:17 +02:00
Julien Fontanet
b1245bc5be fix(usage): Xen → XenServer 2017-03-30 16:50:44 +02:00
Julien Fontanet
c2feab245e fix: update yarn.lock 2017-03-30 16:48:18 +02:00
Julien Fontanet
cb3753213e fix(README): Xen → XenServer 2017-03-08 14:19:21 +01:00
greenkeeper[bot]
ec8c7a24af chore(package): update jest to version 19.0.1 (#2)
https://greenkeeper.io/
2017-02-22 12:10:24 +01:00
greenkeeper[bot]
2456be2da3 chore(package): update tslint-config-standard to version 3.0.0 (#6)
https://greenkeeper.io/
2017-01-19 10:10:08 +01:00
Julien Fontanet
8c5d4240f9 chore(package): update all dependencies 2017-01-17 10:34:25 +01:00
Julien Fontanet
b1e12d1542 chore: add yarn.lock 2017-01-17 10:28:34 +01:00
Julien Fontanet
a58d7d2ff4 chore(package): use husky instead of ghooks 2017-01-17 10:28:03 +01:00
Julien Fontanet
5308b8b9ed fix(README): should be installed globally 2017-01-17 10:26:32 +01:00
greenkeeper[bot]
c15dffce8f chore(package): update @types/node to version 7.0.0 (#5)
https://greenkeeper.io/
2017-01-11 09:22:18 +01:00
greenkeeper[bot]
874680462e chore(package): update dependencies (#4)
https://greenkeeper.io/
2016-11-28 15:13:31 +01:00
greenkeeper[bot]
bb42540775 chore(package): update tslint-config-standard to version 2.0.0 (#3)
https://greenkeeper.io/
2016-11-21 23:27:47 +01:00
Julien Fontanet
b18511c905 chore(package): update all dependencies 2016-11-08 15:44:21 +01:00
greenkeeper[bot]
5c660f4f64 chore(package): update dependencies (#1)
https://greenkeeper.io/
2016-11-02 09:33:33 +01:00
Julien Fontanet
f2bae73f77 0.0.3 2016-10-31 17:32:32 +01:00
Julien Fontanet
e54d34f269 feat(cli): prefix labels if colors not supported 2016-10-31 17:32:00 +01:00
Julien Fontanet
6470cbd2ee 0.0.2 2016-10-31 17:08:02 +01:00
Julien Fontanet
c06ebcb4a4 fix(askPassword): prompt on stderr 2016-10-31 17:07:34 +01:00
Julien Fontanet
3eaa72c98c 0.0.1 2016-10-31 16:37:40 +01:00
Julien Fontanet
694fff060d fix(package): fix bin 2016-10-31 16:37:36 +01:00
Julien Fontanet
2705062ac3 chore(README): replace placeholders 2016-10-31 16:31:59 +01:00
Julien Fontanet
3df055a296 chore(package): publish 2016-10-31 16:29:36 +01:00
Julien Fontanet
802bc15e0c initial commit 2016-10-31 16:27:15 +01:00
Julien Fontanet
ad2de40a9d chore(package): update @types/through2 to v2.0.29 2016-09-23 09:41:05 +02:00
Julien Fontanet
19298570f8 chore(package): remove unused dep 2016-09-19 14:55:50 +02:00
Julien Fontanet
1da4d1f1e9 chore: repo moved to vatesfr 2016-09-19 14:53:33 +02:00
Julien Fontanet
fe4e9c18fa feat(cli): print usage on missing argument 2016-09-19 14:48:52 +02:00
Julien Fontanet
2c9f84f17f feat(package): add description and keywords 2016-09-19 14:48:52 +02:00
Julien Fontanet
0b2e76600b feat(README): add usage 2016-09-19 14:48:52 +02:00
Julien Fontanet
873554fc01 It works! 2016-09-19 14:43:39 +02:00
Julien Fontanet
82e2d013ae chore(package): reorder entry in package.json 2016-09-19 10:23:33 +02:00
Julien Fontanet
1eb5e80f1f fix(types): fix type definitions 2016-09-19 10:23:18 +02:00
Julien Fontanet
9c0ab5b3cb Initial commit 2016-09-16 18:09:18 +02:00
533 changed files with 20450 additions and 12269 deletions

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
# xo_fs_nfs=nfs://ip:/folder
# xo_fs_smb=smb://login:pass@domain\\ip\folder

View File

@@ -1,22 +1,28 @@
module.exports = {
extends: ['standard', 'standard-jsx'],
extends: ['standard', 'standard-jsx', 'prettier'],
globals: {
__DEV__: true,
$Dict: true,
$Diff: true,
$ElementType: true,
$Exact: true,
$Keys: true,
$PropertyType: true,
$Shape: true,
},
parser: 'babel-eslint',
parserOptions: {
ecmaFeatures: {
legacyDecorators: true,
},
},
rules: {
'comma-dangle': ['error', 'always-multiline'],
indent: 'off',
'no-var': 'error',
'node/no-extraneous-import': 'error',
'node/no-extraneous-require': 'error',
'prefer-const': 'error',
// See https://github.com/prettier/eslint-config-prettier/issues/65
'react/jsx-indent': 'off',
},
}

1
.gitignore vendored
View File

@@ -30,3 +30,4 @@ pnpm-debug.log
pnpm-debug.log.*
yarn-error.log
yarn-error.log.*
.env

View File

@@ -1,4 +1,5 @@
module.exports = {
jsxSingleQuote: true,
semi: false,
singleQuote: true,
trailingComma: 'es5',

View File

@@ -2,7 +2,6 @@ language: node_js
node_js:
#- stable # disable for now due to an issue of indirect dep upath with Node 9
- 8
- 6
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
@@ -10,9 +9,9 @@ sudo: false
addons:
apt:
packages:
- qemu-utils
- blktap-utils
- vmdk-stream-converter
- qemu-utils
- blktap-utils
- vmdk-stream-converter
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash
@@ -22,5 +21,4 @@ cache:
yarn: true
script:
- yarn run test
- yarn run test-integration
- yarn run travis-tests

View File

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

View File

@@ -0,0 +1,24 @@
/benchmark/
/benchmarks/
*.bench.js
*.bench.js.map
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/fixture/
/fixtures/
*.fixture.js
*.fixture.js.map
*.fixtures.js
*.fixtures.js.map
/test/
/tests/
*.spec.js
*.spec.js.map
__snapshots__/

View File

@@ -0,0 +1,49 @@
# @xen-orchestra/async-map [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> ${pkg.description}
## Install
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/async-map):
```
> npm install --save @xen-orchestra/async-map
```
## Usage
**TODO**
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
you've encountered;
- fork and create a pull request.
## License
ISC © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,50 @@
{
"name": "@xen-orchestra/async-map",
"version": "0.0.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/async-map",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@isonoe.net"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"dist/"
],
"browserslist": [
">2%"
],
"engines": {
"node": ">=6"
},
"dependencies": {
"lodash": "^4.17.4"
},
"devDependencies": {
"@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"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepare": "yarn run build",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,43 @@
// type MaybePromise<T> = Promise<T> | T
//
// declare export function asyncMap<T1, T2>(
// collection: MaybePromise<T1[]>,
// (T1, number) => MaybePromise<T2>
// ): Promise<T2[]>
// declare export function asyncMap<K, V1, V2>(
// collection: MaybePromise<{ [K]: V1 }>,
// (V1, K) => MaybePromise<V2>
// ): Promise<V2[]>
import map from 'lodash/map'
// Similar to map() + Promise.all() but wait for all promises to
// settle before rejecting (with the first error)
const asyncMap = (collection, iteratee) => {
let then
if (collection != null && typeof (then = collection.then) === 'function') {
return then.call(collection, collection => asyncMap(collection, iteratee))
}
let errorContainer
const onError = error => {
if (errorContainer === undefined) {
errorContainer = { error }
}
}
return Promise.all(
map(collection, (item, key, collection) =>
new Promise(resolve => {
resolve(iteratee(item, key, collection))
}).catch(onError)
)
).then(values => {
if (errorContainer !== undefined) {
throw errorContainer.error
}
return values
})
}
export { asyncMap as default }

View File

@@ -11,23 +11,27 @@ const configs = {
'@babel/plugin-proposal-decorators': {
legacy: true,
},
'@babel/preset-env' (pkg) {
'@babel/plugin-proposal-pipeline-operator': {
proposal: 'minimal',
},
'@babel/preset-env'(pkg) {
return {
debug: !__TEST__,
loose: true,
// disabled until https://github.com/babel/babel/issues/8323 is resolved
// 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',
}
},
@@ -38,11 +42,11 @@ const getConfig = (key, ...args) => {
return config === undefined
? {}
: typeof config === 'function'
? config(...args)
: config
? config(...args)
: config
}
module.exports = function (pkg, plugins, presets) {
module.exports = function(pkg, plugins, presets) {
plugins === undefined && (plugins = {})
presets === undefined && (presets = {})

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env node
const defer = require('golike-defer').default
const { NULL_REF, Xapi } = require('xen-api')
const pkg = require('./package.json')
Xapi.prototype.getVmDisks = async function(vm) {
const disks = { __proto__: null }
await Promise.all([
...vm.VBDs.map(async vbdRef => {
const vbd = await this.getRecord('VBD', vbdRef)
let vdiRef
if (vbd.type === 'Disk' && (vdiRef = vbd.VDI) !== NULL_REF) {
disks[vbd.userdevice] = await this.getRecord('VDI', vdiRef)
}
}),
])
return disks
}
defer(async function main($defer, args) {
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
const cliName = Object.keys(pkg.bin)[0]
return console.error(
'%s',
`
Usage: ${cliName} <source XAPI URL> <source snapshot UUID> <target XAPI URL> <target VM UUID> <backup job id> <backup schedule id>
${cliName} v${pkg.version}
`
)
}
const [
srcXapiUrl,
srcSnapshotUuid,
tgtXapiUrl,
tgtVmUuid,
jobId,
scheduleId,
] = args
const srcXapi = new Xapi({
allowUnauthorized: true,
url: srcXapiUrl,
watchEvents: false,
})
await srcXapi.connect()
defer.call(srcXapi, 'disconnect')
const tgtXapi = new Xapi({
allowUnauthorized: true,
url: tgtXapiUrl,
watchEvents: false,
})
await tgtXapi.connect()
defer.call(tgtXapi, 'disconnect')
const [srcSnapshot, tgtVm] = await Promise.all([
srcXapi.getRecordByUuid('VM', srcSnapshotUuid),
tgtXapi.getRecordByUuid('VM', tgtVmUuid),
])
const srcVm = await srcXapi.getRecord('VM', srcSnapshot.snapshot_of)
const metadata = {
'xo:backup:datetime': srcSnapshot.snapshot_time,
'xo:backup:job': jobId,
'xo:backup:schedule': scheduleId,
'xo:backup:vm': srcVm.uuid,
}
const [srcDisks, tgtDisks] = await Promise.all([
srcXapi.getVmDisks(srcSnapshot),
tgtXapi.getVmDisks(tgtVm),
])
const userDevices = Object.keys(tgtDisks)
const tgtSr = await tgtXapi.getRecord(
'SR',
tgtDisks[Object.keys(tgtDisks)[0]].SR
)
await Promise.all([
srcXapi.setFieldEntries(srcSnapshot, 'other_config', metadata),
srcXapi.setFieldEntries(srcSnapshot, 'other_config', {
'xo:backup:exported': 'true',
}),
tgtXapi.setField(
tgtVm,
'name_label',
`${srcVm.name_label} (${srcSnapshot.snapshot_time})`
),
tgtXapi.setFieldEntries(tgtVm, 'other_config', metadata),
tgtXapi.setFieldEntries(tgtVm, 'other_config', {
'xo:backup:sr': tgtSr.uuid,
'xo:copy_of': srcSnapshotUuid,
}),
tgtXapi.setFieldEntries(tgtVm, 'blocked_operations', {
start:
'Start operation for this vm is blocked, clone it if you want to use it.',
}),
Promise.all(
userDevices.map(userDevice => {
const srcDisk = srcDisks[userDevice]
const tgtDisk = tgtDisks[userDevice]
return tgtXapi.setFieldEntry(
tgtDisk,
'other_config',
'xo:copy_of',
srcDisk.uuid
)
})
),
])
})(process.argv.slice(2)).catch(console.error.bind(console, 'Fatal error:'))

View File

@@ -0,0 +1,20 @@
{
"name": "@xen-orchestra/cr-seed-cli",
"version": "0.2.0",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/cr-seed-cli",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"engines": {
"node": ">=8"
},
"bin": {
"xo-cr-seed": "./index.js"
},
"dependencies": {
"golike-defer": "^0.4.1",
"xen-api": "^0.22.0"
}
}

View File

@@ -41,10 +41,10 @@
"moment-timezone": "^0.5.14"
},
"devDependencies": {
"@babel/cli": "7.0.0-beta.49",
"@babel/core": "7.0.0-beta.49",
"@babel/preset-env": "7.0.0-beta.49",
"@babel/preset-flow": "7.0.0-beta.49",
"@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

@@ -6,9 +6,14 @@ import parse from './parse'
const MAX_DELAY = 2 ** 31 - 1
class Job {
constructor (schedule, fn) {
constructor(schedule, fn) {
const wrapper = () => {
const result = fn()
let result
try {
result = fn()
} catch (_) {
// catch any thrown value to ensure it does not break the job
}
let then
if (result != null && typeof (then = result.then) === 'function') {
then.call(result, scheduleNext, scheduleNext)
@@ -28,30 +33,32 @@ class Job {
this._timeout = undefined
}
start () {
start() {
this.stop()
this._scheduleNext()
}
stop () {
stop() {
clearTimeout(this._timeout)
}
}
class Schedule {
constructor (pattern, zone = 'utc') {
constructor(pattern, zone = 'utc') {
this._schedule = parse(pattern)
this._createDate =
zone.toLowerCase() === 'utc'
? moment.utc
: zone === 'local' ? moment : () => moment.tz(zone)
: zone === 'local'
? moment
: () => moment.tz(zone)
}
createJob (fn) {
createJob(fn) {
return new Job(this, fn)
}
next (n) {
next(n) {
const dates = new Array(n)
const schedule = this._schedule
let date = this._createDate()
@@ -61,12 +68,12 @@ class Schedule {
return dates
}
_nextDelay () {
_nextDelay() {
const now = this._createDate()
return next(this._schedule, now) - now
}
startJob (fn) {
startJob(fn) {
const job = this.createJob(fn)
job.start()
return job.stop.bind(job)

View File

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

View File

@@ -0,0 +1,24 @@
/benchmark/
/benchmarks/
*.bench.js
*.bench.js.map
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/fixture/
/fixtures/
*.fixture.js
*.fixture.js.map
*.fixtures.js
*.fixtures.js.map
/test/
/tests/
*.spec.js
*.spec.js.map
__snapshots__/

View File

@@ -0,0 +1,49 @@
# ${pkg.name} [![Build Status](https://travis-ci.org/${pkg.shortGitHubPath}.png?branch=master)](https://travis-ci.org/${pkg.shortGitHubPath})
> ${pkg.description}
## Install
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
```
> npm install --save ${pkg.name}
```
## Usage
**TODO**
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](${pkg.bugs})
you've encountered;
- fork and create a pull request.
## License
${pkg.license} © [${pkg.author.name}](${pkg.author.url})

View File

@@ -0,0 +1,47 @@
{
"name": "@xen-orchestra/defined",
"version": "0.0.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/defined",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"dist/"
],
"browserslist": [
">2%"
],
"engines": {
"node": ">=6"
},
"dependencies": {},
"devDependencies": {
"@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"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -1,3 +1,5 @@
// @flow
// Usage:
//
// ```js
@@ -11,7 +13,7 @@
// process.env.http_proxy
// ])
// ```
export default function defined () {
export default function defined() {
let args = arguments
let n = args.length
if (n === 1) {
@@ -39,7 +41,7 @@ export default function defined () {
// const getFriendName = _ => _.friends[0].name
// const friendName = get(getFriendName, props.user)
// ```
export const get = (accessor, arg) => {
export const get = (accessor: (input: ?any) => any, arg: ?any) => {
try {
return accessor(arg)
} catch (error) {
@@ -58,5 +60,5 @@ export const get = (accessor, arg) => {
// _ => new ProxyAgent(_)
// )
// ```
export const ifDef = (value, thenFn) =>
export const ifDef = (value: ?any, thenFn: (value: any) => any) =>
value !== undefined ? thenFn(value) : value

View File

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

View File

@@ -0,0 +1,24 @@
/benchmark/
/benchmarks/
*.bench.js
*.bench.js.map
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/fixture/
/fixtures/
*.fixture.js
*.fixture.js.map
*.fixtures.js
*.fixtures.js.map
/test/
/tests/
*.spec.js
*.spec.js.map
__snapshots__/

View File

@@ -0,0 +1,71 @@
# @xen-orchestra/emit-async [![Build Status](https://travis-ci.org/${pkg.shortGitHubPath}.png?branch=master)](https://travis-ci.org/${pkg.shortGitHubPath})
> ${pkg.description}
## Install
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/emit-async):
```
> npm install --save @xen-orchestra/emit-async
```
## Usage
```js
import EE from 'events'
import emitAsync from '@xen-orchestra/emit-async'
const ee = new EE()
ee.emitAsync = emitAsync
ee.on('start', async function () {
// whatever
})
// similar to EventEmmiter#emit() but returns a promise which resolves when all
// listeners have resolved
await ee.emitAsync('start')
// by default, it will rejects as soon as one listener reject, you can customise
// error handling though:
await ee.emitAsync({
onError (error) {
console.warn(error)
}
}, 'start')
```
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](${pkg.bugs})
you've encountered;
- fork and create a pull request.
## License
${pkg.license} © [${pkg.author.name}](${pkg.author.url})

View File

@@ -0,0 +1,46 @@
{
"name": "@xen-orchestra/emit-async",
"version": "0.0.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/emit-async",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"dist/"
],
"browserslist": [
">2%"
],
"engines": {
"node": ">=6"
},
"dependencies": {},
"devDependencies": {
"@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",
"rimraf": "^2.6.2"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,26 @@
export default function emitAsync(event) {
let opts
let i = 1
// an option object has been passed as first param
if (typeof event !== 'string') {
opts = event
event = arguments[i++]
}
const n = arguments.length - i
const args = new Array(n)
for (let j = 0; j < n; ++j) {
args[j] = arguments[j + i]
}
const onError = opts != null && opts.onError
return Promise.all(
this.listeners(event).map(listener =>
new Promise(resolve => {
resolve(listener.apply(this, args))
}).catch(onError)
)
)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@xen-orchestra/fs",
"version": "0.2.0",
"version": "0.5.0",
"license": "AGPL-3.0",
"description": "The File System for Xen Orchestra backups.",
"keywords": [],
@@ -20,26 +20,29 @@
"node": ">=6"
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.49",
"@marsaud/smb2": "^0.8.0",
"execa": "^0.10.0",
"fs-extra": "^6.0.1",
"get-stream": "^3.0.0",
"@marsaud/smb2": "^0.13.0",
"@xen-orchestra/async-map": "^0.0.0",
"execa": "^1.0.0",
"fs-extra": "^7.0.0",
"get-stream": "^4.0.0",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5",
"through2": "^2.0.3",
"promise-toolbox": "^0.11.0",
"readable-stream": "^3.0.6",
"through2": "^3.0.0",
"tmp": "^0.0.33",
"xo-remote-parser": "^0.4.0"
"xo-remote-parser": "^0.5.0"
},
"devDependencies": {
"@babel/cli": "7.0.0-beta.49",
"@babel/core": "7.0.0-beta.49",
"@babel/plugin-proposal-function-bind": "7.0.0-beta.49",
"@babel/plugin-transform-runtime": "^7.0.0-beta.49",
"@babel/preset-env": "7.0.0-beta.49",
"@babel/preset-flow": "7.0.0-beta.49",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-decorators": "^7.1.6",
"@babel/plugin-proposal-function-bind": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"async-iterator-to-stream": "^1.1.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"dotenv": "^6.1.0",
"index-modules": "^0.3.0",
"rimraf": "^2.6.2"
},

View File

@@ -1,13 +1,18 @@
// @flow
// $FlowFixMe
import asyncMap from '@xen-orchestra/async-map'
import getStream from 'get-stream'
import { randomBytes } from 'crypto'
import { fromCallback, fromEvent, ignoreErrors } from 'promise-toolbox'
import { type Readable, type Writable } from 'stream'
import path from 'path'
import { fromCallback, fromEvent, ignoreErrors, timeout } from 'promise-toolbox'
import { parse } from 'xo-remote-parser'
import { randomBytes } from 'crypto'
import { type Readable, type Writable } from 'stream'
import { createChecksumStream, validChecksumOfReadStream } from './checksum'
const { dirname, resolve } = path.posix
type Data = Buffer | Readable | string
type FileDescriptor = {| fd: mixed, path: string |}
type LaxReadable = Readable & Object
@@ -17,176 +22,152 @@ type File = FileDescriptor | string
const checksumFile = file => file + '.checksum'
export default class RemoteHandlerAbstract {
_remote: Object
constructor (remote: any) {
this._remote = { ...remote, ...parse(remote.url) }
if (this._remote.type !== this.type) {
throw new Error('Incorrect remote type')
}
// normalize the path:
// - does not contains `.` or `..` (cannot escape root dir)
// - always starts with `/`
const normalizePath = path => resolve('/', path)
const DEFAULT_TIMEOUT = 6e5 // 10 min
const ignoreEnoent = error => {
if (error == null || error.code !== 'ENOENT') {
throw error
}
}
class PrefixWrapper {
constructor(remote, prefix) {
this._prefix = prefix
this._remote = remote
}
get type (): string {
throw new Error('Not implemented')
get type() {
return this._remote.type
}
/**
* Asks the handler to sync the state of the effective remote with its' metadata
*/
async sync (): Promise<mixed> {
return this._sync()
}
async _sync (): Promise<mixed> {
throw new Error('Not implemented')
}
/**
* Free the resources possibly dedicated to put the remote at work, when it is no more needed
*/
async forget (): Promise<void> {
await this._forget()
}
async _forget (): Promise<void> {
throw new Error('Not implemented')
}
async test (): Promise<Object> {
const testFileName = `${Date.now()}.test`
const data = await fromCallback(cb => randomBytes(1024 * 1024, cb))
let step = 'write'
try {
await this.outputFile(testFileName, data)
step = 'read'
const read = await this.readFile(testFileName)
if (data.compare(read) !== 0) {
throw new Error('output and input did not match')
}
return {
success: true,
}
} catch (error) {
return {
success: false,
step,
file: testFileName,
error: error.message || String(error),
}
} finally {
ignoreErrors.call(this.unlink(testFileName))
}
}
async outputFile (file: string, data: Data, options?: Object): Promise<void> {
return this._outputFile(file, data, {
flags: 'wx',
...options,
})
}
async _outputFile (file: string, data: Data, options?: Object): Promise<void> {
const stream = await this.createOutputStream(file, options)
const promise = fromEvent(stream, 'finish')
stream.end(data)
await promise
}
async read (
file: File,
buffer: Buffer,
position?: number
): Promise<{| bytesRead: number, buffer: Buffer |}> {
return this._read(file, buffer, position)
}
_read (
file: File,
buffer: Buffer,
position?: number
): Promise<{| bytesRead: number, buffer: Buffer |}> {
throw new Error('Not implemented')
}
async readFile (file: string, options?: Object): Promise<Buffer> {
return this._readFile(file, options)
}
_readFile (file: string, options?: Object): Promise<Buffer> {
return this.createReadStream(file, options).then(getStream.buffer)
}
async rename (
oldPath: string,
newPath: string,
{ checksum = false }: Object = {}
) {
let p = this._rename(oldPath, newPath)
if (checksum) {
p = Promise.all([
p,
this._rename(checksumFile(oldPath), checksumFile(newPath)),
])
}
return p
}
async _rename (oldPath: string, newPath: string) {
throw new Error('Not implemented')
}
async list (
dir: string = '.',
{
filter,
prependDir = false,
}: { filter?: (name: string) => boolean, prependDir?: boolean } = {}
): Promise<string[]> {
let entries = await this._list(dir)
if (filter !== undefined) {
entries = entries.filter(filter)
}
if (prependDir) {
entries.forEach((entry, i) => {
entries[i] = dir + '/' + entry
// necessary to remove the prefix from the path with `prependDir` option
async list(dir, opts) {
const entries = await this._remote.list(this._resolve(dir), opts)
if (opts != null && opts.prependDir) {
const n = this._prefix.length
entries.forEach((entry, i, entries) => {
entries[i] = entry.slice(n)
})
}
return entries
}
async _list (dir: string): Promise<string[]> {
rename(oldPath, newPath) {
return this._remote.rename(this._resolve(oldPath), this._resolve(newPath))
}
_resolve(path) {
return this._prefix + normalizePath(path)
}
}
export default class RemoteHandlerAbstract {
_remote: Object
_timeout: number
constructor(remote: any, options: Object = {}) {
if (remote.url === 'test://') {
this._remote = remote
} else {
this._remote = { ...remote, ...parse(remote.url) }
if (this._remote.type !== this.type) {
throw new Error('Incorrect remote type')
}
}
;({ timeout: this._timeout = DEFAULT_TIMEOUT } = options)
}
// Public members
get type(): string {
throw new Error('Not implemented')
}
createReadStream (
file: string,
addPrefix(prefix: string) {
prefix = normalizePath(prefix)
return prefix === '/' ? this : new PrefixWrapper(this, prefix)
}
async closeFile(fd: FileDescriptor): Promise<void> {
await timeout.call(this._closeFile(fd.fd), this._timeout)
}
async createOutputStream(
file: File,
{ checksum = false, ...options }: Object = {}
): Promise<LaxWritable> {
if (typeof file === 'string') {
file = normalizePath(file)
}
const path = typeof file === 'string' ? file : file.path
const streamP = timeout.call(
this._createOutputStream(file, {
flags: 'wx',
...options,
}),
this._timeout
)
if (!checksum) {
return streamP
}
const checksumStream = createChecksumStream()
const forwardError = error => {
checksumStream.emit('error', error)
}
const stream = await streamP
stream.on('error', forwardError)
checksumStream.pipe(stream)
// $FlowFixMe
checksumStream.checksumWritten = checksumStream.checksum
.then(value =>
this._outputFile(checksumFile(path), value, { flags: 'wx' })
)
.catch(forwardError)
return checksumStream
}
createReadStream(
file: File,
{ checksum = false, ignoreMissingChecksum = false, ...options }: Object = {}
): Promise<LaxReadable> {
if (typeof file === 'string') {
file = normalizePath(file)
}
const path = typeof file === 'string' ? file : file.path
const streamP = this._createReadStream(file, options).then(stream => {
// detect early errors
let promise = fromEvent(stream, 'readable')
const streamP = timeout
.call(this._createReadStream(file, options), this._timeout)
.then(stream => {
// detect early errors
let promise = fromEvent(stream, 'readable')
// try to add the length prop if missing and not a range stream
if (
stream.length === undefined &&
options.end === undefined &&
options.start === undefined
) {
promise = Promise.all([
promise,
ignoreErrors.call(
this.getSize(file).then(size => {
stream.length = size
})
),
])
}
// try to add the length prop if missing and not a range stream
if (
stream.length === undefined &&
options.end === undefined &&
options.start === undefined
) {
promise = Promise.all([
promise,
ignoreErrors.call(
this._getSize(file).then(size => {
stream.length = size
})
),
])
}
return promise.then(() => stream)
})
return promise.then(() => stream)
})
if (!checksum) {
return streamP
@@ -195,7 +176,7 @@ export default class RemoteHandlerAbstract {
// avoid a unhandled rejection warning
ignoreErrors.call(streamP)
return this.readFile(checksumFile(path)).then(
return this._readFile(checksumFile(path), { flags: 'r' }).then(
checksum =>
streamP.then(stream => {
const { length } = stream
@@ -216,92 +197,372 @@ export default class RemoteHandlerAbstract {
)
}
async _createReadStream (
createWriteStream(
file: File,
options: { end?: number, flags?: string, start?: number } = {}
): Promise<LaxWritable> {
return timeout.call(
this._createWriteStream(
typeof file === 'string' ? normalizePath(file) : file,
{
flags: 'wx',
...options,
}
)
)
}
// Free the resources possibly dedicated to put the remote at work, when it
// is no more needed
//
// FIXME: Some handlers are implemented based on system-wide mecanisms (such
// as mount), forgetting them might breaking other processes using the same
// remote.
async forget(): Promise<void> {
await this._forget()
}
async getSize(file: File): Promise<number> {
return timeout.call(
this._getSize(typeof file === 'string' ? normalizePath(file) : file),
this._timeout
)
}
async list(
dir: string,
{
filter,
prependDir = false,
}: { filter?: (name: string) => boolean, prependDir?: boolean } = {}
): Promise<string[]> {
const virtualDir = normalizePath(dir)
dir = normalizePath(dir)
let entries = await timeout.call(this._list(dir), this._timeout)
if (filter !== undefined) {
entries = entries.filter(filter)
}
if (prependDir) {
entries.forEach((entry, i) => {
entries[i] = virtualDir + '/' + entry
})
}
return entries
}
async mkdir(dir: string): Promise<void> {
dir = normalizePath(dir)
try {
await this._mkdir(dir)
} catch (error) {
if (error == null || error.code !== 'EEXIST') {
throw error
}
// this operation will throw if it's not already a directory
await this._list(dir)
}
}
async mktree(dir: string): Promise<void> {
await this._mktree(normalizePath(dir))
}
async openFile(path: string, flags: string): Promise<FileDescriptor> {
path = normalizePath(path)
return {
fd: await timeout.call(this._openFile(path, flags), this._timeout),
path,
}
}
async outputFile(
file: string,
options?: Object
): Promise<LaxReadable> {
throw new Error('Not implemented')
data: Data,
{ flags = 'wx' }: { flags?: string } = {}
): Promise<void> {
await this._outputFile(normalizePath(file), data, { flags })
}
async openFile (path: string, flags?: string): Promise<FileDescriptor> {
return { fd: await this._openFile(path, flags), path }
async read(
file: File,
buffer: Buffer,
position?: number
): Promise<{| bytesRead: number, buffer: Buffer |}> {
return this._read(
typeof file === 'string' ? normalizePath(file) : file,
buffer,
position
)
}
async _openFile (path: string, flags?: string): Promise<mixed> {
throw new Error('Not implemented')
async readFile(
file: string,
{ flags = 'r' }: { flags?: string } = {}
): Promise<Buffer> {
return this._readFile(normalizePath(file), { flags })
}
async closeFile (fd: FileDescriptor): Promise<void> {
await this._closeFile(fd.fd)
}
async refreshChecksum(path: string): Promise<void> {
path = normalizePath(path)
async _closeFile (fd: mixed): Promise<void> {
throw new Error('Not implemented')
}
async refreshChecksum (path: string): Promise<void> {
const stream = (await this.createReadStream(path)).pipe(
const stream = (await this._createReadStream(path, { flags: 'r' })).pipe(
createChecksumStream()
)
stream.resume() // start reading the whole file
await this.outputFile(checksumFile(path), await stream.checksum)
}
async createOutputStream (
file: File,
{ checksum = false, ...options }: Object = {}
): Promise<LaxWritable> {
const path = typeof file === 'string' ? file : file.path
const streamP = this._createOutputStream(file, {
await this._outputFile(checksumFile(path), await stream.checksum, {
flags: 'wx',
...options,
})
if (!checksum) {
return streamP
}
const checksumStream = createChecksumStream()
const forwardError = error => {
checksumStream.emit('error', error)
}
const stream = await streamP
stream.on('error', forwardError)
checksumStream.pipe(stream)
// $FlowFixMe
checksumStream.checksumWritten = checksumStream.checksum
.then(value => this.outputFile(checksumFile(path), value))
.catch(forwardError)
return checksumStream
}
async _createOutputStream (
file: mixed,
options?: Object
): Promise<LaxWritable> {
throw new Error('Not implemented')
async rename(
oldPath: string,
newPath: string,
{ checksum = false }: Object = {}
) {
oldPath = normalizePath(oldPath)
newPath = normalizePath(newPath)
let p = timeout.call(this._rename(oldPath, newPath), this._timeout)
if (checksum) {
p = Promise.all([
p,
this._rename(checksumFile(oldPath), checksumFile(newPath)),
])
}
return p
}
async unlink (file: string, { checksum = true }: Object = {}): Promise<void> {
async rmdir(dir: string): Promise<void> {
await timeout.call(
this._rmdir(normalizePath(dir)).catch(ignoreEnoent),
this._timeout
)
}
async rmtree(dir: string): Promise<void> {
await this._rmtree(normalizePath(dir))
}
// Asks the handler to sync the state of the effective remote with its'
// metadata
//
// This method MUST ALWAYS be called before using the handler.
async sync(): Promise<void> {
await this._sync()
}
async test(): Promise<Object> {
const testFileName = normalizePath(`${Date.now()}.test`)
const data = await fromCallback(cb => randomBytes(1024 * 1024, cb))
let step = 'write'
try {
await this._outputFile(testFileName, data, { flags: 'wx' })
step = 'read'
const read = await this._readFile(testFileName, { flags: 'r' })
if (!data.equals(read)) {
throw new Error('output and input did not match')
}
return {
success: true,
}
} catch (error) {
return {
success: false,
step,
file: testFileName,
error: error.message || String(error),
}
} finally {
ignoreErrors.call(this._unlink(testFileName))
}
}
async unlink(file: string, { checksum = true }: Object = {}): Promise<void> {
file = normalizePath(file)
if (checksum) {
ignoreErrors.call(this._unlink(checksumFile(file)))
}
await this._unlink(file)
await this._unlink(file).catch(ignoreEnoent)
}
async _unlink (file: mixed): Promise<void> {
async writeFile(
file: string,
data: Data,
{ flags = 'wx' }: { flags?: string } = {}
): Promise<void> {
await this._writeFile(normalizePath(file), data, { flags })
}
// Methods that can be implemented by inheriting classes
async _closeFile(fd: mixed): Promise<void> {
throw new Error('Not implemented')
}
async getSize (file: mixed): Promise<number> {
return this._getSize(file)
async _createOutputStream(file: File, options: Object): Promise<LaxWritable> {
try {
return await this._createWriteStream(file, options)
} catch (error) {
if (typeof file !== 'string' || error.code !== 'ENOENT') {
throw error
}
}
await this._mktree(dirname(file))
return this._createOutputStream(file, options)
}
async _getSize (file: mixed): Promise<number> {
async _createReadStream(file: File, options?: Object): Promise<LaxReadable> {
throw new Error('Not implemented')
}
async _createWriteStream(file: File, options: Object): Promise<LaxWritable> {
throw new Error('Not implemented')
}
// called to finalize the remote
async _forget(): Promise<void> {}
async _getSize(file: File): Promise<number> {
throw new Error('Not implemented')
}
async _list(dir: string): Promise<string[]> {
throw new Error('Not implemented')
}
async _mkdir(dir: string): Promise<void> {
throw new Error('Not implemented')
}
async _mktree(dir: string): Promise<void> {
try {
return await this.mkdir(dir)
} catch (error) {
if (error.code !== 'ENOENT') {
throw error
}
}
await this._mktree(dirname(dir))
return this._mktree(dir)
}
async _openFile(path: string, flags: string): Promise<mixed> {
throw new Error('Not implemented')
}
async _outputFile(
file: string,
data: Data,
options: { flags?: string }
): Promise<void> {
try {
return await this._writeFile(file, data, options)
} catch (error) {
if (error.code !== 'ENOENT') {
throw error
}
}
await this._mktree(dirname(file))
return this._outputFile(file, data, options)
}
_read(
file: File,
buffer: Buffer,
position?: number
): Promise<{| bytesRead: number, buffer: Buffer |}> {
throw new Error('Not implemented')
}
_readFile(file: string, options?: Object): Promise<Buffer> {
return this._createReadStream(file, options).then(getStream.buffer)
}
async _rename(oldPath: string, newPath: string) {
throw new Error('Not implemented')
}
async _rmdir(dir: string) {
throw new Error('Not implemented')
}
async _rmtree(dir: string) {
try {
return await this._rmdir(dir)
} catch (error) {
if (error.code !== 'ENOTEMPTY') {
throw error
}
}
const files = await this._list(dir)
await asyncMap(files, file =>
this._unlink(`${dir}/${file}`).catch(error => {
if (error.code === 'EISDIR') {
return this._rmtree(`${dir}/${file}`)
}
throw error
})
)
return this._rmtree(dir)
}
// called to initialize the remote
async _sync(): Promise<void> {}
async _unlink(file: string): Promise<void> {
throw new Error('Not implemented')
}
async _writeFile(
file: string,
data: Data,
options: { flags?: string }
): Promise<void> {
throw new Error('Not implemented')
}
}
function createPrefixWrapperMethods() {
const pPw = PrefixWrapper.prototype
const pRha = RemoteHandlerAbstract.prototype
const {
defineProperty,
getOwnPropertyDescriptor,
prototype: { hasOwnProperty },
} = Object
Object.getOwnPropertyNames(pRha).forEach(name => {
let descriptor, value
if (
hasOwnProperty.call(pPw, name) ||
name[0] === '_' ||
typeof (value = (descriptor = getOwnPropertyDescriptor(pRha, name))
.value) !== 'function'
) {
return
}
descriptor.value = function() {
let path
if (arguments.length !== 0 && typeof (path = arguments[0]) === 'string') {
arguments[0] = this._resolve(path)
}
return value.apply(this._remote, arguments)
}
defineProperty(pPw, name, descriptor)
})
}
createPrefixWrapperMethods()

View File

@@ -0,0 +1,113 @@
/* eslint-env jest */
import { TimeoutError } from 'promise-toolbox'
import AbstractHandler from './abstract'
const TIMEOUT = 10e3
class TestHandler extends AbstractHandler {
constructor(impl) {
super({ url: 'test://' }, { timeout: TIMEOUT })
Object.keys(impl).forEach(method => {
this[`_${method}`] = impl[method]
})
}
}
describe('closeFile()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
closeFile: () => new Promise(() => {}),
})
const promise = testHandler.closeFile({ fd: undefined, path: '' })
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('createOutputStream()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
createOutputStream: () => new Promise(() => {}),
})
const promise = testHandler.createOutputStream('File')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('createReadStream()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
createReadStream: () => new Promise(() => {}),
})
const promise = testHandler.createReadStream('file')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('getSize()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
getSize: () => new Promise(() => {}),
})
const promise = testHandler.getSize('')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('list()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
list: () => new Promise(() => {}),
})
const promise = testHandler.list('.')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('openFile()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
openFile: () => new Promise(() => {}),
})
const promise = testHandler.openFile('path')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('rename()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
rename: () => new Promise(() => {}),
})
const promise = testHandler.rename('oldPath', 'newPath')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})
describe('rmdir()', () => {
it(`throws in case of timeout`, async () => {
const testHandler = new TestHandler({
rmdir: () => new Promise(() => {}),
})
const promise = testHandler.rmdir('dir')
jest.advanceTimersByTime(TIMEOUT)
await expect(promise).rejects.toThrowError(TimeoutError)
})
})

View File

@@ -1,6 +1,5 @@
// @flow
// $FlowFixMe
import through2 from 'through2'
import { createHash } from 'crypto'
import { defer, fromEvent } from 'promise-toolbox'
@@ -85,8 +84,8 @@ export const validChecksumOfReadStream = (
callback(
checksum !== expectedChecksum
? new Error(
`Bad checksum (${checksum}), expected: ${expectedChecksum}`
)
`Bad checksum (${checksum}), expected: ${expectedChecksum}`
)
: null
)
}

View File

@@ -1,26 +0,0 @@
/* eslint-env jest */
import rimraf from 'rimraf'
import tmp from 'tmp'
import { fromCallback as pFromCallback } from 'promise-toolbox'
import { getHandler } from '.'
const initialDir = process.cwd()
beforeEach(async () => {
const dir = await pFromCallback(cb => tmp.dir(cb))
process.chdir(dir)
})
afterEach(async () => {
const tmpDir = process.cwd()
process.chdir(initialDir)
await pFromCallback(cb => rimraf(tmpDir, cb))
})
test("fs test doesn't crash", async () => {
const handler = getHandler({ url: 'file://' + process.cwd() })
const result = await handler.test()
expect(result.success).toBeTruthy()
})

View File

@@ -0,0 +1,292 @@
/* eslint-env jest */
import 'dotenv/config'
import asyncIteratorToStream from 'async-iterator-to-stream'
import getStream from 'get-stream'
import { fromCallback } from 'promise-toolbox'
import { pipeline } from 'readable-stream'
import { random } from 'lodash'
import { tmpdir } from 'os'
import { getHandler } from '.'
// https://gist.github.com/julien-f/3228c3f34fdac01ade09
const unsecureRandomBytes = n => {
const bytes = Buffer.alloc(n)
const odd = n & 1
for (let i = 0, m = n - odd; i < m; i += 2) {
bytes.writeUInt16BE((Math.random() * 65536) | 0, i)
}
if (odd) {
bytes.writeUInt8((Math.random() * 256) | 0, n - 1)
}
return bytes
}
const TEST_DATA_LEN = 1024
const TEST_DATA = unsecureRandomBytes(TEST_DATA_LEN)
const createTestDataStream = asyncIteratorToStream(function*() {
yield TEST_DATA
})
const rejectionOf = p =>
p.then(
value => {
throw value
},
reason => reason
)
const handlers = [`file://${tmpdir()}`]
if (process.env.xo_fs_nfs) handlers.push(process.env.xo_fs_nfs)
if (process.env.xo_fs_smb) handlers.push(process.env.xo_fs_smb)
handlers.forEach(url => {
describe(url, () => {
let handler
const testWithFileDescriptor = (path, flags, fn) => {
it('with path', () => fn({ file: path, flags }))
it('with file descriptor', async () => {
const file = await handler.openFile(path, flags)
try {
await fn({ file })
} finally {
await handler.closeFile(file)
}
})
}
beforeAll(async () => {
handler = getHandler({ url }).addPrefix(`xo-fs-tests-${Date.now()}`)
await handler.sync()
})
afterAll(async () => {
await handler.forget()
handler = undefined
})
beforeEach(async () => {
// ensure test dir exists
await handler.mkdir('.')
})
afterEach(async () => {
await handler.rmtree('.')
})
describe('#type', () => {
it('returns the type of the remote', () => {
expect(typeof handler.type).toBe('string')
})
})
describe('#createOutputStream()', () => {
it('creates parent dir if missing', async () => {
const stream = await handler.createOutputStream('dir/file')
await fromCallback(cb => pipeline(createTestDataStream(), stream, cb))
await expect(await handler.readFile('dir/file')).toEqual(TEST_DATA)
})
})
describe('#createReadStream()', () => {
beforeEach(() => handler.outputFile('file', TEST_DATA))
testWithFileDescriptor('file', 'r', async ({ file, flags }) => {
await expect(
await getStream.buffer(
await handler.createReadStream(file, { flags })
)
).toEqual(TEST_DATA)
})
})
describe('#createWriteStream()', () => {
testWithFileDescriptor('file', 'wx', async ({ file, flags }) => {
const stream = await handler.createWriteStream(file, { flags })
await fromCallback(cb => pipeline(createTestDataStream(), stream, cb))
await expect(await handler.readFile('file')).toEqual(TEST_DATA)
})
it('fails if parent dir is missing', async () => {
const error = await rejectionOf(handler.createWriteStream('dir/file'))
expect(error.code).toBe('ENOENT')
})
})
describe('#getSize()', () => {
beforeEach(() => handler.outputFile('file', TEST_DATA))
testWithFileDescriptor('file', 'r', async () => {
expect(await handler.getSize('file')).toEqual(TEST_DATA_LEN)
})
})
describe('#list()', () => {
it(`should list the content of folder`, async () => {
await handler.outputFile('file', TEST_DATA)
await expect(await handler.list('.')).toEqual(['file'])
})
it('can prepend the directory to entries', async () => {
await handler.outputFile('dir/file', '')
expect(await handler.list('dir', { prependDir: true })).toEqual([
'/dir/file',
])
})
it('can prepend the directory to entries', async () => {
await handler.outputFile('dir/file', '')
expect(await handler.list('dir', { prependDir: true })).toEqual([
'/dir/file',
])
})
})
describe('#mkdir()', () => {
it('creates a directory', async () => {
await handler.mkdir('dir')
await expect(await handler.list('.')).toEqual(['dir'])
})
it('does not throw on existing directory', async () => {
await handler.mkdir('dir')
await handler.mkdir('dir')
})
it('throws ENOTDIR on existing file', async () => {
await handler.outputFile('file', '')
const error = await rejectionOf(handler.mkdir('file'))
expect(error.code).toBe('ENOTDIR')
})
})
describe('#mktree()', () => {
it('creates a tree of directories', async () => {
await handler.mktree('dir/dir')
await expect(await handler.list('.')).toEqual(['dir'])
await expect(await handler.list('dir')).toEqual(['dir'])
})
it('does not throw on existing directory', async () => {
await handler.mktree('dir/dir')
await handler.mktree('dir/dir')
})
it('throws ENOTDIR on existing file', async () => {
await handler.outputFile('dir/file', '')
const error = await rejectionOf(handler.mktree('dir/file'))
expect(error.code).toBe('ENOTDIR')
})
it('throws ENOTDIR on existing file in path', async () => {
await handler.outputFile('file', '')
const error = await rejectionOf(handler.mktree('file/dir'))
expect(error.code).toBe('ENOTDIR')
})
})
describe('#outputFile()', () => {
it('writes data to a file', async () => {
await handler.outputFile('file', TEST_DATA)
expect(await handler.readFile('file')).toEqual(TEST_DATA)
})
it('throws on existing files', async () => {
await handler.outputFile('file', '')
const error = await rejectionOf(handler.outputFile('file', ''))
expect(error.code).toBe('EEXIST')
})
})
describe('#read()', () => {
beforeEach(() => handler.outputFile('file', TEST_DATA))
const start = random(TEST_DATA_LEN)
const size = random(TEST_DATA_LEN)
testWithFileDescriptor('file', 'r', async ({ file }) => {
const buffer = Buffer.alloc(size)
const result = await handler.read(file, buffer, start)
expect(result.buffer).toBe(buffer)
expect(result).toEqual({
buffer,
bytesRead: Math.min(size, TEST_DATA_LEN - start),
})
})
})
describe('#readFile', () => {
it('returns a buffer containing the contents of the file', async () => {
await handler.outputFile('file', TEST_DATA)
expect(await handler.readFile('file')).toEqual(TEST_DATA)
})
it('throws on missing file', async () => {
const error = await rejectionOf(handler.readFile('file'))
expect(error.code).toBe('ENOENT')
})
})
describe('#rename()', () => {
it(`should rename the file`, async () => {
await handler.outputFile('file', TEST_DATA)
await handler.rename('file', `file2`)
expect(await handler.list('.')).toEqual(['file2'])
expect(await handler.readFile(`file2`)).toEqual(TEST_DATA)
})
})
describe('#rmdir()', () => {
it('should remove an empty directory', async () => {
await handler.mkdir('dir')
await handler.rmdir('dir')
expect(await handler.list('.')).toEqual([])
})
it(`should throw on non-empty directory`, async () => {
await handler.outputFile('dir/file', '')
const error = await rejectionOf(handler.rmdir('.'))
await expect(error.code).toEqual('ENOTEMPTY')
})
it('does not throw on missing directory', async () => {
await handler.rmdir('dir')
})
})
describe('#rmtree', () => {
it(`should remove a directory resursively`, async () => {
await handler.outputFile('dir/file', '')
await handler.rmtree('dir')
expect(await handler.list('.')).toEqual([])
})
})
describe('#test()', () => {
it('tests the remote appears to be working', async () => {
expect(await handler.test()).toEqual({
success: true,
})
})
})
describe('#unlink()', () => {
it(`should remove the file`, async () => {
await handler.outputFile('file', TEST_DATA)
await handler.unlink('file')
await expect(await handler.list('.')).toEqual([])
})
it('does not throw on missing file', async () => {
await handler.unlink('file')
})
})
})
})

View File

@@ -14,7 +14,7 @@ const HANDLERS = {
nfs: RemoteHandlerNfs,
}
export const getHandler = (remote: Remote): RemoteHandler => {
export const getHandler = (remote: Remote, ...rest: any): RemoteHandler => {
// FIXME: should be done in xo-remote-parser.
const type = remote.url.split('://')[0]
@@ -22,5 +22,5 @@ export const getHandler = (remote: Remote): RemoteHandler => {
if (!Handler) {
throw new Error('Unhandled remote type')
}
return new Handler(remote)
return new Handler(remote, ...rest)
}

View File

@@ -1,56 +1,71 @@
import fs from 'fs-extra'
import { dirname, resolve } from 'path'
import { noop, startsWith } from 'lodash'
import { fromEvent } from 'promise-toolbox'
import RemoteHandlerAbstract from './abstract'
export default class LocalHandler extends RemoteHandlerAbstract {
get type () {
get type() {
return 'file'
}
_getRealPath () {
_getRealPath() {
return this._remote.path
}
_getFilePath (file) {
const realPath = this._getRealPath()
const parts = [realPath]
if (file) {
parts.push(file)
_getFilePath(file) {
return this._getRealPath() + file
}
async _closeFile(fd) {
return fs.close(fd)
}
async _createReadStream(file, options) {
if (typeof file === 'string') {
const stream = fs.createReadStream(this._getFilePath(file), options)
await fromEvent(stream, 'open')
return stream
}
const path = resolve.apply(null, parts)
if (!startsWith(path, realPath)) {
throw new Error('Remote path is unavailable')
return fs.createReadStream('', {
autoClose: false,
...options,
fd: file.fd,
})
}
async _createWriteStream(file, options) {
if (typeof file === 'string') {
const stream = fs.createWriteStream(this._getFilePath(file), options)
await fromEvent(stream, 'open')
return stream
}
return path
return fs.createWriteStream('', {
autoClose: false,
...options,
fd: file.fd,
})
}
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
}
}
return this._remote
async _getSize(file) {
const stats = await fs.stat(
this._getFilePath(typeof file === 'string' ? file : file.path)
)
return stats.size
}
async _forget () {
return noop()
async _list(dir) {
return fs.readdir(this._getFilePath(dir))
}
async _outputFile (file, data, options) {
const path = this._getFilePath(file)
await fs.ensureDir(dirname(path))
await fs.writeFile(path, data, options)
_mkdir(dir) {
return fs.mkdir(this._getFilePath(dir))
}
async _read (file, buffer, position) {
async _openFile(path, flags) {
return fs.open(this._getFilePath(path), flags)
}
async _read(file, buffer, position) {
const needsClose = typeof file === 'string'
file = needsClose ? await fs.open(this._getFilePath(file), 'r') : file.fd
try {
@@ -68,62 +83,29 @@ export default class LocalHandler extends RemoteHandlerAbstract {
}
}
async _readFile (file, options) {
async _readFile(file, options) {
return fs.readFile(this._getFilePath(file), options)
}
async _rename (oldPath, newPath) {
async _rename(oldPath, newPath) {
return fs.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
}
async _list (dir = '.') {
return fs.readdir(this._getFilePath(dir))
async _rmdir(dir) {
return fs.rmdir(this._getFilePath(dir))
}
async _createReadStream (file, options) {
return typeof file === 'string'
? fs.createReadStream(this._getFilePath(file), options)
: fs.createReadStream('', {
autoClose: false,
...options,
fd: file.fd,
})
async _sync() {
const path = this._getRealPath('/')
await fs.ensureDir(path)
await fs.access(path, fs.R_OK | fs.W_OK)
}
async _createOutputStream (file, options) {
if (typeof file === 'string') {
const path = this._getFilePath(file)
await fs.ensureDir(dirname(path))
return fs.createWriteStream(path, options)
}
return fs.createWriteStream('', {
autoClose: false,
...options,
fd: file.fd,
})
async _unlink(file) {
return fs.unlink(this._getFilePath(file))
}
async _unlink (file) {
return fs.unlink(this._getFilePath(file)).catch(error => {
// do not throw if the file did not exist
if (error == null || error.code !== 'ENOENT') {
throw error
}
})
}
async _getSize (file) {
const stats = await fs.stat(
this._getFilePath(typeof file === 'string' ? file : file.path)
)
return stats.size
}
async _openFile (path, flags) {
return fs.open(this._getFilePath(path), flags)
}
async _closeFile (fd) {
return fs.close(fd)
_writeFile(file, data, { flags }) {
return fs.writeFile(this._getFilePath(file), data, { flag: flags })
}
}

View File

@@ -1,89 +1,82 @@
import execa from 'execa'
import fs from 'fs-extra'
import { forEach } from 'lodash'
import { join } from 'path'
import { tmpdir } from 'os'
import LocalHandler from './local'
const DEFAULT_NFS_OPTIONS = 'vers=3'
export default class NfsHandler extends LocalHandler {
get type () {
constructor(
remote,
{ mountsDir = join(tmpdir(), 'xo-fs-mounts'), ...opts } = {}
) {
super(remote, opts)
this._realPath = join(
mountsDir,
remote.id ||
Math.random()
.toString(36)
.slice(2)
)
}
get type() {
return 'nfs'
}
_getRealPath () {
return `/run/xo-server/mounts/${this._remote.id}`
_getRealPath() {
return this._realPath
}
async _loadRealMounts () {
let stdout
const mounted = {}
try {
stdout = await execa.stdout('findmnt', [
'-P',
'-t',
'nfs,nfs4',
'--output',
'SOURCE,TARGET',
'--noheadings',
])
const regex = /^SOURCE="([^:]*):(.*)" TARGET="(.*)"$/
forEach(stdout.split('\n'), m => {
if (m) {
const match = regex.exec(m)
mounted[match[3]] = {
host: match[1],
share: match[2],
}
}
})
} catch (exc) {
// When no mounts are found, the call pretends to fail...
if (exc.stderr !== '') {
throw exc
}
}
this._realMounts = mounted
return mounted
}
_matchesRealMount () {
return this._getRealPath() in this._realMounts
}
async _mount () {
async _mount() {
await fs.ensureDir(this._getRealPath())
const { host, path, port } = this._remote
return execa('mount', [
'-t',
'nfs',
'-o',
'vers=3',
`${host}${port !== undefined ? ':' + port : ''}:${path}`,
this._getRealPath(),
])
const { host, path, port, options } = this._remote
return execa(
'mount',
[
'-t',
'nfs',
'-o',
DEFAULT_NFS_OPTIONS + (options !== undefined ? `,${options}` : ''),
`${host}${port !== undefined ? ':' + port : ''}:${path}`,
this._getRealPath(),
],
{
env: {
LANG: 'C',
},
}
).catch(error => {
if (
error == null ||
typeof error.stderr !== 'string' ||
!error.stderr.includes('already mounted')
) {
throw error
}
})
}
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
async _umount() {
await execa('umount', ['--force', this._getRealPath()], {
env: {
LANG: 'C',
},
}).catch(error => {
if (
error == null ||
typeof error.stderr !== 'string' ||
!error.stderr.includes('not mounted')
) {
throw error
}
} else if (!this._matchesRealMount() && this._remote.enabled) {
try {
await this._mount()
} catch (exc) {
this._remote.enabled = false
this._remote.error = exc.message
}
}
return this._remote
})
}
async _forget () {
async _forget() {
try {
await this._umount(this._remote)
} catch (_) {
@@ -91,7 +84,9 @@ export default class NfsHandler extends LocalHandler {
}
}
async _umount (remote) {
await execa('umount', ['--force', this._getRealPath()])
async _sync() {
await this._mount()
return this._remote
}
}

View File

@@ -1,247 +1,167 @@
import Smb2 from '@marsaud/smb2'
import { lastly as pFinally } from 'promise-toolbox'
import RemoteHandlerAbstract from './abstract'
const noop = () => {}
// Normalize the error code for file not found.
const normalizeError = error => {
const wrapError = (error, code) => ({
__proto__: error,
cause: error,
code,
})
const normalizeError = (error, shouldBeDirectory) => {
const { code } = error
return code === 'STATUS_OBJECT_NAME_NOT_FOUND' ||
code === 'STATUS_OBJECT_PATH_NOT_FOUND'
? Object.create(error, {
code: {
configurable: true,
readable: true,
value: 'ENOENT',
writable: true,
},
})
throw code === 'STATUS_DIRECTORY_NOT_EMPTY'
? wrapError(error, 'ENOTEMPTY')
: code === 'STATUS_FILE_IS_A_DIRECTORY'
? wrapError(error, 'EISDIR')
: code === 'STATUS_NOT_A_DIRECTORY'
? wrapError(error, 'ENOTDIR')
: code === 'STATUS_OBJECT_NAME_NOT_FOUND' ||
code === 'STATUS_OBJECT_PATH_NOT_FOUND'
? wrapError(error, 'ENOENT')
: code === 'STATUS_OBJECT_NAME_COLLISION'
? wrapError(error, 'EEXIST')
: code === 'STATUS_NOT_SUPPORTED' || code === 'STATUS_INVALID_PARAMETER'
? wrapError(error, shouldBeDirectory ? 'ENOTDIR' : 'EISDIR')
: error
}
const normalizeDirError = error => normalizeError(error, true)
export default class SmbHandler extends RemoteHandlerAbstract {
constructor (remote) {
super(remote)
this._forget = noop
constructor(remote, opts) {
super(remote, opts)
// defined in _sync()
this._client = undefined
const prefix = this._remote.path
this._prefix = prefix !== '' ? prefix + '\\' : prefix
}
get type () {
get type() {
return 'smb'
}
_getClient () {
_getFilePath(file) {
return (
this._prefix +
(typeof file === 'string' ? file : file.path)
.slice(1)
.replace(/\//g, '\\')
)
}
_dirname(file) {
const parts = file.split('\\')
parts.pop()
return parts.join('\\')
}
_closeFile(file) {
return this._client.close(file).catch(normalizeError)
}
_createReadStream(file, options) {
if (typeof file === 'string') {
file = this._getFilePath(file)
} else {
options = { autoClose: false, ...options, fd: file.fd }
file = ''
}
return this._client.createReadStream(file, options).catch(normalizeError)
}
_createWriteStream(file, options) {
if (typeof file === 'string') {
file = this._getFilePath(file)
} else {
options = { autoClose: false, ...options, fd: file.fd }
file = ''
}
return this._client.createWriteStream(file, options).catch(normalizeError)
}
_forget() {
const client = this._client
this._client = undefined
return client.disconnect()
}
_getSize(file) {
return this._client.getSize(this._getFilePath(file)).catch(normalizeError)
}
_list(dir) {
return this._client.readdir(this._getFilePath(dir)).catch(normalizeDirError)
}
_mkdir(dir) {
return this._client.mkdir(this._getFilePath(dir)).catch(normalizeDirError)
}
// TODO: add flags
_openFile(path, flags) {
return this._client
.open(this._getFilePath(path), flags)
.catch(normalizeError)
}
async _read(file, buffer, position) {
const client = this._client
const needsClose = typeof file === 'string'
file = needsClose ? await client.open(this._getFilePath(file)) : file.fd
try {
return await client.read(file, buffer, 0, buffer.length, position)
} catch (error) {
normalizeError(error)
} finally {
if (needsClose) {
await client.close(file)
}
}
}
_readFile(file, options) {
return this._client
.readFile(this._getFilePath(file), options)
.catch(normalizeError)
}
_rename(oldPath, newPath) {
return this._client
.rename(this._getFilePath(oldPath), this._getFilePath(newPath), {
replace: true,
})
.catch(normalizeError)
}
_rmdir(dir) {
return this._client.rmdir(this._getFilePath(dir)).catch(normalizeDirError)
}
_sync() {
const remote = this._remote
return new Smb2({
this._client = new Smb2({
share: `\\\\${remote.host}`,
domain: remote.domain,
username: remote.username,
password: remote.password,
autoCloseTimeout: 0,
})
// Check access (smb2 does not expose connect in public so far...)
return this.list('.')
}
_getFilePath (file) {
if (file === '.') {
file = undefined
}
let path = this._remote.path !== '' ? this._remote.path : ''
// Ensure remote path is a directory.
if (path !== '' && path[path.length - 1] !== '\\') {
path += '\\'
}
if (file) {
path += file.replace(/\//g, '\\')
}
return path
_unlink(file) {
return this._client.unlink(this._getFilePath(file)).catch(normalizeError)
}
_dirname (file) {
const parts = file.split('\\')
parts.pop()
return parts.join('\\')
}
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
}
}
return this._remote
}
async _outputFile (file, data, options = {}) {
const client = this._getClient()
const path = this._getFilePath(file)
const dir = this._dirname(path)
if (dir) {
await client.ensureDir(dir)
}
return client.writeFile(path, data, options)::pFinally(() => {
client.disconnect()
})
}
async _read (file, buffer, position) {
const needsClose = typeof file === 'string'
let client
if (needsClose) {
client = this._getClient()
file = await client.open(this._getFilePath(file))
} else {
;({ client, file } = file.fd)
}
try {
return await client.read(file, buffer, 0, buffer.length, position)
} finally {
if (needsClose) {
await client.close(file)
client.disconnect()
}
}
}
async _readFile (file, options = {}) {
const client = this._getClient()
let content
try {
content = await client
.readFile(this._getFilePath(file), options)
::pFinally(() => {
client.disconnect()
})
} catch (error) {
throw normalizeError(error)
}
return content
}
async _rename (oldPath, newPath) {
const client = this._getClient()
try {
await client
.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
::pFinally(() => {
client.disconnect()
})
} catch (error) {
throw normalizeError(error)
}
}
async _list (dir = '.') {
const client = this._getClient()
let list
try {
list = await client.readdir(this._getFilePath(dir))::pFinally(() => {
client.disconnect()
})
} catch (error) {
throw normalizeError(error)
}
return list
}
async _createReadStream (file, options = {}) {
if (typeof file !== 'string') {
file = file.path
}
const client = this._getClient()
let stream
try {
// FIXME ensure that options are properly handled by @marsaud/smb2
stream = await client.createReadStream(this._getFilePath(file), options)
stream.on('end', () => client.disconnect())
} catch (error) {
throw normalizeError(error)
}
return stream
}
async _createOutputStream (file, options = {}) {
if (typeof file !== 'string') {
file = file.path
}
const client = this._getClient()
const path = this._getFilePath(file)
const dir = this._dirname(path)
let stream
try {
if (dir) {
await client.ensureDir(dir)
}
stream = await client.createWriteStream(path, options) // FIXME ensure that options are properly handled by @marsaud/smb2
} catch (err) {
client.disconnect()
throw err
}
stream.on('finish', () => client.disconnect())
return stream
}
async _unlink (file) {
const client = this._getClient()
try {
await client.unlink(this._getFilePath(file))::pFinally(() => {
client.disconnect()
})
} catch (error) {
throw normalizeError(error)
}
}
async _getSize (file) {
const client = await this._getClient()
let size
try {
size = await client
.getSize(this._getFilePath(typeof file === 'string' ? file : file.path))
::pFinally(() => {
client.disconnect()
})
} catch (error) {
throw normalizeError(error)
}
return size
}
// TODO: add flags
async _openFile (path) {
const client = this._getClient()
return {
client,
file: await client.open(this._getFilePath(path)),
}
}
async _closeFile ({ client, file }) {
await client.close(file)
client.disconnect()
_writeFile(file, data, options) {
return this._client
.writeFile(this._getFilePath(file), data, options)
.catch(normalizeError)
}
}

View File

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

View File

@@ -0,0 +1,24 @@
/benchmark/
/benchmarks/
*.bench.js
*.bench.js.map
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/fixture/
/fixtures/
*.fixture.js
*.fixture.js.map
*.fixtures.js
*.fixtures.js.map
/test/
/tests/
*.spec.js
*.spec.js.map
__snapshots__/

View File

@@ -0,0 +1,160 @@
# @xen-orchestra/log [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> ${pkg.description}
## Install
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/log):
```
> npm install --save @xen-orchestra/log
```
## Usage
Everywhere something should be logged:
```js
import createLogger from '@xen-orchestra/log'
const log = createLogger('my-module')
log.debug('only useful for debugging')
log.info('this information is relevant to the user')
log.warn('something went wrong but did not prevent current action')
log.error('something went wrong')
log.fatal('service/app is going down')
```
Then, at application level, configure the logs are handled:
```js
import { configure, catchGlobalErrors } from '@xen-orchestra/log/configure'
import transportConsole from '@xen-orchestra/log/transports/console'
import transportEmail from '@xen-orchestra/log/transports/email'
const transport = transportEmail({
service: 'gmail',
auth: {
user: 'jane.smith@gmail.com',
pass: 'H&NbECcpXF|pyXe#%ZEb'
},
from: 'jane.smith@gmail.com',
to: [
'jane.smith@gmail.com',
'sam.doe@yahoo.com'
]
})
configure([
{
// if filter is a string, then it is pattern
// (https://github.com/visionmedia/debug#wildcards) which is
// matched against the namespace of the logs
filter: process.env.DEBUG,
transport: transportConsole()
},
{
// only levels >= warn
level: 'warn',
transport
}
])
// send all global errors (uncaught exceptions, warnings, unhandled rejections)
// to this transport
catchGlobalErrors(transport)
```
### Transports
#### Console
```js
import transportConsole from '@xen-orchestra/log/transports/console'
configure(transports.console())
```
#### Email
Optional dependency:
```
> yarn add nodemailer pretty-format
```
Configuration:
```js
import transportEmail from '@xen-orchestra/log/transports/email'
configure(transportEmail({
service: 'gmail',
auth: {
user: 'jane.smith@gmail.com',
pass: 'H&NbECcpXF|pyXe#%ZEb'
},
from: 'jane.smith@gmail.com',
to: [
'jane.smith@gmail.com',
'sam.doe@yahoo.com'
]
}))
```
#### Syslog
Optional dependency:
```
> yarn add split-host syslog-client
```
Configuration:
```js
import transportSyslog from '@xen-orchestra/log/transports/syslog'
// By default, log to udp://localhost:514
configure(transportSyslog())
// But TCP, a different host, or a different port can be used
configure(transportSyslog('tcp://syslog.company.lan'))
```
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/vatesfr/xo-web/issues/)
you've encountered;
- fork and create a pull request.
## License
ISC © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1 @@
module.exports = require('./dist/configure')

View File

@@ -0,0 +1,52 @@
{
"name": "@xen-orchestra/log",
"version": "0.1.4",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/log",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"configure.js",
"dist/",
"transports/"
],
"browserslist": [
">2%"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"lodash": "^4.17.4",
"promise-toolbox": "^0.11.0"
},
"devDependencies": {
"@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",
"index-modules": "^0.3.0",
"rimraf": "^2.6.2"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,106 @@
import createConsoleTransport from './transports/console'
import LEVELS, { resolve } from './levels'
import { compileGlobPattern } from './utils'
// ===================================================================
const createTransport = config => {
if (typeof config === 'function') {
return config
}
if (Array.isArray(config)) {
const transports = config.map(createTransport)
const { length } = transports
return function() {
for (let i = 0; i < length; ++i) {
transports[i].apply(this, arguments)
}
}
}
let { filter, transport } = config
const level = resolve(config.level)
if (filter !== undefined) {
if (typeof filter === 'string') {
const re = compileGlobPattern(filter)
filter = log => re.test(log.namespace)
}
const orig = transport
transport = function(log) {
if ((level !== undefined && log.level >= level) || filter(log)) {
return orig.apply(this, arguments)
}
}
} else if (level !== undefined) {
const orig = transport
transport = function(log) {
if (log.level >= level) {
return orig.apply(this, arguments)
}
}
}
return transport
}
const symbol =
typeof Symbol !== 'undefined'
? Symbol.for('@xen-orchestra/log')
: '@@@xen-orchestra/log'
global[symbol] = createTransport({
// display warnings or above, and all that are enabled via DEBUG or
// NODE_DEBUG env
filter: process.env.DEBUG || process.env.NODE_DEBUG,
level: LEVELS.INFO,
transport: createConsoleTransport(),
})
export const configure = config => {
global[symbol] = createTransport(config)
}
// -------------------------------------------------------------------
export const catchGlobalErrors = logger => {
// patch process
const onUncaughtException = error => {
logger.error('uncaught exception', { error })
}
const onUnhandledRejection = error => {
logger.warn('possibly unhandled rejection', { error })
}
const onWarning = error => {
logger.warn('Node warning', { error })
}
process.on('uncaughtException', onUncaughtException)
process.on('unhandledRejection', onUnhandledRejection)
process.on('warning', onWarning)
// patch EventEmitter
const EventEmitter = require('events')
const { prototype } = EventEmitter
const { emit } = prototype
function patchedEmit(event, error) {
if (event === 'error' && this.listenerCount(event) === 0) {
logger.error('unhandled error event', { error })
return false
}
return emit.apply(this, arguments)
}
prototype.emit = patchedEmit
return () => {
process.removeListener('uncaughtException', onUncaughtException)
process.removeListener('unhandledRejection', onUnhandledRejection)
process.removeListener('warning', onWarning)
if (prototype.emit === patchedEmit) {
prototype.emit = emit
}
}
}

View File

@@ -0,0 +1,76 @@
import createTransport from './transports/console'
import LEVELS from './levels'
const symbol =
typeof Symbol !== 'undefined'
? Symbol.for('@xen-orchestra/log')
: '@@@xen-orchestra/log'
if (!(symbol in global)) {
// the default behavior, without requiring `configure` is to avoid
// logging anything unless it's a real error
const transport = createTransport()
global[symbol] = log => log.level > LEVELS.WARN && transport(log)
}
// -------------------------------------------------------------------
function Log(data, level, namespace, message, time) {
this.data = data
this.level = level
this.namespace = namespace
this.message = message
this.time = time
}
function Logger(namespace) {
this._namespace = namespace
// bind all logging methods
for (const name in LEVELS) {
const lowerCase = name.toLowerCase()
this[lowerCase] = this[lowerCase].bind(this)
}
}
const { prototype } = Logger
for (const name in LEVELS) {
const level = LEVELS[name]
prototype[name.toLowerCase()] = function(message, data) {
if (typeof message !== 'string') {
if (message instanceof Error) {
data = { error: message }
;({ message = 'an error has occurred' } = message)
} else {
return this.warn('incorrect value passed to logger', {
level,
value: message,
})
}
}
global[symbol](new Log(data, level, this._namespace, message, new Date()))
}
}
prototype.wrap = function(message, fn) {
const logger = this
const warnAndRethrow = error => {
logger.warn(message, { error })
throw error
}
return function() {
try {
const result = fn.apply(this, arguments)
const then = result != null && result.then
return typeof then === 'function'
? then.call(result, warnAndRethrow)
: result
} catch (error) {
warnAndRethrow(error)
}
}
}
const createLogger = namespace => new Logger(namespace)
export { createLogger as default }

View File

@@ -0,0 +1,24 @@
const LEVELS = Object.create(null)
export { LEVELS as default }
// https://github.com/trentm/node-bunyan#levels
LEVELS.FATAL = 60 // service/app is going down
LEVELS.ERROR = 50 // fatal for current action
LEVELS.WARN = 40 // something went wrong but it's not fatal
LEVELS.INFO = 30 // detail on unusual but normal operation
LEVELS.DEBUG = 20
export const NAMES = Object.create(null)
for (const name in LEVELS) {
NAMES[LEVELS[name]] = name
}
export const resolve = level => {
if (typeof level === 'string') {
level = LEVELS[level.toUpperCase()]
}
return level
}
Object.freeze(LEVELS)
Object.freeze(NAMES)

View File

@@ -0,0 +1,32 @@
/* eslint-env jest */
import { forEach, isInteger } from 'lodash'
import LEVELS, { NAMES, resolve } from './levels'
describe('LEVELS', () => {
it('maps level names to their integer values', () => {
forEach(LEVELS, (value, name) => {
expect(isInteger(value)).toBe(true)
})
})
})
describe('NAMES', () => {
it('maps level values to their names', () => {
forEach(LEVELS, (value, name) => {
expect(NAMES[value]).toBe(name)
})
})
})
describe('resolve()', () => {
it('returns level values either from values or names', () => {
forEach(LEVELS, value => {
expect(resolve(value)).toBe(value)
})
forEach(NAMES, (name, value) => {
expect(resolve(name)).toBe(+value)
})
})
})

View File

@@ -0,0 +1,24 @@
import LEVELS, { NAMES } from '../levels'
// Bind console methods (necessary for browsers)
const debugConsole = console.log.bind(console)
const infoConsole = console.info.bind(console)
const warnConsole = console.warn.bind(console)
const errorConsole = console.error.bind(console)
const { ERROR, INFO, WARN } = LEVELS
const consoleTransport = ({ data, level, namespace, message, time }) => {
const fn =
level < INFO
? debugConsole
: level < WARN
? infoConsole
: level < ERROR
? warnConsole
: errorConsole
fn('%s - %s - [%s] %s', time.toISOString(), namespace, NAMES[level], message)
data != null && fn(data)
}
export default () => consoleTransport

View File

@@ -0,0 +1,68 @@
import fromCallback from 'promise-toolbox/fromCallback'
import prettyFormat from 'pretty-format' // eslint-disable-line node/no-extraneous-import
import { createTransport } from 'nodemailer' // eslint-disable-line node/no-extraneous-import
import { evalTemplate, required } from '../utils'
import { NAMES } from '../levels'
export default ({
// transport options (https://nodemailer.com/smtp/)
auth,
authMethod,
host,
ignoreTLS,
port,
proxy,
requireTLS,
secure,
service,
tls,
// message options (https://nodemailer.com/message/)
bcc,
cc,
from = required('from'),
to = required('to'),
subject = '[{{level}} - {{namespace}}] {{time}} {{message}}',
}) => {
const transporter = createTransport(
{
auth,
authMethod,
host,
ignoreTLS,
port,
proxy,
requireTLS,
secure,
service,
tls,
disableFileAccess: true,
disableUrlAccess: true,
},
{
bcc,
cc,
from,
to,
}
)
return log =>
fromCallback(cb =>
transporter.sendMail(
{
subject: evalTemplate(subject, key =>
key === 'level'
? NAMES[log.level]
: key === 'time'
? log.time.toISOString()
: log[key]
),
text: prettyFormat(log.data),
},
cb
)
)
}

View File

@@ -0,0 +1,7 @@
export default () => {
const memoryLogger = log => {
logs.push(log)
}
const logs = (memoryLogger.logs = [])
return memoryLogger
}

View File

@@ -0,0 +1,42 @@
import fromCallback from 'promise-toolbox/fromCallback'
import splitHost from 'split-host' // eslint-disable-line node/no-extraneous-import node/no-missing-import
import startsWith from 'lodash/startsWith'
import { createClient, Facility, Severity, Transport } from 'syslog-client' // eslint-disable-line node/no-extraneous-import node/no-missing-import
import LEVELS from '../levels'
// https://github.com/paulgrove/node-syslog-client#syslogseverity
const LEVEL_TO_SEVERITY = {
[LEVELS.FATAL]: Severity.Critical,
[LEVELS.ERROR]: Severity.Error,
[LEVELS.WARN]: Severity.Warning,
[LEVELS.INFO]: Severity.Informational,
[LEVELS.DEBUG]: Severity.Debug,
}
const facility = Facility.User
export default target => {
const opts = {}
if (target !== undefined) {
if (startsWith(target, 'tcp://')) {
target = target.slice(6)
opts.transport = Transport.Tcp
} else if (startsWith(target, 'udp://')) {
target = target.slice(6)
opts.transport = Transport.Udp
}
;({ host: target, port: opts.port } = splitHost(target))
}
const client = createClient(target, opts)
return log =>
fromCallback(cb =>
client.log(log.message, {
facility,
severity: LEVEL_TO_SEVERITY[log.level],
})
)
}

View File

@@ -0,0 +1,62 @@
import escapeRegExp from 'lodash/escapeRegExp'
// ===================================================================
const TPL_RE = /\{\{(.+?)\}\}/g
export const evalTemplate = (tpl, data) => {
const getData =
typeof data === 'function' ? (_, key) => data(key) : (_, key) => data[key]
return tpl.replace(TPL_RE, getData)
}
// -------------------------------------------------------------------
const compileGlobPatternFragment = pattern =>
pattern
.split('*')
.map(escapeRegExp)
.join('.*')
export const compileGlobPattern = pattern => {
const no = []
const yes = []
pattern.split(/[\s,]+/).forEach(pattern => {
if (pattern[0] === '-') {
no.push(pattern.slice(1))
} else {
yes.push(pattern)
}
})
const raw = ['^']
if (no.length !== 0) {
raw.push('(?!', no.map(compileGlobPatternFragment).join('|'), ')')
}
if (yes.length !== 0) {
raw.push('(?:', yes.map(compileGlobPatternFragment).join('|'), ')')
} else {
raw.push('.*')
}
raw.push('$')
return new RegExp(raw.join(''))
}
// -------------------------------------------------------------------
export const required = name => {
throw new Error(`missing required arg ${name}`)
}
// -------------------------------------------------------------------
export const serializeError = error => ({
...error,
message: error.message,
name: error.name,
stack: error.stack,
})

View File

@@ -0,0 +1,13 @@
/* eslint-env jest */
import { compileGlobPattern } from './utils'
describe('compileGlobPattern()', () => {
it('works', () => {
const re = compileGlobPattern('foo, ba*, -bar')
expect(re.test('foo')).toBe(true)
expect(re.test('bar')).toBe(false)
expect(re.test('baz')).toBe(true)
expect(re.test('qux')).toBe(false)
})
})

View File

@@ -0,0 +1 @@
module.exports = require('../dist/transports/console.js')

View File

@@ -0,0 +1 @@
module.exports = require('../dist/transports/email.js')

View File

@@ -0,0 +1 @@
module.exports = require('../dist/transports/memory.js')

View File

@@ -0,0 +1 @@
module.exports = require('../dist/transports/syslog.js')

View File

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

View File

@@ -0,0 +1,24 @@
/benchmark/
/benchmarks/
*.bench.js
*.bench.js.map
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/fixture/
/fixtures/
*.fixture.js
*.fixture.js.map
*.fixtures.js
*.fixtures.js.map
/test/
/tests/
*.spec.js
*.spec.js.map
__snapshots__/

View File

@@ -0,0 +1,49 @@
# ${pkg.name} [![Build Status](https://travis-ci.org/${pkg.shortGitHubPath}.png?branch=master)](https://travis-ci.org/${pkg.shortGitHubPath})
> ${pkg.description}
## Install
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
```
> npm install --save ${pkg.name}
```
## Usage
**TODO**
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](${pkg.bugs})
you've encountered;
- fork and create a pull request.
## License
${pkg.license} © [${pkg.author.name}](${pkg.author.url})

View File

@@ -0,0 +1,49 @@
{
"name": "@xen-orchestra/mixin",
"version": "0.0.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/mixin",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"dist/"
],
"browserslist": [
">2%"
],
"engines": {
"node": ">=6"
},
"dependencies": {
"bind-property-descriptor": "^1.0.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-dev": "^1.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"rimraf": "^2.6.2"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,130 @@
import { getBoundPropertyDescriptor } from 'bind-property-descriptor'
// ===================================================================
const { defineProperties, getOwnPropertyDescriptor } = Object
const isIgnoredProperty = name => name[0] === '_' || name === 'constructor'
const IGNORED_STATIC_PROPERTIES = {
__proto__: null,
arguments: true,
caller: true,
length: true,
name: true,
prototype: true,
}
const isIgnoredStaticProperty = name => name in IGNORED_STATIC_PROPERTIES
const ownKeys =
(typeof Reflect !== 'undefined' && Reflect.ownKeys) ||
(({ getOwnPropertyNames: names, getOwnPropertySymbols: symbols }) =>
symbols !== undefined ? obj => names(obj).concat(symbols(obj)) : names)(
Object
)
// -------------------------------------------------------------------
const mixin = Mixins => Class => {
if (__DEV__ && !Array.isArray(Mixins)) {
throw new TypeError('Mixins should be an array')
}
const { name } = Class
// Copy properties of plain object mix-ins to the prototype.
{
const allMixins = Mixins
Mixins = []
const { prototype } = Class
const descriptors = { __proto__: null }
allMixins.forEach(Mixin => {
if (typeof Mixin === 'function') {
Mixins.push(Mixin)
return
}
for (const prop of ownKeys(Mixin)) {
if (__DEV__ && prop in prototype) {
throw new Error(`${name}#${prop} is already defined`)
}
;(descriptors[prop] = getOwnPropertyDescriptor(
Mixin,
prop
)).enumerable = false // Object methods are enumerable but class methods are not.
}
})
defineProperties(prototype, descriptors)
}
const n = Mixins.length
function DecoratedClass(...args) {
const instance = new Class(...args)
for (let i = 0; i < n; ++i) {
const Mixin = Mixins[i]
const { prototype } = Mixin
const mixinInstance = new Mixin(instance, ...args)
const descriptors = { __proto__: null }
const props = ownKeys(prototype)
for (let j = 0, m = props.length; j < m; ++j) {
const prop = props[j]
if (isIgnoredProperty(prop)) {
continue
}
if (prop in instance) {
throw new Error(`${name}#${prop} is already defined`)
}
descriptors[prop] = getBoundPropertyDescriptor(
prototype,
prop,
mixinInstance
)
}
defineProperties(instance, descriptors)
}
return instance
}
// Copy original and mixed-in static properties on Decorator class.
const descriptors = { __proto__: null }
ownKeys(Class).forEach(prop => {
let descriptor
if (
!(
isIgnoredStaticProperty(prop) &&
// if they already exist...
(descriptor = getOwnPropertyDescriptor(DecoratedClass, prop)) !==
undefined &&
// and are not configurable.
!descriptor.configurable
)
) {
descriptors[prop] = getOwnPropertyDescriptor(Class, prop)
}
})
Mixins.forEach(Mixin => {
ownKeys(Mixin).forEach(prop => {
if (isIgnoredStaticProperty(prop)) {
return
}
if (__DEV__ && prop in descriptors) {
throw new Error(`${name}.${prop} is already defined`)
}
descriptors[prop] = getOwnPropertyDescriptor(Mixin, prop)
})
})
defineProperties(DecoratedClass, descriptors)
return DecoratedClass
}
export { mixin as default }

View File

@@ -2,15 +2,391 @@
## *next*
### Enhancements
- [Users] Display user groups [#3719](https://github.com/vatesfr/xen-orchestra/issues/3719) (PR [#3740](https://github.com/vatesfr/xen-orchestra/pull/3740))
- [VDI] Display VDI's SR [3021](https://github.com/vatesfr/xen-orchestra/issues/3021) (PR [#3285](https://github.com/vatesfr/xen-orchestra/pull/3285))
- [Health, VM/disks] Display SR's container [#3021](https://github.com/vatesfr/xen-orchestra/issues/3021) (PRs [#3747](https://github.com/vatesfr/xen-orchestra/pull/3747), [#3751](https://github.com/vatesfr/xen-orchestra/pull/3751))
- [Servers] Auto-connect to ejected host [#2238](https://github.com/vatesfr/xen-orchestra/issues/2238) (PR [#3738](https://github.com/vatesfr/xen-orchestra/pull/3738))
- [Backup NG] Add "XOSAN" in excluded tags by default [#2128](https://github.com/vatesfr/xen-orchestra/issues/3563) (PR [#3559](https://github.com/vatesfr/xen-orchestra/pull/3563))
- [VM] add tooltip for VM status icon [#3749](https://github.com/vatesfr/xen-orchestra/issues/3749) (PR [#3765](https://github.com/vatesfr/xen-orchestra/pull/3765))
- [New XOSAN] Improve view and possibility to sort SRs by name/size/free space [#2416](https://github.com/vatesfr/xen-orchestra/issues/2416) (PR [#3691](https://github.com/vatesfr/xen-orchestra/pull/3691))
- [Backup NG] Disable HA on replicated VM (CR, DR) [#2359](https://github.com/vatesfr/xen-orchestra/issues/2359) (PR [#3755](https://github.com/vatesfr/xen-orchestra/pull/3755))
- [Backup NG] Display the last run status for each schedule with the possibility to show the associated log [#3769](https://github.com/vatesfr/xen-orchestra/issues/3769) (PR [#3779](https://github.com/vatesfr/xen-orchestra/pull/3779))
- [Backup NG] Add a link to the documentation [#3789](https://github.com/vatesfr/xen-orchestra/issues/3789) (PR [#3790](https://github.com/vatesfr/xen-orchestra/pull/3790))
- [Backup NG] Ability to copy schedule/job id to the clipboard [#3753](https://github.com/vatesfr/xen-orchestra/issues/3753) (PR [#3791](https://github.com/vatesfr/xen-orchestra/pull/3791))
- [Backup NG / logs] Merge the job log status with the display details button [#3797](https://github.com/vatesfr/xen-orchestra/issues/3797) (PR [#3800](https://github.com/vatesfr/xen-orchestra/pull/3800))
### Bug fixes
- [Home/SRs] Fixed SR status for non admin users [#2204](https://github.com/vatesfr/xen-orchestra/issues/2204) (PR [#3742](https://github.com/vatesfr/xen-orchestra/pull/3742))
- [Servers] Fix occasional "server's pool already connected" errors when pool is not connected (PR [#3782](https://github.com/vatesfr/xen-orchestra/pull/3782))
- [Self] Fix missing objects when the self service view is the first one to be loaded when opening XO [#2689](https://github.com/vatesfr/xen-orchestra/issues/2689) (PR [#3096](https://github.com/vatesfr/xen-orchestra/pull/3096))
### Released packages
- @xen-orchestra/fs v0.5.0
- xen-api v0.23.0
- xo-acl-resolver v0.4.1
- xo-server v5.32.0
- xo-web v5.32.0
## **5.29.0** (2018-11-29)
- [Perf alert] Ability to trigger an alarm if a host/VM/SR usage value is below the threshold [#3612](https://github.com/vatesfr/xen-orchestra/issues/3612) (PR [#3675](https://github.com/vatesfr/xen-orchestra/pull/3675))
- [Home/VMs] Display pool's name [#2226](https://github.com/vatesfr/xen-orchestra/issues/2226) (PR [#3709](https://github.com/vatesfr/xen-orchestra/pull/3709))
- [Servers] Prevent new connection if pool is already connected [#2238](https://github.com/vatesfr/xen-orchestra/issues/2238) (PR [#3724](https://github.com/vatesfr/xen-orchestra/pull/3724))
- [VM] Pause (like Suspend but doesn't copy RAM on disk) [#3727](https://github.com/vatesfr/xen-orchestra/issues/3727) (PR [#3731](https://github.com/vatesfr/xen-orchestra/pull/3731))
### Bug fixes
- [Servers] Fix deleting server on joining a pool [#2238](https://github.com/vatesfr/xen-orchestra/issues/2238) (PR [#3728](https://github.com/vatesfr/xen-orchestra/pull/3728))
### Released packages
- xen-api v0.22.0
- xo-server-perf-alert v0.2.0
- xo-server-usage-report v0.7.1
- xo-server v5.31.0
- xo-web v5.31.0
## **5.28.2** (2018-11-16)
### Enhancements
- [VM] Ability to set nested virtualization in settings [#3619](https://github.com/vatesfr/xen-orchestra/issues/3619) (PR [#3625](https://github.com/vatesfr/xen-orchestra/pull/3625))
- [Legacy Backup] Restore and File restore functionalities moved to the Backup NG view [#3499](https://github.com/vatesfr/xen-orchestra/issues/3499) (PR [#3610](https://github.com/vatesfr/xen-orchestra/pull/3610))
- [Backup NG logs] Display warning in case of missing VMs instead of a ghosts VMs tasks (PR [#3647](https://github.com/vatesfr/xen-orchestra/pull/3647))
- [VM] On migration, automatically selects the host and SR when only one is available [#3502](https://github.com/vatesfr/xen-orchestra/issues/3502) (PR [#3654](https://github.com/vatesfr/xen-orchestra/pull/3654))
- [VM] Display VGA and video RAM for PVHVM guests [#3576](https://github.com/vatesfr/xen-orchestra/issues/3576) (PR [#3664](https://github.com/vatesfr/xen-orchestra/pull/3664))
- [Backup NG form] Display a warning to let the user know that the Delta Backup and the Continuous Replication are not supported on XenServer < 6.5 [#3540](https://github.com/vatesfr/xen-orchestra/issues/3540) (PR [#3668](https://github.com/vatesfr/xen-orchestra/pull/3668))
- [Backup NG form] Omit VMs(Simple Backup)/pools(Smart Backup/Resident on) with XenServer < 6.5 from the selection when the Delta Backup mode or the Continuous Replication mode are selected [#3540](https://github.com/vatesfr/xen-orchestra/issues/3540) (PR [#3668](https://github.com/vatesfr/xen-orchestra/pull/3668))
- [VM] Allow to switch the Virtualization mode [#2372](https://github.com/vatesfr/xen-orchestra/issues/2372) (PR [#3669](https://github.com/vatesfr/xen-orchestra/pull/3669))
### Bug fixes
- [Backup ng logs] Fix restarting VMs with concurrency issue [#3603](https://github.com/vatesfr/xen-orchestra/issues/3603) (PR [#3634](https://github.com/vatesfr/xen-orchestra/pull/3634))
- Validate modal containing a confirm text input by pressing the Enter key [#2735](https://github.com/vatesfr/xen-orchestra/issues/2735) (PR [#2890](https://github.com/vatesfr/xen-orchestra/pull/2890))
- [Patches] Bulk install correctly ignores upgrade patches on licensed hosts (PR [#3651](https://github.com/vatesfr/xen-orchestra/pull/3651))
- [Backup NG logs] Handle failed restores (PR [#3648](https://github.com/vatesfr/xen-orchestra/pull/3648))
- [Self/New VM] Incorrect limit computation [#3658](https://github.com/vatesfr/xen-orchestra/issues/3658) (PR [#3666](https://github.com/vatesfr/xen-orchestra/pull/3666))
- [Plugins] Don't expose credentials in config to users (PR [#3671](https://github.com/vatesfr/xen-orchestra/pull/3671))
- [Self/New VM] `not enough … available in the set …` error in some cases (PR [#3667](https://github.com/vatesfr/xen-orchestra/pull/3667))
- [XOSAN] Creation stuck at "Configuring VMs" [#3688](https://github.com/vatesfr/xen-orchestra/issues/3688) (PR [#3689](https://github.com/vatesfr/xen-orchestra/pull/3689))
- [Backup NG] Errors listing backups on SMB remotes with extraneous files (PR [#3685](https://github.com/vatesfr/xen-orchestra/pull/3685))
- [Remotes] Don't expose credentials to users [#3682](https://github.com/vatesfr/xen-orchestra/issues/3682) (PR [#3687](https://github.com/vatesfr/xen-orchestra/pull/3687))
- [VM] Correctly display guest metrics updates (tools, network, etc.) [#3533](https://github.com/vatesfr/xen-orchestra/issues/3533) (PR [#3694](https://github.com/vatesfr/xen-orchestra/pull/3694))
- [VM Templates] Fix deletion [#3498](https://github.com/vatesfr/xen-orchestra/issues/3498) (PR [#3695](https://github.com/vatesfr/xen-orchestra/pull/3695))
### Released packages
- xen-api v0.21.0
- xo-common v0.2.0
- xo-acl-resolver v0.4.0
- xo-server v5.30.1
- xo-web v5.30.0
## **5.28.1** (2018-11-05)
### Enhancements
### Bug fixes
- [Backup NG] Increase timeout in stale remotes detection to limit false positives (PR [#3632](https://github.com/vatesfr/xen-orchestra/pull/3632))
- Fix re-registration issue ([4e35b19ac](https://github.com/vatesfr/xen-orchestra/commit/4e35b19ac56c60f61c0e771cde70a50402797b8a))
- [Backup NG logs] Fix started jobs filter [#3636](https://github.com/vatesfr/xen-orchestra/issues/3636) (PR [#3641](https://github.com/vatesfr/xen-orchestra/pull/3641))
- [New VM] CPU and memory user inputs were ignored since previous release [#3644](https://github.com/vatesfr/xen-orchestra/issues/3644) (PR [#3646](https://github.com/vatesfr/xen-orchestra/pull/3646))
### Released packages
- @xen-orchestra/fs v0.4.1
- xo-server v5.29.4
- xo-web v5.29.3
## **5.28.0** (2018-10-31)
### Enhancements
- [Usage Report] Add IOPS read/write/total per VM [#3309](https://github.com/vatesfr/xen-orchestra/issues/3309) (PR [#3455](https://github.com/vatesfr/xen-orchestra/pull/3455))
- [Self service] Sort resource sets by name (PR [#3507](https://github.com/vatesfr/xen-orchestra/pull/3507))
- [Usage Report] Add top 3 SRs which use the most IOPS read/write/total [#3306](https://github.com/vatesfr/xen-orchestra/issues/3306) (PR [#3508](https://github.com/vatesfr/xen-orchestra/pull/3508))
- [New VM] Display a warning when the memory is below the template memory static min [#3496](https://github.com/vatesfr/xen-orchestra/issues/3496) (PR [#3513](https://github.com/vatesfr/xen-orchestra/pull/3513))
- [Backup NG form] Add link to plugins setting [#3457](https://github.com/vatesfr/xen-orchestra/issues/3457) (PR [#3514](https://github.com/vatesfr/xen-orchestra/pull/3514))
- [Backup reports] Add job and run ID [#3488](https://github.com/vatesfr/xen-orchestra/issues/3488) (PR [#3516](https://github.com/vatesfr/xen-orchestra/pull/3516))
- [Usage Report] Add top 3 VMs which use the most IOPS read/write/total [#3308](https://github.com/vatesfr/xen-orchestra/issues/3308) (PR [#3463](https://github.com/vatesfr/xen-orchestra/pull/3463))
- [Settings/logs] Homogenize action buttons in table and enable bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3528](https://github.com/vatesfr/xen-orchestra/pull/3528))
- [Settings/acls] Add bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3536](https://github.com/vatesfr/xen-orchestra/pull/3536))
- [Home] Improve search usage: raw numbers also match in names [#2906](https://github.com/vatesfr/xen-orchestra/issues/2906) (PR [#3552](https://github.com/vatesfr/xen-orchestra/pull/3552))
- [Backup NG] Timeout of a job is now in hours [#3550](https://github.com/vatesfr/xen-orchestra/issues/3550) (PR [#3553](https://github.com/vatesfr/xen-orchestra/pull/3553))
- [Backup NG] Explicit error if a VM is missing [#3434](https://github.com/vatesfr/xen-orchestra/issues/3434) (PR [#3522](https://github.com/vatesfr/xen-orchestra/pull/3522))
- [Backup NG] Show all advanced settings with non-default values in overview [#3549](https://github.com/vatesfr/xen-orchestra/issues/3549) (PR [#3554](https://github.com/vatesfr/xen-orchestra/pull/3554))
- [Backup NG] Collapse advanced settings by default [#3551](https://github.com/vatesfr/xen-orchestra/issues/3551) (PR [#3559](https://github.com/vatesfr/xen-orchestra/pull/3559))
- [Scheduling] Merge selection and interval tabs [#1902](https://github.com/vatesfr/xen-orchestra/issues/1902) (PR [#3519](https://github.com/vatesfr/xen-orchestra/pull/3519))
- [Backup NG/Restore] The backup selector now also shows the job name [#3366](https://github.com/vatesfr/xen-orchestra/issues/3366) (PR [#3564](https://github.com/vatesfr/xen-orchestra/pull/3564))
- Sort buttons by criticality in tables [#3168](https://github.com/vatesfr/xen-orchestra/issues/3168) (PR [#3545](https://github.com/vatesfr/xen-orchestra/pull/3545))
- [Usage Report] Ability to send a daily report [#3544](https://github.com/vatesfr/xen-orchestra/issues/3544) (PR [#3582](https://github.com/vatesfr/xen-orchestra/pull/3582))
- [Backup NG logs] Disable state filters with no entries [#3438](https://github.com/vatesfr/xen-orchestra/issues/3438) (PR [#3442](https://github.com/vatesfr/xen-orchestra/pull/3442))
- [ACLs] Global performance improvement on UI for non-admin users [#3578](https://github.com/vatesfr/xen-orchestra/issues/3578) (PR [#3584](https://github.com/vatesfr/xen-orchestra/pull/3584))
- [Backup NG] Improve the Schedule's view (Replace table by list) [#3491](https://github.com/vatesfr/xen-orchestra/issues/3491) (PR [#3586](https://github.com/vatesfr/xen-orchestra/pull/3586))
- ([Host/Storage], [Sr/hosts]) add bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3539](https://github.com/vatesfr/xen-orchestra/pull/3539))
- [xo-server] Use @xen-orchestra/log for basic logging [#3555](https://github.com/vatesfr/xen-orchestra/issues/3555) (PR [#3579](https://github.com/vatesfr/xen-orchestra/pull/3579))
- [Backup Report] Log error when job failed [#3458](https://github.com/vatesfr/xen-orchestra/issues/3458) (PR [#3593](https://github.com/vatesfr/xen-orchestra/pull/3593))
- [Backup NG] Display logs for backup restoration [#2511](https://github.com/vatesfr/xen-orchestra/issues/2511) (PR [#3609](https://github.com/vatesfr/xen-orchestra/pull/3609))
- [XOA] Display product version and list of all installed packages [#3560](https://github.com/vatesfr/xen-orchestra/issues/3560) (PR [#3621](https://github.com/vatesfr/xen-orchestra/pull/3621))
### Bug fixes
- [Remotes] Fix removal of broken remotes [#3327](https://github.com/vatesfr/xen-orchestra/issues/3327) (PR [#3521](https://github.com/vatesfr/xen-orchestra/pull/3521))
- [Backups] Fix stuck backups due to broken NFS remotes [#3467](https://github.com/vatesfr/xen-orchestra/issues/3467) (PR [#3534](https://github.com/vatesfr/xen-orchestra/pull/3534))
- [New VM] Fix missing cloud config when creating multiple VMs at once in some cases [#3532](https://github.com/vatesfr/xen-orchestra/issues/3532) (PR [#3535](https://github.com/vatesfr/xen-orchestra/pull/3535))
- [VM] Fix an error when an admin tried to add a disk on a Self VM whose resource set had been deleted [#2814](https://github.com/vatesfr/xen-orchestra/issues/2814) (PR [#3530](https://github.com/vatesfr/xen-orchestra/pull/3530))
- [Self/Create VM] Fix some quotas based on the template instead of the user inputs [#2683](https://github.com/vatesfr/xen-orchestra/issues/2683) (PR [#3546](https://github.com/vatesfr/xen-orchestra/pull/3546))
- [Self] Ignore DR and CR VMs when computing quotas [#3064](https://github.com/vatesfr/xen-orchestra/issues/3064) (PR [#3561](https://github.com/vatesfr/xen-orchestra/pull/3561))
- [Patches] Wrongly requiring to eject CDs from halted VMs and snapshots before installing patches (PR [#3611](https://github.com/vatesfr/xen-orchestra/pull/3611))
- [Jobs] Ensure the scheduling is not interrupted in rare cases (PR [#3617](https://github.com/vatesfr/xen-orchestra/pull/3617))
- [Home] Fix `server.getAll` error at login when user is not admin [#2335](https://github.com/vatesfr/xen-orchestra/issues/2335) (PR [#3613](https://github.com/vatesfr/xen-orchestra/pull/3613))
### Released packages
- xo-server-backup-reports v0.15.0
- xo-common v0.1.2
- @xen-orchestra/log v0.1.0
- @xen-orchestra/fs v0.4.0
- complex-matcher v0.5.0
- vhd-lib v0.4.0
- xen-api v0.20.0
- xo-server-usage-report v0.7.0
- xo-server v5.29.0
- xo-web v5.29.0
## **5.27.2** (2018-10-05)
### Enhancements
- [Host/Networks] Remove "Add network" button [#3386](https://github.com/vatesfr/xen-orchestra/issues/3386) (PR [#3478](https://github.com/vatesfr/xen-orchestra/pull/3478))
- [Host/networks] Private networks table [#3387](https://github.com/vatesfr/xen-orchestra/issues/3387) (PR [#3481](https://github.com/vatesfr/xen-orchestra/pull/3481))
- [Home/pool] Patch count pill now shows the number of unique patches in the pool [#3321](https://github.com/vatesfr/xen-orchestra/issues/3321) (PR [#3483](https://github.com/vatesfr/xen-orchestra/pull/3483))
- [Patches] Pre-install checks to avoid errors [#3252](https://github.com/vatesfr/xen-orchestra/issues/3252) (PR [#3484](https://github.com/vatesfr/xen-orchestra/pull/3484))
- [Vm/Snapshots] Allow VM operators to create snapshots and delete those they created [#3443](https://github.com/vatesfr/xen-orchestra/issues/3443) (PR [#3482](https://github.com/vatesfr/xen-orchestra/pull/3482))
- [VM/clone] Handle ACLs and Self Service [#3139](https://github.com/vatesfr/xen-orchestra/issues/3139) (PR [#3493](https://github.com/vatesfr/xen-orchestra/pull/3493))
### Bug fixes
- [Backup NG] Fix `Cannot read property 'uuid' of undefined` when a disk is removed from a VM to backup (PR [#3479](https://github.com/vatesfr/xen-orchestra/pull/3479))
- [Backup NG] Fix unexpected full after failure, interruption or basic rolling snapshot (PR [#3485](https://github.com/vatesfr/xen-orchestra/pull/3485))
- [Usage report] Display top 3 used SRs instead of top 3 biggest SRs [#3307](https://github.com/vatesfr/xen-orchestra/issues/3307) (PR [#3475](https://github.com/vatesfr/xen-orchestra/pull/3475))
### Released packages
- vhd-lib v0.3.2
- xo-vmdk-to-vhd v0.1.5
- xo-server-usage-report v0.6.0
- xo-acl-resolver v0.3.0
- xo-server v5.28.0
- xo-web v5.28.0
## **5.27.1** (2018-09-28)
### Enhancements
### Bug fixes
- [OVA Import] Allow import of files bigger than 127GB (PR [#3451](https://github.com/vatesfr/xen-orchestra/pull/3451))
- [File restore] Fix a path issue when going back to the parent folder (PR [#3446](https://github.com/vatesfr/xen-orchestra/pull/3446))
- [File restore] Fix a minor issue when showing which selected files are redundant (PR [#3447](https://github.com/vatesfr/xen-orchestra/pull/3447))
- [Memory] Fix a major leak [#2580](https://github.com/vatesfr/xen-orchestra/issues/2580) [#2820](https://github.com/vatesfr/xen-orchestra/issues/2820) (PR [#3453](https://github.com/vatesfr/xen-orchestra/pull/3453))
- [NFS Remotes] Fix `already mounted` race condition [#3380](https://github.com/vatesfr/xen-orchestra/issues/3380) (PR [#3460](https://github.com/vatesfr/xen-orchestra/pull/3460))
- Fix `Cannot read property 'type' of undefined` when deleting a VM (PR [#3465](https://github.com/vatesfr/xen-orchestra/pull/3465))
### Released packages
- @xen-orchestra/fs v0.3.1
- vhd-lib v0.3.1
- xo-vmdk-to-vhd v0.1.4
- xo-server v5.27.2
- xo-web v5.27.1
## **5.27.0** (2018-09-24)
### Enhancements
- [Remotes] Test the remote automatically on changes [#3323](https://github.com/vatesfr/xen-orchestra/issues/3323) (PR [#3397](https://github.com/vatesfr/xen-orchestra/pull/3397))
- [Remotes] Use *WORKGROUP* as default domain for new SMB remote (PR [#3398](https://github.com/vatesfr/xen-orchestra/pull/3398))
- [Backup NG form] Display a tip to encourage users to create vms on a thin-provisioned storage [#3334](https://github.com/vatesfr/xen-orchestra/issues/3334) (PR [#3402](https://github.com/vatesfr/xen-orchestra/pull/3402))
- [Backup NG form] improve schedule's form [#3138](https://github.com/vatesfr/xen-orchestra/issues/3138) (PR [#3359](https://github.com/vatesfr/xen-orchestra/pull/3359))
- [Backup NG Overview] Display transferred and merged data size for backup jobs [#3340](https://github.com/vatesfr/xen-orchestra/issues/3340) (PR [#3408](https://github.com/vatesfr/xen-orchestra/pull/3408))
- [VM] Display the PVHVM status [#3014](https://github.com/vatesfr/xen-orchestra/issues/3014) (PR [#3418](https://github.com/vatesfr/xen-orchestra/pull/3418))
- [Backup reports] Ability to test the plugin (PR [#3421](https://github.com/vatesfr/xen-orchestra/pull/3421))
- [Backup NG] Ability to restart failed VMs' backup [#3339](https://github.com/vatesfr/xen-orchestra/issues/3339) (PR [#3420](https://github.com/vatesfr/xen-orchestra/pull/3420))
- [VM] Ability to change the NIC type [#3423](https://github.com/vatesfr/xen-orchestra/issues/3423) (PR [#3440](https://github.com/vatesfr/xen-orchestra/pull/3440))
- [Backup NG Overview] Display the schedule's name [#3444](https://github.com/vatesfr/xen-orchestra/issues/3444) (PR [#3445](https://github.com/vatesfr/xen-orchestra/pull/3445))
### Bug fixes
- [Remotes] Rename connect(ed)/disconnect(ed) to enable(d)/disable(d) [#3323](https://github.com/vatesfr/xen-orchestra/issues/3323) (PR [#3396](https://github.com/vatesfr/xen-orchestra/pull/3396))
- [Remotes] Fix error appears twice on testing (PR [#3399](https://github.com/vatesfr/xen-orchestra/pull/3399))
- [Backup NG] Don't fail on VMs with empty VBDs (like CDs or floppy disks) (PR [#3410](https://github.com/vatesfr/xen-orchestra/pull/3410))
- [XOA updater] Fix issue where trial request would fail [#3407](https://github.com/vatesfr/xen-orchestra/issues/3407) (PR [#3412](https://github.com/vatesfr/xen-orchestra/pull/3412))
- [Backup NG logs] Fix log's value not being updated in the copy and report button [#3273](https://github.com/vatesfr/xen-orchestra/issues/3273) (PR [#3360](https://github.com/vatesfr/xen-orchestra/pull/3360))
- [Backup NG] Fix issue when *Delete first* was enabled for some of the remotes [#3424](https://github.com/vatesfr/xen-orchestra/issues/3424) (PR [#3427](https://github.com/vatesfr/xen-orchestra/pull/3427))
- [VM/host consoles] Work around a XenServer/XCP-ng issue which lead to some consoles not working [#3432](https://github.com/vatesfr/xen-orchestra/issues/3432) (PR [#3435](https://github.com/vatesfr/xen-orchestra/pull/3435))
- [Backup NG] Remove extraneous snapshots in case of multiple schedules [#3132](https://github.com/vatesfr/xen-orchestra/issues/3132) (PR [#3439](https://github.com/vatesfr/xen-orchestra/pull/3439))
- [Backup NG] Fix page reloaded on creating a schedule [#3461](https://github.com/vatesfr/xen-orchestra/issues/3461) (PR [#3462](https://github.com/vatesfr/xen-orchestra/pull/3462))
### Released packages
- xo-server-backup-reports v0.14.0
- @xen-orchestra/async-map v0.0.0
- @xen-orchestra/defined v0.0.0
- @xen-orchestra/emit-async v0.0.0
- @xen-orchestra/mixin v0.0.0
- xo-server v5.27.0
- xo-web v5.27.0
## **5.26.0** (2018-09-07)
### 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
## **5.24.0** (2018-08-09)
### Enhancements
- [Remotes] Make SMB subfolder field optional [#3249](https://github.com/vatesfr/xen-orchestra/issues/3249) (PR [#3250](https://github.com/vatesfr/xen-orchestra/pull/3250))
- [Backup NG form] Make the smart mode's toggle more visible [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) (PR [#3263](https://github.com/vatesfr/xen-orchestra/pull/3263))
- Move the copy clipboard of the VM's UUID to the header [#3221](https://github.com/vatesfr/xen-orchestra/issues/3221) (PR [#3248](https://github.com/vatesfr/xen-orchestra/pull/3248))
- [Health / Orphaned VMs] Homogenize action buttons in table and enable bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3274](https://github.com/vatesfr/xen-orchestra/pull/3274))
- [Health / Orphaned snapshot VDIs] Homogenize action buttons in table and enable bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3270](https://github.com/vatesfr/xen-orchestra/pull/3270))
- [Health / Alarms] Homogenize action buttons in table and enable bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3271](https://github.com/vatesfr/xen-orchestra/pull/3271))
- [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))
- [Patching] Check date consistency before patching to avoid error on install [#3056](https://github.com/vatesfr/xen-orchestra/issues/3056)
### Bug fixes
- [Pools] Filter GPU groups by pool [#3176](https://github.com/vatesfr/xen-orchestra/issues/3176) (PR [#3253](https://github.com/vatesfr/xen-orchestra/pull/3253))
- [Backup NG] Fix delta backups with SMB remotes [#3224](https://github.com/vatesfr/xen-orchestra/issues/3224) (PR [#3278](https://github.com/vatesfr/xen-orchestra/pull/3278))
- Fix VM restoration getting stuck on local SRs [#3245](https://github.com/vatesfr/xen-orchestra/issues/3245) (PR [#3243](https://github.com/vatesfr/xen-orchestra/pull/3243))
### Released packages
- xen-api v0.17.0
- @xen-orchestra/fs 0.2.1
- xo-server v5.24.0
- xo-web v5.24.0
## **5.23.0** (2018-07-26)
### Enhancements
- Export VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3194](https://github.com/vatesfr/xen-orchestra/pull/3194))
- Search syntax support wildcard (`*`) and regular expressions [#3190](https://github.com/vatesfr/xen-orchestra/issues/3190) (PRs [#3198](https://github.com/vatesfr/xen-orchestra/pull/3198) & [#3199](https://github.com/vatesfr/xen-orchestra/pull/3199))
- Import VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3216](https://github.com/vatesfr/xen-orchestra/pull/3216))
- [Backup NG form] Ability to edit a schedule's name [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) [#3071](https://github.com/vatesfr/xen-orchestra/issues/3071) (PR [#3143](https://github.com/vatesfr/xen-orchestra/pull/3143))
- [Remotes] Ability to change the type of a remote [#2423](https://github.com/vatesfr/xen-orchestra/issues/2423) (PR [#3207](https://github.com/vatesfr/xen-orchestra/pull/3207))
- [Backup NG new] Ability to set a job's timeout [#2978](https://github.com/vatesfr/xen-orchestra/issues/2978) (PR [#3222](https://github.com/vatesfr/xen-orchestra/pull/3222))
- [Remotes] Ability to edit/delete a remote with an invalid URL [#3182](https://github.com/vatesfr/xen-orchestra/issues/3182) (PR [#3226](https://github.com/vatesfr/xen-orchestra/pull/3226))
- [Backup NG logs] Prevent user from deleting logs to help resolving issues [#3153](https://github.com/vatesfr/xen-orchestra/issues/3153) (PR [#3235](https://github.com/vatesfr/xen-orchestra/pull/3235))
### Bug fixes
- [Backup Reports] Report not sent if reportWhen failure and at least a VM is successfull [#3181](https://github.com/vatesfr/xen-orchestra/issues/3181) (PR [#3185](https://github.com/vatesfr/xen-orchestra/pull/3185))
- [Backup NG] Correctly migrate report setting from legacy jobs [#3180](https://github.com/vatesfr/xen-orchestra/issues/3180) (PR [#3206](https://github.com/vatesfr/xen-orchestra/pull/3206))
- [Backup NG] remove incomplete XVA files [#3159](https://github.com/vatesfr/xen-orchestra/issues/3159) (PR [#3215](https://github.com/vatesfr/xen-orchestra/pull/3215))
- [Backup NG form] Ability to edit a schedule's state [#3223](https://github.com/vatesfr/xen-orchestra/issues/3223) (PR [#3228](https://github.com/vatesfr/xen-orchestra/pull/3228))
### Released packages
- xo-remote-parser v0.5.0
- complex-matcher v0.4.0
- xo-server-backup-reports v0.12.3
- xo-server v5.23.0
- xo-web v5.23.0
## **5.22.1** (2018-07-13)
### Bug fixes
- [Remote select] Gracefully ignore remotes with invalid URL (PR [#3178](https://github.com/vatesfr/xen-orchestra/pull/3178))
### Released packages
- xo-web v5.22.1
## **5.22.0** (2018-07-12)
### Enhancements

View File

@@ -12,6 +12,7 @@
* [Trial activation](trial_activation.md)
* [Plugins](plugins.md)
* [Logs](logs.md)
* [Compatibility](supported-version.md)
* [Troubleshooting](troubleshooting.md)
* [From the sources](from_the_sources.md)
* [Configuration](configuration.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/assets/log-runId.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -10,24 +10,40 @@ Another good way to check if there is activity is the XOA VM stats view (on the
## Error messages
### `VDI chain protection`
### VDI chain protection
> This message is relevant for *Continuous Delta Backup* or *Continuous Replication* only.
This means your previous VM disks and snapshots should be "merged" (*coalesced* in the XenServer world) before we take a new snapshot. This mechanism is handled by XenServer itself, not Xen Orchestra. But we can check your existing VDI chain and avoid creating more snapshots than your storage can merge. Otherwise, this will lead to catastrophic consequences. Xen Orchestra is the **only** XenServer/XCP backup product dealing with this.
This means your previous VM disks and snapshots should be "merged" (*coalesced* in the XenServer world) before we can take a new snapshot. This mechanism is handled by XenServer itself, not Xen Orchestra. But we can check your existing VDI chain and avoid creating more snapshots than your storage can merge. Otherwise, this will lead to catastrophic consequences. Xen Orchestra is the **only** XenServer/XCP backup product dealing with this.
Without this detection, you could have 2 potential issues:
* `The Snapshot Chain is too Long`
* `SR_BACKEND_FAILURE_44 (insufficient space)`
The first issue is a chain that contains more than 30 elements (fixed XenServer limit), and the other one means it's full because the "coalesce" process couldn't keep up the pace and the storage became filled.
The first issue is a chain that contains more than 30 elements (fixed XenServer limit), and the other one means it's full because the "coalesce" process couldn't keep up the pace and the storage filled up.
In the end, this message is a **protection mechanism against damaging your SR**. The backup job will fail, but XenServer itself should eventually automatically coalesce the snapshot chain, and the the next time the backup job should complete.
Just remember this: **coalesce will happen every time a snapshot is removed**.
> You can read more on this on our dedicated blog post regarding [XenServer coalesce detection](https://xen-orchestra.com/blog/xenserver-coalesce-detection-in-xen-orchestra/).
### `SR_BACKEND_FAILURE_44 (insufficient space)`
### Troubleshooting a constant VDI Chain Protection message (XenServer failure to coalesce)
As previously mentioned, this message can be normal and it just means XenServer needs to perform a coalesce to merge old snapshots. However if you repeatedly get this message and it seems XenServer is not coalescing, You can take a few steps to determine why.
First check SMlog on the XenServer host for messages relating to VDI corruption or coalesce job failure. For example, by running `cat /var/log/SMlog | grep -i exception` or `cat /var/log/SMlog | grep -i error` on the XenServer host with the affected storage.
Coalesce jobs can also fail to run if the SR does not have enough free space. Check the problematic SR and make sure it has enough free space, generally 30% or more free is recommended depending on VM size.
You can check if a coalesce job is currently active by running `ps axf | grep vhd` on the XenServer host and looking for a VHD process in the results (one of the resulting processes will be the grep command you just ran, ignore that one).
If you don't see any running coalesce jobs, and can't find any other reason that XenServer has not started one, you can attempt to make it start a coalesce job by rescanning the SR. This is harmless to try, but will not always result in a coalesce. Visit the problematic SR in the XOA UI, then click the "Rescan All Disks" button towards the top right: it looks like a refresh circle icon. This should begin the coalesce process - if you click the Advanced tab in the SR view, the "disks needing to be coalesced" list should become smaller and smaller.
### Parse Error
This is most likely due to running a backup job that uses Delta functionality (eg: delta backups, or continuous replication) on a version of XenServer older than 6.5. To use delta functionality you must run [XenServer 6.5 or later](https://xen-orchestra.com/docs/supported-version.html).
### SR_BACKEND_FAILURE_44 (insufficient space)
> This message can be triggered by any backup method.
@@ -44,7 +60,7 @@ Workarounds:
This message appears when the previous replicated VM has been deleted on the target side which breaks the replication. To reset the process it's necessary to delete VM snapshot related to this CR job on the original VM. The name of this snapshot is: `XO_DELTA_EXPORT: <name label of target SR> (<UUID of target SR>)`
### LICENCE_RESTRICTION(PCI_device_for_auto_update)
### LICENSE_RESTRICTION(PCI_device_for_auto_update)
This message appears when you try to do a backup/snapshot from a VM that was previously on a host with an active commercial XenServer license but is now on a host with a free edition of XenServer.
@@ -54,4 +70,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

@@ -29,13 +29,19 @@ You also have a filter to search anything related to these logs.
> Logs are not "live" tasks. If you restart XOA during a backup, the log associated with the job will stay in orange (in progress), because it wasn't finished. It will stay forever unfinished because the job was cut in the middle.
## Backups execution
Each backups' job execution is identified by a `runId`. You can find this `runId` in its detailed log.
![](./assets/log-runId.png)
## 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)
@@ -135,3 +141,12 @@ To make the mount point persistent in XOA, edit the `/etc/fstab` file, and add:
```
This way, without modifying your previous scheduled snapshot, they will be written to this new local mountpoint!
## High availability (HA) disabled on replicated VMs
Replicated VMs HA are taken into account by XS/XCP-ng. To avoid the resultant troubles, HA will be disabled from the replicated VMs and a tag indicating this change will be added.
![](./assets/disabled-dr-ha-tag.png)
![](./assets/disabled-cr-ha-tag.png)
> The tag won't be automatically removed by XO on the replicated VMs, even if HA is re-enabled.

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

@@ -14,13 +14,13 @@ As you may have seen,in other parts of the documentation, XO is composed of two
### NodeJS
XO needs Node.js. **Please always use the LTS version of Node**.
XO needs Node.js. **Please use Node 8**.
We'll consider at this point that you've got a working node on your box. E.g:
```
$ node -v
v8.9.1
v8.12.0
```
If not, see [this page](https://nodejs.org/en/download/package-manager/) for instructions on how to install Node.
@@ -38,7 +38,7 @@ XO needs the following packages to be installed. Redis is used as a database by
For example, on Debian:
```
apt-get install build-essential redis-server libpng-dev git python-minimal
apt-get install build-essential redis-server libpng-dev git python-minimal libvhdi-utils lvm2
```
## Fetching the Code
@@ -100,6 +100,9 @@ That's it! Use your browser to visit the xo-server IP address, and it works! :)
If you would like to update your current version, enter your `xen-orchestra` directory and run the following:
```
# This will clear any changes you made in the repository!!
$ git checkout .
$ git pull --ff-only
$ yarn
$ yarn build

View File

@@ -52,6 +52,19 @@ Simply type the string, if it contains special characters just surround it with
> The search is recursive, case insensitive and non-anchored (i.e. matches if the pattern is contained in a string).
A simple string can also contain a wildcard character (`*`) to match any character in a portion of the string:
- `foo*bar`: matches `foobar`, `foo - bar`, etc.
#### Regular expression
For more advanced string matching, you can use regular expressions:
- `/^DNS server \d+$/`: matches `DNS server 1`, `DNS server 05` but not `DNS server`
- `/foo/i`: with the `i` flag, it ignores the case, therefore it matches `Foo` and `FOO`
[More information about supported regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
#### Searching a specific property
Type the property name, followed by a colon `:` and a subfilter:

34
docs/supported-version.md Normal file
View File

@@ -0,0 +1,34 @@
# Xen Orchestra compatibility
Xen Orchestra is designed to work exclusively on [XCP-ng](https://xcp-ng.org/) and [Citrix XenServer](https://xenserver.org/) hypervisor (any version). Xen Orchestra should be fully functional with any version of these two hypervisors. However, to benefit from the best support quality, our product is tested to work the best with the following versions:
## Citrix XenServer (Citrix hypervisor)
Backup restore for large VM disks (>1TiB usage) is [broken on all XenServer versions](https://bugs.xenserver.org/browse/XSO-868) until Citrix release a fix.
* XenServer 7.6
* XenServer 7.5
* [VDI I/O error](https://bugs.xenserver.org/browse/XSO-873), waiting for Citrix to release our fix
* XenServer 7.4
* XenServer 7.3
* XenServer 7.2
* XenServer 7.1
* XenServer 7.0
* XenServer 6.5
* Random Delta backup issues
* XenServer 6.1 and 6.2
* No Delta backup and CR support
* XenServer 5.x
* Basic administration features
![](https://xen-orchestra.com/blog/content/images/2018/08/Xen-Server.jpeg)
## XCP-ng
All the pending fixes are already integrated in the latest XCP-ng version. We strongly suggest people to keep using the latest XCP-ng version as far as possible.
* XCP-ng 7.6
* XCP-ng 7.5
* XCP-ng 7.4.1
![](https://xen-orchestra.com/blog/content/images/2018/02/logo1glossy.png)

View File

@@ -1,4 +1,3 @@
# Troubleshooting
This page recaps the actions you can perform if you have any problems with your XOA.
@@ -7,12 +6,21 @@ This page recaps the actions you can perform if you have any problems with your
> Auto deploy failed. - No SR specified and Pool default SR is null
It means you don't have a default SR set on the pool you are importing XOA to. To set a default SR, you must first find the SR UUID you want, with `xe sr-list`. When you have the UUID, you can set the default SR with: `xe pool-param-set default-SR=<SR_UUID>`. When this is done, re-enter the deploy script command and it will work!
It means you don't have a default SR set on the pool you are importing XOA on. To set a default SR, you must first find the SR UUID you want, with `xe sr-list`. When you have the UUID, you can set the default SR with: `xe pool-param-set uuid=<pool-uuid> default-SR=<sr-uuid>`. For the pool UUID, just press tab after `xe pool-param-set uuid=` and it will autofill your pool UUID. When this is done, re-enter the deploy script command and it will work!
## XOA unreachable after boot
XOA uses HVM mode. If your physical host doesn't support virtualization extensions, XOA won't work. To check if your XenServer supports hardware assisted virtualization (HVM), you can enter this command in your host: `grep --color vmx /proc/cpuinfo`. If you don't have any result, it means XOA won't work on this hardware.
## Recover XOA Web-UI login password
If you have lost your password to log in to the XOA webpage, you can reset it. From the XOA CLI (for login/access info for the CLI, [see here](xoa.md#first-console-connection)), use the following command and insert the email/account you wish to recover:
`xo-server-recover-account youremail@here.com`
It will prompt you to set a new password. If you provide an email here that does not exist in XOA yet, it will create a new account using it, with admin permissions - you can use that new account to log in as well.
## Empty page after login
This happens when your antivirus or firewall is blocking the websocket protocol. This is what we use to communicate between `xo-server` and `xo-web` (see the [architecture page](architecture.md)).
@@ -93,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).
@@ -120,7 +135,7 @@ $ openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt -nodes -
$ systemctl restart xo-server.service
```
## XO configuration
## XO Configuration
The system logs are visible by using this command:
@@ -128,11 +143,11 @@ The system logs are visible by using this command:
$ tail -f /var/log/syslog
```
You can read more about logs [in the dedicated chapter](logs.md).
You can read more about logs [in the dedicated logs chapter](logs.md).
### Ghost tasks
If you have ghost tasks accumulating in your XenOrchestra you can try the following actions in order:
If you have ghost tasks accumulating in your Xen Orchestra you can try the following actions in order:
1. refresh the web page
1. disconnect and reconnect the Xen pool/server owning the tasks
@@ -150,7 +165,7 @@ If a package disappears due to a build problem or human error, you can redownloa
### Reset XO configuration
If you have problems with your `xo-server` configuration, you can reset the database. **This operation will delete all your configured users and servers**:
If you have problems with your `xo-server` configuration, you can reset the database. **This operation will delete all your configured users and servers, plus any backup jobs**:
1. `redis-cli`
2. `FLUSHALL`

View File

@@ -1,4 +1,3 @@
# xo-cli
This is another client of `xo-server` - this time in command line form.
@@ -104,5 +103,6 @@ encoding by prefixing with `json:`:
##### VM import
```
> xo-cli vm.import host=60a6939e-8b0a-4352-9954-5bde44bcdf7d @=vm.xva
> xo-cli vm.import sr=60a6939e-8b0a-4352-9954-5bde44bcdf7d @=vm.xva
```
> Note: `xo-cli` only supports the import of XVA files. It will not import OVA files. To import OVA images, you must use the XOA web UI.

View File

@@ -1,33 +1,40 @@
{
"devDependencies": {
"@babel/core": "^7.0.0-beta.49",
"@babel/register": "^7.0.0-beta.49",
"@babel/core": "^7.0.0",
"@babel/register": "^7.0.0",
"babel-core": "^7.0.0-0",
"babel-eslint": "^8.1.2",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.0.1",
"benchmark": "^2.1.4",
"eslint": "^4.14.0",
"eslint-config-standard": "^11.0.0-beta.0",
"eslint-config-standard-jsx": "^5.0.0",
"eslint": "^5.1.0",
"eslint-config-prettier": "^3.3.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": "^8.0.0",
"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.73.0",
"flow-bin": "^0.87.0",
"globby": "^8.0.0",
"husky": "^0.14.3",
"husky": "^1.0.0-rc.15",
"jest": "^23.0.1",
"lodash": "^4.17.4",
"prettier": "^1.10.2",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"sorted-object": "^2.0.1"
},
"engines": {
"yarn": "^1.7.0"
},
"husky": {
"hooks": {
"pre-commit": "scripts/lint-staged"
}
},
"jest": {
"timers": "fake",
"collectCoverage": true,
"projects": [
"<rootDir>"
@@ -49,11 +56,11 @@
"dev": "scripts/run-script --parallel dev",
"dev-test": "jest --bail --watch \"^(?!.*\\.integ\\.spec\\.js$)\"",
"posttest": "scripts/run-script test",
"precommit": "scripts/lint-staged",
"prepare": "scripts/run-script prepare",
"pretest": "eslint --ignore-path .gitignore .",
"test": "jest \"^(?!.*\\.integ\\.spec\\.js$)\"",
"test-integration": "jest \".integ\\.spec\\.js$\""
"test-integration": "jest \".integ\\.spec\\.js$\"",
"travis-tests": "scripts/travis-tests"
},
"workspaces": [
"@xen-orchestra/*",

View File

@@ -1,6 +1,6 @@
{
"name": "complex-matcher",
"version": "0.3.0",
"version": "0.5.0",
"license": "ISC",
"description": "",
"keywords": [],
@@ -30,9 +30,9 @@
"lodash": "^4.17.4"
},
"devDependencies": {
"@babel/cli": "7.0.0-beta.49",
"@babel/core": "7.0.0-beta.49",
"@babel/preset-env": "7.0.0-beta.49",
"@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

@@ -1,7 +1,7 @@
import * as CM from './'
export const pattern =
'foo !"\\\\ \\"" name:|(wonderwoman batman) hasCape? age:32'
'foo !"\\\\ \\"" name:|(wonderwoman batman) hasCape? age:32 chi*go /^foo\\/bar\\./i'
export const ast = new CM.And([
new CM.String('foo'),
@@ -11,5 +11,7 @@ export const ast = new CM.And([
new CM.Or([new CM.String('wonderwoman'), new CM.String('batman')])
),
new CM.TruthyProperty('hasCape'),
new CM.Property('age', new CM.Number(32)),
new CM.Property('age', new CM.NumberOrStringNode('32')),
new CM.GlobPattern('chi*go'),
new CM.RegExp('^foo/bar\\.', 'i'),
])

View File

@@ -1,4 +1,4 @@
import { isPlainObject, some } from 'lodash'
import { escapeRegExp, isPlainObject, some } from 'lodash'
// ===================================================================
@@ -33,17 +33,17 @@ const isRawString = string => {
// -------------------------------------------------------------------
class Node {
createPredicate () {
createPredicate() {
return value => this.match(value)
}
}
export class Null extends Node {
match () {
match() {
return true
}
toString () {
toString() {
return ''
}
}
@@ -51,7 +51,7 @@ export class Null extends Node {
const formatTerms = terms => terms.map(term => term.toString(true)).join(' ')
export class And extends Node {
constructor (children) {
constructor(children) {
super()
if (children.length === 1) {
@@ -60,29 +60,29 @@ export class And extends Node {
this.children = children
}
match (value) {
match(value) {
return this.children.every(child => child.match(value))
}
toString (isNested) {
toString(isNested) {
const terms = formatTerms(this.children)
return isNested ? `(${terms})` : terms
}
}
export class Comparison extends Node {
constructor (operator, value) {
constructor(operator, value) {
super()
this._comparator = Comparison.comparators[operator]
this._operator = operator
this._value = value
}
match (value) {
match(value) {
return typeof value === 'number' && this._comparator(value, this._value)
}
toString () {
toString() {
return this._operator + String(this._value)
}
}
@@ -94,7 +94,7 @@ Comparison.comparators = {
}
export class Or extends Node {
constructor (children) {
constructor(children) {
super()
if (children.length === 1) {
@@ -103,33 +103,33 @@ export class Or extends Node {
this.children = children
}
match (value) {
match(value) {
return this.children.some(child => child.match(value))
}
toString () {
toString() {
return `|(${formatTerms(this.children)})`
}
}
export class Not extends Node {
constructor (child) {
constructor(child) {
super()
this.child = child
}
match (value) {
match(value) {
return !this.child.match(value)
}
toString () {
toString() {
return '!' + this.child.toString(true)
}
}
export class NumberNode extends Node {
constructor (value) {
constructor(value) {
super()
this.value = value
@@ -140,32 +140,60 @@ export class NumberNode extends Node {
})
}
match (value) {
match(value) {
return (
value === this.value ||
(value !== null && typeof value === 'object' && some(value, this.match))
)
}
toString () {
toString() {
return String(this.value)
}
}
export { NumberNode as Number }
export class NumberOrStringNode extends Node {
constructor(value) {
super()
this.value = value
// should not be enumerable for the tests
Object.defineProperty(this, 'match', {
value: this.match.bind(this, value.toLowerCase(), +value),
})
}
match(lcValue, numValue, value) {
return (
value === numValue ||
(typeof value === 'string'
? value.toLowerCase().indexOf(lcValue) !== -1
: (Array.isArray(value) || isPlainObject(value)) &&
some(value, this.match))
)
}
toString() {
return this.value
}
}
export { NumberOrStringNode as NumberOrString }
export class Property extends Node {
constructor (name, child) {
constructor(name, child) {
super()
this.name = name
this.child = child
}
match (value) {
match(value) {
return value != null && this.child.match(value[this.name])
}
toString () {
toString() {
return `${formatString(this.name)}:${this.child.toString(true)}`
}
}
@@ -173,25 +201,40 @@ export class Property extends Node {
const escapeChar = char => '\\' + char
const formatString = value =>
Number.isNaN(+value)
? isRawString(value) ? value : `"${value.replace(/\\|"/g, escapeChar)}"`
? isRawString(value)
? value
: `"${value.replace(/\\|"/g, escapeChar)}"`
: `"${value}"`
export class StringNode extends Node {
constructor (value) {
export class GlobPattern extends Node {
constructor(value) {
// fallback to string node if no wildcard
if (value.indexOf('*') === -1) {
return new StringNode(value)
}
super()
this.lcValue = value.toLowerCase()
this.value = value
// should not be enumerable for the tests
Object.defineProperty(this, 'match', {
value: this.match.bind(this),
value: this.match.bind(
this,
new RegExp(
value
.split('*')
.map(escapeRegExp)
.join('.*'),
'i'
)
),
})
}
match (value) {
match(re, value) {
if (typeof value === 'string') {
return value.toLowerCase().indexOf(this.lcValue) !== -1
return re.test(value)
}
if (Array.isArray(value) || isPlainObject(value)) {
@@ -201,24 +244,83 @@ export class StringNode extends Node {
return false
}
toString () {
toString() {
return this.value
}
}
export class RegExpNode extends Node {
constructor(pattern, flags) {
super()
this.re = new RegExp(pattern, flags)
// should not be enumerable for the tests
Object.defineProperty(this, 'match', {
value: this.match.bind(this),
})
}
match(value) {
if (typeof value === 'string') {
return this.re.test(value)
}
if (Array.isArray(value) || isPlainObject(value)) {
return some(value, this.match)
}
return false
}
toString() {
return this.re.toString()
}
}
export { RegExpNode as RegExp }
export class StringNode extends Node {
constructor(value) {
super()
this.value = value
// should not be enumerable for the tests
Object.defineProperty(this, 'match', {
value: this.match.bind(this, value.toLowerCase()),
})
}
match(lcValue, value) {
if (typeof value === 'string') {
return value.toLowerCase().indexOf(lcValue) !== -1
}
if (Array.isArray(value) || isPlainObject(value)) {
return some(value, this.match)
}
return false
}
toString() {
return formatString(this.value)
}
}
export { StringNode as String }
export class TruthyProperty extends Node {
constructor (name) {
constructor(name) {
super()
this.name = name
}
match (value) {
match(value) {
return value != null && !!value[this.name]
}
toString () {
toString() {
return formatString(this.name) + '?'
}
}
@@ -228,12 +330,12 @@ export class TruthyProperty extends Node {
// https://gist.github.com/yelouafi/556e5159e869952335e01f6b473c4ec1
class Failure {
constructor (pos, expected) {
constructor(pos, expected) {
this.expected = expected
this.pos = pos
}
get value () {
get value() {
throw new Error(
`parse error: expected ${this.expected} at position ${this.pos}`
)
@@ -241,7 +343,7 @@ class Failure {
}
class Success {
constructor (pos, value) {
constructor(pos, value) {
this.pos = pos
this.value = value
}
@@ -250,7 +352,7 @@ class Success {
// -------------------------------------------------------------------
class P {
static alt (...parsers) {
static alt(...parsers) {
const { length } = parsers
return new P((input, pos, end) => {
for (let i = 0; i < length; ++i) {
@@ -263,7 +365,7 @@ class P {
})
}
static grammar (rules) {
static grammar(rules) {
const grammar = {}
Object.keys(rules).forEach(k => {
const rule = rules[k]
@@ -272,14 +374,14 @@ class P {
return grammar
}
static lazy (parserCreator, arg) {
static lazy(parserCreator, arg) {
const parser = new P((input, pos, end) =>
(parser._parse = parserCreator(arg)._parse)(input, pos, end)
)
return parser
}
static regex (regex) {
static regex(regex) {
regex = new RegExp(regex.source, 'y')
return new P((input, pos) => {
regex.lastIndex = pos
@@ -290,7 +392,7 @@ class P {
})
}
static seq (...parsers) {
static seq(...parsers) {
const { length } = parsers
return new P((input, pos, end) => {
const values = new Array(length)
@@ -306,21 +408,20 @@ class P {
})
}
static text (text) {
static text(text) {
const { length } = text
return new P(
(input, pos) =>
input.startsWith(text, pos)
? new Success(pos + length, text)
: new Failure(pos, `'${text}'`)
return new P((input, pos) =>
input.startsWith(text, pos)
? new Success(pos + length, text)
: new Failure(pos, `'${text}'`)
)
}
constructor (parse) {
constructor(parse) {
this._parse = parse
}
map (fn) {
map(fn) {
return new P((input, pos, end) => {
const result = this._parse(input, pos, end)
if (result instanceof Success) {
@@ -330,11 +431,11 @@ class P {
})
}
parse (input, pos = 0, end = input.length) {
parse(input, pos = 0, end = input.length) {
return this._parse(input, pos, end).value
}
repeat (min = 0, max = Infinity) {
repeat(min = 0, max = Infinity) {
return new P((input, pos, end) => {
const value = []
let result
@@ -360,7 +461,7 @@ class P {
})
}
skip (otherParser) {
skip(otherParser) {
return new P((input, pos, end) => {
const result = this._parse(input, pos, end)
if (result instanceof Failure) {
@@ -376,18 +477,28 @@ class P {
}
}
P.eof = new P(
(input, pos, end) =>
pos < end ? new Failure(pos, 'end of input') : new Success(pos)
P.eof = new P((input, pos, end) =>
pos < end ? new Failure(pos, 'end of input') : new Success(pos)
)
// -------------------------------------------------------------------
const parser = P.grammar({
default: r =>
P.seq(r.ws, r.term.repeat(), P.eof).map(
([, terms]) => (terms.length === 0 ? new Null() : new And(terms))
P.seq(r.ws, r.term.repeat(), P.eof).map(([, terms]) =>
terms.length === 0 ? new Null() : new And(terms)
),
globPattern: new P((input, pos, end) => {
let value = ''
let c
while (pos < end && ((c = input[pos]) === '*' || c in RAW_STRING_CHARS)) {
++pos
value += c
}
return value.length === 0
? new Failure(pos, 'a raw string')
: new Success(pos, value)
}),
quotedString: new P((input, pos, end) => {
if (input[pos] !== '"') {
return new Failure(pos, '"')
@@ -405,6 +516,7 @@ const parser = P.grammar({
return new Success(pos, value.join(''))
}),
property: r => P.alt(r.quotedString, r.rawString),
rawString: new P((input, pos, end) => {
let value = ''
let c
@@ -416,7 +528,33 @@ const parser = P.grammar({
? new Failure(pos, 'a raw string')
: new Success(pos, value)
}),
string: r => P.alt(r.quotedString, r.rawString),
regex: new P((input, pos, end) => {
if (input[pos] !== '/') {
return new Failure(pos, '/')
}
++pos
let c
let pattern = ''
let escaped = false
while (pos < end && ((c = input[pos++]) !== '/' || escaped)) {
escaped = c === '\\'
pattern += c
}
if (c !== '/') {
return new Failure(pos, '/')
}
let flags = ''
if (pos < end && (c = input[pos]) === 'i') {
++pos
flags += c
}
return new Success(pos, new RegExpNode(pattern, flags))
}),
term: r =>
P.alt(
P.seq(P.text('('), r.ws, r.term.repeat(1), P.text(')')).map(
@@ -438,20 +576,23 @@ const parser = P.grammar({
}
return new Comparison(op, val)
}),
P.seq(r.string, r.ws, P.text(':'), r.ws, r.term).map(
P.seq(r.property, r.ws, P.text(':'), r.ws, r.term).map(
_ => new Property(_[0], _[4])
),
P.seq(r.string, P.text('?')).map(_ => new TruthyProperty(_[0])),
P.alt(
r.quotedString.map(_ => new StringNode(_)),
r.rawString.map(str => {
const asNum = +str
return Number.isNaN(asNum)
? new StringNode(str)
: new NumberNode(asNum)
})
)
P.seq(r.property, P.text('?')).map(_ => new TruthyProperty(_[0])),
r.value
).skip(r.ws),
value: r =>
P.alt(
r.quotedString.map(_ => new StringNode(_)),
r.regex,
r.globPattern.map(str => {
const asNum = +str
return Number.isNaN(asNum)
? new GlobPattern(str)
: new NumberOrStringNode(str)
})
),
ws: P.regex(/\s*/),
}).default
export const parse = parser.parse.bind(parser)

View File

@@ -3,8 +3,10 @@
import { ast, pattern } from './index.fixtures'
import {
getPropertyClausesStrings,
GlobPattern,
Null,
NumberNode,
NumberOrStringNode,
parse,
setPropertyClause,
} from './'
@@ -31,7 +33,7 @@ describe('parse', () => {
node = parse('32')
expect(node.match(32)).toBe(true)
expect(node.match('32')).toBe(false)
expect(node.match('32')).toBe(true)
expect(node.toString()).toBe('32')
node = parse('"32"')
@@ -41,12 +43,24 @@ describe('parse', () => {
})
})
describe('GlobPattern', () => {
it('matches a glob pattern recursively', () => {
expect(new GlobPattern('b*r').match({ foo: 'bar' })).toBe(true)
})
})
describe('Number', () => {
it('match a number recursively', () => {
expect(new NumberNode(3).match([{ foo: 3 }])).toBe(true)
})
})
describe('NumberOrStringNode', () => {
it('match a string', () => {
expect(new NumberOrStringNode('123').match([{ foo: '123' }])).toBe(true)
})
})
describe('setPropertyClause', () => {
it('creates a node if none passed', () => {
expect(setPropertyClause(undefined, 'foo', 'bar').toString()).toBe(
@@ -82,5 +96,5 @@ describe('setPropertyClause', () => {
})
it('toString', () => {
expect(pattern).toBe(ast.toString())
expect(ast.toString()).toBe(pattern)
})

View File

@@ -28,10 +28,10 @@
},
"dependencies": {},
"devDependencies": {
"@babel/cli": "7.0.0-beta.49",
"@babel/core": "7.0.0-beta.49",
"@babel/preset-env": "7.0.0-beta.49",
"@babel/preset-flow": "7.0.0-beta.49",
"@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,22 +26,21 @@
"node": ">=6"
},
"dependencies": {
"@xen-orchestra/fs": "^0.2.0",
"@xen-orchestra/fs": "^0.5.0",
"cli-progress": "^2.0.0",
"exec-promise": "^0.7.0",
"struct-fu": "^1.2.0",
"vhd-lib": "^0.3.0"
"vhd-lib": "^0.4.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.49",
"@babel/core": "^7.0.0-beta.49",
"@babel/plugin-transform-runtime": "^7.0.0-beta.49",
"@babel/preset-env": "^7.0.0-beta.49",
"@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",
"promise-toolbox": "^0.11.0",
"rimraf": "^2.6.1",
"tmp": "^0.0.33"
},

View File

@@ -5,7 +5,12 @@ import { resolve } from 'path'
export default async args => {
const vhd = new Vhd(getHandler({ url: 'file:///' }), resolve(args[0]))
await vhd.readHeaderAndFooter()
try {
await vhd.readHeaderAndFooter()
} catch (error) {
console.warn(error)
await vhd.readHeaderAndFooter(false)
}
console.log(vhd.header)
console.log(vhd.footer)

View File

@@ -3,7 +3,7 @@ import { mergeVhd } from 'vhd-lib'
import { getHandler } from '@xen-orchestra/fs'
import { resolve } from 'path'
export default async function main (args) {
export default async function main(args) {
if (args.length < 2 || args.some(_ => _ === '-h' || _ === '--help')) {
return `Usage: ${this.command} <child VHD> <parent VHD>`
}
@@ -11,10 +11,11 @@ export default async function main (args) {
const handler = getHandler({ url: 'file:///' })
let bar
await mergeVhd(handler, resolve(args[1]), handler, resolve(args[0]), {
onProgress ({ done, total }) {
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

@@ -3,19 +3,18 @@ import { createSyntheticStream } from 'vhd-lib'
import { createWriteStream } from 'fs'
import { getHandler } from '@xen-orchestra/fs'
export default async function main (args) {
export default async function main(args) {
if (args.length < 2 || args.some(_ => _ === '-h' || _ === '--help')) {
return `Usage: ${this.command} <input VHD> <output VHD>`
}
const handler = getHandler({ url: 'file:///' })
const stream = await createSyntheticStream(handler, path.resolve(args[0]))
return new Promise((resolve, reject) => {
createSyntheticStream(handler, path.resolve(args[0]))
.on('error', reject)
.pipe(
createWriteStream(args[1])
.on('error', reject)
.on('finish', resolve)
)
stream.on('error', reject).pipe(
createWriteStream(args[1])
.on('error', reject)
.on('finish', resolve)
)
})
}

View File

@@ -4,7 +4,7 @@ import execPromise from 'exec-promise'
import commands from './commands'
function runCommand (commands, [command, ...args]) {
function runCommand(commands, [command, ...args]) {
if (command === undefined || command === '-h' || command === '--help') {
command = 'help'
}

View File

@@ -3,7 +3,7 @@
import execa from 'execa'
import rimraf from 'rimraf'
import tmp from 'tmp'
import { fromCallback as pFromCallback } from 'promise-toolbox'
import { pFromCallback } from 'promise-toolbox'
import command from './commands/info'

View File

@@ -5,15 +5,13 @@ import fs from 'fs-extra'
import getStream from 'get-stream'
import rimraf from 'rimraf'
import tmp from 'tmp'
import { fromEvent, pFromCallback } from 'promise-toolbox'
import { getHandler } from '@xen-orchestra/fs'
import { randomBytes } from 'crypto'
import { fromEvent, fromCallback as pFromCallback } from 'promise-toolbox'
import chainVhd from './chain'
import createReadStream from './createSyntheticStream'
import Vhd from './vhd'
import vhdMerge from './merge'
import { SECTOR_SIZE } from './_constants'
import Vhd, { chainVhd, createSyntheticStream, mergeVhd as vhdMerge } from './'
import { SECTOR_SIZE } from './src/_constants'
const initialDir = process.cwd()
@@ -30,18 +28,18 @@ afterEach(async () => {
await pFromCallback(cb => rimraf(tmpDir, cb))
})
async function createRandomFile (name, sizeMb) {
async function createRandomFile(name, sizeMb) {
await execa('bash', [
'-c',
`< /dev/urandom tr -dc "\\t\\n [:alnum:]" | head -c ${sizeMb}M >${name}`,
])
}
async function checkFile (vhdName) {
async function checkFile(vhdName) {
await execa('vhd-util', ['check', '-p', '-b', '-t', '-n', vhdName])
}
async function recoverRawContent (vhdName, rawName, originalSize) {
async function recoverRawContent(vhdName, rawName, originalSize) {
await checkFile(vhdName)
await execa('qemu-img', ['convert', '-fvpc', '-Oraw', vhdName, rawName])
if (originalSize !== undefined) {
@@ -49,7 +47,7 @@ async function recoverRawContent (vhdName, rawName, originalSize) {
}
}
async function convertFromRawToVhd (rawName, vhdName) {
async function convertFromRawToVhd(rawName, vhdName) {
await execa('qemu-img', ['convert', '-f', 'raw', '-Ovpc', rawName, vhdName])
}
@@ -270,14 +268,18 @@ test('coalesce works in normal cases', async () => {
test('createSyntheticStream passes vhd-util check', async () => {
const initalSize = 4
const expectedVhdSize = 4197888
await createRandomFile('randomfile', initalSize)
await convertFromRawToVhd('randomfile', 'randomfile.vhd')
const handler = getHandler({ url: 'file://' + process.cwd() })
const stream = createReadStream(handler, 'randomfile.vhd')
const stream = await createSyntheticStream(handler, 'randomfile.vhd')
expect(stream.length).toEqual(expectedVhdSize)
await fromEvent(
stream.pipe(await fs.createWriteStream('recovered.vhd')),
'finish'
)
await checkFile('recovered.vhd')
const stats = await fs.stat('recovered.vhd')
expect(stats.size).toEqual(expectedVhdSize)
await execa('qemu-img', ['compare', 'recovered.vhd', 'randomfile'])
})

View File

@@ -1,6 +1,6 @@
{
"name": "vhd-lib",
"version": "0.3.0",
"version": "0.4.0",
"license": "AGPL-3.0",
"description": "Primitives for VHD file handling",
"keywords": [],
@@ -20,27 +20,26 @@
"node": ">=6"
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.49",
"async-iterator-to-stream": "^1.0.2",
"core-js": "3.0.0-beta.3",
"from2": "^2.3.0",
"fs-extra": "^6.0.1",
"fs-extra": "^7.0.0",
"limit-concurrency-decorator": "^0.4.0",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"struct-fu": "^1.2.0",
"uuid": "^3.0.1"
},
"devDependencies": {
"@babel/cli": "7.0.0-beta.49",
"@babel/core": "7.0.0-beta.49",
"@babel/plugin-transform-runtime": "^7.0.0-beta.49",
"@babel/preset-env": "7.0.0-beta.49",
"@babel/preset-flow": "7.0.0-beta.49",
"@xen-orchestra/fs": "^0.2.0",
"@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.5.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

@@ -1,13 +1,10 @@
import { SECTOR_SIZE } from './_constants'
export default function computeGeometryForSize (size) {
const totalSectors = Math.ceil(size / 512)
export default function computeGeometryForSize(size) {
const totalSectors = Math.min(Math.ceil(size / 512), 65535 * 16 * 255)
let sectorsPerTrackCylinder
let heads
let cylinderTimesHeads
if (totalSectors > 65535 * 16 * 255) {
throw Error('disk is too big')
}
// straight copypasta from the file spec appendix on CHS Calculation
if (totalSectors >= 65535 * 16 * 63) {
sectorsPerTrackCylinder = 255

View File

@@ -14,7 +14,7 @@ import {
PLATFORM_WI2K,
} from './_constants'
export function createFooter (
export function createFooter(
size,
timestamp,
geometry,
@@ -39,7 +39,7 @@ export function createFooter (
return footer
}
export function createHeader (
export function createHeader(
maxTableEntries,
tableOffset = HEADER_SIZE + FOOTER_SIZE,
blockSize = VHD_BLOCK_SIZE_BYTES

View File

@@ -95,7 +95,7 @@ export const unpackField = (field, buf) => {
// Returns the checksum of a raw struct.
// The raw struct (footer or header) is altered with the new sum.
export function checksumStruct (buf, struct) {
export function checksumStruct(buf, struct) {
const checksumField = struct.fields.checksum
let sum = 0

View File

@@ -3,7 +3,7 @@ import { dirname, relative } from 'path'
import Vhd from './vhd'
import { DISK_TYPE_DIFFERENCING } from './_constants'
export default async function chain (
export default async function chain(
parentHandler,
parentPath,
childHandler,

View File

@@ -2,7 +2,7 @@ import asyncIteratorToStream from 'async-iterator-to-stream'
import Vhd from './vhd'
export default asyncIteratorToStream(async function * (handler, path) {
export default asyncIteratorToStream(async function*(handler, path) {
const fd = await handler.openFile(path, 'r')
try {
const vhd = new Vhd(handler, fd)

View File

@@ -3,7 +3,7 @@ import asyncIteratorToStream from 'async-iterator-to-stream'
import computeGeometryForSize from './_computeGeometryForSize'
import { createFooter } from './_createFooterHeader'
export default asyncIteratorToStream(async function * (size, blockParser) {
export default asyncIteratorToStream(async function*(size, blockParser) {
const geometry = computeGeometryForSize(size)
const actualSize = geometry.actualSize
const footer = createFooter(
@@ -13,7 +13,7 @@ export default asyncIteratorToStream(async function * (size, blockParser) {
)
let position = 0
function * filePadding (paddingLength) {
function* filePadding(paddingLength) {
if (paddingLength > 0) {
const chunkSize = 1024 * 1024 // 1Mo
for (
@@ -33,10 +33,10 @@ export default asyncIteratorToStream(async function * (size, blockParser) {
if (paddingLength < 0) {
throw new Error('Received out of order blocks')
}
yield * filePadding(paddingLength)
yield* filePadding(paddingLength)
yield next.data
position = next.offsetBytes + next.data.length
}
yield * filePadding(actualSize - position)
yield* filePadding(actualSize - position)
yield footer
})

View File

@@ -17,9 +17,9 @@ import { set as setBitmap } from './_bitmap'
const VHD_BLOCK_SIZE_SECTORS = VHD_BLOCK_SIZE_BYTES / SECTOR_SIZE
/**
* @returns {Array} an array of occupation bitmap, each bit mapping an input block size of bytes
* @returns currentVhdPositionSector the first free sector after the data
*/
function createBAT (
function createBAT(
firstBlockPosition,
blockAddressList,
ratio,
@@ -36,9 +36,10 @@ function createBAT (
(bitmapSize + VHD_BLOCK_SIZE_BYTES) / SECTOR_SIZE
}
})
return currentVhdPositionSector
}
export default asyncIteratorToStream(async function * (
export default async function createReadableStream(
diskSize,
incomingBlockSize,
blockAddressList,
@@ -58,7 +59,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
@@ -79,9 +80,16 @@ export default asyncIteratorToStream(async function * (
const bitmapSize =
Math.ceil(VHD_BLOCK_SIZE_SECTORS / 8 / SECTOR_SIZE) * SECTOR_SIZE
const bat = Buffer.alloc(tablePhysicalSizeBytes, 0xff)
createBAT(firstBlockPosition, blockAddressList, ratio, bat, bitmapSize)
const endOfData = createBAT(
firstBlockPosition,
blockAddressList,
ratio,
bat,
bitmapSize
)
const fileSize = endOfData * SECTOR_SIZE + FOOTER_SIZE
let position = 0
function * yieldAndTrack (buffer, expectedPosition) {
function* yieldAndTrack(buffer, expectedPosition) {
if (expectedPosition !== undefined) {
assert.strictEqual(position, expectedPosition)
}
@@ -90,7 +98,7 @@ export default asyncIteratorToStream(async function * (
position += buffer.length
}
}
async function * generateFileContent (blockIterator, bitmapSize, ratio) {
async function* generateFileContent(blockIterator, bitmapSize, ratio) {
let currentBlock = -1
let currentVhdBlockIndex = -1
let currentBlockWithBitmap = Buffer.alloc(0)
@@ -100,7 +108,7 @@ export default asyncIteratorToStream(async function * (
const batIndex = Math.floor(next.offsetBytes / VHD_BLOCK_SIZE_BYTES)
if (batIndex !== currentVhdBlockIndex) {
if (currentVhdBlockIndex >= 0) {
yield * yieldAndTrack(
yield* yieldAndTrack(
currentBlockWithBitmap,
bat.readUInt32BE(currentVhdBlockIndex * 4) * SECTOR_SIZE
)
@@ -115,14 +123,21 @@ 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)
yield* yieldAndTrack(currentBlockWithBitmap)
}
yield * yieldAndTrack(footer, 0)
yield * yieldAndTrack(header, FOOTER_SIZE)
yield * yieldAndTrack(bat, FOOTER_SIZE + HEADER_SIZE)
yield * generateFileContent(blockIterator, bitmapSize, ratio)
yield * yieldAndTrack(footer)
})
async function* iterator() {
yield* yieldAndTrack(footer, 0)
yield* yieldAndTrack(header, FOOTER_SIZE)
yield* yieldAndTrack(bat, FOOTER_SIZE + HEADER_SIZE)
yield* generateFileContent(blockIterator, bitmapSize, ratio)
yield* yieldAndTrack(footer)
}
const stream = asyncIteratorToStream(iterator())
stream.length = fileSize
return stream
}

View File

@@ -15,18 +15,24 @@ import { test as mapTestBit } from './_bitmap'
const resolveRelativeFromFile = (file, path) =>
resolve('/', dirname(file), path).slice(1)
export default asyncIteratorToStream(function * (handler, path) {
export default async function createSyntheticStream(handler, path) {
const fds = []
const cleanup = () => {
for (let i = 0, n = fds.length; i < n; ++i) {
handler.closeFile(fds[i]).catch(error => {
console.warn('createReadStream, closeFd', i, error)
})
}
}
try {
const vhds = []
while (true) {
const fd = yield handler.openFile(path, 'r')
const fd = await handler.openFile(path, 'r')
fds.push(fd)
const vhd = new Vhd(handler, fd)
vhds.push(vhd)
yield vhd.readHeaderAndFooter()
yield vhd.readBlockAllocationTable()
await vhd.readHeaderAndFooter()
await vhd.readBlockAllocationTable()
if (vhd.footer.diskType === DISK_TYPE_DYNAMIC) {
break
@@ -64,14 +70,8 @@ export default asyncIteratorToStream(function * (handler, path) {
const nBlocks = Math.ceil(footer.currentSize / header.blockSize)
const blocksOwner = new Array(nBlocks)
for (
let iBlock = 0,
blockOffset = Math.ceil(
(header.tableOffset + bat.length) / SECTOR_SIZE
);
iBlock < nBlocks;
++iBlock
) {
let blockOffset = Math.ceil((header.tableOffset + bat.length) / SECTOR_SIZE)
for (let iBlock = 0; iBlock < nBlocks; ++iBlock) {
let blockSector = BLOCK_UNUSED
for (let i = 0; i < nVhds; ++i) {
if (vhds[i].containsBlock(iBlock)) {
@@ -83,71 +83,78 @@ export default asyncIteratorToStream(function * (handler, path) {
}
bat.writeUInt32BE(blockSector, iBlock * 4)
}
const fileSize = blockOffset * SECTOR_SIZE + FOOTER_SIZE
footer = fuFooter.pack(footer)
checksumStruct(footer, fuFooter)
yield footer
const iterator = function*() {
try {
footer = fuFooter.pack(footer)
checksumStruct(footer, fuFooter)
yield footer
header = fuHeader.pack(header)
checksumStruct(header, fuHeader)
yield header
header = fuHeader.pack(header)
checksumStruct(header, fuHeader)
yield header
yield bat
yield bat
// TODO: for generic usage the bitmap needs to be properly computed for each block
const bitmap = Buffer.alloc(vhd.bitmapSize, 0xff)
for (let iBlock = 0; iBlock < nBlocks; ++iBlock) {
const owner = blocksOwner[iBlock]
if (owner === undefined) {
continue
}
yield bitmap
const blocksByVhd = new Map()
const emitBlockSectors = function * (iVhd, i, n) {
const vhd = vhds[iVhd]
const isRootVhd = vhd === rootVhd
if (!vhd.containsBlock(iBlock)) {
if (isRootVhd) {
yield Buffer.alloc((n - i) * SECTOR_SIZE)
} else {
yield * emitBlockSectors(iVhd + 1, i, n)
// TODO: for generic usage the bitmap needs to be properly computed for each block
const bitmap = Buffer.alloc(vhd.bitmapSize, 0xff)
for (let iBlock = 0; iBlock < nBlocks; ++iBlock) {
const owner = blocksOwner[iBlock]
if (owner === undefined) {
continue
}
return
}
let block = blocksByVhd.get(vhd)
if (block === undefined) {
block = yield vhd._readBlock(iBlock)
blocksByVhd.set(vhd, block)
}
const { bitmap, data } = block
if (isRootVhd) {
yield data.slice(i * SECTOR_SIZE, n * SECTOR_SIZE)
return
}
while (i < n) {
const hasData = mapTestBit(bitmap, i)
const start = i
do {
++i
} while (i < n && mapTestBit(bitmap, i) === hasData)
if (hasData) {
yield data.slice(start * SECTOR_SIZE, i * SECTOR_SIZE)
} else {
yield * emitBlockSectors(iVhd + 1, start, i)
yield bitmap
const blocksByVhd = new Map()
const emitBlockSectors = function*(iVhd, i, n) {
const vhd = vhds[iVhd]
const isRootVhd = vhd === rootVhd
if (!vhd.containsBlock(iBlock)) {
if (isRootVhd) {
yield Buffer.alloc((n - i) * SECTOR_SIZE)
} else {
yield* emitBlockSectors(iVhd + 1, i, n)
}
return
}
let block = blocksByVhd.get(vhd)
if (block === undefined) {
block = yield vhd._readBlock(iBlock)
blocksByVhd.set(vhd, block)
}
const { bitmap, data } = block
if (isRootVhd) {
yield data.slice(i * SECTOR_SIZE, n * SECTOR_SIZE)
return
}
while (i < n) {
const hasData = mapTestBit(bitmap, i)
const start = i
do {
++i
} while (i < n && mapTestBit(bitmap, i) === hasData)
if (hasData) {
yield data.slice(start * SECTOR_SIZE, i * SECTOR_SIZE)
} else {
yield* emitBlockSectors(iVhd + 1, start, i)
}
}
}
yield* emitBlockSectors(owner, 0, sectorsPerBlockData)
}
yield footer
} finally {
cleanup()
}
yield * emitBlockSectors(owner, 0, sectorsPerBlockData)
}
yield footer
} finally {
for (let i = 0, n = fds.length; i < n; ++i) {
handler.closeFile(fds[i]).catch(error => {
console.warn('createReadStream, closeFd', i, error)
})
}
const stream = asyncIteratorToStream(iterator())
stream.length = fileSize
return stream
} catch (e) {
cleanup()
throw e
}
})
}

View File

@@ -1,3 +1,6 @@
// see https://github.com/babel/babel/issues/8450
import 'core-js/features/symbol/async-iterator'
export { default } from './vhd'
export { default as chainVhd } from './chain'
export { default as createContentStream } from './createContentStream'

View File

@@ -10,7 +10,7 @@ import { DISK_TYPE_DIFFERENCING, DISK_TYPE_DYNAMIC } from './_constants'
// Merge vhd child into vhd parent.
//
// TODO: rename the VHD file during the merge
export default concurrency(2)(async function merge (
export default concurrency(2)(async function merge(
parentHandler,
parentPath,
childHandler,
@@ -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

@@ -79,11 +79,11 @@ BUF_BLOCK_UNUSED.writeUInt32BE(BLOCK_UNUSED, 0)
// - sectorSize = 512
export default class Vhd {
get batSize () {
get batSize() {
return computeBatSize(this.header.maxTableEntries)
}
constructor (handler, path) {
constructor(handler, path) {
this._handler = handler
this._path = path
}
@@ -92,22 +92,22 @@ export default class Vhd {
// Read functions.
// =================================================================
async _read (start, n) {
async _read(start, n) {
const { bytesRead, buffer } = await this._handler.read(
this._path,
Buffer.alloc(n),
start
)
assert.equal(bytesRead, n)
assert.strictEqual(bytesRead, n)
return buffer
}
containsBlock (id) {
containsBlock(id) {
return this._getBatEntry(id) !== BLOCK_UNUSED
}
// Returns the first address after metadata. (In bytes)
getEndOfHeaders () {
getEndOfHeaders() {
const { header } = this
let end = FOOTER_SIZE + HEADER_SIZE
@@ -132,7 +132,7 @@ export default class Vhd {
}
// Returns the first sector after data.
getEndOfData () {
getEndOfData() {
let end = Math.ceil(this.getEndOfHeaders() / SECTOR_SIZE)
const fullBlockSize = this.sectorsOfBitmap + this.sectorsPerBlock
@@ -153,7 +153,7 @@ export default class Vhd {
// TODO: extract the checks into reusable functions:
// - better human reporting
// - auto repair if possible
async readHeaderAndFooter (checkSecondFooter = true) {
async readHeaderAndFooter(checkSecondFooter = true) {
const buf = await this._read(0, FOOTER_SIZE + HEADER_SIZE)
const bufFooter = buf.slice(0, FOOTER_SIZE)
const bufHeader = buf.slice(FOOTER_SIZE)
@@ -206,7 +206,7 @@ export default class Vhd {
}
// Returns a buffer that contains the block allocation table of a vhd file.
async readBlockAllocationTable () {
async readBlockAllocationTable() {
const { header } = this
this.blockTable = await this._read(
header.tableOffset,
@@ -215,11 +215,11 @@ export default class Vhd {
}
// return the first sector (bitmap) of a block
_getBatEntry (block) {
_getBatEntry(block) {
return this.blockTable.readUInt32BE(block * 4)
}
_readBlock (blockId, onlyBitmap = false) {
_readBlock(blockId, onlyBitmap = false) {
const blockAddr = this._getBatEntry(blockId)
if (blockAddr === BLOCK_UNUSED) {
throw new Error(`no such block ${blockId}`)
@@ -228,23 +228,22 @@ export default class Vhd {
return this._read(
sectorsToBytes(blockAddr),
onlyBitmap ? this.bitmapSize : this.fullBlockSize
).then(
buf =>
onlyBitmap
? { id: blockId, bitmap: buf }
: {
id: blockId,
bitmap: buf.slice(0, this.bitmapSize),
data: buf.slice(this.bitmapSize),
buffer: buf,
}
).then(buf =>
onlyBitmap
? { id: blockId, bitmap: buf }
: {
id: blockId,
bitmap: buf.slice(0, this.bitmapSize),
data: buf.slice(this.bitmapSize),
buffer: buf,
}
)
}
// get the identifiers and first sectors of the first and last block
// in the file
//
_getFirstAndLastBlocks () {
_getFirstAndLastBlocks() {
const n = this.header.maxTableEntries
const bat = this.blockTable
let i = 0
@@ -289,7 +288,7 @@ export default class Vhd {
// =================================================================
// Write a buffer/stream at a given position in a vhd file.
async _write (data, offset) {
async _write(data, offset) {
debug(
`_write offset=${offset} size=${
Buffer.isBuffer(data) ? data.length : '???'
@@ -308,7 +307,7 @@ export default class Vhd {
: fromEvent(data.pipe(stream), 'finish')
}
async _freeFirstBlockSpace (spaceNeededBytes) {
async _freeFirstBlockSpace(spaceNeededBytes) {
try {
const { first, firstSector, lastSector } = this._getFirstAndLastBlocks()
const tableOffset = this.header.tableOffset
@@ -348,7 +347,7 @@ export default class Vhd {
}
}
async ensureBatSize (entries) {
async ensureBatSize(entries) {
const { header } = this
const prevMaxTableEntries = header.maxTableEntries
if (prevMaxTableEntries >= entries) {
@@ -373,7 +372,7 @@ export default class Vhd {
}
// set the first sector (bitmap) of a block
_setBatEntry (block, blockSector) {
_setBatEntry(block, blockSector) {
const i = block * 4
const { blockTable } = this
@@ -384,7 +383,7 @@ export default class Vhd {
// Make a new empty block at vhd end.
// Update block allocation table in context and in file.
async createBlock (blockId) {
async createBlock(blockId) {
const blockAddr = Math.ceil(this.getEndOfData() / SECTOR_SIZE)
debug(`create block ${blockId} at ${blockAddr}`)
@@ -403,7 +402,7 @@ export default class Vhd {
}
// Write a bitmap at a block address.
async writeBlockBitmap (blockAddr, bitmap) {
async writeBlockBitmap(blockAddr, bitmap) {
const { bitmapSize } = this
if (bitmap.length !== bitmapSize) {
@@ -420,7 +419,7 @@ export default class Vhd {
await this._write(bitmap, sectorsToBytes(blockAddr))
}
async writeEntireBlock (block) {
async writeEntireBlock(block) {
let blockAddr = this._getBatEntry(block.id)
if (blockAddr === BLOCK_UNUSED) {
@@ -429,7 +428,7 @@ export default class Vhd {
await this._write(block.buffer, sectorsToBytes(blockAddr))
}
async writeBlockSectors (block, beginSectorId, endSectorId, parentBitmap) {
async writeBlockSectors(block, beginSectorId, endSectorId, parentBitmap) {
let blockAddr = this._getBatEntry(block.id)
if (blockAddr === BLOCK_UNUSED) {
@@ -461,7 +460,7 @@ export default class Vhd {
)
}
async coalesceBlock (child, blockId) {
async coalesceBlock(child, blockId) {
const block = await child._readBlock(blockId)
const { bitmap, data } = block
@@ -503,7 +502,7 @@ export default class Vhd {
}
// Write a context footer. (At the end and beginning of a vhd file.)
async writeFooter (onlyEndFooter = false) {
async writeFooter(onlyEndFooter = false) {
const { footer } = this
const rawFooter = fuFooter.pack(footer)
@@ -523,7 +522,7 @@ export default class Vhd {
await this._write(rawFooter, offset)
}
writeHeader () {
writeHeader() {
const { header } = this
const rawHeader = fuHeader.pack(header)
header.checksum = checksumStruct(rawHeader, fuHeader)
@@ -536,7 +535,7 @@ export default class Vhd {
return this._write(rawHeader, offset)
}
async writeData (offsetSectors, buffer) {
async writeData(offsetSectors, buffer) {
const bufferSizeSectors = Math.ceil(buffer.length / SECTOR_SIZE)
const startBlock = Math.floor(offsetSectors / this.sectorsPerBlock)
const endBufferSectors = offsetSectors + bufferSizeSectors
@@ -589,7 +588,7 @@ export default class Vhd {
await this.writeFooter()
}
async ensureSpaceForParentLocators (neededSectors) {
async ensureSpaceForParentLocators(neededSectors) {
const firstLocatorOffset = FOOTER_SIZE + HEADER_SIZE
const currentSpace =
Math.floor(this.header.tableOffset / SECTOR_SIZE) -
@@ -603,7 +602,7 @@ export default class Vhd {
return firstLocatorOffset
}
async setUniqueParentLocator (fileNameString) {
async setUniqueParentLocator(fileNameString) {
const { header } = this
header.parentLocatorEntry[0].platformCode = PLATFORM_W2KU
const encodedFilename = Buffer.from(fileNameString, 'utf16le')

View File

@@ -3,11 +3,11 @@ import execa from 'execa'
import rimraf from 'rimraf'
import tmp from 'tmp'
import { createWriteStream, readFile } from 'fs-promise'
import { fromCallback as pFromCallback, fromEvent } from 'promise-toolbox'
import { fromEvent, pFromCallback } from 'promise-toolbox'
import { createFooter } from './_createFooterHeader'
import createReadableRawVHDStream from './createReadableRawStream'
import createReadableSparseVHDStream from './createReadableSparseStream'
import { createReadableRawStream, createReadableSparseStream } from './'
import { createFooter } from './src/_createFooterHeader'
const initialDir = process.cwd()
@@ -54,7 +54,7 @@ test('ReadableRawVHDStream does not crash', async () => {
},
}
const fileSize = 1000
const stream = createReadableRawVHDStream(fileSize, mockParser)
const stream = createReadableRawStream(fileSize, mockParser)
const pipe = stream.pipe(createWriteStream('output.vhd'))
await fromEvent(pipe, 'finish')
await execa('vhd-util', ['check', '-t', '-i', '-n', 'output.vhd'])
@@ -85,7 +85,7 @@ test('ReadableRawVHDStream detects when blocks are out of order', async () => {
}
return expect(
new Promise((resolve, reject) => {
const stream = createReadableRawVHDStream(100000, mockParser)
const stream = createReadableRawStream(100000, mockParser)
stream.on('error', reject)
const pipe = stream.pipe(createWriteStream('outputStream'))
pipe.on('finish', resolve)
@@ -107,12 +107,13 @@ test('ReadableSparseVHDStream can handle a sparse file', async () => {
},
]
const fileSize = blockSize * 110
const stream = createReadableSparseVHDStream(
const stream = await createReadableSparseStream(
fileSize,
blockSize,
blocks.map(b => b.offsetBytes),
blocks
)
expect(stream.length).toEqual(4197888)
const pipe = stream.pipe(createWriteStream('output.vhd'))
await fromEvent(pipe, 'finish')
await execa('vhd-util', ['check', '-t', '-i', '-n', 'output.vhd'])

View File

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

View File

@@ -0,0 +1,24 @@
/benchmark/
/benchmarks/
*.bench.js
*.bench.js.map
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/fixture/
/fixtures/
*.fixture.js
*.fixture.js.map
*.fixtures.js
*.fixtures.js.map
/test/
/tests/
*.spec.js
*.spec.js.map
__snapshots__/

Some files were not shown because too many files have changed in this diff Show More