Compare commits

...

810 Commits

Author SHA1 Message Date
Julien Fontanet
4bd7a76d47 feat(xen-api/Record#resolve_): resolve props named as types 2021-04-07 16:00:35 +02:00
Julien Fontanet
3c5d73224a feat(normalize-packages): delete empty dependencies, description and keywords fields 2021-04-07 15:14:48 +02:00
Julien Fontanet
05f9c07836 chore: add descriptions to all pkgs (but mixin) 2021-04-07 15:13:18 +02:00
Julien Fontanet
a7ba6add39 feat(read-chunk): ensure function is properly named 2021-04-07 14:02:50 +02:00
Julien Fontanet
479973bf06 fix(xapi): add missing cli.js
Introduced by be9b5332d9
2021-04-07 14:00:40 +02:00
Julien Fontanet
854c9fe794 fix(.gitignore): handle @vates/* pkgs 2021-04-07 13:59:07 +02:00
Julien Fontanet
5a17c75fe4 feat: unified .npmignore for all packages
Ensure sources, tests and USAGE.Md files will not be published.
2021-04-07 13:58:14 +02:00
Julien Fontanet
4dc5eff252 chore(normalize-packages): remove unused require 2021-04-07 13:39:12 +02:00
Julien Fontanet
7fe0d78154 fix(compose/USAGE): typo 2021-04-07 13:36:24 +02:00
Julien Fontanet
2c709dc205 fix(xo-server/proxies): readChunk import
Introduced by 538253cdc
2021-04-07 13:09:29 +02:00
Julien Fontanet
9353349a39 chore(vhd-lib): use @vates/read-chunk 2021-04-07 13:08:44 +02:00
Julien Fontanet
d3049b2bfa feat(@vates/read-chunk): 0.1.2 2021-04-07 13:07:57 +02:00
Julien Fontanet
61cb2529bd feat(read-chunk/package.json): only publish index.js 2021-04-07 13:05:49 +02:00
Julien Fontanet
e6c6e4395f chore(read-chunk): add tests 2021-04-07 13:03:56 +02:00
Julien Fontanet
959c955616 feat(read-chunk): handle 0 size 2021-04-07 12:51:27 +02:00
Julien Fontanet
538253cdc1 chore(xo-server/proxies): use @vates/read-chunk 2021-04-07 12:31:16 +02:00
Julien Fontanet
b4c6594333 chore(xo-server/utils): remove unused wrap 2021-04-07 12:25:47 +02:00
Julien Fontanet
a7f5f8889c chore(xo-server/http): use @xen-orchestra/defined 2021-04-07 12:15:33 +02:00
Julien Fontanet
1c9b4cf552 feat(xo-server/Xo): pass appDir, AppName and httpServer
As done in xo-proxy.

This may allow sharing mixins in the future.
2021-04-07 10:41:50 +02:00
Julien Fontanet
ce09f487bd feat(xo-server/XenServers): dont auto connect on safe mode 2021-04-07 10:41:50 +02:00
Julien Fontanet
a5d1decf40 feat(xo-server/Xo): safeMode param 2021-04-07 10:41:50 +02:00
Julien Fontanet
7024c7d598 chore(xo-server/Xo): pass config as named param
As done in xo-proxy.
2021-04-07 10:41:50 +02:00
Jon Sands
8109253eeb fix(xo-web/en): grammar fixes (#5713) 2021-04-06 10:31:52 +02:00
Julien Fontanet
b61f1e3803 fix(xapi/VM_snapshot): VM#refresh_snapshots does not exist
Fixes xoa-support#3587
2021-04-04 22:41:46 +02:00
Julien Fontanet
db40f80be7 chore(CHANGELOG.unreleased): formatting 2021-04-04 22:36:35 +02:00
Mathieu
26eaf97032 fix(xo-web/restore): generateNewMACAddresses disabled by default (#5707) 2021-04-04 18:41:37 +02:00
Ronan Abhamon
da349374bf feat(load-balancer): add option to disable migration (#5706) 2021-04-02 17:32:26 +02:00
Julien Fontanet
0ffa925fee chore(xo-server-load-balancer): format with Prettier 2021-04-02 15:34:38 +02:00
Julien Fontanet
082787c4cf chore: update dependencies 2021-04-02 15:34:19 +02:00
Julien Fontanet
be9b5332d9 feat(xapi): add xo-xapi CLI
Allows to easily test @xen-orchestra/xapi features.
2021-04-01 14:47:04 +02:00
Julien Fontanet
97ae3ba7d3 feat(@xen-orchestra/proxy): 0.12.0 2021-04-01 14:09:09 +02:00
Julien Fontanet
d047f401c2 fix(CHANGELOG): move Highlights title under badge
Introduced by 1e9e78223
2021-04-01 14:08:12 +02:00
Julien Fontanet
1e9e78223b fix(CHANGELOG): missing Highlights title 2021-04-01 13:44:21 +02:00
Julien Fontanet
6d5baebd08 feat: release 5.57.0 2021-04-01 13:29:58 +02:00
Pierre Donias
4e758dbb85 feat: technical release (#5705) 2021-04-01 11:28:09 +02:00
Julien Fontanet
40d943c620 feat(backups/CR writer): delete interrupted copies in prepare() 2021-04-01 10:52:06 +02:00
Julien Fontanet
e69b6c4dc8 fix(backups/delta writers): compute old entries before run 2021-04-01 10:48:39 +02:00
Julien Fontanet
23444f7083 fix(xo-server): Backup/getConnectedRecord receive XAPI not XO type 2021-03-31 16:48:20 +02:00
Julien Fontanet
8c077b96df chore(xo-server/getXapiObject): clarify that second param is an XO type 2021-03-31 16:31:56 +02:00
Pierre Donias
4b1a055a88 feat: technical release (#5704) 2021-03-31 11:15:41 +02:00
Rajaa.BARHTAOUI
b4ddcc1dec feat(xo-server,xo-web/startVm): avoid booting VM if there's an identical MAC address elsewhere (#5655)
Fixes #5601
2021-03-31 10:50:32 +02:00
badrAZ
271d2e3abc feat(fs): expose highWaterMark stream option (#5676) 2021-03-31 10:34:42 +02:00
Julien Fontanet
37b6399398 fix(backups/importDeltaVm): reverse newMacAddresses condition for VIF creation
Introduced by 4df8c9610
2021-03-30 21:26:08 +02:00
Julien Fontanet
ebf19b1506 fix(proxy/backup.importVmBackup): settings param is optional
Introduced by b475b265a
2021-03-30 21:26:08 +02:00
Julien Fontanet
e4dd773644 fix(proxy/api/ndJsonStream): dont fail if one message cannot be JSONified 2021-03-30 21:26:08 +02:00
Mathieu
f9b3a1f293 feat(xo-web/restore): support new mac addresses (#5697) 2021-03-30 17:48:45 +02:00
Rajaa.BARHTAOUI
7c9850ada8 feat(xo-server-perf-alert): ability to choose all hosts, VMs and SRs (#5692)
Fixes #2987
2021-03-30 17:26:48 +02:00
Ronan Abhamon
9ef05b8afe feat(load-balancer): add new anti-affinity mode (#5652)
Fixes #5600
2021-03-30 17:25:41 +02:00
Mathieu
efdd196441 feat(xo-web/proxies): move proxy actions to dropdown (#5688) 2021-03-30 16:56:10 +02:00
Julien Fontanet
6e780a3876 fix(xapi/call{,Async}): fix call to retry
Introduced by 3bb7d2c29
2021-03-30 15:42:05 +02:00
Julien Fontanet
b475b265ae feat(import VM backup): newMacAddresses setting
Related to #5697
2021-03-30 14:31:08 +02:00
Julien Fontanet
3bb7d2c294 feat(xapi/call{,Async}): retry if too many pending tasks
Logic from xo-server/xapi/call
2021-03-30 14:21:19 +02:00
Julien Fontanet
594a148a39 feat(xo-server): VDI_destroy instead of deleteVdi 2021-03-30 14:08:25 +02:00
Julien Fontanet
779591db36 feat(xapi/VDI_destroy): dont fail if VDI not found
Aligned with xo-server/xapi/deleteVdi.
2021-03-30 13:59:10 +02:00
Julien Fontanet
c002eeffb7 chore(proxy): remove unused Node test
The proxy now requires Node >=12
2021-03-30 09:23:15 +02:00
Julien Fontanet
1dac973d70 feat(backups/Task.wrapFn): compatibility with @decorateWith 2021-03-30 09:20:30 +02:00
Julien Fontanet
f5024f0e75 feat(backups/delta writers): split run method in prepare/transfer/cleanup
Fixes xoa-support#3523

This avoids starting the transfer before the writers are ready, which caused it to failed with `deleteFirst` when deletion was so long that the transfer stalled.
2021-03-30 09:20:30 +02:00
Pierre Donias
cf320c08c5 feat: technical release (#5702) 2021-03-29 16:45:00 +02:00
Rajaa.BARHTAOUI
8973c9550c fix(xo-web/vm/advanced): remove noop (#5700)
See https://github.com/vatesfr/xen-orchestra/pull/3774/files#r602860699
2021-03-29 15:10:48 +02:00
Julien Fontanet
bb671f0e93 feat(xapi): update to xo-common@0.7.0 2021-03-29 15:01:12 +02:00
Julien Fontanet
a8774b5011 chore(log/transport/console): remove unused code 2021-03-29 14:59:14 +02:00
Julien Fontanet
f092cd41bc chore: named import from @xen-orchestra/log 2021-03-29 14:06:29 +02:00
Julien Fontanet
b17ec9731a fix(xapi/task_create): dont use super
Introduced by 021810201b

It cannot be used due to our mixin architecture.
2021-03-28 23:29:17 +02:00
Julien Fontanet
021810201b fix(xapi/task_create): remove duplicate [XO] prefix
Found while investigating xoa-support#3553
2021-03-28 14:30:36 +02:00
Julien Fontanet
6038dc9c8a fix(xo-web/vm/advanced): RTL819 → RTL8139
Fixes #5698
2021-03-28 12:23:52 +02:00
Julien Fontanet
4df8c9610a fix(backups/importDeltaVm): new mac_seed when using newMacAddresses 2021-03-26 17:14:37 +01:00
Julien Fontanet
6c12dd4f16 feat(xapi/VM_create): generateMacSeed option 2021-03-26 17:14:37 +01:00
badrAZ
ad3b8fa59f feat(xo-server-usage-report): add VM IP addresses to the report (#5696) 2021-03-26 14:39:35 +01:00
Mathieu
cb52a8b51b feat(xo-server): VM_destroy instead of deleteVm (#5693)
Continuation of 5f1c127
2021-03-26 14:23:04 +01:00
badrAZ
22ba1302d2 fix(xo-server-backup-reports): support failed targets (#5694)
Introduced by d282d8dd5
2021-03-26 10:27:08 +01:00
Mathieu
7d04559921 fix(xo-web/disk import): an error has occurred (#5683)
Fixes #5663

undefined files in collection due to unhandled disk formats
2021-03-26 09:37:31 +01:00
Julien Fontanet
e40e35d30c fix(xo-server): dont provide mac_send when importing OVA VM
Fixes xoa-support#3544
2021-03-25 15:51:02 +01:00
Julien Fontanet
d1af9f236c feat(backup/importDeltaVm): restoreMacAddresses option 2021-03-24 16:39:08 +01:00
Julien Fontanet
45a0ff26c5 feat(xapi/VIF_create): MAC must be passed explicitely 2021-03-24 16:38:19 +01:00
Julien Fontanet
1fd330d7a4 fix(CHANGELOG.unreleased): @vates/disposable 2021-03-24 16:03:49 +01:00
Julien Fontanet
09833f31cf fix(xapi/VM_export): correctly destroy snapshot after export 2021-03-24 16:00:45 +01:00
Julien Fontanet
20e7a036cf feat(xapi): no longer use promise-toolbox/cancelable 2021-03-24 15:34:47 +01:00
Julien Fontanet
e6667c1782 fix(xapi/VM_destroy): only update blocked operations when bypassBlockedOperation 2021-03-24 15:13:09 +01:00
Julien Fontanet
657935eba5 feat(xapi/VM_destroy): split bypassBlockedOperation out of force 2021-03-24 15:12:25 +01:00
Julien Fontanet
67b905a757 feat(xapi/VM_destroy): forceDeleteDefaultTemplate default to force 2021-03-24 15:11:49 +01:00
Julien Fontanet
55cede0434 fix(xapi/VM_destroy): ignore missing VM.set_is_default_template
Introduced by 5f1c1278e
2021-03-24 15:09:09 +01:00
Julien Fontanet
c7677d6d1e fix(xapi/VM_destroy): only update default template when forceDeleteDefaultTemplate 2021-03-24 15:07:30 +01:00
Mathieu
d191ca54ad feat(xo-server/xapi-object-to-xo): display full driver version (#5691) 2021-03-24 13:38:49 +01:00
badrAZ
20f4c952fe feat(@xen-orchestra/backups#RemoteAdapter): ability to clean broken backups (#5684) 2021-03-24 10:00:47 +01:00
Julien Fontanet
0bd09896f3 feat(docs/xoa): network config for other interfaces 2021-03-24 09:49:59 +01:00
badrAZ
60ecfbfb8e feat(xo-server, proxy, @xen-orchestra/backups): execute backup jobs on different processes (#5660) 2021-03-24 09:25:52 +01:00
Julien Fontanet
8921d78610 fix(disposable/deduped): call dispose with disposable context 2021-03-24 00:27:25 +01:00
Julien Fontanet
b243ff94e9 feat(fs/lock): returns a disposable 2021-03-23 23:04:49 +01:00
Mathieu
5f1c1278e3 fix(xapi/VM_destroy): handle is_default_template (#5644) 2021-03-23 17:28:18 +01:00
badrAZ
fa56e594b1 feat(xo-server-transport-email): ability to customize transport settings (#5681)
See xoa-support#3327

This functionnality can help users to get more info about their SMTP issues
2021-03-23 15:47:31 +01:00
badrAZ
c9b64927be feat(@xen-orchestra/fs): ability to lock a path (#5689)
Related to #5684
2021-03-23 13:00:15 +01:00
Julien Fontanet
3689cb2a99 chore(fs/outputStream): dont normalize path twice 2021-03-23 12:53:17 +01:00
Julien Fontanet
3bb7541361 fix(fs/local): opts is optional 2021-03-23 12:53:17 +01:00
Julien Fontanet
7b15aa5f83 fix(package.json/jest.moduleNameMapper): dont map sub-modules
For instance `@xen-orchestra/async-map/legacy`.
2021-03-23 12:53:17 +01:00
Julien Fontanet
690d3036db chore(xo-server): use native Promise#finally() (#5687) 2021-03-22 17:25:40 +01:00
Julien Fontanet
416e8d02a1 chore(xo-server): usingDisposable.use (#5686)
`using` is deprecated.
2021-03-22 17:25:28 +01:00
Julien Fontanet
a968c2d2b7 chore(xo-server): remove promise-toolbox/all uses (#5685) 2021-03-22 15:58:13 +01:00
Julien Fontanet
b4787bf444 fix(xo-server/(re)deployProxy): works whith missing bound VM 2021-03-22 14:44:39 +01:00
Julien Fontanet
a4d90e8aff fix(xo-web/proxies): ignore running jobs on force upgrade
See #3527
2021-03-22 14:37:34 +01:00
Julien Fontanet
32d0606ee4 feat(@xen-orchestra/proxy): 0.11.6 2021-03-22 13:41:28 +01:00
Julien Fontanet
4541f7c758 chore(xo-server): remove pSettle (#5682) 2021-03-22 10:37:07 +01:00
Julien Fontanet
65428d629c feat: release 5.56.2 2021-03-22 10:36:24 +01:00
Julien Fontanet
bdfd9cc617 feat: technical release 2021-03-22 09:56:31 +01:00
Julien Fontanet
6d324921a0 fix(CHANGELOG.unreleased): add xapi to packages to release
Introduced by dcf0f5c5a
2021-03-19 14:53:29 +01:00
Julien Fontanet
dcf0f5c5a3 fix(xapi/VM_create): ignore missing VM.set_bios_strings
Fixes xoa-support#3516
2021-03-19 14:51:51 +01:00
Julien Fontanet
d98f851a2c fix(xo-web/restore): dont break if log is missing result
Found when investigating xoa-support#3516
2021-03-19 13:46:22 +01:00
Julien Fontanet
a95b102396 fix(backups/ImportVmBackup#run): dont swallow errors
Found when investigating xoa-support#3516
2021-03-19 13:38:17 +01:00
Julien Fontanet
7e2fbbaae6 fix(xo-server/createAuthenticationToken): parse expiresIn param
Introduced by 92cf6bb887
2021-03-19 13:02:19 +01:00
Julien Fontanet
070e8b0b54 fix(docs): restore full_backups.md
Introduced by 078f40281
2021-03-19 11:04:42 +01:00
Yannick Achy
7b49a1296c feat(doc): various changes (#5679) 2021-03-19 11:02:24 +01:00
Julien Fontanet
1e278bde92 feat(xo-common/Error.is): apply predicate only on data
See #5644
2021-03-19 10:30:35 +01:00
Yannick Achy
078f402819 feat(doc): check dead links and duplicate (#5664) 2021-03-18 09:35:07 +01:00
Mathieu
52af565f77 fix(fs/localHandler): resource temporarily unavailable (#5612)
Fixes xoa-support#3414
2021-03-17 15:28:04 +01:00
Julien Fontanet
853905e52f feat(xen-api): automatically retry ro calls on ECONNRESET (#5674)
See xoa-support#3266
2021-03-16 17:31:18 +01:00
Julien Fontanet
2e0e1d2aac fix(xo-server/xen-servers): fix autoconnection
Fixes #5675

Introduced by 2fbfc97cca
2021-03-16 13:59:01 +01:00
Julien Fontanet
7f33a62bb5 fix(async-map/asyncMap): fix typing of first param 2021-03-16 11:32:39 +01:00
Julien Fontanet
bdb59ea429 fix(Travis CI): use Node 14 2021-03-15 18:26:22 +01:00
Julien Fontanet
1c0ffe39f7 fix(xapi): merge vdiDestroyRetryWhenInUse after defaults 2021-03-15 13:22:41 +01:00
Julien Fontanet
2fbfc97cca fix(xo-server): connect servers after config import (#5672)
Fixes #5670
2021-03-15 11:49:41 +01:00
Pierre Donias
482299e765 fix(xo-web/pool): disconnectServer → disableServer (#5671)
Fixes #5669
Introduced c7aaeca530
2021-03-15 09:53:34 +01:00
Julien Fontanet
54f4734847 chore: remove babel-plugin-lodash where unnecessary 2021-03-13 20:47:20 +01:00
Julien Fontanet
0fb6cef577 chore(defined): remove Flow 2021-03-13 20:47:20 +01:00
badrAZ
7eec264961 chore(@xen-orchestra/backups): fix logger names (#5666) 2021-03-12 13:53:43 +01:00
Julien Fontanet
aff874c68a chore(xo-server,xo-server-load-balancer): phase out mapToArray (#5662)
Use native `Array#map` or `Object.values` where possible and import directly from `lodash`.

Reasons:
- less dependencies
- more idiomatic
- better example for new code
2021-03-11 15:17:28 +01:00
Julien Fontanet
27abee0850 chore(xo-server-load-balancer): typo ressource → resource 2021-03-11 14:17:02 +01:00
Julien Fontanet
bcfb19f7c5 feat(normalize-packages): delete empty bin field 2021-03-11 12:15:53 +01:00
Julien Fontanet
306a8ce0df feat(@xen-orchestra/proxy): 0.11.5 2021-03-10 13:48:15 +01:00
Julien Fontanet
d9ea8d2c9c feat: release 5.56.1 2021-03-10 13:04:11 +01:00
Julien Fontanet
b479956bb2 feat: technical release (#5657) 2021-03-10 11:11:27 +01:00
Julien Fontanet
b32dc0e450 fix(xen-api/call): allow *.get_all_records in read only 2021-03-10 09:41:49 +01:00
badrAZ
5cca5d69af fix(@xen-orchestra/backups/Backup.js): fix rolling snapshot not ran if alone (#5656) 2021-03-09 18:24:58 +01:00
badrAZ
e0e89213d3 chore(xo-server): delete unused workers mixin (#5654)
Due to 0811da9014
2021-03-09 15:26:21 +01:00
Julien Fontanet
e246c19eb3 feat(xen-api/Ref): introduce new utils to manipulate refs (#5650)
Fixes xoa-support#3463

See xapi-project/xen-api#4338
2021-03-09 14:59:32 +01:00
badrAZ
d282d8dd52 fix(@xen-orchestra/backups): missing targets no longer prevent runs (#5651)
Fixes #5353
2021-03-09 14:37:52 +01:00
badrAZ
9601ad13ee fix(xo-web/proxies): fix "invalid parameters" error on canceling proxy deploy (#5649)
Issue: fetchProxyUpgrades called with an undefined proxy in the proxies collection.

Solution: Interrupt the deployment process on cancel, in order to not fetch updates in this case.
2021-03-09 10:37:59 +01:00
badrAZ
b7603e109d feat(xo-web/backup/new): ability to force full backup per schedule in case of CR (#5648)
Fixes #5541
2021-03-09 09:38:56 +01:00
Julien Fontanet
066f54906b chore: format with Prettier 2021-03-08 17:41:10 +01:00
Julien Fontanet
ea0aa9df70 chore(xen-api): disable problematic ESLint rules on specific lines 2021-03-08 14:23:57 +01:00
badrAZ
0811da9014 feat(xo-server): use @xen-orchestra/backups lib to run VM backups (#5642) 2021-03-08 14:05:41 +01:00
Julien Fontanet
d601290c46 fix(destroy VM): try harder to destroy VDIs (#5645)
Should fix #4926

Work-around XCP-ng/XenServer unmount from control-domain delay, especially with iSCSI SRs.

This issue impacts a lot XO backups which create snapshots, export them and delete them.
2021-03-08 09:46:51 +01:00
badrAZ
64357aff55 fix(@xen-orchestra/xapi): fix full VM backup imported as a template (#5646) 2021-03-05 17:41:50 +01:00
badrAZ
a20a3311b5 fix(xo-server/backup-ng): fix "xapi._assertHealthyVdiChains is not a function" error (#5647)
Introduced by 4c27562650
2021-03-05 17:33:24 +01:00
Julien Fontanet
ffce5d4bb5 chore(@xen-orchestra/xapi): make xen-api a peer dep 2021-03-05 16:17:02 +01:00
badrAZ
cbfadc019a fix(@xen-orchestra/backups): fix "asyncMapSettled is not a function" error (#5643) 2021-03-05 12:23:25 +01:00
Julien Fontanet
bf5427f3e8 feat(@xen-orchestra/proxy): 0.11.4 2021-03-05 11:39:06 +01:00
badrAZ
4c27562650 fix(xo-server/xapi): dont override @xen-orchestra/xapi#_assertHealthyVdiChain (#5641) 2021-03-05 11:38:49 +01:00
badrAZ
e8d20532ba feat(xo-server): use @xen-orchestra/backups lib to run metadata backups (#5616) 2021-03-05 10:51:00 +01:00
Mathieu
d928157569 fix(xo-web/vm/tab-network): an error has occurred when trying to sort empty network (#5639)
This issue happens when you have an ACL role on one VM, but you don't have an ACL role on the network of this VM.
2021-03-05 09:37:40 +01:00
Mathieu
872b05a7de feat(xo-server/VIF): set MAC address requires Admin ACL on network (#5631)
Fixes #4700
2021-03-05 09:27:22 +01:00
Julien Fontanet
6ea71ec6a2 chore(xapi/VM_destroy): add new lines 2021-03-04 16:11:08 +01:00
Julien Fontanet
139cb72209 chore(xapi/VM_destroy): use VM_getDisks 2021-03-04 16:11:08 +01:00
Julien Fontanet
855a15e696 fix(xapi/VM_getDisks): sync iteration 2021-03-04 16:11:08 +01:00
Mathieu
eeebd3fc1b fix(xo-web/DropdownButton): add required id prop (#5628)
See https://react-bootstrap.netlify.app/components/dropdowns/#dropdown-button-props
2021-03-04 15:56:01 +01:00
Julien Fontanet
a4b209c654 fix(disposable/deduped): race condition when disposed during acquisition
Introduced in 43aad3d11
2021-03-04 10:46:03 +01:00
Julien Fontanet
43aad3d117 feat(disposable/deduped): works with sync factories 2021-03-03 17:43:39 +01:00
Mathieu
f2d4fdd4d2 fix(xo-web/editable/number): throw error if onChange fails (#5634) 2021-03-03 16:33:41 +01:00
Julien Fontanet
a630106d80 feat(@xen-orchestra/backups): 0.6.1 2021-03-03 12:05:33 +01:00
Julien Fontanet
c7acd455c5 feat(@xen-orchestra/async-map): 0.1.2 2021-03-03 12:05:05 +01:00
Julien Fontanet
555a9d4883 fix(async-map/asyncMapSettled): issue when latest promise rejects 2021-03-03 11:58:51 +01:00
Julien Fontanet
ec4ce0c70c fix(async-map/test): missing await 2021-03-03 11:57:51 +01:00
Julien Fontanet
edf275badc fix(backups/RemoteAdapter#readDeltaVmBackup): new asyncMapSettled does not support plain objects
Introduced by 20377e9c56
2021-03-03 11:49:21 +01:00
Julien Fontanet
2e91285f02 feat(xo-web/debug): similar display for success and result 2021-03-03 10:06:36 +01:00
Julien Fontanet
ec69ba7e0e feat(xo-web/debug): display API call result 2021-03-03 10:06:03 +01:00
Julien Fontanet
3804ca18cb feat(@xen-orchestra/proxy): 0.11.3 2021-03-03 09:19:14 +01:00
Julien Fontanet
9ea3222da8 feat(@xen-orchestra/xapi): 0.4.3 2021-03-03 09:18:58 +01:00
Julien Fontanet
df48524ca5 feat(@xen-orchestra/async-map): 0.1.1 2021-03-03 09:17:08 +01:00
Julien Fontanet
b3aff1162c feat(@xen-orchestra/proxy): 0.11.2 2021-03-03 08:58:42 +01:00
Julien Fontanet
891ca8a31b feat(@xen-orchestra/xapi): 0.4.2 2021-03-03 08:58:30 +01:00
Julien Fontanet
ba99ac8b17 fix(package.json): support non-transpiled @xen-orchestra/* and xo-* 2021-03-02 16:53:02 +01:00
badrAZ
1ff25943dc fix(xo-server): enable async_hooks support in Bluebird (#5635)
Necessary for `@xen-orchestra/backups`.
2021-03-02 13:22:47 +01:00
Julien Fontanet
deb58e40d5 chore(xo-server/bin): format with Prettier 2021-03-02 11:39:42 +01:00
Julien Fontanet
eab6eb8fab chore(xen-api): event-to-promise → promise-toolbox/fromEvent 2021-03-02 10:20:38 +01:00
Julien Fontanet
ff65367851 chore(async-map): add basic tests 2021-03-01 17:25:08 +01:00
Julien Fontanet
f16e29c63e fix(async-map/asyncMapSettled): fix hasError condition 2021-03-01 17:22:05 +01:00
Julien Fontanet
cdfeb094b3 chore(async-map/package.json): remove unused browserslist 2021-03-01 17:01:48 +01:00
Julien Fontanet
b63c5d2987 chore: dont import @xen-orchestra/log/dist 2021-03-01 17:00:42 +01:00
Julien Fontanet
015309c882 chore(backups-cli): use @xen-orchestra/async-map 2021-03-01 16:55:06 +01:00
Julien Fontanet
20377e9c56 feat(async-map): new implementations
These implementations are no longer compatible with plain objects but support iterables.

The previous implementation is still available as `@xen-orchestra/async-map/legacy`.
2021-03-01 16:55:06 +01:00
Mathieu
08857a6198 fix(xo-server): fix asyncMap is not defined (#5632)
Introduced by 57612ee
2021-03-01 16:54:53 +01:00
badrAZ
d9ce1b3a97 feat(xo-server#importVmBackupNg): use @xen-orchestra/backups lib (#5630) 2021-03-01 13:36:23 +01:00
Julien Fontanet
d166073b16 chore(xo-server/package.json): fix deps sorting
Introduced by 624f32826

Sorting values is different than sorting JSON text.
2021-03-01 09:48:13 +01:00
Julien Fontanet
f858c196f4 chore: rename asyncMap → asyncMapSettled
To express more clearly this function's behavior.
2021-03-01 09:45:56 +01:00
Julien Fontanet
57612eeced feat(async-map): remove build step 2021-02-28 23:32:23 +01:00
Julien Fontanet
be2257153c feat(proxy-cli): clearer call headers 2021-02-26 17:50:47 +01:00
Julien Fontanet
d920a97f4f feat(proxy-cli): supports nested sequences 2021-02-26 17:23:49 +01:00
Julien Fontanet
322f2a1728 chore(xo-server/runJob): group runningJobs logic 2021-02-26 16:52:43 +01:00
Julien Fontanet
cfe6b0d9ab fix(xo-server/runJob): emit job:terminated and forward error
Introduced in fd560c351
2021-02-26 16:52:43 +01:00
Pierre Donias
e229deb238 feat: release 5.56.0 (#5629) 2021-02-26 16:50:00 +01:00
Julien Fontanet
8cdde947bc feat(@xen-orchestra/proxy): 0.11.1 2021-02-26 16:07:47 +01:00
Julien Fontanet
c1b3ddf87a fix(proxy): add missing peerdep xen-api 2021-02-26 16:07:47 +01:00
Pierre Donias
27d97add1e feat: technical release (#5627) 2021-02-26 16:07:15 +01:00
Mathieu
3783724c40 fix(xo-web/task): items-per-page dropdown position (#5584) 2021-02-26 15:21:45 +01:00
Julien Fontanet
67bc4ffe68 feat(@xen-orchestra/proxy): 0.11.0 2021-02-26 15:15:05 +01:00
Julien Fontanet
453bbfbbde feat(@xen-orchestra/backups): 0.6.0 2021-02-26 15:11:35 +01:00
Julien Fontanet
ff463c4261 chore(proxy-cli): mutualize request options 2021-02-26 15:10:42 +01:00
Damien Thenot
748b77ae7a fix(docs/advanced): typo telemtry → telemetry (#5625) 2021-02-26 14:57:11 +01:00
Rajaa.BARHTAOUI
58c1005657 fix(xo-web/migrateVms): explicit main SR (#5615)
See #5577

So that when there's no default SR on the pool and the VM's snapshot has orphan
VDI-snapshots, xo-server knows where to migrate them
2021-02-26 14:01:48 +01:00
Rajaa.BARHTAOUI
9271eb61ac fix(xo-web/vm/advanced): fix 'an error has occurred' (#5604)
Fixes #5592
2021-02-26 13:39:34 +01:00
Rajaa.BARHTAOUI
c82cee25a5 fix(xo-web): fix 'mapVdisSrs is assigned a value but never used' error (#5617)
Introduced by 90cafa126f
2021-02-26 13:34:33 +01:00
badrAZ
2e5dfa5845 fix(xo-server#deleteVmBackupNg): pass remote record to getBackupsRemoteAdapter instead of ID (#5624)
Introduced by baa5847949
2021-02-26 12:07:04 +01:00
Julien Fontanet
693c07b927 chore: update app-conf to 0.9.0 2021-02-26 12:02:39 +01:00
Julien Fontanet
71a6f70f46 chore: update promise-toolbox to 0.17.0
Allow using `Disposable.use()`.
2021-02-26 12:02:39 +01:00
badrAZ
2952b5a7ec feat(xo-server#deleteVmBackupNg): use @xen-orchestra/backups lib (#5623) 2021-02-26 11:40:28 +01:00
badrAZ
baa5847949 feat(xo-server#_listVmBackupsOnRemote): use @xen-orchestra/backups lib (#5622) 2021-02-26 11:17:45 +01:00
Rajaa.BARHTAOUI
b9ce0bd99d fix(xo-web): fix 'mapValues is defined but never used' error (#5618)
Introduced by 062fb3ba30
2021-02-26 10:11:04 +01:00
Julien Fontanet
aac61d8120 chore: update golike-defer to 0.5.1 2021-02-25 18:41:11 +01:00
Julien Fontanet
1f6edfdbcc fix(xo-server/runJob): upgrade defer to fix import
Introduced by fd560c351
2021-02-25 18:40:55 +01:00
Mathieu
9d1ce7fadf fix(backups/importDeltaVm): restore the bios_strings (#5598)
Aligned with XAPI behavior for XVA exports/imports.
2021-02-25 17:26:43 +01:00
Julien Fontanet
fd560c351f fix(xo-server/runJob): register job as soon as job.start (#5620) 2021-02-25 17:00:07 +01:00
badrAZ
b45556062d fix(@xen-orchestra/backups): don't double JSON config (#5621)
Config returned by `Xo#exportConfig` is already a string, it must not be JSON encoded again.
2021-02-25 16:49:45 +01:00
badrAZ
5be45599ed fix(xo-server/metadata-backup): XO config not restored (#5619)
Introduced by 61c3057060

`log.taskId` cannot be compared with the `rootTaskId` because it's generated by the Task lib and the `rootTaskId` is generated by the `xo-server` logger.
2021-02-25 16:41:39 +01:00
Julien Fontanet
9b2533dbc9 chore(yarn.lock): update 2021-02-25 14:43:16 +01:00
Julien Fontanet
ec1a4b1974 fix(proxy/backup.run): bind getConnectedRecord
Introduced in 4eb9aa9cc
2021-02-25 11:19:20 +01:00
Julien Fontanet
bb9fde17c9 chore(xo-server/_backupVm): update todo list 2021-02-25 11:03:54 +01:00
Rajaa.BARHTAOUI
8cb524080c fix(xo-server#_migrateVmWithStorageMotion): don't migrate VM VDIs to default SR (#5577)
See xoa-support#3248
See xoa-support#3355
2021-02-25 10:35:14 +01:00
badrAZ
171ec54781 feat(xo-server#restoreMetadataBackup): use @xen-orchestra/backups lib (#5611) 2021-02-25 09:43:15 +01:00
Julien Fontanet
5d9503b78c feat(backups/Backup): getAdapter accepts ids instead of remotes
This should make it easier to interface with xo-server.
2021-02-24 17:33:26 +01:00
Julien Fontanet
f56cb69c2e chore(backups/Backup): remove unused property 2021-02-24 17:30:43 +01:00
Julien Fontanet
4eb9aa9ccb feat(backups/Backup): pass directly getConnectedRecord (#5614)
This should make it easier to interface with xo-server.
2021-02-24 17:25:46 +01:00
Pierre Donias
11801f306c feat: technical release (#5613) 2021-02-24 15:54:37 +01:00
badrAZ
95c2944f30 feat(xo-server#deleteMetadataBackup): use @xen-orchestra/backups lib (#5610) 2021-02-24 11:38:27 +01:00
badrAZ
5bd4c54ab6 feat(xo-server#_listXoMetadataBackups): use @xen-orchestra/backups lib (#5609) 2021-02-24 10:16:27 +01:00
Julien Fontanet
95d6d0a0fe chore(backups/formatVmBackup): ensure function is nammed 2021-02-24 10:08:59 +01:00
Julien Fontanet
7941be083a chore(backups): rename task.js → Task.js
To be in line with other modules in this lib.
2021-02-24 10:07:36 +01:00
badrAZ
e36efaec08 feat(xo-server#_listPoolMetadataBackups): use @xen-orchestra/backups lib (#5607) 2021-02-24 10:02:57 +01:00
Julien Fontanet
637afdb540 feat(@vates/toggle-scripts): 1.0.0 2021-02-24 08:55:38 +01:00
Julien Fontanet
dafdedef9a feat(toggle-scripts): supports npm < 7 2021-02-24 08:54:29 +01:00
Julien Fontanet
ce17ee2ae6 fix(toggle-scripts/package.json): fix files entry 2021-02-24 08:48:33 +01:00
Julien Fontanet
e74daa97d2 fix(toggle-scripts): fix usage 2021-02-23 21:54:31 +01:00
Julien Fontanet
44d64d1b80 fix(proxy): dont run systemd-service-installer in dev
See ronivay/XenOrchestraInstallerUpdater#62
2021-02-23 21:37:26 +01:00
Julien Fontanet
1a4731aa83 feat(@vates/toggle-scripts): 0.1.0 2021-02-23 21:32:58 +01:00
Julien Fontanet
a75e1c52b7 feat(toggle-scripts): CLI to enable/disable package.json scripts 2021-02-23 21:28:23 +01:00
Julien Fontanet
1b97cb263c feat(proxy/config): resourceDebouce → resourceCacheDelay
Similar to xo-server.
2021-02-23 19:54:20 +01:00
badrAZ
5c9a47b6b7 feat(xo-server#fetchBackupNgPartitionFiles): use @xen-orchestra/backups lib (#5606) 2021-02-23 17:48:11 +01:00
badrAZ
8a5fe86193 feat(xo-server#listBackupNgPartitionFiles): use @xen-orchestra/backups lib (#5605) 2021-02-23 17:40:02 +01:00
badrAZ
d9531e24a3 feat(xo-server/listBackupNgDiskPartitions): use @xen-orchestra/backups lib (#5599) 2021-02-23 17:34:55 +01:00
Julien Fontanet
624f328269 chore(xo-server/package.json): sort deps 2021-02-23 16:29:46 +01:00
badrAZ
a6f4e6771d fix(xo-server/package.json): missing dependency (#5603)
Introduced by a958fe86d7

Used in a506c21b80/%40xen-orchestra/backups/RemoteAdapter.js (L14)
2021-02-23 15:34:31 +01:00
Julien Fontanet
a506c21b80 feat(docs/installation): XO now requires Node >=14.5 2021-02-23 15:26:21 +01:00
Julien Fontanet
981193ed23 feat(docs/from the sources): XO now requires Node >=14.5 2021-02-23 15:12:42 +01:00
Julien Fontanet
85a6204db2 feat(@xen-orchestra/backups-cli): 0.4.0 2021-02-23 14:41:47 +01:00
Julien Fontanet
b82aba1181 chore(backups-cli): add usage to README 2021-02-23 14:40:15 +01:00
Julien Fontanet
0a6dea2c79 feat(backups-cli/clean-vms): better usage 2021-02-23 14:32:16 +01:00
Julien Fontanet
69b6d75927 feat(CHANGELOG.unreleased): release backups 2021-02-23 12:35:04 +01:00
Julien Fontanet
eff2d48cc5 feat(backups/RemoteAdapter#outputStream): make path first param
Similar to `fs/Abstract#outputStream()`.
2021-02-23 12:34:26 +01:00
Julien Fontanet
ca5af2505c fix(fs/outputStream): always make path first param
Introduced by 7a1377119
2021-02-23 11:47:19 +01:00
Julien Fontanet
a958fe86d7 feat(proxy): version 1 (#4495)
Co-authored-by: badrAZ <azizbibadr@gmail.com>
Co-authored-by: Mathieu <70369997+MathieuRA@users.noreply.github.com>
2021-02-23 08:58:10 +01:00
Julien Fontanet
3ed488e10f chore(compose): regenerate README 2021-02-22 15:09:23 +01:00
Julien Fontanet
dcc11f16b1 feat(@vates/compose): 2.0.0 2021-02-22 14:42:45 +01:00
Julien Fontanet
209706b70d feat(compose): forwards this to all functions 2021-02-22 14:42:18 +01:00
badrAZ
1bc80eb485 fix(package.json#moduleNameMapper): fix tests for non-transpiled @vates/ packages (#5593) 2021-02-19 16:34:14 +01:00
badrAZ
9ab9e3fe46 feat(vates/disposable): utilities for disposables (#5590) 2021-02-19 16:22:41 +01:00
Mathieu
d654c096ed feat(xo-web/menu): xoa.check sync between menu and support page (#5534) 2021-02-19 10:21:52 +01:00
Olivier Lambert
f5d5884988 feat(docs/advanced): add a section about terraform provider (#5589) 2021-02-19 09:22:05 +01:00
Julien Fontanet
2c016204bf feat(@vates/compose): 1.2.0 2021-02-18 17:03:17 +01:00
Julien Fontanet
04fd625bde feat(compose): this and args passed to first function 2021-02-18 17:03:17 +01:00
Mathieu
8455d4a49f fix(xo-web/select): wrapping text if label is too long (#5580)
See https://xcp-ng.org/forum/topic/4072/create-vm-network-names-too-large
2021-02-18 16:51:52 +01:00
Yannick Achy
a3960bb7c5 feat(docs/full backup): document offline backup (#5582)
Co-authored-by: yannick Achy <yannick.achy@vates.fr>
2021-02-18 15:15:21 +01:00
Mathieu
769262d60e fix(changelog): add # to PR reference (#5585) 2021-02-18 14:49:14 +01:00
Julien Fontanet
942567586f feat(@vates/compose): 1.1.1 2021-02-18 11:29:10 +01:00
Julien Fontanet
ba6baaec0a fix(compose): update README 2021-02-18 11:27:24 +01:00
Julien Fontanet
a8ac6fc738 fix(compose): require Node 7.6 2021-02-18 11:26:00 +01:00
Julien Fontanet
b027d3b1d6 feat(@vates/compose): 1.1.0 2021-02-18 11:25:33 +01:00
Julien Fontanet
71f9d268c9 feat(compose): async functions support 2021-02-18 11:25:14 +01:00
Julien Fontanet
2b91d4af99 feat(compose): right to left support 2021-02-18 11:25:14 +01:00
Julien Fontanet
0ec0e286ba feat(compose/README): document functions in an array 2021-02-18 11:25:14 +01:00
Rajaa.BARHTAOUI
258ae64568 fix(xo-web/home/vm): bulk intra pool migration: fix map VDI -> SR (#5578)
See xoa-support#3355
See xoa-support#3248
2021-02-18 11:04:47 +01:00
Rajaa.BARHTAOUI
90cafa126f feat(xo-web/migrateVm): show error when no main SR selected (#5568)
See xoa-support#3355
2021-02-18 10:57:00 +01:00
Julien Fontanet
43d31e285c feat(@vates/compose): 1.0.0 2021-02-18 10:40:27 +01:00
Julien Fontanet
57945e6751 feat(compose): new lib to compose functions 2021-02-18 10:39:57 +01:00
Rajaa.BARHTAOUI
fce56cbf4c fix(xo-server/utils/parseXml): keep all values as strings (#5581)
Fixes #5497

All values must be kept as strings because that's what the previous implementation used to do.

Introduced by 525369e0ce

See https://github.com/vatesfr/xen-orchestra/issues/5497#issuecomment-780314187

```
{
  'iscsi-target': {
    LUN: {
      vendor: 'TrueNAS',
      serial: '9eaa394581f3003',
      LUNid: 55,
      size: 10995116277760,
      SCSIid: '36589cfc000000581d40d6d5140d9b9da'
    }
  }
}
```
2021-02-18 09:46:17 +01:00
Julien Fontanet
7a13771198 fix(fs/outputStream): make path the first param
From #5373
2021-02-17 17:11:55 +01:00
Julien Fontanet
819c798e99 chore: format mardown files with Prettier 2021-02-17 16:04:22 +01:00
Julien Fontanet
8560ca0661 chore(vhd-lib/Vhd#_getBatEntry): name param blockId
Because it's not a full block but an identifier.
2021-02-17 14:39:02 +01:00
Julien Fontanet
82cdfe7014 chore(vhd-lib): fix a test
From #5481
2021-02-17 14:30:40 +01:00
Julien Fontanet
52642f5854 chore(vhd-lib): move tests into src
From #5481

Fix Jest module mapping.
2021-02-17 14:29:40 +01:00
Julien Fontanet
6c6f9f5a44 feat(backups-cli/clean-vms): rename force flag to remove 2021-02-17 14:01:14 +01:00
Julien Fontanet
039ce15253 chore(xo-server/backups-ng): use invalidParameter.is() 2021-02-17 14:01:14 +01:00
badrAZ
695a4c785c feat(xo-server/backups-ng): handle proxy VM restoration logs (#5576) 2021-02-17 11:03:52 +01:00
Rajaa.BARHTAOUI
7d7f160159 fix(xo-web/migrateVm): don't automatically select migration network (#5564)
See xoa-support#3355
2021-02-17 10:06:03 +01:00
Julien Fontanet
b454b4dff1 feat(xo-server/backups): add proxy id to logs 2021-02-17 09:53:00 +01:00
Nicolas Raynaud
e5d711dd28 fix(fs/S3Handler#_write): work when file doesn't exist (#5561) 2021-02-16 13:45:01 +01:00
badrAZ
10b127ca55 feat(xo-web/backup): ability to force full backup per schedule (#5546)
Fixes #5541
2021-02-16 11:31:17 +01:00
Julien Fontanet
fb4dff4fca chore(xo-cli/USAGE): clearer JSON param explanation 2021-02-16 10:52:22 +01:00
badrAZ
ef25b364ec fix(xo-server/metadata-backups): fix interrupted status on backup running (#5573)
Introduced by 8a3ae59f77
2021-02-15 14:31:01 +01:00
Nicolas Raynaud
9394db986d fix(import/disk): allow uppercase extensions (#5574)
See https://xcp-ng.org/forum/topic/4216/cannot-import-a-large-vhd-using-import-disk-option
2021-02-15 12:05:49 +01:00
Rajaa.BARHTAOUI
9226c6cac1 fix(xo-server/api): don't log host.stats errors (#5553)
See xoa-support#3323

This avoids flooding the logs with ECONNREFUSED errors when the host's toolstack
is restarted
2021-02-12 11:29:16 +01:00
Julien Fontanet
283193e992 feat(complex-matcher): 0.7.0 2021-02-11 13:54:29 +01:00
Julien Fontanet
72f8a6d220 chore(complex-matcher): add test for non-ASCII raw strings
Related to f5e4fb49c
2021-02-11 13:53:54 +01:00
Albin Hedman
f5e4fb49c3 feat(complex-matcher): allow most letters to be unquoted (#5555) 2021-02-11 11:14:44 +01:00
Olivier Lambert
3cd15c783c fix(docs): update the deploy script to fix an issue (#5565) 2021-02-11 10:17:42 +01:00
badrAZ
bf51ba860a fix(xo-server, xen-api): clear XO cache on event fetch failure (#5526)
Fixes #5475
2021-02-10 16:17:46 +01:00
Mathieu
6aa8515df4 feat(xo-web/restore): allow all licenses to restore backups (#5547)
See xcp-ng.org/forum/topic/4165/crashed-xoa-can-t-boot-a-clean-xoa-and-use-restore-because-of-registration/7
2021-02-10 15:53:29 +01:00
Julien Fontanet
3bf4ee35a1 fix(xo-server/Xapi#_disconnectVbd): dont swallow unhandled error
Detected while working on #5543
2021-02-09 14:47:55 +01:00
badrAZ
e08c600740 fix(xo-server/xapi): handle VM/VDI export stream close (#5538) 2021-02-09 14:24:35 +01:00
badrAZ
f823690b44 feat(xo-common,xo-server,xo-web/proxy upgrade): confirm before interrupting backup job (#5533) 2021-02-05 18:11:09 +01:00
Pierre Donias
350b0c1e3c feat: release 5.55.1 (#5551) 2021-02-05 16:13:32 +01:00
Pierre Donias
b01a6124a9 feat: technical release (#5550) 2021-02-05 15:46:04 +01:00
Rajaa.BARHTAOUI
b00652f9eb fix(xo-server#_migrateVmWithStorageMotion): fix VIF_NOT_IN_MAP error (#5544) 2021-02-04 17:17:24 +01:00
Mathieu
19159a203a feat(xo-web/task): display age and estimated duration (#5530)
See https://xcp-ng.org/forum/topic/4083/task-list-enhancement
2021-02-04 15:07:48 +01:00
Pierre Donias
be8c77af5a fix(xo-server-auth-ldap/synchronizeGroups): fix adding users to groups (#5545)
Fixes xoa-support#3333
Introduced by 8cfaabedeb

`synchronizeGroups` (called without a user) tries to find XO users that belong
to LDAP groups and add them to those groups. In order to find those users, it
was using the `userIdAttribute` attribute instead of the
`membersMapping.userAttribute` attribute from the configuration.
2021-02-04 11:45:59 +01:00
Mathieu
8bb7803d23 feat(docs): migration to new XOA (#5542)
Co-authored-by: Jon Sands <fohdeesha@gmail.com>
2021-02-04 09:41:17 +01:00
Julien Fontanet
54a85a8dd0 fix(xo-server/utils/parseXml): fix buffer support
Fixes #5539

Introduced by 525369e0c
2021-01-30 10:51:18 +01:00
Julien Fontanet
6fd40c0a7c chore(xo-server/utils/parseXml): add basic test 2021-01-30 10:50:59 +01:00
Rajaa.BARHTAOUI
97dd423486 feat: release 5.55.0 (#5537) 2021-01-29 15:05:21 +01:00
Rajaa.BARHTAOUI
281d60df4f feat: technical release (#5531) 2021-01-27 16:44:25 +01:00
badrAZ
43933f4089 fix(xo-server/metadata-backups): ensure cache reset at the end (#5529)
Introduced by 61c3057060
2021-01-27 14:41:15 +01:00
badrAZ
4f7e140737 feat(xo-web/proxies): better upgrade feedback (#5525)
- display up-to-date message instead of upgrade button
- add "force upgrade" button to the advanced actions
2021-01-27 11:05:33 +01:00
badrAZ
2b6945a382 feat(xo-server/metadata-backups): delete proxy backups (#5520) 2021-01-27 09:54:09 +01:00
badrAZ
8a3ae59f77 feat(xo-server/metadata-backups): restore proxy backup (#5519) 2021-01-27 08:57:02 +01:00
Mathieu
db253875cc feat(xo-server,xo-web/vm): expose VM UEFI secure boot option (#5527)
Fixes #5502
2021-01-26 17:08:21 +01:00
Jerome Charaoui
a8359dcb75 feat(docs/backups): modifier tags 2021-01-26 16:43:43 +01:00
Julien Fontanet
e5dac06d91 feat(xo-server): add x-forwarded headers on proxied requests 2021-01-26 10:34:32 +01:00
Rajaa.BARHTAOUI
e9f82558ed feat(xo-web/vm/console): ability to open RDP session (#5523)
Fixes #5495
See xoa-support#3254
2021-01-25 16:14:20 +01:00
badrAZ
26f5ef5e31 feat(xo-server/metadata-backups): list proxy backups (#5517) 2021-01-23 12:42:55 +01:00
Rajaa.BARHTAOUI
874e889b36 fix(xo-web/dashboard/health): filter out snapshots from "duplicated MAC addresses" table (#5524)
See fb3f2d46fa (commitcomment-46167022)
2021-01-22 16:06:57 +01:00
badrAZ
bece5f7083 feat(xo-server, xo-web/metadata-backup): ability to define a proxy for the backup (#4206) 2021-01-21 13:27:33 +01:00
Rajaa.BARHTAOUI
2f535e6db1 fix(xo-web): SR_NOT_ATTACHED when migration network is selected (#5516)
See xoa-support#3248
Introduced by 062fb3ba30 and 4dd7d86ea8

- Issue:  
  When choosing a network for VM migration within a pool: 
    - default SR is shared: the VMs' VDIs will be migrated to this SR
    - if not: `SR_NOT_ATTACHED` error will be thrown.
- Fix: when the migration network is defined, the SR will be required.
2021-01-21 12:07:42 +01:00
badrAZ
61c3057060 feat(xo-server/metadata-backups): run backups through proxy (#5499) 2021-01-21 12:04:30 +01:00
badrAZ
063d7d5cc4 fix(xo-web/backup/overview): fix proxy item not updated on ID change (#5522) 2021-01-21 10:55:35 +01:00
Rajaa.BARHTAOUI
0e0211050b feat(xo-server,xo-web/pool): ability to set default migration network (#5465)
See xoa-support#3118
See https://github.com/vatesfr/xen-orchestra/issues/3788#issuecomment-743207834
2021-01-21 09:44:09 +01:00
Rajaa.BARHTAOUI
c8c7245da1 fix(xo-web/home/SR): sort SR usage in % (#5513)
Fixes #5463
2021-01-20 16:53:59 +01:00
Julien Fontanet
3e27e50bab feat(xo-server/utils/parseXml): silent validation
Related to #5497

Error is longed ATM to avoid breaking existing code, but will be thrown in the future.
2021-01-20 12:03:03 +01:00
badrAZ
6b9d3ed60e fix(xo-server/remotes): update remote error on test (#5514)
See xoa-support#3255
2021-01-20 08:37:51 +01:00
Pierre Donias
11a78111de fix(xo-web/home,xo-server): fix ghost missing patch on XCP-ng (#5509)
Fixes #4922
Fixes xoa-support#3216

- xo-server: get error message from updater.py plugin's message and throw it
- xo-web: when counting missing patches, ignore hosts that failed
2021-01-19 10:12:59 +01:00
Mathieu
2655421171 fix(xo-web/host/stats): network throughput legend labels (#5483)
See xoa-support#3211
2021-01-15 10:02:10 +01:00
Pierre Donias
c6bc2ea485 fix(xo-server,xo-web/reattach SR): multiple fixes (#5488)
Fixes xoa-support#3227
Fixes #4546

- Fix already attached SRs not being displayed and show their names
- Refresh SR list after SR has been reattached
- Properly reattach SR:
   - Introduce SR (already done)
   - Compute device config from form data
   - Create PBDs for each host
   - Plug in PBDs
- Fix SR list key props to fix loading feedback inconsistencies
- Always show form summary once the SR location has been selected
2021-01-14 23:34:31 +01:00
Nicolas Raynaud
289b7a3dbe fix(fs/s3): TimeoutError: Connection timed out after 120000ms (#5397) 2021-01-14 23:23:40 +01:00
badrAZ
70083c6dca fix(xo-server): check remote is enabled even when bound to proxy (#5501) 2021-01-14 17:37:30 +01:00
Julien Fontanet
3e25b92369 chore: update dependencies 2021-01-14 16:57:44 +01:00
Rajaa.BARHTAOUI
806eaaf14b fix(xo-server/vif#set): changing network set locking mode to 'network_default' (#5500) 2021-01-14 16:53:38 +01:00
Mathieu
fb3f2d46fa feat(xo-web/dashboard/health): duplicated MAC addresses (#5468)
Fixes #5448
2021-01-14 15:26:54 +01:00
Julien Fontanet
14d06fe754 fix(xo-server/store): dont fail on serializing circular values
See https://github.com/vatesfr/xen-orchestra/pull/5397#discussion_r555170305
2021-01-14 14:59:08 +01:00
Rajaa.BARHTAOUI
752146028b feat(xo-web): assign custom date-time fields (#5473)
Fixes #4730

On pools, hosts, SRs, and VMs
2021-01-14 14:22:07 +01:00
badrAZ
6c6ae30ce5 fix(xo-web/restore/metadata): ignore disabled remotes (#5504) 2021-01-14 09:10:02 +01:00
Nicolas Raynaud
b00750bfa3 chore(xo-vmdk-to-vhd): fix integration tests (#5490) 2021-01-09 19:19:34 +01:00
Pierre Donias
55eac005a0 feat(xo-web/XOA/update): add link to channel's changelog (#5494)
See xoa-support#3257
2021-01-08 16:35:47 +01:00
badrAZ
257524de18 fix(xo-server-backup-reports): fix markdown (#5479)
Observed while investigating #3195

This PR:
  - removes extra indent
  - add a line between sections to handle the case when it was before a nested list
2021-01-08 15:26:18 +01:00
Julien Fontanet
d4f78056dd feat(xo-lib): 0.10.1 2021-01-08 14:50:06 +01:00
Julien Fontanet
66c054f24b fix(xo-lib): correctly use . as default URL base
Fixes #5489

Introduced by b25adf7f5
2021-01-08 14:50:06 +01:00
Julien Fontanet
711b722118 fix(xo-server-backup-reports/test): don't swallow errors (#5491)
See #5486
2021-01-08 14:24:13 +01:00
Mathieu
26614b5f40 feat(xo-server-web-books): waiting response from server (#5420)
Fixes #4948
2021-01-08 12:12:52 +01:00
Pierre Donias
9240211f3e fix(xo-server config): change restartHostTimeout from 5min to 20min (#5493)
Fixes #5492
2021-01-08 09:37:56 +01:00
badrAZ
67d84d956e chore(xo-server/docs): fix command (#5487) 2021-01-07 11:11:21 +01:00
Julien Fontanet
97b620f98f fix(xo-server): rm xenStore entries after use (#5482)
This should already be done by the deploy scripts but in case it's not, xo-server should do it.
2021-01-06 11:50:36 +01:00
Julien Fontanet
2f5c91a1e1 feat(docs/CR/manual seed): percent encoding 2021-01-06 11:25:24 +01:00
Rajaa.BARHTAOUI
038dad834d fix(xo-server): set VIF locking mode to 'locked' when adding allowed IPs (#5472)
Introduced by #5357
See xoa-support#2929 (last comment)
2021-01-05 09:54:16 +01:00
Rajaa.BARHTAOUI
b3cd265955 fix(CHANGELOG.md): fix packages versions (#5478) 2021-01-04 16:23:56 +01:00
Olivier Lambert
2c670bc838 docs(infra mgmt): add maintenance mode explanation (#5480)
Fixes #5477
2021-01-04 16:04:10 +01:00
Julien Fontanet
30c2b8e192 fix(backups-cli clean-vms): document --merge 2021-01-02 23:29:37 +01:00
Julien Fontanet
a00d45522b chore(xo-server/utils): remove unused formatXml 2020-12-31 15:57:48 +01:00
Julien Fontanet
525369e0ce chore(xo-server/utils/parseXml): use fast-xml-parser instead of xml2js
It's faster, the API is simpler and it appears to be better maintained.
2020-12-31 10:51:25 +01:00
Julien Fontanet
ba413f3e8f feat: release 5.54.0 2020-12-29 15:56:53 +01:00
Julien Fontanet
4afebca77b feat(@xen-orchestra/backups-cli): 0.3.0 2020-12-23 10:43:44 +01:00
Julien Fontanet
d2eb92143d feat(backups-cli/clean-vms): split --merge out of --force 2020-12-23 10:43:32 +01:00
Olivier Lambert
e01d3c64fe docs(supported hosts): add XCP-ng 8.2 LTS (#5469) 2020-12-18 13:15:15 +01:00
Pierre Donias
9f497c9c2c docs(users): LDAP groups synchronization (#5459)
See #1884
2020-12-18 10:28:57 +01:00
Julien Fontanet
9aae154c4e fix(xo-web/xoa/update): handle installer2 namespace 2020-12-17 14:23:03 +01:00
Pierre Donias
339f012794 feat: technical release (#5464) 2020-12-16 17:53:43 +01:00
Julien Fontanet
af500d7b7b fix(yarn.lock): sync 2020-12-16 17:39:43 +01:00
Pierre Donias
16a71b3917 feat(xo-web): set rolling pool updates plan (#5462) 2020-12-16 17:30:08 +01:00
Pierre Donias
7dfa104f65 feat(xo-server,xo-web/user): prevent setting email/pwd of LDAP users (#5460)
See #1884
2020-12-16 15:28:13 +01:00
Julien Fontanet
44a7b1761f fix(xo-server/vm.export): fail instead of waiting when queue is full
Fixes xoa-support#3035

Otherwise the client request may timeout before the transfer starts.
2020-12-15 21:57:02 +01:00
Julien Fontanet
22c8ea255c feat(xo-cli): 0.11.1 2020-12-15 12:54:53 +01:00
Julien Fontanet
a1c10828d8 feat(xo-lib): 0.10.0 2020-12-15 12:52:56 +01:00
Julien Fontanet
25d69d1bd7 fix(xo-lib): handle URLs ending with /
Thanks @rushikeshjadhav
2020-12-15 12:51:20 +01:00
Mathieu
a84961f8ba feat(xo-server,xo-web/host): maintenance mode (#5421)
ON: disable & evacuate host
OFF: enable host
2020-12-14 17:15:27 +01:00
Mathieu
e17b6790b5 fix(xo-web/dashboard): filter out udev SRs (#5453)
Fixes #5423
2020-12-14 17:01:57 +01:00
badrAZ
815aed52d3 fix(xo-web/file-restore): re-initialize partitions' list on backup/disk change (#5454) 2020-12-14 14:48:36 +01:00
Mathieu
a03581ccd3 fix(xo-web/host): component not loaded if controller is undefined (#5417) 2020-12-14 14:29:31 +01:00
Mathieu
c10f6e6c6a fix(xo-web/host): dont fetch missing patches when host is halted (#5407)
Fixes #5053
See fe7901ca7f
2020-12-14 11:22:34 +01:00
Pierre Donias
18abd0384f feat: rolling pool updates (#5430)
See #5286
2020-12-14 11:08:19 +01:00
Julien Fontanet
4292bdd7b4 feat(xo-server): 5.72.1 2020-12-11 09:56:12 +01:00
Julien Fontanet
1149648399 feat(xo-server): update hashy to 0.10.0 2020-12-11 09:51:59 +01:00
Rajaa.BARHTAOUI
b6846eb21d feat: release 5.53.1 (#5449) 2020-12-10 17:04:29 +01:00
Rajaa.BARHTAOUI
d19546fcb4 feat: technical release (#5447) 2020-12-10 16:23:41 +01:00
Julien Fontanet
6a1eb198d1 chore(xo-server/docs): better part listing formatting 2020-12-10 16:12:00 +01:00
badrAZ
e4757d4345 chore(xo-server/docs): add "partx" on a raw disk (#5446) 2020-12-10 15:52:45 +01:00
Rajaa.BARHTAOUI
3873a59a37 feat(xo-web/{vm,sr}/disks): improve VDI actions tooltips (#5435) 2020-12-10 15:16:33 +01:00
Pierre Donias
cf9f6c10d7 feat(xo-server,xo-web/host): set control domain memory (#5437)
Fixes #2218
2020-12-10 15:05:42 +01:00
badrAZ
8bcd9debc2 Chore(xo-server/docs/file-restoration): add tips (#5438) 2020-12-09 11:27:30 +01:00
Pierre Donias
510a159eee fix(xo-web/modal/form): pass onChange and value to form's body component (#5434)
Fixes #4705
2020-12-08 16:46:55 +01:00
Rajaa.BARHTAOUI
062fb3ba30 feat(xo-web/home/vm): ability to choose network for bulk migration within a pool (#5427)
See xoa-support#3118
2020-12-08 16:45:12 +01:00
badrAZ
3bc477d21b fix(xo-server/docs/file-restoration): use sizelimit options (#5433)
When creating a loop device, the `sizelimit` options is necessary to indicate the end of the data to mount, otherwise the following error is thrown when trying to mount multiple parts of the disk:

```
overlapping loop device exists for /tmp/proxy1/vhdi1.
```

As an example, let's take this disk:

```
[ partition 0 | partition 1 | partition 2 ]
```

Mounting *partition 1* without `sizelimit` will mount the whole space starting from *partition 1* up to the end, which prevents *partition 2* from being mounted.

`man losetup`: https://manpages.ubuntu.com/manpages/precise/en/man8/losetup.8.html
2020-12-08 16:24:03 +01:00
Rajaa.BARHTAOUI
79eb2feb2c feat(xo-web/home): sort VMs by disk physical usage (#5418)
See xoa-support#3059
2020-12-08 14:15:15 +01:00
Julien Fontanet
1fa42a5753 chore(xo-server): update hashy to 0.9.0
BREAKING: requires Node >=10.

See https://xcp-ng.org/forum/topic/3107/python3-support
2020-12-08 11:57:35 +01:00
Mathieu
2eaab408dd fix(xo-server/jobs): bug fix when run job manually (#5426) 2020-12-08 10:34:05 +01:00
Nicolas Raynaud
f7fd0d9121 fix(upload-ova): compatibility with latest API (#5432) 2020-12-07 11:11:37 +01:00
badrAZ
3b7b776ac4 feat(xo-server/backups-ng): ability to delete VM backups though proxy (#5428) 2020-12-07 11:05:30 +01:00
Rajaa.BARHTAOUI
43abc8440b feat(xo-server,xo-web/new/sr): show serial and ID in LUN selector (#5422)
See xoa-support#3080
2020-12-04 16:55:25 +01:00
Julien Fontanet
37515b5da9 chore(xo-web/common/xo/index.js): fix formatting 2020-12-04 12:10:09 +01:00
Julien Fontanet
2dec327013 chore: update dependencies 2020-12-04 12:09:32 +01:00
Julien Fontanet
8f4dae3134 chore(multi-key-map/README): sync with USAGE 2020-12-03 14:16:13 +01:00
Julien Fontanet
a584daa92d chore(emit-async/README): minor improvements/fixes 2020-12-03 14:16:13 +01:00
Julien Fontanet
43431aa9a0 chore(fs/outputStream): remove duplicate await 2020-12-03 14:16:13 +01:00
Mathieu
f196d2abec feat(xo-web/plugins): user feedback on successful test (#5409) 2020-12-03 09:10:57 +01:00
Julien Fontanet
4a6724f664 fea: release 5.53.0 (#5416) 2020-11-30 15:39:32 +01:00
Julien Fontanet
a960737207 feat: technical release (#5415) 2020-11-30 15:33:14 +01:00
badrAZ
da08bd7fff fix(xo-server/backups-ng): invalidate cache in case of proxy backup (#5414) 2020-11-30 15:19:44 +01:00
Julien Fontanet
517430f23d feat(xo-server/vm.set memory{,Max}): better error when DMC disabled and VM running 2020-11-30 14:39:36 +01:00
Julien Fontanet
48e82ac15b feat(xo-server/vm.set memory{,Max}): handle MEMORY_CONSTRAINT_VIOLATION_MAXPIN
Fixes #4978
Fixes #5326
Fixes xoa-support#1187

This error is used when DMC is disabled.
2020-11-30 14:39:36 +01:00
Julien Fontanet
eead64ff71 feat(xo-server/authenticateUser): make throttling delay configurable 2020-11-30 12:53:40 +01:00
Rajaa.BARHTAOUI
9ac6db2f4c fix(xo-web/vm/snapshots): missing color on bulk delete (#5410) 2020-11-30 11:59:14 +01:00
Julien Fontanet
92cf6bb887 chore(xo-server/authentication): parseDuration in constructor 2020-11-30 11:23:49 +01:00
Mathieu
1d3978ce2f fix(xo-web/host): unsubscribe missing patches when the props change (#5370) 2020-11-30 11:20:28 +01:00
badrAZ
16c71da487 feat: technical release (#5406) 2020-11-27 16:22:15 +01:00
badrAZ
214dbafd62 fix(xo-server/jobs): don't throw when the job owner doesn't exist (#5405)
Introduced by 31b19725b7
2020-11-27 16:11:48 +01:00
badrAZ
89b162704c feat: technical release (#5404) 2020-11-27 15:15:00 +01:00
Rajaa.BARHTAOUI
fbf906d97c feat(xo-web/SortedTable): allow to change the number of items per page (#5355)
See xoa-support#3020
2020-11-27 14:20:33 +01:00
Rajaa.BARHTAOUI
7961ff0785 feat(xo-server, xo-web): expose and edit custom fields (#5387)
See #4730
2020-11-27 13:39:08 +01:00
Pierre Donias
00e53f455b feat(xo-web/Dashboard): make Overview & Health available on all plans (#5401) 2020-11-27 11:10:32 +01:00
Mathieu
d1d4839a09 feat(xo-web/trial): end of trial info banner (#5374)
See company#558
2020-11-27 10:37:51 +01:00
Mathieu
31b19725b7 feat(xo-server/jobs): backup job support for webhooks (#5360)
Fixes #5205
2020-11-27 10:15:51 +01:00
badrAZ
a776eaf61a feat(xo-server,xo-web): file restore via proxies (#5359) 2020-11-26 17:14:06 +01:00
badrAZ
ae2a92d229 feat(xo-server,fs): stricter perms for backup dirs (#5378)
Fixes xoa-support#3088
2020-11-26 11:02:33 +01:00
badrAZ
dedc4aa8b9 feat(xo-server/callProxyMethod): support binary response (#5399) 2020-11-25 18:16:20 +01:00
Julien Fontanet
7a8ca2f068 chore: change print width to 120 chars 2020-11-24 10:51:35 +01:00
Nicolas Raynaud
fdf52a3d59 feat(OVA/VMDK import): transmit VMDK tables in multipart POST request (#5372)
See xoa-support#3060

The VMDK block tables could become bigger than the max allowed size of a websocket message. The tables are now sent in a multipart POST in the same transaction as the file.
2020-11-23 10:27:35 +01:00
Pierre Donias
e0987059d3 fix(xo-server-auth-ldap): typo defaults → default (#5388)
The "Check certificate" option's default value was incorrectly set in the UI. So if it was left untouched by the user (showing as OFF since `undefined`), it would be saved as `undefined`, and then fallback to the actual default value (`true`) when used in the code. So it would try to check the certificate even though the user didn't want to.
2020-11-20 14:10:58 +01:00
Pierre Donias
ee7217c7c9 feat(xo-server,xo-web/VIF): set any allowed IP as an admin (#5367)
Fixes #2535
See #1872
See #5358
2020-11-20 10:35:23 +01:00
Pierre Donias
1027659f34 fix(xo-web/backup/restore): refresh button loading feedback (#5381)
See xoa-support#3093
Introduced by 48eeab974c
2020-11-20 10:05:21 +01:00
Albin Hedman
424a212cc3 feat(xo-web/dashboard/health): add 'missing guest tools' section (#5376) 2020-11-18 09:34:12 +01:00
Nicolas Raynaud
949ddbdcd7 feat(new SR): use zfs type when XCP-ng 8.2+ #5302 (#5330) 2020-11-16 15:04:39 +01:00
Rajaa.BARHTAOUI
7fcfc306f9 fix(xo-server/vif.set): don't change locking mode automatically (#5357)
See xoa-support#2929
2020-11-10 15:15:54 +01:00
Rajaa.BARHTAOUI
a691e033eb fix(xo-web/pool/network): remove unnecessary braces (#5366) 2020-11-10 09:19:24 +01:00
Rajaa.BARHTAOUI
b76f62d470 fix(xo-web): missing key props (#5365) 2020-11-10 09:16:52 +01:00
Julien Fontanet
01a90a1694 feat(xapi-explore-sr): 0.3.0 2020-11-09 12:33:45 +01:00
badrAZ
97bcc7afb6 feat(@vates): add MultiKeyMap (#5362) 2020-11-09 12:30:35 +01:00
Olivier Lambert
9fa0ec440d fix(xapi-explore-sr): replace XenServer by Host (#5363) 2020-11-09 12:19:44 +01:00
Mathieu
28559cde02 feat(xo-web/copyVm): enable intrapool VM copy for everyone (#5333)
Fixes #4890
2020-11-05 14:20:26 +01:00
Rajaa.BARHTAOUI
6970d48cc3 fix(xo-web/home/pool): add missing memory icon (#5356)
See https://xcp-ng.org/forum/topic/3783/missing-icon-on-the-ui
2020-11-05 10:25:40 +01:00
Julien Fontanet
52801c5afc fix(fs/nfs): only use vers=3 if no other options (#5354)
Fixes #4940
2020-11-02 22:21:27 +01:00
Pierre Donias
7797bce814 feat(xo-server,xo-web/groups): prevent edition of LDAP groups (#5351)
See #1884
2020-11-02 16:18:43 +01:00
Rajaa.BARHTAOUI
18762dc624 feat: release 5.52.0 (#5352) 2020-10-30 14:23:00 +01:00
Julien Fontanet
5a828a6465 fix(CHANGELOG): "Packages to release* → *Released packages* 2020-10-30 11:48:53 +01:00
Julien Fontanet
eaa9f36478 feat: technical release (#5350) 2020-10-30 10:09:37 +01:00
Julien Fontanet
2b63134bcf feat(xo-web): add API debug mode
Adding `?debug` in URL (as search param), log all API calls in browser console.
2020-10-30 09:34:17 +01:00
Julien Fontanet
8dcff63aea fix(xo-server/{CR,DR}): add tags at VM creation (#5341)
Fixes https://xcp-ng.org/forum/topic/3285/cr-tag-ignored-kind-of-in-smart-backup

Necessary to avoid smart mode from matching the replicated VMs currently being imported.
2020-10-29 17:27:04 +01:00
Pierre Donias
c2777607be feat(xo-server-auth-ldap,xo-web): LDAP group manual sync (#5343)
See #1884

When the auth-ldap plugin is configured to synchronize LDAP groups, a group will
only be created in XO when a user that belongs to that group logs into XO. This
commit adds a button that allows an admin to force the synchronization of all
LDAP groups.
This can be useful if an admin wants to configure ACLs for those groups without
having to wait for a user of each group to log into XO at least once.
2020-10-29 17:13:38 +01:00
Julien Fontanet
9ba2b18fdb feat(xo-server/backup): dont backup migrating VMs
Fixes xoa-support#2953
2020-10-29 16:27:38 +01:00
Mathieu
4ebc10db6a fix(xo-web/formatSize): don't throw if the size is undefined (#5348) 2020-10-29 15:52:23 +01:00
Mathieu
610b6c7bb0 feat(xo-server,xo-web/host): get/set scheduler granularity (#5320)
Fixes #5291
2020-10-29 14:58:09 +01:00
Rajaa.BARHTAOUI
357333c4e4 feat: technical release (#5347) 2020-10-28 16:07:21 +01:00
Rajaa.BARHTAOUI
723334a685 feat(xo-web/pool/network): add tooltip for Automatic column (#5345)
See xoa-support#2978
2020-10-28 11:50:49 +01:00
Mathieu
b2c218ff83 fix(xo-web/home): error notification when bulk VM snapshot fails (#5323) 2020-10-28 10:55:42 +01:00
badrAZ
adabd6966d feat(xo-server, xo-web/host): ability to install a certificate (#5332)
Fixes #5134
2020-10-27 16:36:26 +01:00
Mathieu
b3eb1270dd fix(xo-web/user): use default filter when custom filter is unset (#5321) 2020-10-27 15:25:38 +01:00
Mathieu
7659a195d3 fix(xo-web/vm): show snapshot button for self service user (#5324)
Introduced by a88798cc22
2020-10-27 15:17:33 +01:00
Julien Fontanet
8d2e23f4a8 fix(xo-server): remove TLS config to use native settings
See https://xcp-ng.org/forum/topic/3747/xen-orchestra-tls-minimum-version-requirement-how-to-set
2020-10-26 15:54:29 +01:00
Mathieu
539d7dab5d fix(xo-web/backup): add tooltip on running backup job button (#5325)
See https://xcp-ng.org/forum/topic/3687/xo-interface-and-usability/5
2020-10-26 15:22:35 +01:00
Pierre Donias
06d43cdb24 chore(xo-server-auth-ldap): remove unused dependency (#5344) 2020-10-26 09:32:04 +01:00
badrAZ
af7bcf19ab docs(users): add audit log documentation (#5342)
* docs: audit log
* docs: grammar edit for audit log info
* Fixes #5340

Co-authored-by: Jon Sands <fohdeesha@gmail.com>
2020-10-24 12:53:38 +02:00
Rajaa.BARHTAOUI
7ebeb37881 fix(xo-web#addSshKey): SSH key must be unique (#5329)
Mostly needed by the SortedTable, which uses the key as the item's ID.
2020-10-23 14:30:15 +02:00
Julien Fontanet
4911bbe3a2 chore: normalize packages 2020-10-23 10:21:52 +02:00
Julien Fontanet
e0b6ab3f8a feat(value-matcher/README): document patterns 2020-10-23 10:21:52 +02:00
Rajaa.BARHTAOUI
8736c2cf9a feat(xo-server,xo-web/VIF): require network ACLs to change VIF locking mode (#5283)
See xoa-support#2929
2020-10-23 10:18:29 +02:00
Rajaa.BARHTAOUI
d825c33b55 feat(xo-server,xo-web/networks): ability to change a VIF's network for self users (#5203)
Fixes #5020
2020-10-23 10:05:03 +02:00
badrAZ
171ecaaf62 feat(xo-server, xo-web/host): display installed certificates (#5319)
See #5134
2020-10-21 14:36:55 +02:00
Julien Fontanet
5e6d5d4eb0 docs(xo-server): file restoration (#5336) 2020-10-20 15:35:16 +02:00
Rajaa.BARHTAOUI
3733a3c335 fix(xo-web): component names must be in PascalCase (#5322) 2020-10-20 15:19:59 +02:00
badrAZ
7fca6defd6 fix(xo-web, xo-server): fix host power state (#5288)
Fixes #4919
2020-10-20 10:01:48 +02:00
Julien Fontanet
2a270b399e fix(xo-server/getRemoteHandler): synchronize to avoid race conds during sync 2020-10-19 22:35:13 +02:00
Julien Fontanet
64109aee05 chore(vhd-lib/Vhd#_createBlock): assert block not already allocated 2020-10-19 22:33:58 +02:00
Julien Fontanet
e1d9395128 feat(vhd-lib/Vhd#_createBlock): dont initialize block
It's unnecessary, the block is written right after creation.
2020-10-19 22:30:57 +02:00
Julien Fontanet
32eec95c26 feat(@xen-orchestra/backups-cli): 0.2.1 2020-10-19 17:15:00 +02:00
badrAZ
f41cca45aa feat(audit-core, xo-server-audit): ability to delete a range of records (#5277) 2020-10-19 13:09:08 +02:00
Rajaa.BARHTAOUI
48eeab974c feat(xo-web/backup/restore): load backups in parallel (#5278)
See #5176
2020-10-19 11:47:33 +02:00
Pierre Donias
eed44156ae feat: release 5.51.1 (#5328) 2020-10-14 16:12:37 +02:00
Pierre Donias
1177d9bdd8 feat: technical release (#5327) 2020-10-14 15:23:00 +02:00
badrAZ
d151a94285 feat(xo-server, xo-web/proxy): ability to update http proxy cfg (#5148) 2020-10-14 14:43:33 +02:00
badrAZ
a7fe6453ee feat(xo-server#_migrateVmWithStorageMotion): improve migration error (#5306)
Fixes #5282
2020-10-14 13:17:55 +02:00
Rajaa.BARHTAOUI
313eb136f4 fix(xo-web): some methods call throw 'not enough permissions' (#5303)
See #5285
2020-10-13 15:30:06 +02:00
Mathieu
98591ff83d fix(xo-server/deleteResourceSet): detach VMs from resource set (#5312)
Fixes #4797
2020-10-12 11:33:29 +02:00
Mathieu
0b9d78560b feat(xo-web/home): better sort of bulk VM migrate selector (#5308)
Fixes #4462
2020-10-12 10:22:24 +02:00
Rajaa.BARHTAOUI
32a930e598 feat(xo-web/vm/network): remove unused method (#5316)
Introduced by c4d96fbc49
2020-10-12 10:05:57 +02:00
Rajaa.BARHTAOUI
edd8512196 feat(xo-web/vm): hide backup tab for non-admin users (#5317)
Fixes #5309
2020-10-12 10:02:08 +02:00
Mathieu
7a6aec34ae fix(xo-web/ips): fix the range formatting (#5314)
Fixes #3170
2020-10-09 17:15:50 +02:00
badrAZ
009a0c5703 feat(xo-web/backup/overview): add link from backup job/schedule to corresponding logs (#5260)
Fixes #4564
2020-10-09 14:54:23 +02:00
badrAZ
a99086b6bd chore(xo-web/log-alert-body): remove unused subscription (#5292)
Introduced By 3370014ddf
2020-10-07 15:17:29 +02:00
badrAZ
a186672447 feat(xo-web/sorted-table): ability to collapse actions (#5311)
See #5148
2020-10-07 11:13:10 +02:00
badrAZ
0b8a7c0d09 feat(xo-web/backup/new): enable created schedules by default (#5280)
See xoa-support#2921
2020-10-07 10:36:10 +02:00
BenjiReis
1990bf3d7a fix(sdn-controller): use correct bridge address to create tunnels (#5281)
Fixes xoa-support#2919
2020-10-06 23:54:02 +02:00
Julien Fontanet
ea74a7e401 fix(backups-cli clean-vms): fix limit-concurrency-decorator import 2020-10-06 18:35:35 +02:00
Nicolas Raynaud
bf12c3ff74 fix(xo-server/backup-ng): use getRemoteWithCredentials (#5315)
Introduced in #4907

Fix #5253

Otherwise the handler will be incorrectly defined due to the obfuscation of credentials.
2020-10-06 18:13:27 +02:00
Rajaa.BARHTAOUI
9d261aae76 fix(xo-web/home): hide backup filter for non-admin users (#5287)
See #5285
2020-10-06 16:47:20 +02:00
badrAZ
3d8c8fd745 feat(xo-web/backup-reports): hide merge task when no merge (#5263) 2020-10-06 16:08:12 +02:00
Rajaa.BARHTAOUI
6ad7db522a fix(xo-web/vm/disks): fix "not enough permissions" error (#5299)
Introduced by 1116530a6b
2020-10-06 15:16:27 +02:00
Rajaa.BARHTAOUI
385984b1d8 fix(xo-web/vm/disks): VDI disappears after migration (#5296)
Related to 1116530a6b
2020-10-06 10:38:33 +02:00
Mathieu
4f3d4b06b5 fix(xo-web/user): fix custom filters in default filter select (#5298) 2020-10-02 14:40:17 +02:00
badrAZ
2291986e2c fix(xo-web/new/network): omit bond slave PIFs from selection (#5262)
See xcp-ng.org/forum/topic/3524/old-networks-not-removed-after-creating-bond/8
2020-10-02 14:12:53 +02:00
Nicolas Raynaud
fc81cf4d70 fix(xo-web/remotes): fix S3 secret key edit UI (#5305)
Fixes #5233
2020-10-02 11:38:18 +02:00
Pierre Donias
fdeab86a87 fix(xo-web/xoa): don't show expired notifications (#5304) 2020-10-01 16:40:27 +02:00
Julien Fontanet
3616b7a67b feat(xo-server/sensitive-values): make obfuscated value obvious 2020-10-01 15:01:21 +02:00
Julien Fontanet
83ea57d825 feat(xo-server/vm.migrate): ensure original error is logged
Fixes https://github.com/vatesfr/xen-orchestra/pull/4364/files#r488539823
2020-10-01 14:52:42 +02:00
Mathieu
24a69bcade feat(xo-server,xo-web/host/advanced): add IOMMU state (#5294)
Fixes #4936
2020-10-01 10:40:10 +02:00
badrAZ
58dc3244be feat: release 5.51.0 (#5295) 2020-09-30 14:19:35 +02:00
badrAZ
61e580b992 feat: technical release (#5293) 2020-09-29 16:40:26 +02:00
Rajaa.BARHTAOUI
1116530a6b feat(xo-web/vm/disks): ability to migrate VDIs to other SRs within resource set (#5201)
See #5020
2020-09-29 16:07:10 +02:00
Pierre Donias
8cfaabedeb feat(xo-server-auth-ldap): import LDAP groups (#5279)
See #1884

When a user logs into XO using LDAP:

- Create a XO user if it doesn't already exist
- Mark it as being provided by LDAP and bind it to the LDAP user with an ID
- If group synchronization is enabled:
  - Fetch all the LDAP groups based on the Base and Filter (plugin config)
  - Create and delete the corresponding XO groups based on the LDAP groups that
    were found
  - Add and remove the XO users from the XO groups based on the LDAP data
2020-09-29 15:35:30 +02:00
Rajaa.BARHTAOUI
66ba05dcd0 feat(xo-web/tasks): display linked objects (#5267)
Fixes #4275
2020-09-29 15:13:56 +02:00
Julien Fontanet
d1db616d1e feat(backups-cli/clean-vms): limit VHD merging concurrency to 1 2020-09-29 10:43:48 +02:00
Nicolas Raynaud
aed09b152a fix(import/ova): speedup the import of gziped vmdk disks nested in .ova (#5275)
This the followup to #5085

Avoid unzipping the entire file from the beginning before each read.
The test case when from 10min down to 26 seconds.

When reading a block from the gzipped file, we keep the current state in memory, if the next read happens at an offset greater than the previous read, we just carry one decompressing the file until the desired position.

The previous code would decompress from the start of the file for every read operation.
2020-09-28 15:42:55 +02:00
Nicolas Raynaud
f755365e23 fix(xo-web/remotes): fix editing bucket and directory for S3 (#5276) 2020-09-28 15:33:06 +02:00
badrAZ
ccd34c1610 fix(CHANGELOG): update advanced filter entry (#5290)
Introduced by 45fe70f0fa
2020-09-28 12:12:15 +02:00
badrAZ
f9104e6cc9 fix(xo-web/messages): remove unused messages (#5289)
Introduced by 45fe70f0fa
2020-09-28 12:10:21 +02:00
Nicolas Raynaud
4bb702fe89 fix(fs/S3): support 50GB+ files (#5242) 2020-09-27 20:49:41 +02:00
Julien Fontanet
511a04dad5 feat(xo-server-auth-ldap/configuration): add titles and reorder settings 2020-09-27 20:37:00 +02:00
Nicolas Raynaud
f3527a44d7 fix(vm/import): make the UI respond instantly to .ova file drop (#5274)
When dropping a file on the import zone, the UI didn't acknowledge the file until the vmdk table were parsed.

Now the UI parses the XML instantly, displays it on the UI, and start parsing the tables in the background. If the user clicks the "import" button, the system will finish parsing the tables and start the upload in when fell swoop. Hiding the parsing time in the upload time.
2020-09-27 19:45:32 +02:00
Julien Fontanet
fdbe84cb1e chore(xo-server-test): format with Prettier 2020-09-27 19:42:35 +02:00
badrAZ
45fe70f0fa feat(xo-web/logs/backup-ng): advanced filter (#5208)
See #4406
2020-09-25 16:58:33 +02:00
badrAZ
2aed2fd534 feat(xo-web/logs/backup-ng): log tasks pagination (#5209)
See #4406
2020-09-24 16:36:18 +02:00
Julien Fontanet
a523fa9733 feat(@xen-orchestra/backups-cli): 0.2.0 2020-09-24 15:14:07 +02:00
Julien Fontanet
0f42f032e4 feat(backups-cli/info): compute the used space per job 2020-09-24 15:12:38 +02:00
Dom Del Nano
4575b98fd5 fix(xo-server#removeSubjectToResourceSet): rename to removeSubjectFromResourceSet (#5266) 2020-09-21 17:50:55 +02:00
Pierre Donias
3a0cc0d6f6 fix(xo-server/subjects/addToArraySet): dont erase previous values (#5269)
Prior to this change, adding a value to an existing set that already contains
that value would replace the whole set with a new one containing only that
value.
2020-09-21 12:14:50 +02:00
Pierre Donias
626e2fcb12 fix(xo-server/users): serialize properties on user create as well (#5273)
This didn't break anything because we usually don't assign `groups` and/or
`preferences` (which are the only 2 properties that need serialization) on user
creation.

This also prepares a minimal change to add a `authProviders` object property on
users.
2020-09-18 12:11:20 +02:00
Pierre Donias
592feb54b7 fix(xo-server/_authenticateUser): remove broken/unused provider API (#5270) 2020-09-18 10:54:20 +02:00
badrAZ
9c6b63e7e4 feat: release 5.50.3 (#5272) 2020-09-17 16:19:30 +02:00
badrAZ
4364a74b7a feat: technical release (patch) (#5271) 2020-09-17 16:07:58 +02:00
badrAZ
00f13102f8 feat(xo-server-audit): API method to clean DB (#5150) 2020-09-17 15:03:03 +02:00
badrAZ
3f17389871 fix(xo-server-audit): remove unused variable (#5268) 2020-09-17 10:51:09 +02:00
Julien Fontanet
726ba287b1 chore(xo-server-audit/_uploadLastHash): simplify conditions 2020-09-17 10:07:32 +02:00
Julien Fontanet
42ee29cb3c fix(xo-server-audit/_uploadLastHash): dont hide errors 2020-09-17 10:06:28 +02:00
badrAZ
8a98b6b012 feat(xo-server-audit/_uploadLastHash): check integrity sequentially (#5250)
- implementation is simpler
- stop on first error
2020-09-17 09:35:42 +02:00
Julien Fontanet
14ab694804 fix(xo-cli): mkdirp must not be promisified
Introduced by d622f7a65
2020-09-16 15:56:38 +02:00
Nicolas Raynaud
14b8cda543 fix(xo-vmdk-to-vhd/grabTables): read each entry independently (#5255)
Reading all entries at once cause problems on some VMDKs (those generated by VirtualBox) because they appear to be distributed throughout the VMDK thus making the buffer not fit in memory.

See https://xcp-ng.org/forum/topic/3374/cannot-import-ova-from-virtualbox/14?_=1599689219209
2020-09-16 11:46:10 +02:00
Julien Fontanet
4264e34ffd feat(xo-web/createSubscription): support lazy subscribers (#5158)
These subscribers follow the value of the subscription but do not make the
subscription refresh itself.

A lazy subscriber triggers an initial fetch if no value is available.
2020-09-16 10:49:54 +02:00
Pierre Donias
bd9bf55e43 feat(xo-web/groups): bulk deletion (#5264) 2020-09-16 10:46:35 +02:00
Albin Hedman
7c802bbd33 feat(xo-web/dashboard/health): add 'too many snapshots' section (#5238) 2020-09-14 10:45:37 +02:00
Julien Fontanet
9e37f3f586 feat(xo-web/new VM): hide missing VDIs
See #5222

Related to 15bc30a2d
2020-09-11 11:11:25 +02:00
Nicolas Raynaud
1d4f5d068a fix(xo-web/VM import): make description optional (#5258) 2020-09-11 08:45:10 +02:00
badrAZ
5be5eb80e8 feat: release 5.50.2 (#5257) 2020-09-10 17:03:26 +02:00
Julien Fontanet
12c774a34a feat(travis-tests): dont stop on first failure on master 2020-09-10 16:42:56 +02:00
badrAZ
14c3fa4378 feat: technical release (patch) (#5256) 2020-09-10 16:36:25 +02:00
badrAZ
2f17420721 feat(xo-web/backup/overview): add link from log to its job (#5202)
See #4564
2020-09-10 16:05:36 +02:00
badrAZ
8d7f8d156f fix(xo-server,xo-web/orphan VDIs): ignore irrelevant VDI types (#5249)
Fixes #5248
2020-09-10 16:00:20 +02:00
Julien Fontanet
38248d8c35 fix(test): auto map @xen-orchestra/*, @vates/* and xo-* 2020-09-10 15:32:08 +02:00
badrAZ
edaae02892 fix(xo-web,xo-server#probeIscsiLuns): handle undefined lun size (#5212)
See xoa-support#2815
See https://xcp-ng.org/forum/topic/3409/getting-error-when-trying-to-mount-iscsi-lun
2020-09-09 12:11:50 +02:00
Rajaa.BARHTAOUI
846eff4984 feat(xo-web/vm/networks): improve tooltip messages (#5227)
See https://github.com/vatesfr/xen-orchestra/issues/4713#issuecomment-667655321
2020-09-09 11:59:54 +02:00
badrAZ
481adf3a1e feat(xo-server-audit): don't save last hash when it doesn't change (#5251) 2020-09-08 14:35:19 +02:00
Julien Fontanet
d622f7a65c chore: update dependencies 2020-09-07 10:26:51 +02:00
badrAZ
a479501aef feat: release 5.50.1 (#5246) 2020-09-04 12:04:55 +02:00
badrAZ
2456374e5a feat: technical release (patch) (#5247) 2020-09-04 11:55:01 +02:00
badrAZ
c77016ea44 feat(xo-server-usage-report): ignore replicated VMs (#5241)
Fixes #4778
2020-09-04 11:42:32 +02:00
badrAZ
6fd45a37e2 feat: technical release (patch) (#5245) 2020-09-04 11:16:45 +02:00
badrAZ
9be56d3ab8 fix(xo-server-audit): handle non-existent XOA plugin (#5239)
See 38de5048bc (commitcomment-41875481)
2020-09-04 10:31:46 +02:00
Julien Fontanet
24b264b6c9 fix(xo-server): TX checksumming is enabled by default
Introduced by fe2de9c1154c5a0b183c6d7897f1d9c376fa4031

Fixes #5234
2020-08-28 16:01:26 +02:00
tonyuh
7f9130470b feat(docs/backup_troubleshooting): SR_OPERATION_NOT_SUPPORTED (#5232)
Add SR_OPERATION_NOT_SUPPORTED error troubleshooting.
2020-08-28 11:49:47 +02:00
Julien Fontanet
b82aa1daa5 feat: release 5.50 2020-08-27 15:06:49 +02:00
Julien Fontanet
53cb325974 feat: technical release 2020-08-27 14:46:33 +02:00
Julien Fontanet
1256c320e3 feat(xo-web/host/network): button to scan PIFs
Fixes #5230
2020-08-27 11:42:28 +02:00
Julien Fontanet
15bc30a2d5 feat(xo-web/iso-device): hide missing VDIs
Fixes #5222
2020-08-27 11:13:59 +02:00
Julien Fontanet
fc3bc8468f feat(xo-server/backup): add proxyId to job log 2020-08-27 10:54:44 +02:00
Julien Fontanet
b4e068f630 chore(xo-server/backup executor): proxyId = job.proxy 2020-08-26 16:50:33 +02:00
Pierre Donias
08eef80673 feat(xo-web/orphan VDIs): show VDI(-snapshot)s that don't have VBDs (#5228)
In the Dashboard > Health > Orphan VDIs table, show non-ISO VDIs and
VDI-snapshot that don't have any VBDs.
2020-08-25 09:32:21 +02:00
Pierre Donias
152f73ebf0 feat: technical release (#5226) 2020-08-20 15:15:34 +02:00
badrAZ
38de5048bc feat(xo-server-audit): backup last hash (#5077) 2020-08-20 12:32:22 +02:00
Rajaa.BARHTAOUI
c4d96fbc49 feat(xo-web/vm/network): ability to change VIF locking mode (#5188)
See #4713
2020-08-20 09:48:50 +02:00
BenjiReis
ff25d402c1 fix(sdn-controller): host.$PIFs can have undefined element (#5217) 2020-08-20 09:25:41 +02:00
Julien Fontanet
f957024605 chore(xo-web): use js-cookie instead of cookies-js (#5224)
See #5223

`cookies-js` is no longer maintained.
2020-08-19 17:18:03 +02:00
badrAZ
006e54e2fd feat(xo-web, xo-server/proxy): improve proxy health check errors (#5191)
Fixes #5161
2020-08-19 16:20:05 +02:00
badrAZ
5f7bc58788 fix(xo-server/sensitive-values): obfuscate params containing "password" (#5220)
Fixes #5219
2020-08-19 10:56:40 +02:00
Fabian Untermoser
bdd93603aa fix(docs/backups): fix typo (#5225) 2020-08-18 21:46:30 +02:00
Rajaa.BARHTAOUI
8392a17cb2 fix(xo-server/authentication): add missing 'createPredicate' (#5221)
Fixes #5218
Introduced by 9ded2641a7
2020-08-18 16:15:35 +02:00
Julien Fontanet
5f7f0b777e fix(xo-server): use http.cookies config everywhere 2020-08-17 11:06:45 +02:00
Julien Fontanet
3f574606d9 feat(backups-cli/clean-vms): display merge progress 2020-08-17 11:06:45 +02:00
badrAZ
45f0f93895 feat(xo-server,xo-web/VM): ability to set VIF TX checksumming (#5182)
Fixes #5095
See xoa-support#2619
2020-08-14 17:14:48 +02:00
badrAZ
af2710135b fix(xo-web/proxies): remove upgrade button style (#5216)
See https://github.com/vatesfr/xen-orchestra/pull/5167/files?file-filters%5B%5D=.md#r469763760

...when the proxy is already up to date
2020-08-13 16:52:29 +02:00
Rajaa.BARHTAOUI
95ed6094fe fix(xo-web/vm/snapshot): fix redirection when copying a VM (#5213)
Introduced by d9211053ce
2020-08-13 11:12:56 +02:00
BenjiReis
6af8ce9eeb feat(sdn-controller): specify tunnel protocol at its creation (#5210) 2020-08-13 11:11:11 +02:00
badrAZ
3ff37f00fe fix(xo-web/deploy-proxy): throw error on trial start failure (#5196)
Introduced by 902953a1fa
2020-08-12 15:11:49 +02:00
Pierre Donias
ed5b066cbe fix(CHANGELOG.unreleased): remove sdn-controller package (#5214)
Introduced by cec5593c70
2020-08-12 12:21:18 +02:00
Mark Martin
cec5593c70 feat(xo-web/vm): protect from accidental shutdown (#5107)
Fixes #5090
2020-08-12 12:17:30 +02:00
Julien Fontanet
04924884ad feat(@xen-orchestra/backups-cli): 0.1.0 2020-08-11 09:57:01 +02:00
Julien Fontanet
3ccf64fcd3 feat(backups-cli/clean-vms): merge single children 2020-08-11 09:56:25 +02:00
Julien Fontanet
8eb7f9b91c fix(xo-server-sdn-controller): remove deprecated uuid import 2020-08-10 11:15:40 +02:00
Julien Fontanet
f25c50c629 chore: update dev deps 2020-08-10 11:15:40 +02:00
Rajaa.BARHTAOUI
e524a1b865 feat: release 5.49.1 (#5207) 2020-08-05 14:46:12 +02:00
Rajaa.BARHTAOUI
ac15e3355e feat: technical release (#5206)
* feat(xo-server-sdn-controller): 1.0.2

* feat(xo-web): 5.67.0

* chore(CHANGELOG): update next
2020-08-05 13:39:50 +02:00
Julien Fontanet
0930a37819 fix(CHANGELOG.unreleased): add xo-server patch
Due to 2789ead99
2020-08-05 12:33:30 +02:00
Rajaa.BARHTAOUI
d62f91a9e6 feat(xo-web/sr/advanced): show thin/thick provisioning for missing SR types (#5204) 2020-08-05 11:15:55 +02:00
Julien Fontanet
2789ead999 fix(xo-server/pool.listMissingPatches): really dont log errors
Previous fix (e1bf68ab3) was incorrect.
2020-08-05 11:10:35 +02:00
BenjiReis
f25fd267dd fix(sdn-controller): only admin can create private networks (#5200) 2020-08-04 14:09:33 +02:00
BenjiReis
47999f1f72 doc(sdn-controller): indicate correct version in OpenFlow requirements (#5199) 2020-08-03 16:47:13 +02:00
Pierre Donias
095bbcd15c feat: release 5.49.0 (#5195) 2020-07-31 14:14:32 +02:00
Pierre Donias
9177bb8451 feat: technical release (patch) (#5194) 2020-07-31 09:41:11 +02:00
Pierre Donias
119bf9b0ff feat(xo-web/VM/network): click on IP address to copy it (#5186)
Fixes #5185
2020-07-31 09:17:37 +02:00
Julien Fontanet
015c6037c4 fix: add prepublishOnly to replace removed prepare scripts
Introduced by 452a7e744
2020-07-30 19:07:36 +02:00
Adam Stankiewicz
452a7e7445 feat(test): remove need for prepare scripts (#5192) 2020-07-30 18:56:13 +02:00
Nicolas Raynaud
407586e2d5 feat(remotes): AWS S3 backup storage (#5037) 2020-07-30 16:47:04 +02:00
badrAZ
ffa431a3cd fix(xo-web/vm/tab-network): add default value to plugins (#5190)
Introduced by 2a74a49995

Plugins can be `undefined` on fetching which triggers the error `Cannot read property "some" of undefined`
2020-07-30 14:57:06 +02:00
Julien Fontanet
281a5ff991 chore: remove unused JSHint comments 2020-07-30 14:32:19 +02:00
BenjiReis
92db9bd284 fix(xo-server-sdn-controller): deactive DH for TLS connections (#5187)
Fixes #5074
2020-07-30 13:17:40 +02:00
Julien Fontanet
ea8f319f45 feat(self-signed): expose days option 2020-07-30 11:20:57 +02:00
Olivier Lambert
a11e9fe04e fix(changelog): caps typo (#5184) 2020-07-29 16:58:10 +02:00
Pierre Donias
27367bd1fc fix(CHANGELOG): wrong xo-server version (#5183) 2020-07-29 16:50:16 +02:00
Pierre Donias
c6f48ae054 feat: technical release (#5181) 2020-07-29 16:32:55 +02:00
Rajaa.BARHTAOUI
7d6efe3694 feat(xo-web/vm/network): improve the VIF locking mode feedback (#5170)
See #4713
2020-07-29 15:55:59 +02:00
Rajaa.BARHTAOUI
f4aad05edc feat(xo-web/backup): show warning if min(fullBackupInterval, retention)>50 (#5144)
See https://xcp-ng.org/forum/post/27539
2020-07-29 15:30:38 +02:00
Pierre Donias
d8f7637ca0 feat(xo-web/self): ability to cancel edition of resource set (#5174)
See xoa-support#2767
2020-07-29 13:59:45 +02:00
badrAZ
f9a7bd199e fix(xo-server#createVm): change network boot priority (#5119)
Fixes #4980
2020-07-29 11:34:24 +02:00
Rajaa.BARHTAOUI
68b7ed284a feat(xo-web/backup/health): show detached VM snapshots (#5125)
Fixes #5086
2020-07-29 10:56:31 +02:00
badrAZ
e782895cf5 feat(xo-server-audit): add extension to exported records (#5180) 2020-07-29 10:52:06 +02:00
Julien Fontanet
a5935b40d5 feat(xo-server/api): user must be signed in by default (#5175)
It's a lot more secure than previous default value.
2020-07-29 10:40:17 +02:00
badrAZ
035d2cb440 fix(xo-server-audit): fix incorrect records content type (#5179) 2020-07-29 10:38:59 +02:00
BenjiReis
2a74a49995 feat(sdn-controller, xo-web): add & remove network rules to a VM's VIFs (#5177) 2020-07-29 09:56:06 +02:00
badrAZ
902953a1fa feat(xo-server, xo-web): display proxy available upgrades (#5167) 2020-07-28 17:13:01 +02:00
Pierre Donias
1ffef91b7a fix(xo-web/copyVm): correctly pass the VM type to copyVms (#5173)
Fixes xoa-support#2773

The modal uses the type to find the objects (either VMs or snapshots) and use
them to generate the names. The missing type caused the VM clones to be named
`undefined_clone`.
2020-07-28 16:29:37 +02:00
Julien Fontanet
3d13d9b0dc feat(xo-server/addApiMethod): check method props 2020-07-28 16:02:32 +02:00
Nicolas Raynaud
adcc5d5692 feat(import/ova): allow import of gzipped vmdk disks (#5085) 2020-07-28 11:52:44 +02:00
badrAZ
c49d70170e feat(xo-server,xo-web/proxy/deploy): ability to set HTTP proxy (#5145) 2020-07-28 11:51:57 +02:00
badrAZ
349a78a5bd fix(xo-web/file-restore): ignore proxy remotes (#5171)
See xoa-support#2741
2020-07-28 11:43:33 +02:00
badrAZ
48734c6896 fix(xo-web/proxies): don't open proxy VM in new tab (#5172) 2020-07-28 10:23:16 +02:00
Rajaa.BARHTAOUI
0f60a3b24d feat(xo-web/home): ability to filter by power state (#5118) 2020-07-27 16:52:07 +02:00
Pierre Donias
d3a88011a6 feat(xo-server/self): can ignore VM snapshots resources usage (#5164)
See xoa-support#2643

With the config option `selfService.ignoreVmSnapshotResources`
2020-07-27 16:08:20 +02:00
Rajaa.BARHTAOUI
9b6e4c605b feat(xo-web/schedules): ability to enable schedule when editing (#5111)
Fixes #5026

This change affects ordinary jobs only, not backup jobs
2020-07-27 15:46:21 +02:00
Nicolas Raynaud
7c91524111 fix(OVA import): allow import of .ova files generated by Red Hat (#5159)
See xoa-support#2713.

 - use <rasd:Address> when <rasd:AddressOnParent> is not available on disks
 - avoid dotfiles and pax headers in tar parser.
2020-07-27 08:24:01 +02:00
Olivier Lambert
e1573069e4 fix(docs/license): truly fix the warning tag (#5169) 2020-07-24 10:08:58 +02:00
marcpezin
f2459c964b Fixing warning closing tag (#5168)
Fixing warning closing tag
2020-07-24 09:20:36 +02:00
Pierre Donias
43aa0b815d feat(xo-web/VM/disks): sort disks by device by default (#5165)
Fixes #5163
2020-07-23 09:53:07 +02:00
marcpezin
0740630e05 fix(docs): rebind license documentation update (#5166) 2020-07-21 17:16:18 +02:00
Rajaa.BARHTAOUI
c9244b2b13 feat(xo-web): log Invalid XML-RPC message error as an unexpected response (#5138)
See xoa-support#2588
2020-07-21 15:11:57 +02:00
Pierre Donias
0d398f867f fix(xo-web/home): link to global filter section (#5157) 2020-07-16 15:32:44 +02:00
Julien Fontanet
b74ec2d7d3 fix(xo-web/backup/restore): dont fail if no backups for a VM (#5156)
Fixes xoa-support#2707

The API does not guarantee that each VM UUID will have at least a backup, and when it happens, the code failed because `first` and `last` where not properly defined.
2020-07-16 14:29:10 +02:00
Julien Fontanet
26a295c8ed chore(xo-web/createSubscription/run): add small comment 2020-07-13 16:13:33 +02:00
Julien Fontanet
2a71d3d20c chore(xo-web/createSubscription): rename loop to run 2020-07-13 16:10:48 +02:00
Pierre Donias
b79605b692 feat: release 5.48.3 (#5154) 2020-07-10 18:12:03 +02:00
Pierre Donias
ea0fc68a53 feat: technical release (patch) (#5153) 2020-07-10 16:38:41 +02:00
badrAZ
1ca5c32de3 feat(xo-web/audit): show warning in case of disabled logs record (#5152) 2020-07-10 15:32:30 +02:00
Julien Fontanet
f51bcfa05a feat(xo-server-audit): add active setting (default false) (#5151) 2020-07-10 14:29:30 +02:00
Pierre Donias
e1bf68ab38 fix(xo-server/pool.listMissingPatches): don't log errors (#5149)
Work-around to avoid generating too many logs
2020-07-10 12:49:37 +02:00
Pierre Donias
99e03b7ce5 fix(xo-web): broken doc links (#5146)
Introduced by 30d69dadbb
2020-07-10 09:14:55 +02:00
Julien Fontanet
cd70d3ea46 fix(Travis CI): use Node 12 (#5147) 2020-07-09 15:51:22 +02:00
Julien Fontanet
d387227cef fix(xo-server/backups): dont ignore proxy job result/error 2020-07-09 09:40:02 +02:00
Pierre Donias
2f4530e426 feat: release 5.48.2 (#5143) 2020-07-07 15:49:34 +02:00
badrAZ
4db181d8bf chore(xo-server-audit): document audit DB strucure (#5078) 2020-07-07 15:22:41 +02:00
Pierre Donias
9a7a1cc752 feat: technical release (#5142) 2020-07-07 15:03:04 +02:00
Pierre Donias
59ca6c6708 feat(xo-web): prevent XO from checking time consistency of halted hosts 2020-07-07 14:51:06 +02:00
Pierre Donias
fe7901ca7f feat(xo-web): prevent XO from listing missing patches on halted XCP-ng hosts
Otherwise it triggers a lot of errors on XCP-ng
2020-07-07 14:51:06 +02:00
Pierre Donias
9351b4a5bb feat(xo-web/backup/logs): better resolution of last run log (#5141) 2020-07-07 13:47:32 +02:00
badrAZ
dfdd0a0496 fix(xo-server-test): extend timeout of deleteTempResources (#5117) 2020-07-07 13:31:13 +02:00
Pierre Donias
cda39ec256 fix(xo-web/backup/edit): tags overwritten by default ones (#5136)
Introduced by 1c042778b6
See xoa-support#2663

It was due to a race condition between the fetch of the default excluded tags
and the fetch of the job (and its schedules) which made the component populate
the form with the job's information.

Changes:
- `Edit` waits for the `job` and the `schedules` before rendering its child
- `New`'s wrapper waits for the `remotes` and the `suggestedExcludedTags` before
  rendering `New`
- `New`: `updateParams` is now called in `initialize` because we don't need to
  wait for the job and the schedules any more to be able to populate the form
2020-07-07 09:40:40 +02:00
Olivier Lambert
3720a46ff3 feat(docs/supported_hosts): no pro support for XS < 6.5 (#5137) 2020-07-06 20:03:47 +02:00
Julien Fontanet
7ea50ea41e fix(xo-server/callProxyMethod): dont use HTTP proxy 2020-07-06 17:06:37 +02:00
Pierre Donias
60a696916b feat: release 5.48.1 (#5133) 2020-07-03 15:25:39 +02:00
Pierre Donias
b6a255d96f feat: technical release (patch) (#5132) 2020-07-03 14:24:40 +02:00
marcpezin
44a0cce7f2 fix(docs/license_management): replace confusing screenshot about activation (#5131) 2020-07-03 12:03:07 +02:00
Nicolas Raynaud
f580e0d26f fix(import/OVA): fix big size parsing in OVA files (#5129) 2020-07-03 11:48:39 +02:00
Rajaa.BARHTAOUI
6beefe86e2 feat(xo-web/backup): don't open edition in new tab (#5130) 2020-07-03 11:45:06 +02:00
Julien Fontanet
cbada35788 fix(xo-server/file restore): dont fail on LVM partitions
Fixes xoa-support#2640

Introduced by 48ce7df43

The issue was due to object spreading copying only own properties but `path` is now an inherited property due to `dedupeUnmount`.
2020-07-03 11:05:04 +02:00
Julien Fontanet
44ff2f872d feat(xo-server/getBackupNgLogs): dont fail on undefined/null errors
See xoa-support#2663

This should almost never happen but if it does, it should not prevent logs from being consolidated.
2020-07-03 09:22:28 +02:00
Julien Fontanet
2198853662 feat(xo-server/logs-cli): can match on missing props 2020-07-03 09:22:28 +02:00
badrAZ
4636109081 fix(xo-web/(file-)restore-legacy): ignore proxy remotes (#5124)
Legacy restore doesn't support proxy remotes
2020-07-02 16:23:36 +02:00
Pierre Donias
1c042778b6 feat(xo-server,xo-web/smart backup): exclude XO Proxy VMs by default (#5128) 2020-07-02 15:06:47 +02:00
Rajaa.BARHTAOUI
34b5962eac fix(xo-web/backup/health): missing noop function (#5126)
Introduced by committing a suggestion https://github.com/vatesfr/xen-orchestra/pull/5062#discussion_r446135166 
without taking into account that the `noop` function is not already defined.
2020-07-02 15:05:55 +02:00
Rajaa.BARHTAOUI
fc7af59eb7 chore(xo-web/home): remove 'tags' filter from selector (#5121)
See https://github.com/vatesfr/xen-orchestra/pull/5118#discussion_r447586676
2020-07-02 14:52:59 +02:00
Olivier Lambert
7e557ca059 feat(docs/supported hosts): add CH 8.2 LTS in the list of supported hosts (#5127) 2020-07-02 09:30:16 +02:00
Julien Fontanet
1d0cea8ad0 feat(xo-server/logs-cli): add --delete command 2020-07-01 18:04:45 +02:00
Julien Fontanet
5c901d7c1e fix(xo-server/logs-cli): dont fail on non-string value 2020-07-01 18:01:34 +02:00
Julien Fontanet
1dffab0bb8 feat(xen-api): 0.29.0 2020-07-01 17:11:19 +02:00
Julien Fontanet
ae89e14ea2 feat(xo-server/getRemoteHandler): throw for proxy remotes 2020-07-01 11:46:28 +02:00
Pierre Donias
908255060c feat: release 5.48.0 (#5123) 2020-06-30 17:25:16 +02:00
Pierre Donias
88278d0041 feat: technical release (patch) (#5122) 2020-06-30 16:51:20 +02:00
Julien Fontanet
86bfd91c9d feat(xo-server/backup): logs proxy support 2020-06-30 15:45:40 +02:00
Julien Fontanet
0ee412ccb9 feat(xo-server/callProxyMethod): allow proxy.address to contain port 2020-06-30 15:20:58 +02:00
Julien Fontanet
b8bd6ea820 chore(xo-server/callProxyMethod): use parse.result to handle errors 2020-06-30 12:14:45 +02:00
Julien Fontanet
98a1ab3033 fix(xo-server/callProxyMethod): destroy lines stream in case of error 2020-06-30 12:14:45 +02:00
Julien Fontanet
e360f53a40 fix(xo-server/callProxyMethod): all lines should be JSON parsed 2020-06-30 12:14:45 +02:00
Julien Fontanet
237ec38003 fix(xo-server/callProxyMethod): lines is an object stream 2020-06-30 12:14:45 +02:00
Julien Fontanet
30ea1bbf87 feat(xo-server/callProxyMethod): expectStream as named option 2020-06-30 12:14:45 +02:00
Pierre Donias
0d0aef6014 feat: technical release (patch) (#5120) 2020-06-30 11:40:44 +02:00
badrAZ
1b7441715c feat(xo-server-perf-alert): regroup items with missing stats in one email (#4413)
Fixes #5104
2020-06-30 09:30:20 +02:00
badrAZ
e3223b6124 fix(xo-web/audit): ref not correctly forwarded (#5106)
Introduced by 9f29a047a7

The high level component (audit UI) need to call a `SortedTable` method. The only way to do it is to have access to its ref.

All components between the high level component and the `SortedTable` should forward this ref. Unfortunately, the commit  9f29a047a7 decorate the `StortedTable` with `withRouter` without forwarding its ref which breaks the audit check integrity feedback.
2020-06-29 15:44:33 +02:00
Julien Fontanet
41fb06187b fix(coalesce-calls/README): fix import 2020-06-29 15:39:14 +02:00
Julien Fontanet
adf0e8ae3b feat(@vates/coalesce-calls): 0.1.0 2020-06-29 15:33:53 +02:00
Rajaa.BARHTAOUI
42dd1efb41 feat(xo-web/home): remove unnecessary condition (#5116)
Introduced by f736381933

Backup filter: remove the needless condition to go to the first page.
2020-06-29 15:24:28 +02:00
badrAZ
b6a6694abf fix(xo-server-test/job with non-existent VM): logged as failed task (#5112)
Since c061505bf8, missing VMs are logged as a failure tasks
2020-06-29 14:04:23 +02:00
Pierre Donias
04f2f50d6d feat: technical release (#5115) 2020-06-26 15:56:40 +02:00
Rajaa.BARHTAOUI
6d1048e5c5 feat(xo-web/backup/health): show detached backups (#5062)
See #4716
2020-06-26 14:45:01 +02:00
Pierre Donias
fe722c8b31 feat(xo-web/licenses): rebind license to this XOA (#5110)
See xoa#55
Requires xoa#58

When an XOA license is bound to another XOA, allow the user to move it to the
current XOA (thus unbinding it from the other one).
2020-06-26 13:12:53 +02:00
badrAZ
0326ce1d85 feat(xo-server-audit): ignore common methods without side effects (#5109)
Ignoring these methods reduce the number of records in the audit which makes it easier to analyze and store.
2020-06-25 16:55:57 +02:00
Rajaa.BARHTAOUI
183ddb68d3 fix(changelog): missing a parenthesis (#5113) 2020-06-25 15:53:30 +02:00
badrAZ
d7fe1afc08 fix(xo-server#listVmBackups): provide schedule ID (#5101)
Introduced by b138438036
Schedule ID is used by [xo-server-test](c1d588264c/packages/xo-server-test/src/_xoConnection.js (L216))
2020-06-25 10:20:17 +02:00
Rajaa.BARHTAOUI
ae9aeaf5fd feat(xo-server,xo-web/SR): expose thin/thick provisioning (#5081)
Fixes #2208
2020-06-24 16:21:23 +02:00
Rajaa.BARHTAOUI
ec9476216f feat(xo-web/sr/disks): add tooltip for disabled migration (#4884) 2020-06-24 16:07:48 +02:00
badrAZ
619f2ef119 fix(VM): support old ipv4 protocol (#5098)
See xoa-support#2540
Indroduced by 7f64cd1801
2020-06-24 15:44:52 +02:00
Rajaa.BARHTAOUI
52020abde8 feat(xo-web/home): don't use SortedTable for VMs (not) backed up (#5046)
Introduced by f736381933
2020-06-24 14:12:37 +02:00
badrAZ
1bd504d67e feat: differentiate PV drivers and management agent (#5007)
Fixes #4783
2020-06-24 11:50:45 +02:00
badrAZ
edc4414de4 fix(xo-server/remotes): inability to get remotes info (#5093)
Introduced by 218bd0f
2020-06-23 11:12:16 +02:00
Olivier Lambert
c1d588264c feat(docs/backups): crop delta images (#5097) 2020-06-19 10:00:25 +02:00
badrAZ
94b84b75ad feat(xo-web/backup): document full backup interval functionality (#5052)
Fixes #4986
2020-06-19 09:43:13 +02:00
Olivier Lambert
b72a4c5aa9 feat(docs/proxy): add missing image and store them locally (#5096) 2020-06-18 21:37:19 +02:00
Julien Fontanet
857a9f3efc feat(xen-api): optional sync traces (#5091)
Initial actions call stacks are often missing from the resulting error for the following reasons:

- low level Node functions don't have proper stack traces
- replies handling maybe separate from requests (eg watchers)

This commit adds the option `syncStackTraces` that works around this, it has a performance impact but is useful for debugging.
2020-06-18 17:19:21 +02:00
badrAZ
ce53128657 feat(xo-server-audit): ignore xoa.getApplianceInfo (#5083)
`xoa.getApplianceInfo` doesn't have any side effect and is called many times.
2020-06-18 15:16:12 +02:00
Rajaa.BARHTAOUI
d9211053ce feat(xo-web/home/template): ability to copy/clone VM templates (#5006)
Fixes #4734
2020-06-18 15:13:12 +02:00
Rajaa.BARHTAOUI
e8316178a0 feat(xo-web/plugins): disable "Test plugin" if not loaded (#5038) 2020-06-18 14:33:18 +02:00
badrAZ
bf763d2cf4 feat(xo-web/backup): add placeholder to report recipients input (#5051)
Fixes #5035
2020-06-17 16:22:38 +02:00
badrAZ
eba5b34982 fix(xo-server#moveVdi): moveVdi should always return a VDI (#5073)
Fixes #5072
2020-06-16 20:56:00 +02:00
Pierre Donias
afb8b3dd6b feat(xo-server,xo-web/vm): protect from accidental deletion (#5045)
Fixes #4773
2020-06-16 15:40:49 +02:00
Julien Fontanet
c5fa94894b feat(xen-api): require Node >=7.6
Native support of async functions which leads to better stack traces.
2020-06-16 12:54:51 +02:00
Pierre Donias
4137758caa fix(xo-web/tooltip): use pointer events instead of mouse events (#5084) 2020-06-15 17:29:40 +02:00
Nicolas Raynaud
3578d16e9e fix(xo-server): correctly import vmdkToVhd (#5087)
Introduced in 5ee1ceced
2020-06-15 17:00:49 +02:00
BenjiReis
3ef263a5cc fix(xo-server-sdn-controller): correctly find preferred center's pool (#5080) 2020-06-15 11:13:01 +02:00
BenjiReis
510460c966 fix(xo-web): dont pass preferredCenter to sdnController.createPrivateNetwork (#5079) 2020-06-13 10:00:08 +02:00
Julien Fontanet
f74ecc53ae chore(xo-server): use @vates/decorate-with 2020-06-12 11:18:47 +02:00
Julien Fontanet
c4121073ad feat(@vates/decorate-with): 0.0.1 2020-06-12 11:12:49 +02:00
Julien Fontanet
9ded2641a7 chore(xo-server/authentication): remove unused import 2020-06-12 11:02:42 +02:00
Julien Fontanet
295ca68d02 chore(xo-server): use @vates/parse-duration 2020-06-12 11:02:42 +02:00
Julien Fontanet
27f53f262b feat(@vates/parse-duration): 0.1.0 2020-06-12 11:02:42 +02:00
Olivier Lambert
3fc16cb414 feat(docs/backups): warn of 99 chain limit for file restore (#5070) 2020-06-11 14:29:49 +02:00
Pierre Donias
90db25d732 fix(xo-web/notifications): set cookie expiration date (#5067)
Without an expiration date, the cookie expires at the end of the session and
read notifications are marked as unread the next time the user connects.
2020-06-11 12:34:33 +02:00
Julien Fontanet
bbb359470e fix(xo-server/backup): remove enumerable xapi on SR records (#5069)
Fixes xoa-support#2527

This was causing huge logs in case of CR/DR errors because this property was included and pulled in all XAPI records.

Instead of making it non-enumerable, I was able to remove entirely because records now already include a non-enumerable `$xapi` property.
2020-06-10 11:25:32 +02:00
badrAZ
319652c7c7 fix(xo-web/migrate-vdi): remove inter-pool migration possibility (#5050) 2020-06-10 09:16:14 +02:00
Julien Fontanet
c9c271fee8 feat(xen-api): $xapi defined on prototype instead of record
This should reduce a bit memory consumption.
2020-06-09 23:17:02 +02:00
marcpezin
ca0755e92b fix(docs/delta-backup): schemas should not display XVA (#5032) 2020-06-09 15:50:13 +02:00
Rajaa.BARHTAOUI
acd38597f6 fix(xo-web/vm/network): VM addresses can be null (#5049)
Introduced by 7f64cd1801
2020-06-09 15:12:04 +02:00
Pierre Donias
f4a5a80f3c fix(xo-web/SR): fix name_label edition from the home view (#5058)
Fixes #5057
Introduced by da468c46ee
2020-06-09 13:38:42 +02:00
Pierre Donias
c45d00fee8 fix(xo-web/import): default sub-route should be /vm (#5056)
Fixes #5055
Introduced by a95d7796bc
2020-06-08 16:31:02 +02:00
Ronan Abhamon
ffae59fa1c feat(changelog): missing entries for #5054 (#5066) 2020-06-08 11:17:38 +02:00
Pierre Donias
b697178f68 feat(xo-web/SortedTable): always show amount of selected items (#5041)
Fixes #4982
2020-06-08 10:57:37 +02:00
Julien Fontanet
83ade5eecb chore(CHANGELOG.unreleased): clarify that packages are not yet released 2020-06-08 10:54:42 +02:00
Ronan Abhamon
6973b92c4a fix(load-balancer): fix memory check condition in the performance plan
We must migrate a VM if the free memory in the destination host is lower than the used VM
memory!
2020-06-08 10:22:34 +02:00
Ronan Abhamon
6261f8a778 feat(load-balancer): add various logs to explicit migration checks and steps 2020-06-08 10:22:34 +02:00
badrAZ
6048493ac6 fix(xo-server#_extractIpFromVmNetworks): support old ipv4 protocol (#5036)
See xoa-support#2540

In case of an old version of guest tools, the VM ipv4 isn't reported.

This issue is due to a non-existant `n/ipv4/m` property in the VM addresses which is a new protocol.
2020-06-08 09:52:33 +02:00
Pierre Donias
1cbd715235 feat(xo-web/new-vm): dont prefill VM description (#5047)
Fixes #4981

Except if the template is a custom template
2020-06-05 14:52:31 +02:00
Julien Fontanet
703fcbccd6 feat(xo-server/xo.exportConfig): entries param
Can be used to export a subset of the config, example:

```
xo.exportConfig entries=["acls"]
```

This will only export `acls` entries and dependencies (`groups` and `users`).
2020-06-05 13:57:54 +02:00
Julien Fontanet
2f9cbec07e feat(xo-cli): support @=- to output to stdout 2020-06-05 13:57:54 +02:00
badrAZ
9f0b22d3e9 fix(xo-server#createVm): move VDIs before resize them (#5044)
See xoa-support#2535

Otherwise resizing might file due to space limitation on the template VDI's SR.
2020-06-05 13:37:18 +02:00
Julien Fontanet
ab5907c09c feat(xo-server/logs-cli): implement --gc 2020-06-05 10:58:26 +02:00
Olivier Lambert
fae0b168f6 feat(docs/load_balancing): various improvements (#5048) 2020-06-04 10:49:26 +02:00
Julien Fontanet
f18e98a63e chore: reformat code with Prettier 2020-06-03 11:03:03 +02:00
Julien Fontanet
3524886d5d chore: update dev dependencies 2020-06-03 11:03:03 +02:00
Julien Fontanet
fb44eea06c fix(xo-server): load globally installed plugin
It has been broken by d4526e1ed

Thanks @Danp2 for the report!
2020-06-02 20:44:33 +02:00
Julien Fontanet
3ea4c757e6 feat(backups-cli): clean checksums for missing XVAs 2020-06-02 15:27:36 +02:00
badrAZ
cfb8d79049 feat: release 5.47.1 (#5043) 2020-06-02 13:40:36 +02:00
badrAZ
1ea86da7af feat: technical release (patch) (#5042) 2020-06-02 13:02:21 +02:00
Julien Fontanet
e289f2dba2 fix(auth-ldap/authenticate): dont use second param as logger (#5039) 2020-06-02 11:11:25 +02:00
Rajaa.BARHTAOUI
7f64cd1801 feat(xo-web/vm/network): show IPs in front of their VIFs (#5003)
Fixes #4882
2020-06-02 09:26:31 +02:00
Julien Fontanet
d4526e1ed2 feat(xo-server): load @xen-orchestra/server-* plugins 2020-06-01 21:06:04 +02:00
Julien Fontanet
34f42216c8 feat(*/README.md): better badges 2020-06-01 14:41:52 +02:00
Julien Fontanet
a26a24a8ad fix(normalize-packages): dont replace author in READMEs 2020-06-01 14:06:09 +02:00
Julien Fontanet
4530fd4164 chore(*/package.json): normalize author to Vates SAS 2020-06-01 14:06:09 +02:00
Olivier Lambert
9156b8f48c feat(docs): merge and clean old docs into new one (#5025) 2020-06-01 11:32:27 +02:00
Julien Fontanet
6212109fc1 feat(@vates/read-chunk): 0.1.1 2020-05-29 15:10:20 +02:00
Julien Fontanet
c22a080e23 fix(read-chunk): not an ES module 2020-05-29 15:10:20 +02:00
Julien Fontanet
834a7109f9 fix: add @vates to list of workspaces 2020-05-29 15:10:20 +02:00
badrAZ
7cbf32202d feat: release 5.47.0 (#5031) 2020-05-29 12:29:41 +02:00
Julien Fontanet
d0b9380dca fix(*/README): license link 2020-05-28 21:24:20 +02:00
Julien Fontanet
13fd9be566 feat(*/README.md): re-generate for all packages 2020-05-28 20:56:14 +02:00
Julien Fontanet
53a9aa6ad2 fix(vhd-lib): dont prefer global install 2020-05-28 20:29:10 +02:00
Julien Fontanet
c2ce4aca1b feat(async-map/package.json): add description 2020-05-28 20:28:52 +02:00
Julien Fontanet
567f6d7cc0 fix: prefer global install for CLIs 2020-05-28 20:28:28 +02:00
Julien Fontanet
489c0b27f9 fix: use AGPL-3.0-or-later licenses for xo pkgs 2020-05-28 20:27:39 +02:00
Julien Fontanet
343f988584 fix: use AGPL-3.0-or-later license identifier
`AGPL-3.0` identifier has been deprecated by SPDX.org
2020-05-28 20:26:55 +02:00
Julien Fontanet
7f676c56c8 feat(normalize-packages): add license to all packages 2020-05-28 16:05:55 +02:00
Julien Fontanet
3c0ca7026f fix(read-chunk/README): fix pkg name 2020-05-28 16:05:55 +02:00
Rajaa.BARHTAOUI
2ceba11aa7 feat: technical release (#5028) 2020-05-28 16:04:13 +02:00
Julien Fontanet
6db5e0b27c feat(@vates/read-chunk): 0.1.0 2020-05-28 14:49:00 +02:00
Julien Fontanet
dfecb801db fix(read-chunk/USAGE): fix pkg name 2020-05-28 14:48:27 +02:00
Julien Fontanet
c10bfe3db2 feat(read-chunk): module to read data from stream 2020-05-28 14:45:22 +02:00
Julien Fontanet
20fb2c99bc feat(normalize-packages): generate README.md from USAGE.md 2020-05-28 14:45:21 +02:00
Pierre Donias
b4a0b5c58b fix(xo-server/setData): dont wait for update if no changes (#5027)
If an other_config entry is set to the same value as it was before, the object
will not be updated and the _waitObject promise will "never" resolve.
2020-05-28 14:01:54 +02:00
badrAZ
faa46c2a21 fix(xo-web/modal#form): defer rendering until all props are set (#5024)
The set default value is not used in the first render due to a race condition between the modal render and the modal value.

To avoid this race condition and multiple renders, the modal should open at the end of the set of its properties.
2020-05-28 11:19:25 +02:00
badrAZ
9691199ae8 feat(xo-web/proxies): start trial if no license available (#5022)
See xoa#54
2020-05-28 10:58:43 +02:00
Rajaa.BARHTAOUI
f736381933 feat(xo-web/home): ability to list VMs which are (not) backed up (#4974)
Fixes #4777
2020-05-28 10:10:34 +02:00
Julien Fontanet
f44e5b3b7a fix(babel-config): remove auto useBuiltIns
Too magical, caused too much problems.
2020-05-27 10:25:41 +02:00
Julien Fontanet
6e24bf5f8c fix(xo-{cli,upload-ova}): remove deprecated dep @babel/polyfill 2020-05-27 10:13:54 +02:00
Julien Fontanet
8fb43e31c5 fix(babel-config): useBuiltIns only if core-js
Because `@babel/polyfill` is deprecated.

See https://babeljs.io/docs/en/babel-preset-env#usebuiltins
2020-05-27 10:13:30 +02:00
Julien Fontanet
0860c80e51 fix(*/README): legacy project URLs 2020-05-27 10:04:00 +02:00
Julien Fontanet
66fc25756f Revert "feat(xo-server): load plugins from mono-repo"
This reverts commit 3e3ce543a8.

This can cause problems due to duplicate loading.
2020-05-27 09:53:46 +02:00
Olivier Lambert
f008e240cd feat(docs/installation): banner explanation (#5023) 2020-05-27 09:44:36 +02:00
BenjiReis
8d7e95d6e9 feat(sdn-controller/new network): choose preferred center (#5000)
Fixes #4991
2020-05-26 16:56:46 +02:00
Julien Fontanet
3e3ce543a8 feat(xo-server): load plugins from mono-repo 2020-05-26 14:01:43 +02:00
Julien Fontanet
6c447a82f1 chore(audit-core): remove unused dev dep 2020-05-26 13:59:06 +02:00
Julien Fontanet
64a0918ff1 chore(yarn.lock): update dependencies 2020-05-26 13:59:06 +02:00
Nicolas Raynaud
9274223701 chore(server): dont handle errors in registerHttpRequest() handlers (#5021)
`registerHttpRequest()` has built in error handling/logging.
2020-05-26 12:19:24 +02:00
Pierre Donias
1368c18844 feat(xo-web): disclaimer banner cooldown (#5008) 2020-05-26 10:59:10 +02:00
Pierre Donias
67a60a7557 feat(xo-web/config): UI for the encrypted config export/import (#4997)
See 1da889e420
2020-05-25 14:58:48 +02:00
badrAZ
3d5fd47748 feat(audit): record failed users' connection attempt (#4900)
See #4844
2020-05-25 12:48:05 +02:00
Julien Fontanet
b9a18807ae fix(xo-server/_handleHttpRequest): dont write if something has been sent 2020-05-25 09:58:03 +02:00
marcpezin
088c0b6321 fix(docs): replace the delta backup image with the right file type (#5019)
Replace `.xva` with `.vhd`
2020-05-25 09:23:30 +02:00
Nicolas Raynaud
ecee11a24c feat(xo-server/OVA import): comptibility with version 2.0 (#4921) 2020-05-25 08:50:32 +02:00
Olivier Lambert
ec8df7ce57 fix(docs): properly add license management to menu (#5018) 2020-05-23 22:16:58 +02:00
Julien Fontanet
4159fd2ffb fix(backups-cli/_fs/readdir2): dont warn on missing dir 2020-05-22 23:48:15 +02:00
Julien Fontanet
1a1d21bbb3 fix(backups-cli/clean-vms): isValidXva is a named import 2020-05-22 23:48:14 +02:00
marcpezin
be1045fed9 feat(docs): license management (#5015)
* licensing documentation

How to bind license in XOA

* Update license-management.md

* Add files via upload

license activation image

* fix links

* fix wording

* adding license management in menu

* Update license-management.md
2020-05-22 16:47:14 +02:00
Olivier Lambert
e43773c712 feat(docs/delta_backups): manual initial seed (#5011) 2020-05-22 14:03:07 +02:00
Olivier Lambert
30d69dadbb feat(docs): major rewrite using vue-press (#4957) 2020-05-21 11:46:24 +02:00
badrAZ
b138438036 feat(xo-server#_listVmBackupsOnRemote): remove unused properties (#4985)
This reduce by 10/100 times the quantity of data sent to the clients, which should improve performance.
2020-05-19 14:25:33 +02:00
badrAZ
d649211330 fix(xo-server-backup-reports): fix No recipients defined error (#4998)
Correctly fall back to plugin recipients if `mailRecipients` is empty.
2020-05-19 14:22:24 +02:00
Nicolas Raynaud
6cf211a9ad fix(xo-server-perf-alert): make message compatible with XenCenter (#5004) 2020-05-18 14:56:11 +02:00
badrAZ
3388e5e8a4 fix(xen-api#_sessionOpen): don't log credentials on error (#4995) 2020-05-18 09:47:52 +02:00
Julien Fontanet
5d497a1908 fix(xo-server-auth-ldap): use this._tlsOptions in bind
Introduced by 5870f6f73
2020-05-17 16:48:57 +02:00
Julien Fontanet
c820646fb6 fix(xo-server-auth-ldap): upgrade ldapts dep
Necessary for 5870f6f73

Also, only pass `tlsOptions` when creating client when using `ldaps`.
2020-05-17 16:11:24 +02:00
Julien Fontanet
5870f6f734 feat(xo-server-auth-ldap): support startTLS
Fixes #4999
2020-05-17 15:24:01 +02:00
Julien Fontanet
6732150121 fix(xo-server-auth-ldap): dont pass bindDN and `bindCredentials to LDAP client
It was left from an older version of the code which was working differently.
2020-05-17 12:47:08 +02:00
Pierre Donias
1dead8b080 feat(xo-web/modal/form): don't close on click or escape (#5002)
See xoa-support#2469

When the user clicks outside of a form modal or presses escape, don't close the
modal as all the information they entered so far will be lost.
2020-05-15 16:24:16 +02:00
BenjiReis
d547aa8ebd fix(sdn-controller): recreate both sides of tunnels when host updated (#4996)
To keep password synchronized.
2020-05-15 16:05:21 +02:00
Julien Fontanet
1da889e420 feat(xo-server/xo.{export,import}Config}): support passphrase (#4935) 2020-05-14 15:23:45 +02:00
Pierre Donias
5d0a308d1d fix(xo-web/SortedTable): don't inject empty filter if there isn't a URL param (#4994) 2020-05-14 11:11:05 +02:00
badrAZ
f9886d52da feat(usage-report): include CSV raw data files to sent email (#4979)
Fixes #4970
2020-05-13 14:42:46 +02:00
badrAZ
4f8e48b7d4 fix(CHANGELOG.unreleased): add missing entry (#4992) 2020-05-12 12:26:22 +02:00
Rajaa.BARHTAOUI
258e07c2ca feat(xo-web/render-xo-item): add pool name to networks (#4990)
Fixes #4885
2020-05-12 11:24:24 +02:00
badrAZ
cc32c50665 feat(xo-web/xoa): display proxy licenses (#4944)
There are three license states:
- license bound to an `XOA` which **is** linked to a proxy: display proxy link
- license bound to an `XOA` which **isn't** linked to a proxy: display `License attached to an unknown proxy` 
- license not bound to any `XOA`: display `No proxy attached`
2020-05-12 09:17:19 +02:00
Ben
ec1d91f73e fix(@xen-orchestra/fs): fix mount for FreeBSD (#4988) (#4989) 2020-05-12 00:08:34 +02:00
badrAZ
eb2f429964 fix(xo-server#getAllRemotesInfo): ignore disabled remotes 2020-05-11 17:49:58 +02:00
Julien Fontanet
1ad067309d fix(xo-cli/README): link to xo-upload-ova 2020-05-11 17:30:12 +02:00
Julien Fontanet
48ce7df43a feat(xo-server/file restore): dedupe mount/unmount (#4961) 2020-05-07 17:36:37 +02:00
Pierre Donias
6555e2c440 chore(xo-web/VM): move boot order setting from Disks to Advanced (#4975)
See #1523
2020-05-07 09:37:00 +02:00
badrAZ
a05191e112 fix(xo-server/proxies): remove duplicated license unbind (#4977)
On destroying a proxy, the license will be unbound twice: on the proxy destruction and on the proxy unregistration.

These unbinds will lead to an error which will be displayed in the console and will have no side effects on the process. This error will not be visible by users.
2020-05-06 14:23:04 +02:00
1016 changed files with 42396 additions and 30852 deletions

View File

@@ -1,13 +1,5 @@
module.exports = {
extends: [
'plugin:eslint-comments/recommended',
'standard',
'standard-jsx',
'prettier',
'prettier/standard',
'prettier/react',
],
extends: ['plugin:eslint-comments/recommended', 'standard', 'standard-jsx', 'prettier'],
globals: {
__DEV__: true,
$Dict: true,
@@ -48,9 +40,5 @@ module.exports = {
'lines-between-class-members': 'off',
'no-console': ['error', { allow: ['warn', 'error'] }],
'no-var': 'error',
'node/no-extraneous-import': 'error',
'node/no-extraneous-require': 'error',
'prefer-const': 'error',
},
}

4
.gitignore vendored
View File

@@ -4,11 +4,15 @@
/lerna-debug.log
/lerna-debug.log.*
/@vates/*/dist/
/@vates/*/node_modules/
/@xen-orchestra/*/dist/
/@xen-orchestra/*/node_modules/
/packages/*/dist/
/packages/*/node_modules/
/@xen-orchestra/proxy/src/app/mixins/index.js
/packages/vhd-cli/src/commands/index.js
/packages/xen-api/examples/node_modules/

View File

@@ -1,6 +1,11 @@
module.exports = {
arrowParens: 'avoid',
jsxSingleQuote: true,
semi: false,
singleQuote: true,
trailingComma: 'es5',
// 2020-11-24: Requested by nraynaud and approved by the rest of the team
//
// https://team.vates.fr/vates/pl/a1i8af1b9id7pgzm3jcg4toacy
printWidth: 120,
}

View File

@@ -1,7 +1,6 @@
language: node_js
node_js:
#- stable # disable for now due to an issue of indirect dep upath with Node 9
- 8
- 14
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,46 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/coalesce-calls
[![Package Version](https://badgen.net/npm/v/@vates/coalesce-calls)](https://npmjs.org/package/@vates/coalesce-calls) ![License](https://badgen.net/npm/license/@vates/coalesce-calls) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/coalesce-calls)](https://bundlephobia.com/result?p=@vates/coalesce-calls) [![Node compatibility](https://badgen.net/npm/node/@vates/coalesce-calls)](https://npmjs.org/package/@vates/coalesce-calls)
> Wraps an async function so that concurrent calls will be coalesced
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/coalesce-calls):
```
> npm install --save @vates/coalesce-calls
```
## Usage
```js
import { coalesceCalls } from '@vates/coalesce-calls'
const connect = coalesceCalls(async function () {
// async operation
})
connect()
// the previous promise result will be returned if the operation is not
// complete yet
connect()
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,13 @@
```js
import { coalesceCalls } from '@vates/coalesce-calls'
const connect = coalesceCalls(async function () {
// async operation
})
connect()
// the previous promise result will be returned if the operation is not
// complete yet
connect()
```

View File

@@ -0,0 +1,14 @@
exports.coalesceCalls = function (fn) {
let promise
const clean = () => {
promise = undefined
}
return function () {
if (promise !== undefined) {
return promise
}
promise = fn.apply(this, arguments)
promise.then(clean, clean)
return promise
}
}

View File

@@ -0,0 +1,33 @@
/* eslint-env jest */
const { coalesceCalls } = require('./')
const pDefer = () => {
const r = {}
r.promise = new Promise((resolve, reject) => {
r.reject = reject
r.resolve = resolve
})
return r
}
describe('coalesceCalls', () => {
it('decorates an async function', async () => {
const fn = coalesceCalls(promise => promise)
const defer1 = pDefer()
const promise1 = fn(defer1.promise)
const defer2 = pDefer()
const promise2 = fn(defer2.promise)
defer1.resolve('foo')
expect(await promise1).toBe('foo')
expect(await promise2).toBe('foo')
const defer3 = pDefer()
const promise3 = fn(defer3.promise)
defer3.resolve('bar')
expect(await promise3).toBe('bar')
})
})

View File

@@ -0,0 +1,38 @@
{
"private": false,
"name": "@vates/coalesce-calls",
"description": "Wraps an async function so that concurrent calls will be coalesced",
"keywords": [
"async",
"calls",
"coalesce",
"decorate",
"decorator",
"merge",
"promise",
"wrap",
"wrapper"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/coalesce-calls",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/coalesce-calls",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"files": [
"index.js"
],
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "ISC",
"version": "0.1.0",
"engines": {
"node": ">=8.10"
},
"scripts": {
"postversion": "npm publish --access public"
}
}

1
@vates/compose/.npmignore Symbolic link
View File

@@ -0,0 +1 @@
../../scripts/npmignore

81
@vates/compose/README.md Normal file
View File

@@ -0,0 +1,81 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/compose
[![Package Version](https://badgen.net/npm/v/@vates/compose)](https://npmjs.org/package/@vates/compose) ![License](https://badgen.net/npm/license/@vates/compose) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/compose)](https://bundlephobia.com/result?p=@vates/compose) [![Node compatibility](https://badgen.net/npm/node/@vates/compose)](https://npmjs.org/package/@vates/compose)
> Compose functions from left to right
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/compose):
```
> npm install --save @vates/compose
```
## Usage
```js
import { compose } from '@vates/compose'
const add2 = x => x + 2
const mul3 = x => x * 3
// const f = x => mul3(add2(x))
const f = compose(add2, mul3)
console.log(f(5))
// → 21
```
> The call context (`this`) of the composed function is forwarded to all functions.
The first function is called with all arguments of the composed function:
```js
const add = (x, y) => x + y
const mul3 = x => x * 3
// const f = (x, y) => mul3(add(x, y))
const f = compose(add, mul3)
console.log(f(4, 5))
// → 27
```
Functions may also be passed in an array:
```js
const f = compose([add2, mul3])
```
Options can be passed as first parameter:
```js
const f = compose(
{
// compose async functions
async: true,
// compose from right to left
right: true,
},
[add2, mul3]
)
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

48
@vates/compose/USAGE.md Normal file
View File

@@ -0,0 +1,48 @@
```js
import { compose } from '@vates/compose'
const add2 = x => x + 2
const mul3 = x => x * 3
// const f = x => mul3(add2(x))
const f = compose(add2, mul3)
console.log(f(5))
// → 21
```
> The call context (`this`) of the composed function is forwarded to all functions.
The first function is called with all arguments of the composed function:
```js
const add = (x, y) => x + y
const mul3 = x => x * 3
// const f = (x, y) => mul3(add(x, y))
const f = compose(add, mul3)
console.log(f(4, 5))
// → 27
```
Functions may also be passed in an array:
```js
const f = compose([add2, mul3])
```
Options can be passed as first parameter:
```js
const f = compose(
{
// compose async functions
async: true,
// compose from right to left
right: true,
},
[add2, mul3]
)
```

46
@vates/compose/index.js Normal file
View File

@@ -0,0 +1,46 @@
'use strict'
const defaultOpts = { async: false, right: false }
exports.compose = function compose(opts, fns) {
if (Array.isArray(opts)) {
fns = opts
opts = defaultOpts
} else if (typeof opts === 'object') {
opts = Object.assign({}, defaultOpts, opts)
if (!Array.isArray(fns)) {
fns = Array.prototype.slice.call(arguments, 1)
}
} else {
fns = Array.from(arguments)
opts = defaultOpts
}
const n = fns.length
if (n === 0) {
throw new TypeError('at least one function must be passed')
}
if (n === 1) {
return fns[0]
}
if (opts.right) {
fns.reverse()
}
return opts.async
? async function () {
let value = await fns[0].apply(this, arguments)
for (let i = 1; i < n; ++i) {
value = await fns[i].call(this, value)
}
return value
}
: function () {
let value = fns[0].apply(this, arguments)
for (let i = 1; i < n; ++i) {
value = fns[i].call(this, value)
}
return value
}
}

View File

@@ -0,0 +1,66 @@
/* eslint-env jest */
const { compose } = require('./')
const add2 = x => x + 2
const mul3 = x => x * 3
describe('compose()', () => {
it('throws when no functions is passed', () => {
expect(() => compose()).toThrow(TypeError)
expect(() => compose([])).toThrow(TypeError)
})
it('applies from left to right', () => {
expect(compose(add2, mul3)(5)).toBe(21)
})
it('accepts functions in an array', () => {
expect(compose([add2, mul3])(5)).toBe(21)
})
it('can apply from right to left', () => {
expect(compose({ right: true }, add2, mul3)(5)).toBe(17)
})
it('accepts options with functions in an array', () => {
expect(compose({ right: true }, [add2, mul3])(5)).toBe(17)
})
it('can compose async functions', async () => {
expect(
await compose(
{ async: true },
async x => x + 2,
async x => x * 3
)(5)
).toBe(21)
})
it('forwards all args to first function', () => {
expect.assertions(1)
const expectedArgs = [Math.random(), Math.random()]
compose(
(...args) => {
expect(args).toEqual(expectedArgs)
},
// add a second function to avoid the one function special case
Function.prototype
)(...expectedArgs)
})
it('forwards context to all functions', () => {
expect.assertions(2)
const expectedThis = {}
compose(
function () {
expect(this).toBe(expectedThis)
},
function () {
expect(this).toBe(expectedThis)
}
).call(expectedThis)
})
})

View File

@@ -0,0 +1,24 @@
{
"private": false,
"name": "@vates/compose",
"description": "Compose functions from left to right",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/compose",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/compose",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "ISC",
"version": "2.0.0",
"engines": {
"node": ">=7.6"
},
"scripts": {
"postversion": "npm publish --access public"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,45 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/decorate-with
[![Package Version](https://badgen.net/npm/v/@vates/decorate-with)](https://npmjs.org/package/@vates/decorate-with) ![License](https://badgen.net/npm/license/@vates/decorate-with) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/decorate-with)](https://bundlephobia.com/result?p=@vates/decorate-with) [![Node compatibility](https://badgen.net/npm/node/@vates/decorate-with)](https://npmjs.org/package/@vates/decorate-with)
> Creates a decorator from a function wrapper
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/decorate-with):
```
> npm install --save @vates/decorate-with
```
## Usage
For instance, allows using Lodash's functions as decorators:
```js
import { decorateWith } from '@vates/decorate-with'
class Foo {
@decorateWith(lodash.debounce, 150)
bar() {
// body
}
}
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,12 @@
For instance, allows using Lodash's functions as decorators:
```js
import { decorateWith } from '@vates/decorate-with'
class Foo {
@decorateWith(lodash.debounce, 150)
bar() {
// body
}
}
```

View File

@@ -0,0 +1,4 @@
exports.decorateWith = (fn, ...args) => (target, name, descriptor) => ({
...descriptor,
value: fn(descriptor.value, ...args),
})

View File

@@ -0,0 +1,30 @@
{
"private": false,
"name": "@vates/decorate-with",
"description": "Creates a decorator from a function wrapper",
"keywords": [
"apply",
"decorator",
"factory",
"wrapper"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/decorate-with",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/decorate-with",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "ISC",
"version": "0.0.1",
"engines": {
"node": ">=8.10"
},
"scripts": {
"postversion": "npm publish --access public"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,89 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/disposable
[![Package Version](https://badgen.net/npm/v/@vates/disposable)](https://npmjs.org/package/@vates/disposable) ![License](https://badgen.net/npm/license/@vates/disposable) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/disposable)](https://bundlephobia.com/result?p=@vates/disposable) [![Node compatibility](https://badgen.net/npm/node/@vates/disposable)](https://npmjs.org/package/@vates/disposable)
> Utilities for disposables
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/disposable):
```
> npm install --save @vates/disposable
```
## Usage
This library contains utilities for disposables as defined by the [`promise-toolbox` library](https://github.com/JsCommunity/promise-toolbox#resource-management).
### `deduped(fn, keyFn)`
Creates a new function that wraps `fn` and instead of creating a new disposables at each call, returns copies of the same one when `keyFn` returns the same keys.
Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed.
`keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together.
```js
import { deduped } from '@vates/disposable/deduped'
// the connection with the passed host will be established once at the first call, then, it will be shared with the next calls
const getConnection = deduped(async function (host)) {
const connection = new Connection(host)
return new Disposabe(connection, () => connection.close())
}, host => [host])
```
### `debounceResource(disposable, delay)`
Creates a new disposable with the same value and with a delayed disposer.
On calling this disposer, the source disposable will be disposed when the `delay` is passed.
```js
import { createDebounceResource } from '@vates/disposable/debounceResource'
const debounceResource = createDebounceResource()
// it will wait for 10 seconds before calling the disposer
using(debounceResource(getConnection(host), 10e3), connection => {})
```
### `debounceResource.flushAll()`
Disposes all delayed disposers and cancels the delaying of the disposables that are in usage.
```js
import { createDebounceResource } from '@vates/disposable/debounceResource'
const debounceResource = createDebounceResource()
const res1 = await debounceResource(res, 10e3)
const res2 = await debounceResource(res, 10e3)
const res3 = await debounceResource(res, 10e3)
rest1.dispose()
rest2.dispose()
// res3 is in usage
debounceResource.flushAll()
// res1 and res2 are immediately disposed
// res3 will be disposed immediately when its disposer will be called
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,56 @@
This library contains utilities for disposables as defined by the [`promise-toolbox` library](https://github.com/JsCommunity/promise-toolbox#resource-management).
### `deduped(fn, keyFn)`
Creates a new function that wraps `fn` and instead of creating a new disposables at each call, returns copies of the same one when `keyFn` returns the same keys.
Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed.
`keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together.
```js
import { deduped } from '@vates/disposable/deduped'
// the connection with the passed host will be established once at the first call, then, it will be shared with the next calls
const getConnection = deduped(async function (host)) {
const connection = new Connection(host)
return new Disposabe(connection, () => connection.close())
}, host => [host])
```
### `debounceResource(disposable, delay)`
Creates a new disposable with the same value and with a delayed disposer.
On calling this disposer, the source disposable will be disposed when the `delay` is passed.
```js
import { createDebounceResource } from '@vates/disposable/debounceResource'
const debounceResource = createDebounceResource()
// it will wait for 10 seconds before calling the disposer
using(debounceResource(getConnection(host), 10e3), connection => {})
```
### `debounceResource.flushAll()`
Disposes all delayed disposers and cancels the delaying of the disposables that are in usage.
```js
import { createDebounceResource } from '@vates/disposable/debounceResource'
const debounceResource = createDebounceResource()
const res1 = await debounceResource(res, 10e3)
const res2 = await debounceResource(res, 10e3)
const res3 = await debounceResource(res, 10e3)
rest1.dispose()
rest2.dispose()
// res3 is in usage
debounceResource.flushAll()
// res1 and res2 are immediately disposed
// res3 will be disposed immediately when its disposer will be called
```

View File

@@ -0,0 +1,56 @@
const { asyncMap } = require('@xen-orchestra/async-map')
const { createLogger } = require('@xen-orchestra/log')
const { warn } = createLogger('vates:disposable:debounceResource')
exports.createDebounceResource = () => {
const flushers = new Set()
async function debounceResource(pDisposable, delay = debounceResource.defaultDelay) {
if (delay === 0) {
return pDisposable
}
const disposable = await pDisposable
let timeoutId
const disposeWrapper = async () => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId)
timeoutId = undefined
flushers.delete(flusher)
try {
await disposable.dispose()
} catch (error) {
warn(error)
}
}
}
const flusher = () => {
const shouldDisposeNow = timeoutId !== undefined
if (shouldDisposeNow) {
return disposeWrapper()
} else {
// will dispose ASAP
delay = 0
}
}
flushers.add(flusher)
return {
dispose() {
timeoutId = setTimeout(disposeWrapper, delay)
},
value: disposable.value,
}
}
debounceResource.flushAll = () => {
// iterate on a sync way in order to not remove a flusher added on processing flushers
const promise = asyncMap(flushers, flush => flush())
flushers.clear()
return promise
}
return debounceResource
}

View File

@@ -0,0 +1,29 @@
/* eslint-env jest */
const { createDebounceResource } = require('./debounceResource')
jest.useFakeTimers()
describe('debounceResource()', () => {
it('calls the resource disposer after 10 seconds', async () => {
const debounceResource = createDebounceResource()
const delay = 10e3
const dispose = jest.fn()
const resource = await debounceResource(
Promise.resolve({
value: '',
dispose,
}),
delay
)
resource.dispose()
expect(dispose).not.toBeCalled()
jest.advanceTimersByTime(delay)
expect(dispose).toBeCalled()
})
})

View File

@@ -0,0 +1,52 @@
const ensureArray = require('ensure-array')
const { MultiKeyMap } = require('@vates/multi-key-map')
function State(factory) {
this.factory = factory
this.users = 0
}
const call = fn => fn()
exports.deduped = (factory, keyFn = (...args) => args) =>
(function () {
const states = new MultiKeyMap()
return function () {
const keys = ensureArray(keyFn.apply(this, arguments))
let state = states.get(keys)
if (state === undefined) {
const result = factory.apply(this, arguments)
const createFactory = disposable => {
const wrapper = {
dispose() {
if (--state.users === 0) {
states.delete(keys)
return disposable.dispose()
}
},
value: disposable.value,
}
return () => {
return wrapper
}
}
if (typeof result.then !== 'function') {
state = new State(createFactory(result))
} else {
result.catch(() => {
states.delete(keys)
})
const pFactory = result.then(createFactory)
state = new State(() => pFactory.then(call))
}
states.set(keys, state)
}
++state.users
return state.factory()
}
})()

View File

@@ -0,0 +1,76 @@
/* eslint-env jest */
const { deduped } = require('./deduped')
describe('deduped()', () => {
it('calls the resource function only once', async () => {
const value = {}
const getResource = jest.fn(async () => ({
value,
dispose: Function.prototype,
}))
const dedupedGetResource = deduped(getResource)
const { value: v1 } = await dedupedGetResource()
const { value: v2 } = await dedupedGetResource()
expect(getResource).toHaveBeenCalledTimes(1)
expect(v1).toBe(value)
expect(v2).toBe(value)
})
it('only disposes the source disposable when its all copies dispose', async () => {
const dispose = jest.fn()
const getResource = async () => ({
value: '',
dispose,
})
const dedupedGetResource = deduped(getResource)
const { dispose: d1 } = await dedupedGetResource()
const { dispose: d2 } = await dedupedGetResource()
d1()
expect(dispose).not.toHaveBeenCalled()
d2()
expect(dispose).toHaveBeenCalledTimes(1)
})
it('works with sync factory', () => {
const value = {}
const dispose = jest.fn()
const dedupedGetResource = deduped(() => ({ value, dispose }))
const d1 = dedupedGetResource()
expect(d1.value).toBe(value)
const d2 = dedupedGetResource()
expect(d2.value).toBe(value)
d1.dispose()
expect(dispose).not.toHaveBeenCalled()
d2.dispose()
expect(dispose).toHaveBeenCalledTimes(1)
})
it('no race condition on dispose before async acquisition', async () => {
const dispose = jest.fn()
const dedupedGetResource = deduped(async () => ({ value: 42, dispose }))
const d1 = await dedupedGetResource()
dedupedGetResource()
d1.dispose()
expect(dispose).not.toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,29 @@
{
"private": false,
"name": "@vates/disposable",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/disposable",
"description": "Utilities for disposables",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/disposable",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "ISC",
"version": "0.1.1",
"engines": {
"node": ">=8.10"
},
"scripts": {
"postversion": "npm publish --access public"
},
"dependencies": {
"@vates/multi-key-map": "^0.1.0",
"@xen-orchestra/log": "^0.2.0",
"ensure-array": "^1.0.0"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,53 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/multi-key-map
[![Package Version](https://badgen.net/npm/v/@vates/multi-key-map)](https://npmjs.org/package/@vates/multi-key-map) ![License](https://badgen.net/npm/license/@vates/multi-key-map) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/multi-key-map)](https://bundlephobia.com/result?p=@vates/multi-key-map) [![Node compatibility](https://badgen.net/npm/node/@vates/multi-key-map)](https://npmjs.org/package/@vates/multi-key-map)
> Create map with values affected to multiple keys
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/multi-key-map):
```
> npm install --save @vates/multi-key-map
```
## Usage
```js
import { MultiKeyMap } from '@vates/multi-key-map'
const map = new MultiKeyMap()
const OBJ = {}
map.set([], 0)
map.set(['foo'], 1)
map.set(['foo', 'bar'], 2)
map.set(['bar', 'foo'], 3)
map.set([OBJ], 4)
map.set([{}], 5)
map.get([]) // 0
map.get(['foo']) // 1
map.get(['foo', 'bar']) // 2
map.get(['bar', 'foo']) // 3
map.get([OBJ]) // 4
map.get([{}]) // undefined
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,20 @@
```js
import { MultiKeyMap } from '@vates/multi-key-map'
const map = new MultiKeyMap()
const OBJ = {}
map.set([], 0)
map.set(['foo'], 1)
map.set(['foo', 'bar'], 2)
map.set(['bar', 'foo'], 3)
map.set([OBJ], 4)
map.set([{}], 5)
map.get([]) // 0
map.get(['foo']) // 1
map.get(['foo', 'bar']) // 2
map.get(['bar', 'foo']) // 3
map.get([OBJ]) // 4
map.get([{}]) // undefined
```

View File

@@ -67,7 +67,7 @@ function set(node, i, keys, value) {
return node
}
export default class MultiKeyMap {
exports.MultiKeyMap = class MultiKeyMap {
constructor() {
// each node is either a value or a Node if it contains children
this._root = undefined

View File

@@ -1,6 +1,6 @@
/* eslint-env jest */
import MultiKeyMap from './_MultiKeyMap'
const { MultiKeyMap } = require('./')
describe('MultiKeyMap', () => {
it('works', () => {

View File

@@ -0,0 +1,28 @@
{
"private": false,
"name": "@vates/multi-key-map",
"description": "Create map with values affected to multiple keys",
"keywords": [
"cache",
"map"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/multi-key-map",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/multi-key-map",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "ISC",
"version": "0.1.0",
"engines": {
"node": ">=8.10"
},
"scripts": {
"postversion": "npm publish --access public"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,47 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/parse-duration
[![Package Version](https://badgen.net/npm/v/@vates/parse-duration)](https://npmjs.org/package/@vates/parse-duration) ![License](https://badgen.net/npm/license/@vates/parse-duration) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/parse-duration)](https://bundlephobia.com/result?p=@vates/parse-duration) [![Node compatibility](https://badgen.net/npm/node/@vates/parse-duration)](https://npmjs.org/package/@vates/parse-duration)
> Small wrapper around ms to parse a duration
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/parse-duration):
```
> npm install --save @vates/parse-duration
```
## Usage
`ms` without magic: always parse a duration and throws if invalid.
```js
import { parseDuration } from '@vates/parse-duration'
parseDuration('2 days')
// 172800000
parseDuration(172800000)
// 172800000
parseDuration(undefined)
// throws TypeError('not a valid duration: undefined')
```
## 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
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,14 @@
`ms` without magic: always parse a duration and throws if invalid.
```js
import { parseDuration } from '@vates/parse-duration'
parseDuration('2 days')
// 172800000
parseDuration(172800000)
// 172800000
parseDuration(undefined)
// throws TypeError('not a valid duration: undefined')
```

View File

@@ -1,6 +1,6 @@
import ms from 'ms'
const ms = require('ms')
export default value => {
exports.parseDuration = value => {
if (typeof value === 'number') {
return value
}

View File

@@ -0,0 +1,32 @@
{
"private": false,
"name": "@vates/parse-duration",
"description": "Small wrapper around ms to parse a duration",
"keywords": [
"duration",
"ms",
"parse"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/parse-duration",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/parse-duration",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "AGPL-3.0-or-later",
"version": "0.1.0",
"engines": {
"node": ">=8.10"
},
"dependencies": {
"ms": "^2.1.2"
},
"scripts": {
"postversion": "npm publish --access public"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,46 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/read-chunk
[![Package Version](https://badgen.net/npm/v/@vates/read-chunk)](https://npmjs.org/package/@vates/read-chunk) ![License](https://badgen.net/npm/license/@vates/read-chunk) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/read-chunk)](https://bundlephobia.com/result?p=@vates/read-chunk) [![Node compatibility](https://badgen.net/npm/node/@vates/read-chunk)](https://npmjs.org/package/@vates/read-chunk)
> Read a chunk of a Node stream
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/read-chunk):
```
> npm install --save @vates/read-chunk
```
## Usage
- returns the next available chunk of data
- like `stream.read()`, a number of bytes can be specified
- returns `null` if the stream has ended
```js
import { readChunk } from '@vates/read-chunk'
;(async () => {
let chunk
while ((chunk = await readChunk(stream, 1024)) !== null) {
// do something with chunk
}
})()
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,13 @@
- returns the next available chunk of data
- like `stream.read()`, a number of bytes can be specified
- returns `null` if the stream has ended
```js
import { readChunk } from '@vates/read-chunk'
;(async () => {
let chunk
while ((chunk = await readChunk(stream, 1024)) !== null) {
// do something with chunk
}
})()
```

View File

@@ -0,0 +1,30 @@
const readChunk = (stream, size) =>
size === 0
? Promise.resolve(Buffer.alloc(0))
: new Promise((resolve, reject) => {
function onEnd() {
resolve(null)
removeListeners()
}
function onError(error) {
reject(error)
removeListeners()
}
function onReadable() {
const data = stream.read(size)
if (data !== null) {
resolve(data)
removeListeners()
}
}
function removeListeners() {
stream.removeListener('end', onEnd)
stream.removeListener('error', onError)
stream.removeListener('readable', onReadable)
}
stream.on('end', onEnd)
stream.on('error', onError)
stream.on('readable', onReadable)
onReadable()
})
exports.readChunk = readChunk

View File

@@ -0,0 +1,43 @@
/* eslint-env jest */
const { Readable } = require('stream')
const { readChunk } = require('./')
const makeStream = it => Readable.from(it, { objectMode: false })
makeStream.obj = Readable.from
describe('readChunk', () => {
it('returns null if stream is empty', async () => {
expect(await readChunk(makeStream([]))).toBe(null)
})
describe('with binary stream', () => {
it('returns the first chunk of data', async () => {
expect(await readChunk(makeStream(['foo', 'bar']))).toEqual(Buffer.from('foo'))
})
it('returns a chunk of the specified size (smaller than first)', async () => {
expect(await readChunk(makeStream(['foo', 'bar']), 2)).toEqual(Buffer.from('fo'))
})
it('returns a chunk of the specified size (larger than first)', async () => {
expect(await readChunk(makeStream(['foo', 'bar']), 4)).toEqual(Buffer.from('foob'))
})
it('returns less data if stream ends', async () => {
expect(await readChunk(makeStream(['foo', 'bar']), 10)).toEqual(Buffer.from('foobar'))
})
it('returns an empty buffer if the specified size is 0', async () => {
expect(await readChunk(makeStream(['foo', 'bar']), 0)).toEqual(Buffer.alloc(0))
})
})
describe('with object stream', () => {
it('returns the first chunk of data verbatim', async () => {
const chunks = [{}, {}]
expect(await readChunk(makeStream.obj(chunks))).toBe(chunks[0])
})
})
})

View File

@@ -0,0 +1,36 @@
{
"private": false,
"name": "@vates/read-chunk",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/read-chunk",
"description": "Read a chunk of a Node stream",
"license": "ISC",
"keywords": [
"async",
"chunk",
"data",
"node",
"promise",
"read",
"stream"
],
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/read-chunk",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"version": "0.1.2",
"engines": {
"node": ">=8.10"
},
"files": [
"index.js"
],
"scripts": {
"postversion": "npm publish --access public"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,59 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @vates/toggle-scripts
[![Package Version](https://badgen.net/npm/v/@vates/toggle-scripts)](https://npmjs.org/package/@vates/toggle-scripts) ![License](https://badgen.net/npm/license/@vates/toggle-scripts) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@vates/toggle-scripts)](https://bundlephobia.com/result?p=@vates/toggle-scripts) [![Node compatibility](https://badgen.net/npm/node/@vates/toggle-scripts)](https://npmjs.org/package/@vates/toggle-scripts)
> Easily enable/disable scripts in package.json
## Install
Installation of the [npm package](https://npmjs.org/package/@vates/toggle-scripts):
```
> npm install --save @vates/toggle-scripts
```
## Usage
```
Usage: toggle-scripts options...
Easily enable/disable scripts in package.json
Options
+<script> Enable the script <script>, ie remove the prefix `_`
-<script> Disable the script <script>, ie prefix it with `_`
Examples
toggle-scripts +postinstall +preuninstall
toggle-scripts -postinstall -preuninstall
```
For example, if you want `postinstall` hook only in dev:
```json
// package.json
{
"scripts": {
"postinstall": "<some dev only command>",
"prepublishOnly": "toggle-scripts -postinstall",
"postpublish": "toggle-scripts +postinstall"
}
}
```
## 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](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,26 @@
```
Usage: toggle-scripts options...
Easily enable/disable scripts in package.json
Options
+<script> Enable the script <script>, ie remove the prefix `_`
-<script> Disable the script <script>, ie prefix it with `_`
Examples
toggle-scripts +postinstall +preuninstall
toggle-scripts -postinstall -preuninstall
```
For example, if you want `postinstall` hook only in dev:
```json
// package.json
{
"scripts": {
"postinstall": "<some dev only command>",
"prepublishOnly": "toggle-scripts -postinstall",
"postpublish": "toggle-scripts +postinstall"
}
}
```

60
@vates/toggle-scripts/index.js Executable file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env node
const fs = require('fs')
const mapKeys = (object, iteratee) => {
const result = {}
for (const key of Object.keys(object)) {
result[iteratee(key, object)] = object[key]
}
return result
}
const args = process.argv.slice(2)
if (args.length === 0) {
const { description, name, version } = require('./package.json')
const bin = 'toggle-scripts'
process.stdout.write(`Usage: ${bin} options...
${description}
Options
+<script> Enable the script <script>, ie remove the prefix \`_\`
-<script> Disable the script <script>, ie prefix it with \`_\`
Examples
${bin} +postinstall +preuninstall
${bin} -postinstall -preuninstall
${name} v${version}
`)
process.exit()
}
const plan = { __proto__: null }
for (const arg of args) {
const action = arg[0]
const script = arg.slice(1)
if (action === '+') {
plan['_' + script] = script
} else if (action === '-') {
plan[script] = '_' + script
} else {
throw new Error('invalid param: ' + arg)
}
}
const pkgPath = process.env.npm_package_json || './package.json'
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
pkg.scripts = mapKeys(pkg.scripts, (name, scripts) => {
const newName = plan[name]
if (newName === undefined) {
return name
}
if (newName in scripts) {
throw new Error('script already defined: ' + name)
}
return newName
})
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')

View File

@@ -0,0 +1,41 @@
{
"private": false,
"name": "@vates/toggle-scripts",
"description": "Easily enable/disable scripts in package.json",
"keywords": [
"dev",
"disable",
"enable",
"lifecycle",
"npm",
"package.json",
"pinst",
"postinstall",
"script",
"scripts",
"toggle"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/toggle-scripts",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@vates/toggle-scripts",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "ISC",
"version": "1.0.0",
"engines": {
"node": ">=6"
},
"files": [
"index.js"
],
"bin": "./index.js",
"scripts": {
"postversion": "npm publish --access public"
}
}

View File

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

View File

@@ -1,24 +0,0 @@
/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 @@
../../scripts/npmignore

View File

@@ -1,6 +1,10 @@
# @xen-orchestra/async-map [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
> ${pkg.description}
# @xen-orchestra/async-map
[![Package Version](https://badgen.net/npm/v/@xen-orchestra/async-map)](https://npmjs.org/package/@xen-orchestra/async-map) ![License](https://badgen.net/npm/license/@xen-orchestra/async-map) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@xen-orchestra/async-map)](https://bundlephobia.com/result?p=@xen-orchestra/async-map) [![Node compatibility](https://badgen.net/npm/node/@xen-orchestra/async-map)](https://npmjs.org/package/@xen-orchestra/async-map)
> Promise.all + map for all iterables
## Install
@@ -12,25 +16,61 @@ Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/async
## Usage
**TODO**
### `asyncMap(iterable, iteratee, thisArg = iterable)`
## Development
Similar to `Promise.all + Array#map` for all iterables: calls `iteratee` for each item in `iterable`, and returns a promise of an array containing the awaited result of each calls to `iteratee`.
It rejects as soon as te first call to `iteratee` rejects.
```js
import { asyncMap } from '@xen-orchestra/async-map'
const array = await asyncMap(iterable, iteratee, thisArg)
```
# Install dependencies
> yarn
# Run the tests
> yarn test
It can be used with any iterables (`Array`, `Map`, etc.):
# Continuously compile
> yarn dev
```js
const map = new Map()
map.set('foo', 42)
map.set('bar', 3.14)
# Continuously run the tests
> yarn dev-test
const array = await asyncMap(map, async function ([key, value]) {
// TODO: do async computation
//
// the map can be accessed via `this`
})
```
# Build for production (automatically called by npm install)
> yarn build
#### Use with plain objects
Plain objects are not iterable, but you can use `Object.keys`, `Object.values` or `Object.entries` to help:
```js
const object = {
foo: 42,
bar: 3.14,
}
const array = await asyncMap(
Object.entries(object),
async function ([key, value]) {
// TODO: do async computation
//
// the object can be accessed via `this` because it's been passed as third arg
},
object
)
```
### `asyncMapSettled(iterable, iteratee, thisArg = iterable)`
Similar to `asyncMap` but waits for all promises to settle before rejecting.
```js
import { asyncMapSettled } from '@xen-orchestra/async-map'
const array = await asyncMapSettled(iterable, iteratee, thisArg)
```
## Contributions
@@ -46,4 +86,4 @@ You may:
## License
ISC © [Vates SAS](https://vates.fr)
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,56 @@
### `asyncMap(iterable, iteratee, thisArg = iterable)`
Similar to `Promise.all + Array#map` for all iterables: calls `iteratee` for each item in `iterable`, and returns a promise of an array containing the awaited result of each calls to `iteratee`.
It rejects as soon as te first call to `iteratee` rejects.
```js
import { asyncMap } from '@xen-orchestra/async-map'
const array = await asyncMap(iterable, iteratee, thisArg)
```
It can be used with any iterables (`Array`, `Map`, etc.):
```js
const map = new Map()
map.set('foo', 42)
map.set('bar', 3.14)
const array = await asyncMap(map, async function ([key, value]) {
// TODO: do async computation
//
// the map can be accessed via `this`
})
```
#### Use with plain objects
Plain objects are not iterable, but you can use `Object.keys`, `Object.values` or `Object.entries` to help:
```js
const object = {
foo: 42,
bar: 3.14,
}
const array = await asyncMap(
Object.entries(object),
async function ([key, value]) {
// TODO: do async computation
//
// the object can be accessed via `this` because it's been passed as third arg
},
object
)
```
### `asyncMapSettled(iterable, iteratee, thisArg = iterable)`
Similar to `asyncMap` but waits for all promises to settle before rejecting.
```js
import { asyncMapSettled } from '@xen-orchestra/async-map'
const array = await asyncMapSettled(iterable, iteratee, thisArg)
```

View File

@@ -0,0 +1,71 @@
const wrapCall = (fn, arg, thisArg) => {
try {
return Promise.resolve(fn.call(thisArg, arg))
} catch (error) {
return Promise.reject(error)
}
}
/**
* Similar to Promise.all + Array#map but supports all iterables and does not trigger ESLint array-callback-return
*
* WARNING: Does not handle plain objects
*
* @template Item,This
* @param {Iterable<Item>} iterable
* @param {(this: This, item: Item) => (Item | PromiseLike<Item>)} mapFn
* @param {This} [thisArg]
* @returns {Promise<Item[]>}
*/
exports.asyncMap = function asyncMap(iterable, mapFn, thisArg = iterable) {
return Promise.all(Array.from(iterable, mapFn, thisArg))
}
/**
* Like `asyncMap` but wait for all promises to settle before rejecting
*
* @template Item,This
* @param {Iterable<Item>} iterable
* @param {(this: This, item: Item) => (Item | PromiseLike<Item>)} mapFn
* @param {This} [thisArg]
* @returns {Promise<Item[]>}
*/
exports.asyncMapSettled = function asyncMapSettled(iterable, mapFn, thisArg = iterable) {
return new Promise((resolve, reject) => {
const onError = e => {
if (result !== undefined) {
error = e
result = undefined
}
if (--n === 0) {
reject(error)
}
}
const onValue = (i, value) => {
const hasError = result === undefined
if (!hasError) {
result[i] = value
}
if (--n === 0) {
if (hasError) {
reject(error)
} else {
resolve(result)
}
}
}
let n = 0
for (const item of iterable) {
const i = n++
wrapCall(mapFn, item, thisArg).then(value => onValue(i, value), onError)
}
if (n === 0) {
return resolve([])
}
let error
let result = new Array(n)
})
}

View File

@@ -0,0 +1,71 @@
/* eslint-env jest */
const { asyncMapSettled } = require('./')
const noop = Function.prototype
describe('asyncMapSettled', () => {
it('works', async () => {
const values = [Math.random(), Math.random()]
const spy = jest.fn(async v => v * 2)
const iterable = new Set(values)
// returns an array containing the result of each calls
expect(await asyncMapSettled(iterable, spy)).toEqual(values.map(value => value * 2))
for (let i = 0, n = values.length; i < n; ++i) {
// each call receive the current item as sole argument
expect(spy.mock.calls[i]).toEqual([values[i]])
// each call as this bind to the iterable
expect(spy.mock.instances[i]).toBe(iterable)
}
})
it('can use a specified thisArg', () => {
const thisArg = {}
const spy = jest.fn()
asyncMapSettled(['foo'], spy, thisArg)
expect(spy.mock.instances[0]).toBe(thisArg)
})
it('rejects only when all calls as resolved', async () => {
const defers = []
const promise = asyncMapSettled([1, 2], () => {
let resolve, reject
// eslint-disable-next-line promise/param-names
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
defers.push({ promise, resolve, reject })
return promise
})
let hasSettled = false
promise.catch(noop).then(() => {
hasSettled = true
})
const error = new Error()
defers[0].reject(error)
// wait for all microtasks to settle
await new Promise(resolve => setImmediate(resolve))
expect(hasSettled).toBe(false)
defers[1].resolve()
// wait for all microtasks to settle
await new Promise(resolve => setImmediate(resolve))
expect(hasSettled).toBe(true)
await expect(promise).rejects.toBe(error)
})
it('issues when latest promise rejects', async () => {
const error = new Error()
await expect(asyncMapSettled([1], () => Promise.reject(error))).rejects.toBe(error)
})
})

View File

@@ -9,14 +9,18 @@
// (V1, K) => MaybePromise<V2>
// ): Promise<V2[]>
import map from 'lodash/map'
const map = require('lodash/map')
// Similar to map() + Promise.all() but wait for all promises to
// settle before rejecting (with the first error)
const asyncMap = (collection, iteratee) => {
/**
* Similar to map() + Promise.all() but wait for all promises to settle before
* rejecting (with the first error)
*
* @deprecated Don't support iterables, please use new implementations
*/
module.exports = function asyncMapLegacy(collection, iteratee) {
let then
if (collection != null && typeof (then = collection.then) === 'function') {
return then.call(collection, collection => asyncMap(collection, iteratee))
return then.call(collection, collection => asyncMapLegacy(collection, iteratee))
}
let errorContainer
@@ -39,5 +43,3 @@ const asyncMap = (collection, iteratee) => {
return values
})
}
export { asyncMap as default }

View File

@@ -1,10 +1,17 @@
{
"private": false,
"name": "@xen-orchestra/async-map",
"version": "0.0.0",
"version": "0.1.2",
"license": "ISC",
"description": "",
"keywords": [],
"description": "Promise.all + map for all iterables",
"keywords": [
"array",
"async",
"iterable",
"map",
"settled",
"typescript"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/async-map",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
@@ -13,17 +20,13 @@
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@isonoe.net"
"name": "Vates SAS",
"url": "https://vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"dist/"
],
"browserslist": [
">2%"
"index.js",
"legacy.js"
],
"engines": {
"node": ">=6"
@@ -31,23 +34,7 @@
"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": "^6.0.3",
"rimraf": "^3.0.0"
},
"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",
"postversion": "npm publish"
}
}

View File

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

View File

@@ -1,24 +0,0 @@
/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 @@
../../scripts/npmignore

View File

@@ -0,0 +1,28 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @xen-orchestra/audit-core
[![Package Version](https://badgen.net/npm/v/@xen-orchestra/audit-core)](https://npmjs.org/package/@xen-orchestra/audit-core) ![License](https://badgen.net/npm/license/@xen-orchestra/audit-core) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@xen-orchestra/audit-core)](https://bundlephobia.com/result?p=@xen-orchestra/audit-core) [![Node compatibility](https://badgen.net/npm/node/@xen-orchestra/audit-core)](https://npmjs.org/package/@xen-orchestra/audit-core)
## Install
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/audit-core):
```
> npm install --save @xen-orchestra/audit-core
```
## 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
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)

View File

View File

@@ -7,7 +7,7 @@
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"version": "0.1.1",
"version": "0.2.0",
"engines": {
"node": ">=8.10"
},
@@ -26,15 +26,20 @@
"@babel/plugin-proposal-decorators": "^7.8.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.0",
"@babel/preset-env": "^7.7.4",
"@babel/preset-typescript": "^7.7.4",
"cross": "^1.0.0",
"rimraf": "^3.0.0"
},
"dependencies": {
"@xen-orchestra/log": "^0.2.0",
"core-js": "^3.6.4",
"golike-defer": "^0.4.1",
"golike-defer": "^0.5.1",
"lodash": "^4.17.15",
"object-hash": "^2.0.1"
},
"private": false
"private": false,
"license": "AGPL-3.0-or-later",
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
}
}

View File

@@ -4,6 +4,9 @@ import 'core-js/features/symbol/async-iterator'
import assert from 'assert'
import defer from 'golike-defer'
import hash from 'object-hash'
import { createLogger } from '@xen-orchestra/log'
const log = createLogger('xo:audit-core')
export class Storage {
constructor() {
@@ -25,7 +28,7 @@ export class Storage {
//
// http://man7.org/linux/man-pages/man3/crypt.3.html#NOTES
const ID_TO_ALGORITHM = {
'5': 'sha256',
5: 'sha256',
}
export class AlteredRecordError extends Error {
@@ -65,8 +68,17 @@ export class AuditCore {
@defer
async add($defer, subject, event, data) {
const time = Date.now()
$defer(await this._storage.acquireLock())
return this._addUnsafe({
data,
event,
subject,
time,
})
}
async _addUnsafe({ data, event, subject, time }) {
const storage = this._storage
$defer(await storage.acquireLock())
// delete "undefined" properties and normalize data with JSON.stringify
const record = JSON.parse(
@@ -107,9 +119,7 @@ export class AuditCore {
if (record === undefined) {
throw new MissingRecordError(newest, nValid)
}
if (
newest !== createHash(record, newest.slice(1, newest.indexOf('$', 1)))
) {
if (newest !== createHash(record, newest.slice(1, newest.indexOf('$', 1)))) {
throw new AlteredRecordError(newest, nValid, record)
}
newest = record.previousId
@@ -139,4 +149,45 @@ export class AuditCore {
await this._storage.del(id)
}
}
@defer
async deleteRangeAndRewrite($defer, newest, oldest) {
assert.notStrictEqual(newest, undefined)
assert.notStrictEqual(oldest, undefined)
const storage = this._storage
$defer(await storage.acquireLock())
assert.notStrictEqual(await storage.get(newest), undefined)
const oldestRecord = await storage.get(oldest)
assert.notStrictEqual(oldestRecord, undefined)
const lastId = await storage.getLastId()
const recentRecords = []
for await (const record of this.getFrom(lastId)) {
if (record.id === newest) {
break
}
recentRecords.push(record)
}
for await (const record of this.getFrom(newest)) {
await storage.del(record.id)
if (record.id === oldest) {
break
}
}
await storage.setLastId(oldestRecord.previousId)
for (const record of recentRecords) {
try {
await this._addUnsafe(record)
await storage.del(record.id)
} catch (error) {
log.error(error)
}
}
}
}

View File

@@ -1,12 +1,6 @@
/* eslint-env jest */
import {
AlteredRecordError,
AuditCore,
MissingRecordError,
NULL_ID,
Storage,
} from '.'
import { AlteredRecordError, AuditCore, MissingRecordError, NULL_ID, Storage } from '.'
const asyncIteratorToArray = async asyncIterator => {
const array = []
@@ -88,16 +82,13 @@ describe('auditCore', () => {
it('detects that a record is missing', async () => {
const [newestRecord, deletedRecord] = await storeAuditRecords()
const nValidRecords = await auditCore.checkIntegrity(
NULL_ID,
newestRecord.id
)
const nValidRecords = await auditCore.checkIntegrity(NULL_ID, newestRecord.id)
expect(nValidRecords).toBe(DATA.length)
await db.del(deletedRecord.id)
await expect(
auditCore.checkIntegrity(NULL_ID, newestRecord.id)
).rejects.toEqual(new MissingRecordError(deletedRecord.id, 1))
await expect(auditCore.checkIntegrity(NULL_ID, newestRecord.id)).rejects.toEqual(
new MissingRecordError(deletedRecord.id, 1)
)
})
it('detects that a record has been altered', async () => {
@@ -106,9 +97,7 @@ describe('auditCore', () => {
alteredRecord.event = ''
await db.put(alteredRecord)
await expect(
auditCore.checkIntegrity(NULL_ID, newestRecord.id)
).rejects.toEqual(
await expect(auditCore.checkIntegrity(NULL_ID, newestRecord.id)).rejects.toEqual(
new AlteredRecordError(alteredRecord.id, 1, alteredRecord)
)
})

View File

@@ -17,9 +17,10 @@ interface Record {
}
export class AuditCore {
constructor(storage: Storage) {}
public add(subject: any, event: string, data: any): Promise<Record> {}
public checkIntegrity(oldest: string, newest: string): Promise<number> {}
public getFrom(newest?: string): AsyncIterator {}
public deleteFrom(newest: string): Promise<void> {}
constructor(storage: Storage) { }
public add(subject: any, event: string, data: any): Promise<Record> { }
public checkIntegrity(oldest: string, newest: string): Promise<number> { }
public getFrom(newest?: string): AsyncIterator { }
public deleteFrom(newest: string): Promise<void> { }
public deleteRangeAndRewrite(newest: string, oldest: string): Promise<void> { }
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,18 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @xen-orchestra/babel-config
## 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
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)

View File

View File

@@ -32,27 +32,19 @@ const configs = {
}
return { browsers: pkg.browserslist, node }
})(),
useBuiltIns: '@babel/polyfill' in (pkg.dependencies || {}) && 'usage',
}
},
}
const getConfig = (key, ...args) => {
const config = configs[key]
return config === undefined
? {}
: typeof config === 'function'
? config(...args)
: config
return config === undefined ? {} : typeof config === 'function' ? config(...args) : config
}
// some plugins must be used in a specific order
const pluginsOrder = [
'@babel/plugin-proposal-decorators',
'@babel/plugin-proposal-class-properties',
]
const pluginsOrder = ['@babel/plugin-proposal-decorators', '@babel/plugin-proposal-class-properties']
module.exports = function(pkg, plugins, presets) {
module.exports = function (pkg, plugins, presets) {
plugins === undefined && (plugins = {})
presets === undefined && (presets = {})

View File

@@ -11,5 +11,10 @@
},
"engines": {
"node": ">=6"
},
"license": "AGPL-3.0-or-later",
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,48 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @xen-orchestra/backups-cli
[![Package Version](https://badgen.net/npm/v/@xen-orchestra/backups-cli)](https://npmjs.org/package/@xen-orchestra/backups-cli) ![License](https://badgen.net/npm/license/@xen-orchestra/backups-cli) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@xen-orchestra/backups-cli)](https://bundlephobia.com/result?p=@xen-orchestra/backups-cli) [![Node compatibility](https://badgen.net/npm/node/@xen-orchestra/backups-cli)](https://npmjs.org/package/@xen-orchestra/backups-cli)
## Install
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/backups-cli):
```
> npm install --global @xen-orchestra/backups-cli
```
## Usage
```
> xo-backups --help
Usage:
xo-backups clean-vms [--merge] [--remove] xo-vm-backups/*
Detects and repair issues with VM backups.
Options:
-m, --merge Merge (or continue merging) VHD files that are unused
-r, --remove Remove unused, incomplete, orphan, or corrupted files
xo-backups create-symlink-index xo-vm-backups <field path>
xo-backups info xo-vm-backups/*
```
## 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
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,17 @@
```
> xo-backups --help
Usage:
xo-backups clean-vms [--merge] [--remove] xo-vm-backups/*
Detects and repair issues with VM backups.
Options:
-m, --merge Merge (or continue merging) VHD files that are unused
-r, --remove Remove unused, incomplete, orphan, or corrupted files
xo-backups create-symlink-index xo-vm-backups <field path>
xo-backups info xo-vm-backups/*
```

View File

@@ -1,7 +0,0 @@
const curryRight = require('lodash/curryRight')
module.exports = curryRight((iterable, fn) =>
Promise.all(
Array.isArray(iterable) ? iterable.map(fn) : Array.from(iterable, fn)
)
)

View File

@@ -3,7 +3,7 @@ const getopts = require('getopts')
const { version } = require('./package.json')
module.exports = commands =>
async function(args, prefix) {
async function (args, prefix) {
const opts = getopts(args, {
alias: {
help: 'h',

View File

@@ -3,6 +3,17 @@ const { dirname } = require('path')
const fs = require('promise-toolbox/promisifyAll')(require('fs'))
module.exports = fs
fs.getSize = path =>
fs.stat(path).then(
_ => _.size,
error => {
if (error.code === 'ENOENT') {
return 0
}
throw error
}
)
fs.mktree = async function mkdirp(path) {
try {
await fs.mkdir(path)
@@ -34,14 +45,15 @@ fs.readdir2 = path =>
return entries
},
error => {
if (
error != null &&
(error.code === 'ENOENT' || error.code === 'ENOTDIR')
) {
const { code } = error
if (code === 'ENOENT') {
// do nothing
} else if (code === 'ENOTDIR') {
console.warn('WARN: readdir(%s)', path, error)
return []
} else {
throw error
}
throw error
return []
}
)

View File

@@ -1,308 +1,33 @@
#!/usr/bin/env node
// assigned when options are parsed by the main function
let force
// -----------------------------------------------------------------------------
const assert = require('assert')
const flatten = require('lodash/flatten')
const asyncMap = require('lodash/curryRight')(require('@xen-orchestra/async-map').asyncMap)
const getopts = require('getopts')
const isValidXva = require('@xen-orchestra/backups/isValidXva')
const lockfile = require('proper-lockfile')
const pipe = require('promise-toolbox/pipe')
const { default: Vhd } = require('vhd-lib')
const { dirname, resolve } = require('path')
const { DISK_TYPE_DIFFERENCING } = require('vhd-lib/dist/_constants')
const { RemoteAdapter } = require('@xen-orchestra/backups/RemoteAdapter')
const { resolve } = require('path')
const asyncMap = require('../_asyncMap')
const fs = require('../_fs')
const handler = require('@xen-orchestra/fs').getHandler({ url: 'file://' })
// -----------------------------------------------------------------------------
// chain is an array of VHDs from child to parent
//
// the whole chain will be merged into parent, parent will be renamed to child
// and all the others will deleted
async function mergeVhdChain(chain) {
assert(chain.length >= 2)
const child = chain[0]
const parent = chain[chain.length - 1]
const children = chain.slice(0, -1).reverse()
console.warn('Unused parents of VHD', child)
chain
.slice(1)
.reverse()
.forEach(parent => {
console.warn(' ', parent)
})
force && console.warn(' merging…')
console.warn('')
if (force) {
// `mergeVhd` does not work with a stream, either
// - make it accept a stream
// - or create synthetic VHD which is not a stream
return console.warn('TODO: implement merge')
// await mergeVhd(
// handler,
// parent,
// handler,
// children.length === 1
// ? child
// : await createSyntheticStream(handler, children)
// )
}
await Promise.all([
force && fs.rename(parent, child),
asyncMap(children.slice(0, -1), child => {
console.warn('Unused VHD', child)
force && console.warn(' deleting…')
console.warn('')
return force && handler.unlink(child)
}),
])
}
const listVhds = pipe([
vmDir => vmDir + '/vdis',
fs.readdir2,
asyncMap(fs.readdir2),
flatten,
asyncMap(fs.readdir2),
flatten,
_ => _.filter(_ => _.endsWith('.vhd')),
])
async function handleVm(vmDir) {
const vhds = new Set()
const vhdParents = { __proto__: null }
const vhdChildren = { __proto__: null }
// remove broken VHDs
await asyncMap(await listVhds(vmDir), async path => {
try {
const vhd = new Vhd(handler, path)
await vhd.readHeaderAndFooter()
vhds.add(path)
if (vhd.footer.diskType === DISK_TYPE_DIFFERENCING) {
const parent = resolve(dirname(path), vhd.header.parentUnicodeName)
vhdParents[path] = parent
if (parent in vhdChildren) {
const error = new Error(
'this script does not support multiple VHD children'
)
error.parent = parent
error.child1 = vhdChildren[parent]
error.child2 = path
throw error // should we throw?
}
vhdChildren[parent] = path
}
} catch (error) {
console.warn('Error while checking VHD', path)
console.warn(' ', error)
if (error != null && error.code === 'ERR_ASSERTION') {
force && console.warn(' deleting…')
console.warn('')
force && (await handler.unlink(path))
}
}
})
// remove VHDs with missing ancestors
{
const deletions = []
// return true if the VHD has been deleted or is missing
const deleteIfOrphan = vhd => {
const parent = vhdParents[vhd]
if (parent === undefined) {
return
}
// no longer needs to be checked
delete vhdParents[vhd]
deleteIfOrphan(parent)
if (!vhds.has(parent)) {
vhds.delete(vhd)
console.warn('Error while checking VHD', vhd)
console.warn(' missing parent', parent)
force && console.warn(' deleting…')
console.warn('')
force && deletions.push(handler.unlink(vhd))
}
}
// > A property that is deleted before it has been visited will not be
// > visited later.
// >
// > -- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#Deleted_added_or_modified_properties
for (const child in vhdParents) {
deleteIfOrphan(child)
}
await Promise.all(deletions)
}
const [jsons, xvas] = await fs
.readdir2(vmDir)
.then(entries => [
entries.filter(_ => _.endsWith('.json')),
new Set(entries.filter(_ => _.endsWith('.xva'))),
])
await asyncMap(xvas, async path => {
// check is not good enough to delete the file, the best we can do is report
// it
if (!(await isValidXva(path))) {
console.warn('Potential broken XVA', path)
console.warn('')
}
})
const unusedVhds = new Set(vhds)
const unusedXvas = new Set(xvas)
// compile the list of unused XVAs and VHDs, and remove backup metadata which
// reference a missing XVA/VHD
await asyncMap(jsons, async json => {
const metadata = JSON.parse(await fs.readFile(json))
const { mode } = metadata
if (mode === 'full') {
const linkedXva = resolve(vmDir, metadata.xva)
if (xvas.has(linkedXva)) {
unusedXvas.delete(linkedXva)
} else {
console.warn('Error while checking backup', json)
console.warn(' missing file', linkedXva)
force && console.warn(' deleting…')
console.warn('')
force && (await handler.unlink(json))
}
} else if (mode === 'delta') {
const linkedVhds = (() => {
const { vhds } = metadata
return Object.keys(vhds).map(key => resolve(vmDir, vhds[key]))
})()
// FIXME: find better approach by keeping as much of the backup as
// possible (existing disks) even if one disk is missing
if (linkedVhds.every(_ => vhds.has(_))) {
linkedVhds.forEach(_ => unusedVhds.delete(_))
} else {
console.warn('Error while checking backup', json)
const missingVhds = linkedVhds.filter(_ => !vhds.has(_))
console.warn(
' %i/%i missing VHDs',
missingVhds.length,
linkedVhds.length
)
missingVhds.forEach(vhd => {
console.warn(' ', vhd)
})
force && console.warn(' deleting…')
console.warn('')
force && (await handler.unlink(json))
}
}
})
// TODO: parallelize by vm/job/vdi
const unusedVhdsDeletion = []
{
// VHD chains (as list from child to ancestor) to merge indexed by last
// ancestor
const vhdChainsToMerge = { __proto__: null }
const toCheck = new Set(unusedVhds)
const getUsedChildChainOrDelete = vhd => {
if (vhd in vhdChainsToMerge) {
const chain = vhdChainsToMerge[vhd]
delete vhdChainsToMerge[vhd]
return chain
}
if (!unusedVhds.has(vhd)) {
return [vhd]
}
// no longer needs to be checked
toCheck.delete(vhd)
const child = vhdChildren[vhd]
if (child !== undefined) {
const chain = getUsedChildChainOrDelete(child)
if (chain !== undefined) {
chain.push(vhd)
return chain
}
}
console.warn('Unused VHD', vhd)
force && console.warn(' deleting…')
console.warn('')
force && unusedVhdsDeletion.push(handler.unlink(vhd))
}
toCheck.forEach(vhd => {
vhdChainsToMerge[vhd] = getUsedChildChainOrDelete(vhd)
})
Object.keys(vhdChainsToMerge).forEach(key => {
const chain = vhdChainsToMerge[key]
if (chain !== undefined) {
unusedVhdsDeletion.push(mergeVhdChain(chain))
}
})
}
await Promise.all([
unusedVhdsDeletion,
asyncMap(unusedXvas, path => {
console.warn('Unused XVA', path)
force && console.warn(' deleting…')
console.warn('')
return force && handler.unlink(path)
}),
])
}
// -----------------------------------------------------------------------------
const adapter = new RemoteAdapter(require('@xen-orchestra/fs').getHandler({ url: 'file://' }))
module.exports = async function main(args) {
const opts = getopts(args, {
const { _, remove, merge } = getopts(args, {
alias: {
force: 'f',
remove: 'r',
merge: 'm',
},
boolean: ['force'],
boolean: ['merge', 'remove'],
default: {
force: false,
merge: false,
remove: false,
},
})
;({ force } = opts)
await asyncMap(opts._, async vmDir => {
await asyncMap(_, async vmDir => {
vmDir = resolve(vmDir)
// TODO: implement this in `xo-server`, not easy because not compatible with
// `@xen-orchestra/fs`.
const release = await lockfile.lock(vmDir)
try {
await handleVm(vmDir)
await adapter.cleanVm(vmDir, { remove, merge, onLog: log => console.warn(log) })
} catch (error) {
console.error('handleVm', vmDir, error)
} finally {
await release()
console.error('adapter.cleanVm', vmDir, error)
}
})
}

View File

@@ -1,8 +1,8 @@
const filenamify = require('filenamify')
const get = require('lodash/get')
const { asyncMap } = require('@xen-orchestra/async-map')
const { dirname, join, relative } = require('path')
const asyncMap = require('../_asyncMap')
const { mktree, readdir2, readFile, symlink2 } = require('../_fs')
module.exports = async function createSymlinkIndex([backupDir, fieldPath]) {

View File

@@ -0,0 +1,54 @@
const groupBy = require('lodash/groupBy')
const { asyncMap } = require('@xen-orchestra/async-map')
const { createHash } = require('crypto')
const { dirname, resolve } = require('path')
const { readdir2, readFile, getSize } = require('../_fs')
const sha512 = str => createHash('sha512').update(str).digest('hex')
const sum = values => values.reduce((a, b) => a + b)
module.exports = async function info(vmDirs) {
const jsonFiles = (
await asyncMap(vmDirs, async vmDir => (await readdir2(vmDir)).filter(_ => _.endsWith('.json')))
).flat()
const hashes = { __proto__: null }
const info = (
await asyncMap(jsonFiles, async jsonFile => {
try {
const jsonDir = dirname(jsonFile)
const json = await readFile(jsonFile)
const hash = sha512(json)
if (hash in hashes) {
console.log(jsonFile, 'duplicate of', hashes[hash])
return
}
hashes[hash] = jsonFile
const metadata = JSON.parse(json)
return {
jsonDir,
jsonFile,
metadata,
size:
json.length +
(await (metadata.mode === 'delta'
? asyncMap(Object.values(metadata.vhds), _ => getSize(resolve(jsonDir, _))).then(sum)
: getSize(resolve(jsonDir, metadata.xva)))),
}
} catch (error) {
console.error(jsonFile, error)
}
})
).filter(_ => _ !== undefined)
const byJobs = groupBy(info, 'metadata.jobId')
Object.keys(byJobs)
.sort()
.forEach(jobId => {
console.log(jobId, sum(byJobs[jobId].map(_ => _.size)))
})
}

View File

@@ -5,7 +5,14 @@ require('./_composeCommands')({
get main() {
return require('./commands/clean-vms')
},
usage: '[--force] xo-vm-backups/*',
usage: `[--merge] [--remove] xo-vm-backups/*
Detects and repair issues with VM backups.
Options:
-m, --merge Merge (or continue merging) VHD files that are unused
-r, --remove Remove unused, incomplete, orphan, or corrupted files
`,
},
'create-symlink-index': {
get main() {
@@ -13,6 +20,12 @@ require('./_composeCommands')({
},
usage: 'xo-vm-backups <field path>',
},
info: {
get main() {
return require('./commands/info')
},
usage: 'xo-vm-backups/*',
},
})(process.argv.slice(2), 'xo-backups').catch(error => {
console.error('main', error)
process.exitCode = 1

View File

@@ -3,16 +3,17 @@
"bin": {
"xo-backups": "index.js"
},
"preferGlobal": true,
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"dependencies": {
"@xen-orchestra/backups": "^0.1.1",
"@xen-orchestra/fs": "^0.10.3",
"@xen-orchestra/async-map": "^0.1.2",
"@xen-orchestra/backups": "^0.9.1",
"@xen-orchestra/fs": "^0.14.0",
"filenamify": "^4.1.0",
"getopts": "^2.2.5",
"lodash": "^4.17.15",
"promise-toolbox": "^0.15.0",
"proper-lockfile": "^4.1.1",
"vhd-lib": "^0.7.2"
"promise-toolbox": "^0.18.0",
"vhd-lib": "^1.0.0"
},
"engines": {
"node": ">=7.10.1"
@@ -31,5 +32,10 @@
"scripts": {
"postversion": "npm publish --access public"
},
"version": "0.0.0"
"version": "0.5.0",
"license": "AGPL-3.0-or-later",
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
}
}

View File

@@ -0,0 +1 @@
../../scripts/npmignore

View File

@@ -0,0 +1,263 @@
const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
const Disposable = require('promise-toolbox/Disposable')
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const limitConcurrency = require('limit-concurrency-decorator').default
const { compileTemplate } = require('@xen-orchestra/template')
const { extractIdsFromSimplePattern } = require('./_extractIdsFromSimplePattern')
const { PoolMetadataBackup } = require('./_PoolMetadataBackup')
const { Task } = require('./Task')
const { VmBackup } = require('./_VmBackup')
const { XoMetadataBackup } = require('./_XoMetadataBackup')
const noop = Function.prototype
const getAdaptersByRemote = adapters => {
const adaptersByRemote = {}
adapters.forEach(({ adapter, remoteId }) => {
adaptersByRemote[remoteId] = adapter
})
return adaptersByRemote
}
const runTask = (...args) => Task.run(...args).catch(noop) // errors are handled by logs
exports.Backup = class Backup {
constructor({ config, getAdapter, getConnectedRecord, job, schedule }) {
this._config = config
this._getRecord = getConnectedRecord
this._job = job
this._schedule = schedule
this._getAdapter = Disposable.factory(function* (remoteId) {
return {
adapter: yield getAdapter(remoteId),
remoteId,
}
})
this._getSnapshotNameLabel = compileTemplate(config.snapshotNameLabelTpl, {
'{job.name}': job.name,
'{vm.name_label}': vm => vm.name_label,
})
}
run() {
const type = this._job.type
if (type === 'backup') {
return this._runVmBackup()
} else if (type === 'metadataBackup') {
return this._runMetadataBackup()
} else {
throw new Error(`No runner for the backup type ${type}`)
}
}
async _runMetadataBackup() {
const schedule = this._schedule
const job = this._job
const remoteIds = extractIdsFromSimplePattern(job.remotes)
if (remoteIds.length === 0) {
throw new Error('metadata backup job cannot run without remotes')
}
const config = this._config
const settings = {
...config.defaultSettings,
...config.metadata.defaultSettings,
...job.settings[''],
...job.settings[schedule.id],
}
const poolIds = extractIdsFromSimplePattern(job.pools)
const isEmptyPools = poolIds.length === 0
const isXoMetadata = job.xoMetadata !== undefined
if (!isXoMetadata && isEmptyPools) {
throw new Error('no metadata mode found')
}
const { retentionPoolMetadata, retentionXoMetadata } = settings
if (
(retentionPoolMetadata === 0 && retentionXoMetadata === 0) ||
(!isXoMetadata && retentionPoolMetadata === 0) ||
(isEmptyPools && retentionXoMetadata === 0)
) {
throw new Error('no retentions corresponding to the metadata modes found')
}
await Disposable.use(
Disposable.all(
poolIds.map(id =>
this._getRecord('pool', id).catch(error => {
// See https://github.com/vatesfr/xen-orchestra/commit/6aa6cfba8ec939c0288f0fa740f6dfad98c43cbb
runTask(
{
name: 'get pool record',
data: { type: 'pool', id },
},
() => Promise.reject(error)
)
})
)
),
Disposable.all(
remoteIds.map(id =>
this._getAdapter(id).catch(error => {
// See https://github.com/vatesfr/xen-orchestra/commit/6aa6cfba8ec939c0288f0fa740f6dfad98c43cbb
runTask(
{
name: 'get remote adapter',
data: { type: 'remote', id },
},
() => Promise.reject(error)
)
})
)
),
async (pools, remoteAdapters) => {
// remove adapters that failed (already handled)
remoteAdapters = remoteAdapters.filter(_ => _ !== undefined)
if (remoteAdapters.length === 0) {
return
}
remoteAdapters = getAdaptersByRemote(remoteAdapters)
// remove pools that failed (already handled)
pools = pools.filter(_ => _ !== undefined)
const promises = []
if (pools.length !== 0 && settings.retentionPoolMetadata !== 0) {
promises.push(
asyncMap(pools, async pool =>
runTask(
{
name: `Starting metadata backup for the pool (${pool.$id}). (${job.id})`,
data: {
id: pool.$id,
pool,
poolMaster: await ignoreErrors.call(pool.$xapi.getRecord('host', pool.master)),
type: 'pool',
},
},
() =>
new PoolMetadataBackup({
config,
job,
pool,
remoteAdapters,
schedule,
settings,
}).run()
)
)
)
}
if (job.xoMetadata !== undefined && settings.retentionXoMetadata !== 0) {
promises.push(
runTask(
{
name: `Starting XO metadata backup. (${job.id})`,
data: {
type: 'xo',
},
},
() =>
new XoMetadataBackup({
config,
job,
remoteAdapters,
schedule,
settings,
}).run()
)
)
}
await Promise.all(promises)
}
)
}
async _runVmBackup() {
const job = this._job
// FIXME: proper SimpleIdPattern handling
const getSnapshotNameLabel = this._getSnapshotNameLabel
const schedule = this._schedule
const config = this._config
const { settings } = job
const scheduleSettings = {
...config.defaultSettings,
...config.vm.defaultSettings,
...settings[''],
...settings[schedule.id],
}
await Disposable.use(
Disposable.all(
extractIdsFromSimplePattern(job.srs).map(id =>
this._getRecord('SR', id).catch(error => {
runTask(
{
name: 'get SR record',
data: { type: 'SR', id },
},
() => Promise.reject(error)
)
})
)
),
Disposable.all(
extractIdsFromSimplePattern(job.remotes).map(id =>
this._getAdapter(id).catch(error => {
runTask(
{
name: 'get remote adapter',
data: { type: 'remote', id },
},
() => Promise.reject(error)
)
})
)
),
async (srs, remoteAdapters) => {
// remove adapters that failed (already handled)
remoteAdapters = remoteAdapters.filter(_ => _ !== undefined)
// remove srs that failed (already handled)
srs = srs.filter(_ => _ !== undefined)
if (remoteAdapters.length === 0 && srs.length === 0 && scheduleSettings.snapshotRetention === 0) {
return
}
const vmIds = extractIdsFromSimplePattern(job.vms)
Task.info('vms', { vms: vmIds })
remoteAdapters = getAdaptersByRemote(remoteAdapters)
const handleVm = vmUuid =>
runTask({ name: 'backup VM', data: { type: 'VM', id: vmUuid } }, () =>
Disposable.use(this._getRecord('VM', vmUuid), vm =>
new VmBackup({
config,
getSnapshotNameLabel,
job,
// remotes,
remoteAdapters,
schedule,
settings: { ...scheduleSettings, ...settings[vmUuid] },
srs,
vm,
}).run()
)
)
const { concurrency } = scheduleSettings
await asyncMapSettled(vmIds, concurrency === 0 ? handleVm : limitConcurrency(concurrency)(handleVm))
}
)
}
}

View File

@@ -0,0 +1,40 @@
const { asyncMap } = require('@xen-orchestra/async-map')
exports.DurablePartition = class DurablePartition {
// private resource API is used exceptionally to be able to separate resource creation and release
#partitionDisposers = {}
flushAll() {
const partitionDisposers = this.#partitionDisposers
return asyncMap(Object.keys(partitionDisposers), path => {
const disposers = partitionDisposers[path]
delete partitionDisposers[path]
return asyncMap(disposers, d => d(path).catch(noop => {}))
})
}
async mount(adapter, diskId, partitionId) {
const { value: path, dispose } = await adapter.getPartition(diskId, partitionId)
const partitionDisposers = this.#partitionDisposers
if (partitionDisposers[path] === undefined) {
partitionDisposers[path] = []
}
partitionDisposers[path].push(dispose)
return path
}
async unmount(path) {
const partitionDisposers = this.#partitionDisposers
const disposers = partitionDisposers[path]
if (disposers === undefined) {
throw new Error(`No partition corresponding to the path ${path} found`)
}
await disposers.pop()()
if (disposers.length === 0) {
delete partitionDisposers[path]
}
}
}

View File

@@ -0,0 +1,61 @@
const assert = require('assert')
const { formatFilenameDate } = require('./_filenameDate')
const { importDeltaVm } = require('./_deltaVm')
const { Task } = require('./Task')
exports.ImportVmBackup = class ImportVmBackup {
constructor({ adapter, metadata, srUuid, xapi, settings: { newMacAddresses } = {} }) {
this._adapter = adapter
this._importDeltaVmSettings = { newMacAddresses }
this._metadata = metadata
this._srUuid = srUuid
this._xapi = xapi
}
async run() {
const adapter = this._adapter
const metadata = this._metadata
const isFull = metadata.mode === 'full'
let backup
if (isFull) {
backup = await adapter.readFullVmBackup(metadata)
} else {
assert.strictEqual(metadata.mode, 'delta')
backup = await adapter.readDeltaVmBackup(metadata)
}
return Task.run(
{
name: 'transfer',
},
async () => {
const xapi = this._xapi
const srRef = await xapi.call('SR.get_by_uuid', this._srUuid)
const vmRef = isFull
? await xapi.VM_import(backup, srRef)
: await importDeltaVm(backup, await xapi.getRecord('SR', srRef), {
...this._importDeltaVmSettings,
detectBase: false,
})
await Promise.all([
xapi.call('VM.add_tags', vmRef, 'restored from backup'),
xapi.call(
'VM.set_name_label',
vmRef,
`${metadata.vm.name_label} (${formatFilenameDate(metadata.timestamp)})`
),
])
return {
size: metadata.size,
id: await xapi.getField('VM', vmRef, 'uuid'),
}
}
)
}
}

View File

@@ -0,0 +1,28 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
# @xen-orchestra/backups
[![Package Version](https://badgen.net/npm/v/@xen-orchestra/backups)](https://npmjs.org/package/@xen-orchestra/backups) ![License](https://badgen.net/npm/license/@xen-orchestra/backups) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@xen-orchestra/backups)](https://bundlephobia.com/result?p=@xen-orchestra/backups) [![Node compatibility](https://badgen.net/npm/node/@xen-orchestra/backups)](https://npmjs.org/package/@xen-orchestra/backups)
## Install
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/backups):
```
> npm install --save @xen-orchestra/backups
```
## 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
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,558 @@
const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
const Disposable = require('promise-toolbox/Disposable')
const fromCallback = require('promise-toolbox/fromCallback')
const fromEvent = require('promise-toolbox/fromEvent')
const pDefer = require('promise-toolbox/defer')
const pump = require('pump')
const { basename, dirname, join, normalize, resolve } = require('path')
const { createLogger } = require('@xen-orchestra/log')
const { createSyntheticStream, mergeVhd, default: Vhd } = require('vhd-lib')
const { deduped } = require('@vates/disposable/deduped')
const { execFile } = require('child_process')
const { readdir, stat } = require('fs-extra')
const { ZipFile } = require('yazl')
const { BACKUP_DIR } = require('./_getVmBackupDir')
const { cleanVm } = require('./_cleanVm')
const { getTmpDir } = require('./_getTmpDir')
const { isMetadataFile, isVhdFile } = require('./_backupType')
const { listPartitions, LVM_PARTITION_TYPE } = require('./_listPartitions')
const { lvs, pvs } = require('./_lvm')
const DIR_XO_CONFIG_BACKUPS = 'xo-config-backups'
exports.DIR_XO_CONFIG_BACKUPS = DIR_XO_CONFIG_BACKUPS
const DIR_XO_POOL_METADATA_BACKUPS = 'xo-pool-metadata-backups'
exports.DIR_XO_POOL_METADATA_BACKUPS = DIR_XO_POOL_METADATA_BACKUPS
const { warn } = createLogger('xo:backups:RemoteAdapter')
const compareTimestamp = (a, b) => a.timestamp - b.timestamp
const noop = Function.prototype
const resolveRelativeFromFile = (file, path) => resolve('/', dirname(file), path).slice(1)
const resolveSubpath = (root, path) => resolve(root, `.${resolve('/', path)}`)
const RE_VHDI = /^vhdi(\d+)$/
async function addDirectory(files, realPath, metadataPath) {
try {
const subFiles = await readdir(realPath)
await asyncMap(subFiles, file => addDirectory(files, realPath + '/' + file, metadataPath + '/' + file))
} catch (error) {
if (error == null || error.code !== 'ENOTDIR') {
throw error
}
files.push({
realPath,
metadataPath,
})
}
}
const createSafeReaddir = (handler, methodName) => (path, options) =>
handler.list(path, options).catch(error => {
if (error?.code !== 'ENOENT') {
warn(`${methodName} ${path}`, { error })
}
return []
})
const debounceResourceFactory = factory =>
function () {
return this._debounceResource(factory.apply(this, arguments))
}
class RemoteAdapter {
constructor(handler, { debounceResource = res => res, dirMode } = {}) {
this._debounceResource = debounceResource
this._dirMode = dirMode
this._handler = handler
}
get handler() {
return this._handler
}
async _deleteVhd(path) {
const handler = this._handler
const vhds = await asyncMapSettled(
await handler.list(dirname(path), {
filter: isVhdFile,
prependDir: true,
}),
async path => {
try {
const vhd = new Vhd(handler, path)
await vhd.readHeaderAndFooter()
return {
footer: vhd.footer,
header: vhd.header,
path,
}
} catch (error) {
// Do not fail on corrupted VHDs (usually uncleaned temporary files),
// they are probably inconsequent to the backup process and should not
// fail it.
warn(`BackupNg#_deleteVhd ${path}`, { error })
}
}
)
const base = basename(path)
const child = vhds.find(_ => _ !== undefined && _.header.parentUnicodeName === base)
if (child === undefined) {
await handler.unlink(path)
return 0
}
try {
const childPath = child.path
const mergedDataSize = await mergeVhd(handler, path, handler, childPath)
await handler.rename(path, childPath)
return mergedDataSize
} catch (error) {
handler.unlink(path).catch(warn)
throw error
}
}
async _findPartition(devicePath, partitionId) {
const partitions = await listPartitions(devicePath)
const partition = partitions.find(_ => _.id === partitionId)
if (partition === undefined) {
throw new Error(`partition ${partitionId} not found`)
}
return partition
}
_getLvmLogicalVolumes = Disposable.factory(this._getLvmLogicalVolumes)
_getLvmLogicalVolumes = deduped(this._getLvmLogicalVolumes, (devicePath, pvId, vgName) => [devicePath, pvId, vgName])
_getLvmLogicalVolumes = debounceResourceFactory(this._getLvmLogicalVolumes)
async *_getLvmLogicalVolumes(devicePath, pvId, vgName) {
yield this._getLvmPhysicalVolume(devicePath, pvId && (await this._findPartition(devicePath, pvId)))
await fromCallback(execFile, 'vgchange', ['-ay', vgName])
try {
yield lvs(['lv_name', 'lv_path'], vgName)
} finally {
await fromCallback(execFile, 'vgchange', ['-an', vgName])
}
}
_getLvmPhysicalVolume = Disposable.factory(this._getLvmPhysicalVolume)
_getLvmPhysicalVolume = deduped(this._getLvmPhysicalVolume, (devicePath, partition) => [devicePath, partition?.id])
_getLvmPhysicalVolume = debounceResourceFactory(this._getLvmPhysicalVolume)
async *_getLvmPhysicalVolume(devicePath, partition) {
const args = []
if (partition !== undefined) {
args.push('-o', partition.start * 512, '--sizelimit', partition.size)
}
args.push('--show', '-f', devicePath)
const path = (await fromCallback(execFile, 'losetup', args)).trim()
try {
await fromCallback(execFile, 'pvscan', ['--cache', path])
yield path
} finally {
try {
const vgNames = await pvs('vg_name', path)
await fromCallback(execFile, 'vgchange', ['-an', ...vgNames])
} finally {
await fromCallback(execFile, 'losetup', ['-d', path])
}
}
}
_getPartition = Disposable.factory(this._getPartition)
_getPartition = deduped(this._getPartition, (devicePath, partition) => [devicePath, partition?.id])
_getPartition = debounceResourceFactory(this._getPartition)
async *_getPartition(devicePath, partition) {
const options = ['loop', 'ro']
if (partition !== undefined) {
const { size, start } = partition
options.push(`sizelimit=${size}`)
if (start !== undefined) {
options.push(`offset=${start * 512}`)
}
}
const path = yield getTmpDir()
const mount = options => {
return fromCallback(execFile, 'mount', [
`--options=${options.join(',')}`,
`--source=${devicePath}`,
`--target=${path}`,
])
}
// `norecovery` option is used for ext3/ext4/xfs, if it fails it might be
// another fs, try without
try {
await mount([...options, 'norecovery'])
} catch (error) {
await mount(options)
}
try {
yield path
} finally {
await fromCallback(execFile, 'umount', ['--lazy', path])
}
}
_listLvmLogicalVolumes(devicePath, partition, results = []) {
return Disposable.use(this._getLvmPhysicalVolume(devicePath, partition), async path => {
const lvs = await pvs(['lv_name', 'lv_path', 'lv_size', 'vg_name'], path)
const partitionId = partition !== undefined ? partition.id : ''
lvs.forEach((lv, i) => {
const name = lv.lv_name
if (name !== '') {
results.push({
id: `${partitionId}/${lv.vg_name}/${name}`,
name,
size: lv.lv_size,
})
}
})
return results
})
}
_usePartitionFiles = Disposable.factory(this._usePartitionFiles)
async *_usePartitionFiles(diskId, partitionId, paths) {
const path = yield this.getPartition(diskId, partitionId)
const files = []
await asyncMap(paths, file =>
addDirectory(files, resolveSubpath(path, file), normalize('./' + file).replace(/\/+$/, ''))
)
return files
}
fetchPartitionFiles(diskId, partitionId, paths) {
const { promise, reject, resolve } = pDefer()
Disposable.use(
async function* () {
const files = yield this._usePartitionFiles(diskId, partitionId, paths)
const zip = new ZipFile()
files.forEach(({ realPath, metadataPath }) => zip.addFile(realPath, metadataPath))
zip.end()
const { outputStream } = zip
resolve(outputStream)
await fromEvent(outputStream, 'end')
}.bind(this)
).catch(error => {
warn(error)
reject(error)
})
return promise
}
async deleteDeltaVmBackups(backups) {
const handler = this._handler
let mergedDataSize = 0
await asyncMapSettled(backups, ({ _filename, vhds }) =>
Promise.all([
handler.unlink(_filename),
asyncMap(Object.values(vhds), async _ => {
mergedDataSize += await this._deleteVhd(resolveRelativeFromFile(_filename, _))
}),
])
)
return mergedDataSize
}
async deleteMetadataBackup(backupId) {
const uuidReg = '\\w{8}(-\\w{4}){3}-\\w{12}'
const metadataDirReg = 'xo-(config|pool-metadata)-backups'
const timestampReg = '\\d{8}T\\d{6}Z'
const regexp = new RegExp(`^${metadataDirReg}/${uuidReg}(/${uuidReg})?/${timestampReg}`)
if (!regexp.test(backupId)) {
throw new Error(`The id (${backupId}) not correspond to a metadata folder`)
}
await this._handler.rmtree(backupId)
}
async deleteOldMetadataBackups(dir, retention) {
const handler = this.handler
let list = await handler.list(dir)
list.sort()
list = list.filter(timestamp => /^\d{8}T\d{6}Z$/.test(timestamp)).slice(0, -retention)
await asyncMapSettled(list, timestamp => handler.rmtree(`${dir}/${timestamp}`))
}
async deleteFullVmBackups(backups) {
const handler = this._handler
await asyncMapSettled(backups, ({ _filename, xva }) =>
Promise.all([handler.unlink(_filename), handler.unlink(resolveRelativeFromFile(_filename, xva))])
)
}
async deleteVmBackup(filename) {
const metadata = JSON.parse(String(await this._handler.readFile(filename)))
metadata._filename = filename
if (metadata.mode === 'delta') {
await this.deleteDeltaVmBackups([metadata])
} else if (metadata.mode === 'full') {
await this.deleteFullVmBackups([metadata])
} else {
throw new Error(`no deleter for backup mode ${metadata.mode}`)
}
}
getDisk = Disposable.factory(this.getDisk)
getDisk = deduped(this.getDisk, diskId => [diskId])
getDisk = debounceResourceFactory(this.getDisk)
async *getDisk(diskId) {
const handler = this._handler
const diskPath = handler._getFilePath('/' + diskId)
const mountDir = yield getTmpDir()
await fromCallback(execFile, 'vhdimount', [diskPath, mountDir])
try {
let max = 0
let maxEntry
const entries = await readdir(mountDir)
entries.forEach(entry => {
const matches = RE_VHDI.exec(entry)
if (matches !== null) {
const value = +matches[1]
if (value > max) {
max = value
maxEntry = entry
}
}
})
if (max === 0) {
throw new Error('no disks found')
}
yield `${mountDir}/${maxEntry}`
} finally {
await fromCallback(execFile, 'fusermount', ['-uz', mountDir])
}
}
// partitionId values:
//
// - undefined: raw disk
// - `<partitionId>`: partitioned disk
// - `<pvId>/<vgName>/<lvName>`: LVM on a partitioned disk
// - `/<vgName>/lvName>`: LVM on a raw disk
getPartition = Disposable.factory(this.getPartition)
async *getPartition(diskId, partitionId) {
const devicePath = yield this.getDisk(diskId)
if (partitionId === undefined) {
return yield this._getPartition(devicePath)
}
const isLvmPartition = partitionId.includes('/')
if (isLvmPartition) {
const [pvId, vgName, lvName] = partitionId.split('/')
const lvs = yield this._getLvmLogicalVolumes(devicePath, pvId !== '' ? pvId : undefined, vgName)
return yield this._getPartition(lvs.find(_ => _.lv_name === lvName).lv_path)
}
return yield this._getPartition(devicePath, await this._findPartition(devicePath, partitionId))
}
async listAllVmBackups() {
const handler = this._handler
const backups = { __proto__: null }
await asyncMap(await handler.list(BACKUP_DIR), async vmUuid => {
const vmBackups = await this.listVmBackups(vmUuid)
backups[vmUuid] = vmBackups
})
return backups
}
listPartitionFiles(diskId, partitionId, path) {
return Disposable.use(this.getPartition(diskId, partitionId), async rootPath => {
path = resolveSubpath(rootPath, path)
const entriesMap = {}
await asyncMap(await readdir(path), async name => {
try {
const stats = await stat(`${path}/${name}`)
entriesMap[stats.isDirectory() ? `${name}/` : name] = {}
} catch (error) {
if (error == null || error.code !== 'ENOENT') {
throw error
}
}
})
return entriesMap
})
}
listPartitions(diskId) {
return Disposable.use(this.getDisk(diskId), async devicePath => {
const partitions = await listPartitions(devicePath)
if (partitions.length === 0) {
try {
// handle potential raw LVM physical volume
return await this._listLvmLogicalVolumes(devicePath, undefined, partitions)
} catch (error) {
return []
}
}
const results = []
await asyncMapSettled(partitions, partition =>
partition.type === LVM_PARTITION_TYPE
? this._listLvmLogicalVolumes(devicePath, partition, results)
: results.push(partition)
)
return results
})
}
async listPoolMetadataBackups() {
const handler = this._handler
const safeReaddir = createSafeReaddir(handler, 'listPoolMetadataBackups')
const backupsByPool = {}
await asyncMap(await safeReaddir(DIR_XO_POOL_METADATA_BACKUPS, { prependDir: true }), async scheduleDir =>
asyncMap(await safeReaddir(scheduleDir), async poolId => {
const backups = backupsByPool[poolId] ?? (backupsByPool[poolId] = [])
return asyncMap(await safeReaddir(`${scheduleDir}/${poolId}`, { prependDir: true }), async backupDir => {
try {
backups.push({
id: backupDir,
...JSON.parse(String(await handler.readFile(`${backupDir}/metadata.json`))),
})
} catch (error) {
warn(`listPoolMetadataBackups ${backupDir}`, {
error,
})
}
})
})
)
// delete empty entries and sort backups
Object.keys(backupsByPool).forEach(poolId => {
const backups = backupsByPool[poolId]
if (backups.length === 0) {
delete backupsByPool[poolId]
} else {
backups.sort(compareTimestamp)
}
})
return backupsByPool
}
async listVmBackups(vmUuid, predicate) {
const handler = this._handler
const backups = []
try {
const files = await handler.list(`${BACKUP_DIR}/${vmUuid}`, {
filter: isMetadataFile,
prependDir: true,
})
await asyncMap(files, async file => {
try {
const metadata = await this.readVmBackupMetadata(file)
if (predicate === undefined || predicate(metadata)) {
// inject an id usable by importVmBackupNg()
metadata.id = metadata._filename
backups.push(metadata)
}
} catch (error) {
warn(`listVmBackups ${file}`, { error })
}
})
} catch (error) {
let code
if (error == null || ((code = error.code) !== 'ENOENT' && code !== 'ENOTDIR')) {
throw error
}
}
return backups.sort(compareTimestamp)
}
async listXoMetadataBackups() {
const handler = this._handler
const safeReaddir = createSafeReaddir(handler, 'listXoMetadataBackups')
const backups = []
await asyncMap(await safeReaddir(DIR_XO_CONFIG_BACKUPS, { prependDir: true }), async scheduleDir =>
asyncMap(await safeReaddir(scheduleDir, { prependDir: true }), async backupDir => {
try {
backups.push({
id: backupDir,
...JSON.parse(String(await handler.readFile(`${backupDir}/metadata.json`))),
})
} catch (error) {
warn(`listXoMetadataBackups ${backupDir}`, { error })
}
})
)
return backups.sort(compareTimestamp)
}
async outputStream(path, input, { checksum = true, validator = noop } = {}) {
const handler = this._handler
input = await input
const tmpPath = `${dirname(path)}/.${basename(path)}`
const output = await handler.createOutputStream(tmpPath, {
checksum,
dirMode: this._dirMode,
})
try {
await Promise.all([fromCallback(pump, input, output), output.checksumWritten, input.task])
await validator(tmpPath)
await handler.rename(tmpPath, path, { checksum })
} catch (error) {
await handler.unlink(tmpPath, { checksum })
throw error
}
}
async readDeltaVmBackup(metadata) {
const handler = this._handler
const { vbds, vdis, vhds, vifs, vm } = metadata
const dir = dirname(metadata._filename)
const streams = {}
await asyncMapSettled(Object.entries(vdis), async ([id, vdi]) => {
streams[`${id}.vhd`] = await createSyntheticStream(handler, join(dir, vhds[id]))
})
return {
streams,
vbds,
vdis,
version: '1.0.0',
vifs,
vm,
}
}
readFullVmBackup(metadata) {
return this._handler.createReadStream(resolve('/', dirname(metadata._filename), metadata.xva))
}
async readVmBackupMetadata(path) {
return Object.defineProperty(JSON.parse(await this._handler.readFile(path)), '_filename', { value: path })
}
}
RemoteAdapter.prototype.cleanVm = function (vmDir) {
return Disposable.use(this._handler.lock(vmDir), () => cleanVm.apply(this, arguments))
}
exports.RemoteAdapter = RemoteAdapter

View File

@@ -0,0 +1,24 @@
const { DIR_XO_POOL_METADATA_BACKUPS } = require('./RemoteAdapter')
const { PATH_DB_DUMP } = require('./_PoolMetadataBackup')
exports.RestoreMetadataBackup = class RestoreMetadataBackup {
constructor({ backupId, handler, xapi }) {
this._backupId = backupId
this._handler = handler
this._xapi = xapi
}
async run() {
const backupId = this._backupId
const handler = this._handler
const xapi = this._xapi
if (backupId.split('/')[0] === DIR_XO_POOL_METADATA_BACKUPS) {
return xapi.putResource(await handler.createReadStream(`${backupId}/data`), PATH_DB_DUMP, {
task: xapi.task_create('Import pool metadata'),
})
} else {
return String(await handler.readFile(`${backupId}/data.json`))
}
}
}

View File

@@ -0,0 +1,181 @@
const Zone = require('node-zone')
const { SyncThenable } = require('./_syncThenable')
const logAfterEnd = () => {
throw new Error('task has already ended')
}
// Create a serializable object from an error.
//
// Otherwise some fields might be non-enumerable and missing from logs.
const serializeError = error =>
error instanceof Error
? {
...error, // Copy enumerable properties.
code: error.code,
message: error.message,
name: error.name,
stack: error.stack,
}
: error
exports.serializeError = serializeError
class TaskLogger {
constructor(logFn, parentId) {
this._log = logFn
this._parentId = parentId
this._taskId = undefined
}
get taskId() {
const taskId = this._taskId
if (taskId === undefined) {
throw new Error('start the task first')
}
return taskId
}
// create a subtask
fork() {
return new TaskLogger(this._log, this.taskId)
}
info(message, data) {
return this._log({
data,
event: 'info',
message,
taskId: this.taskId,
timestamp: Date.now(),
})
}
run(message, data, fn) {
if (arguments.length === 2) {
fn = data
data = undefined
}
return SyncThenable.tryUnwrap(
SyncThenable.fromFunction(() => {
if (this._taskId !== undefined) {
throw new Error('task has already started')
}
this._taskId = Math.random().toString(36).slice(2)
return this._log({
data,
event: 'start',
message,
parentId: this._parentId,
taskId: this.taskId,
timestamp: Date.now(),
})
})
.then(fn)
.then(
result => {
const log = this._log
this._log = logAfterEnd
return SyncThenable.resolve(
log({
event: 'end',
result,
status: 'success',
taskId: this.taskId,
timestamp: Date.now(),
})
).then(() => result)
},
error => {
const log = this._log
this._log = logAfterEnd
return SyncThenable.resolve(
log({
event: 'end',
result: serializeError(error),
status: 'failure',
taskId: this.taskId,
timestamp: Date.now(),
})
).then(() => {
throw error
})
}
)
)
}
warning(message, data) {
return this._log({
data,
event: 'warning',
message,
taskId: this.taskId,
timestamp: Date.now(),
})
}
wrapFn(fn, message, data) {
const logger = this
return function () {
const evaluate = v => (typeof v === 'function' ? v.apply(this, arguments) : v)
return logger.run(evaluate(message), evaluate(data), () => fn.apply(this, arguments))
}
}
}
const $$task = Symbol('current task logger')
const getCurrent = () => Zone.current.data[$$task]
const Task = {
info(message, data) {
const task = getCurrent()
if (task !== undefined) {
return task.info(message, data)
}
},
run({ name, data, onLog }, fn) {
let parentId
if (onLog === undefined) {
const parent = getCurrent()
if (parent === undefined) {
return fn()
}
onLog = parent._log
parentId = parent.taskId
}
const task = new TaskLogger(onLog, parentId)
const zone = Zone.current.fork('task')
zone.data[$$task] = task
return task.run(name, data, zone.wrap(fn))
},
warning(message, data) {
const task = getCurrent()
if (task !== undefined) {
return task.warning(message, data)
}
},
wrapFn(opts, fn) {
// compatibility with @decorateWith
if (typeof fn !== 'function') {
;[fn, opts] = [opts, fn]
}
const { name, data, onLog } = opts
return function () {
const evaluate = v => (typeof v === 'function' ? v.apply(this, arguments) : v)
return Task.run({ name: evaluate(name), data: evaluate(data), onLog }, () => fn.apply(this, arguments))
}
},
}
exports.Task = Task

View File

View File

@@ -0,0 +1,119 @@
const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const { formatDateTime } = require('@xen-orchestra/xapi')
const { formatFilenameDate } = require('./_filenameDate')
const { getOldEntries } = require('./_getOldEntries')
const { importDeltaVm, TAG_COPY_SRC } = require('./_deltaVm')
const { listReplicatedVms } = require('./_listReplicatedVms')
const { Task } = require('./Task')
exports.ContinuousReplicationWriter = class ContinuousReplicationWriter {
constructor(backup, sr, settings) {
this._backup = backup
this._settings = settings
this._sr = sr
this.transfer = Task.wrapFn(
{
name: 'export',
data: ({ deltaExport }) => ({
id: sr.uuid,
isFull: Object.values(deltaExport.vdis).some(vdi => vdi.other_config['xo:base_delta'] === undefined),
type: 'SR',
}),
},
this.transfer
)
}
async checkBaseVdis(baseUuidToSrcVdi, baseVm) {
const sr = this._sr
const replicatedVm = listReplicatedVms(sr.$xapi, this._backup.job.id, sr.uuid, this._backup.vm.uuid).find(
vm => vm.other_config[TAG_COPY_SRC] === baseVm.uuid
)
if (replicatedVm === undefined) {
return baseUuidToSrcVdi.clear()
}
const xapi = replicatedVm.$xapi
const replicatedVdis = new Set(
await asyncMap(await replicatedVm.$getDisks(), async vdiRef => {
const otherConfig = await xapi.getField('VDI', vdiRef, 'other_config')
return otherConfig[TAG_COPY_SRC]
})
)
for (const uuid of baseUuidToSrcVdi.keys()) {
if (!replicatedVdis.has(uuid)) {
baseUuidToSrcVdi.delete(uuid)
}
}
}
async prepare() {
const settings = this._settings
const { uuid: srUuid, $xapi: xapi } = this._sr
const { scheduleId, vm } = this._backup
// delete previous interrupted copies
ignoreErrors.call(asyncMapSettled(listReplicatedVms(xapi, scheduleId, undefined, vm.uuid), vm => vm.$destroy))
this._oldEntries = getOldEntries(settings.copyRetention - 1, listReplicatedVms(xapi, scheduleId, srUuid, vm.uuid))
if (settings.deleteFirst) {
await this._deleteOldEntries()
} else {
this.cleanup = this._deleteOldEntries
}
}
async _deleteOldEntries() {
return asyncMapSettled(this._oldEntries, vm => vm.$destroy())
}
async transfer({ timestamp, deltaExport, sizeContainers }) {
const sr = this._sr
const { job, scheduleId, vm } = this._backup
const { uuid: srUuid, $xapi: xapi } = sr
let targetVmRef
await Task.run({ name: 'transfer' }, async () => {
targetVmRef = await importDeltaVm(
{
__proto__: deltaExport,
vm: {
...deltaExport.vm,
tags: [...deltaExport.vm.tags, 'Continuous Replication'],
},
},
sr
)
return {
size: Object.values(sizeContainers).reduce((sum, { size }) => sum + size, 0),
}
})
const targetVm = await xapi.getRecord('VM', targetVmRef)
await Promise.all([
targetVm.ha_restart_priority !== '' &&
Promise.all([targetVm.set_ha_restart_priority(''), targetVm.add_tags('HA disabled')]),
targetVm.set_name_label(`${vm.name_label} - ${job.name} - (${formatFilenameDate(timestamp)})`),
targetVm.update_blocked_operations(
'start',
'Start operation for this vm is blocked, clone it if you want to use it.'
),
targetVm.update_other_config({
'xo:backup:sr': srUuid,
// these entries need to be added in case of offline backup
'xo:backup:datetime': formatDateTime(timestamp),
'xo:backup:job': job.id,
'xo:backup:schedule': scheduleId,
'xo:backup:vm': vm.uuid,
}),
])
}
}

View File

@@ -0,0 +1,219 @@
const assert = require('assert')
const map = require('lodash/map')
const mapValues = require('lodash/mapValues')
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const { asyncMap } = require('@xen-orchestra/async-map')
const { chainVhd, checkVhdChain, default: Vhd } = require('vhd-lib')
const { createLogger } = require('@xen-orchestra/log')
const { dirname } = require('path')
const { checkVhd } = require('./_checkVhd')
const { formatFilenameDate } = require('./_filenameDate')
const { getOldEntries } = require('./_getOldEntries')
const { getVmBackupDir } = require('./_getVmBackupDir')
const { packUuid } = require('./_packUuid')
const { Task } = require('./Task')
const { warn } = createLogger('xo:backups:DeltaBackupWriter')
exports.DeltaBackupWriter = class DeltaBackupWriter {
constructor(backup, remoteId, settings) {
this._adapter = backup.remoteAdapters[remoteId]
this._backup = backup
this._settings = settings
this.transfer = Task.wrapFn(
{
name: 'export',
data: ({ deltaExport }) => ({
id: remoteId,
isFull: Object.values(deltaExport.vdis).some(vdi => vdi.other_config['xo:base_delta'] === undefined),
type: 'remote',
}),
},
this.transfer
)
this[settings.deleteFirst ? 'prepare' : 'cleanup'] = this._deleteOldEntries
}
async checkBaseVdis(baseUuidToSrcVdi) {
const { handler } = this._adapter
const backup = this._backup
const backupDir = getVmBackupDir(backup.vm.uuid)
const vdisDir = `${backupDir}/vdis/${backup.job.id}`
await asyncMap(baseUuidToSrcVdi, async ([baseUuid, srcVdi]) => {
let found = false
try {
const vhds = await handler.list(`${vdisDir}/${srcVdi.uuid}`, {
filter: _ => _[0] !== '.' && _.endsWith('.vhd'),
prependDir: true,
})
await asyncMap(vhds, async path => {
try {
await checkVhdChain(handler, path)
const vhd = new Vhd(handler, path)
await vhd.readHeaderAndFooter()
found = found || vhd.footer.uuid.equals(packUuid(baseUuid))
} catch (error) {
warn('checkBaseVdis', { error })
await ignoreErrors.call(handler.unlink(path))
}
})
} catch (error) {
warn('checkBaseVdis', { error })
}
if (!found) {
baseUuidToSrcVdi.delete(baseUuid)
}
})
}
async prepare() {
const adapter = this._adapter
const settings = this._settings
const { scheduleId, vm } = this._backup
const oldEntries = getOldEntries(
settings.exportRetention - 1,
await adapter.listVmBackups(vm.uuid, _ => _.mode === 'delta' && _.scheduleId === scheduleId)
)
this._oldEntries = oldEntries
// FIXME: implement optimized multiple VHDs merging with synthetic
// delta
//
// For the time being, limit the number of deleted backups by run
// because it can take a very long time and can lead to
// interrupted backup with broken VHD chain.
//
// The old backups will be eventually merged in future runs of the
// job.
const { maxMergedDeltasPerRun } = this._settings
if (oldEntries.length > maxMergedDeltasPerRun) {
oldEntries.length = maxMergedDeltasPerRun
}
if (settings.deleteFirst) {
await this._deleteOldEntries()
} else {
this.cleanup = this._deleteOldEntries
}
}
async _deleteOldEntries() {
return Task.run({ name: 'merge' }, async () => {
const adapter = this._adapter
const oldEntries = this._oldEntries
let size = 0
// delete sequentially from newest to oldest to avoid unnecessary merges
for (let i = oldEntries.length; i-- > 0; ) {
size += await adapter.deleteDeltaVmBackups([oldEntries[i]])
}
return {
size,
}
})
}
async transfer({ timestamp, deltaExport, sizeContainers }) {
const adapter = this._adapter
const backup = this._backup
const { job, scheduleId, vm } = backup
const jobId = job.id
const handler = adapter.handler
const backupDir = getVmBackupDir(vm.uuid)
// TODO: clean VM backup directory
const basename = formatFilenameDate(timestamp)
const vhds = mapValues(
deltaExport.vdis,
vdi =>
`vdis/${jobId}/${
vdi.type === 'suspend'
? // doesn't make sense to group by parent for memory because we
// don't do delta for it
vdi.uuid
: vdi.$snapshot_of$uuid
}/${basename}.vhd`
)
const metadataFilename = `${backupDir}/${basename}.json`
const metadataContent = {
jobId,
mode: job.mode,
scheduleId,
timestamp,
vbds: deltaExport.vbds,
vdis: deltaExport.vdis,
version: '2.0.0',
vifs: deltaExport.vifs,
vhds,
vm,
vmSnapshot: this._backup.exportedVm,
}
const { size } = await Task.run({ name: 'transfer' }, async () => {
await Promise.all(
map(deltaExport.vdis, async (vdi, id) => {
const path = `${backupDir}/${vhds[id]}`
const isDelta = vdi.other_config['xo:base_delta'] !== undefined
let parentPath
if (isDelta) {
const vdiDir = dirname(path)
parentPath = (
await handler.list(vdiDir, {
filter: filename => filename[0] !== '.' && filename.endsWith('.vhd'),
prependDir: true,
})
)
.sort()
.pop()
assert.notStrictEqual(parentPath, undefined, `missing parent of ${id}`)
parentPath = parentPath.slice(1) // remove leading slash
// TODO remove when this has been done before the export
await checkVhd(handler, parentPath)
}
await adapter.outputStream(path, deltaExport.streams[`${id}.vhd`], {
// no checksum for VHDs, because they will be invalidated by
// merges and chainings
checksum: false,
validator: tmpPath => checkVhd(handler, tmpPath),
})
if (isDelta) {
await chainVhd(handler, parentPath, handler, path)
}
// set the correct UUID in the VHD
const vhd = new Vhd(handler, path)
await vhd.readHeaderAndFooter()
vhd.footer.uuid = packUuid(vdi.uuid)
await vhd.readBlockAllocationTable() // required by writeFooter()
await vhd.writeFooter()
})
)
return {
size: Object.values(sizeContainers).reduce((sum, { size }) => sum + size, 0),
}
})
metadataContent.size = size
await handler.outputFile(metadataFilename, JSON.stringify(metadataContent), {
dirMode: backup.config.dirMode,
})
// TODO: run cleanup?
}
}

View File

@@ -0,0 +1,85 @@
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const { asyncMapSettled } = require('@xen-orchestra/async-map')
const { formatDateTime } = require('@xen-orchestra/xapi')
const { formatFilenameDate } = require('./_filenameDate')
const { getOldEntries } = require('./_getOldEntries')
const { listReplicatedVms } = require('./_listReplicatedVms')
const { Task } = require('./Task')
exports.DisasterRecoveryWriter = class DisasterRecoveryWriter {
constructor(backup, sr, settings) {
this._backup = backup
this._settings = settings
this._sr = sr
this.run = Task.wrapFn(
{
name: 'export',
data: {
id: sr.uuid,
type: 'SR',
// necessary?
isFull: true,
},
},
this.run
)
}
async run({ timestamp, sizeContainer, stream }) {
const sr = this._sr
const settings = this._settings
const { job, scheduleId, vm } = this._backup
const { uuid: srUuid, $xapi: xapi } = sr
// delete previous interrupted copies
ignoreErrors.call(
asyncMapSettled(listReplicatedVms(xapi, scheduleId, undefined, vm.uuid), vm => xapi.VM_destroy(vm.$ref))
)
const oldVms = getOldEntries(settings.copyRetention - 1, listReplicatedVms(xapi, scheduleId, srUuid, vm.uuid))
const deleteOldBackups = () => asyncMapSettled(oldVms, vm => xapi.VM_destroy(vm.$ref))
const { deleteFirst } = settings
if (deleteFirst) {
await deleteOldBackups()
}
let targetVmRef
await Task.run({ name: 'transfer' }, async () => {
targetVmRef = await xapi.VM_import(stream, sr.$ref, vm =>
Promise.all([
vm.add_tags('Disaster Recovery'),
vm.ha_restart_priority !== '' && Promise.all([vm.set_ha_restart_priority(''), vm.add_tags('HA disabled')]),
vm.set_name_label(`${vm.name_label} - ${job.name} - (${formatFilenameDate(timestamp)})`),
])
)
return { size: sizeContainer.size }
})
const targetVm = await xapi.getRecord('VM', targetVmRef)
await Promise.all([
targetVm.update_blocked_operations(
'start',
'Start operation for this vm is blocked, clone it if you want to use it.'
),
targetVm.update_other_config({
'xo:backup:sr': srUuid,
// these entries need to be added in case of offline backup
'xo:backup:datetime': formatDateTime(timestamp),
'xo:backup:job': job.id,
'xo:backup:schedule': scheduleId,
'xo:backup:vm': vm.uuid,
}),
])
if (!deleteFirst) {
await deleteOldBackups()
}
}
}

View File

@@ -0,0 +1,90 @@
const { formatFilenameDate } = require('./_filenameDate')
const { getOldEntries } = require('./_getOldEntries')
const { getVmBackupDir } = require('./_getVmBackupDir')
const { isValidXva } = require('./isValidXva')
const { Task } = require('./Task')
exports.FullBackupWriter = class FullBackupWriter {
constructor(backup, remoteId, settings) {
this._backup = backup
this._remoteId = remoteId
this._settings = settings
this.run = Task.wrapFn(
{
name: 'export',
data: {
id: remoteId,
type: 'remote',
// necessary?
isFull: true,
},
},
this.run
)
}
async run({ timestamp, sizeContainer, stream }) {
const backup = this._backup
const remoteId = this._remoteId
const settings = this._settings
const { job, scheduleId, vm } = backup
const adapter = backup.remoteAdapters[remoteId]
const handler = adapter.handler
const backupDir = getVmBackupDir(vm.uuid)
// TODO: clean VM backup directory
const oldBackups = getOldEntries(
settings.exportRetention - 1,
await adapter.listVmBackups(vm.uuid, _ => _.mode === 'full' && _.scheduleId === scheduleId)
)
const deleteOldBackups = () => adapter.deleteFullVmBackups(oldBackups)
const basename = formatFilenameDate(timestamp)
const dataBasename = basename + '.xva'
const dataFilename = backupDir + '/' + dataBasename
const metadataFilename = `${backupDir}/${basename}.json`
const metadata = {
jobId: job.id,
mode: job.mode,
scheduleId,
timestamp,
version: '2.0.0',
vm,
vmSnapshot: this._backup.exportedVm,
xva: './' + dataBasename,
}
const { deleteFirst } = settings
if (deleteFirst) {
await deleteOldBackups()
}
await Task.run({ name: 'transfer' }, async () => {
await adapter.outputStream(dataFilename, stream, {
validator: tmpPath => {
if (handler._getFilePath !== undefined) {
return isValidXva(handler._getFilePath('/' + tmpPath))
}
},
})
return { size: sizeContainer.size }
})
metadata.size = sizeContainer.size
await handler.outputFile(metadataFilename, JSON.stringify(metadata), {
dirMode: backup.config.dirMode,
})
if (!deleteFirst) {
await deleteOldBackups()
}
// TODO: run cleanup?
}
}

View File

@@ -0,0 +1,75 @@
const { asyncMap } = require('@xen-orchestra/async-map')
const { DIR_XO_POOL_METADATA_BACKUPS } = require('./RemoteAdapter')
const { forkStreamUnpipe } = require('./_forkStreamUnpipe')
const { formatFilenameDate } = require('./_filenameDate')
const { Task } = require('./Task')
const PATH_DB_DUMP = '/pool/xmldbdump'
exports.PATH_DB_DUMP = PATH_DB_DUMP
exports.PoolMetadataBackup = class PoolMetadataBackup {
constructor({ config, job, pool, remoteAdapters, schedule, settings }) {
this._config = config
this._job = job
this._pool = pool
this._remoteAdapters = remoteAdapters
this._schedule = schedule
this._settings = settings
}
_exportPoolMetadata() {
const xapi = this._pool.$xapi
return xapi.getResource(PATH_DB_DUMP, {
task: xapi.task_create('Export pool metadata'),
})
}
async run() {
const timestamp = Date.now()
const { _job: job, _schedule: schedule, _pool: pool } = this
const poolDir = `${DIR_XO_POOL_METADATA_BACKUPS}/${schedule.id}/${pool.$id}`
const dir = `${poolDir}/${formatFilenameDate(timestamp)}`
const stream = await this._exportPoolMetadata()
const fileName = `${dir}/data`
const metadata = JSON.stringify(
{
jobId: job.id,
jobName: job.name,
pool,
poolMaster: pool.$master,
scheduleId: schedule.id,
scheduleName: schedule.name,
timestamp,
},
null,
2
)
const metaDataFileName = `${dir}/metadata.json`
await asyncMap(
Object.entries(this._remoteAdapters),
([remoteId, adapter]) =>
Task.run(
{
name: `Starting metadata backup for the pool (${pool.$id}) for the remote (${remoteId}). (${job.id})`,
data: {
id: remoteId,
type: 'remote',
},
},
async () => {
// forkStreamUnpipe should be used in a sync way, do not wait for a promise before using it
await adapter.outputStream(fileName, forkStreamUnpipe(stream), { checksum: false })
await adapter.handler.outputFile(metaDataFileName, metadata, {
dirMode: this._config.dirMode,
})
await adapter.deleteOldMetadataBackups(poolDir, this._settings.retentionPoolMetadata)
}
).catch(() => {}) // errors are handled by logs
)
}
}

View File

@@ -0,0 +1,354 @@
const findLast = require('lodash/findLast')
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const keyBy = require('lodash/keyBy')
const mapValues = require('lodash/mapValues')
const { asyncMap } = require('@xen-orchestra/async-map')
const { createLogger } = require('@xen-orchestra/log')
const { formatDateTime } = require('@xen-orchestra/xapi')
const { ContinuousReplicationWriter } = require('./_ContinuousReplicationWriter')
const { DeltaBackupWriter } = require('./_DeltaBackupWriter')
const { DisasterRecoveryWriter } = require('./_DisasterRecoveryWriter')
const { exportDeltaVm } = require('./_deltaVm')
const { forkStreamUnpipe } = require('./_forkStreamUnpipe')
const { FullBackupWriter } = require('./_FullBackupWriter')
const { getOldEntries } = require('./_getOldEntries')
const { Task } = require('./Task')
const { watchStreamSize } = require('./_watchStreamSize')
const { debug, warn } = createLogger('xo:backups:VmBackup')
const forkDeltaExport = deltaExport =>
Object.create(deltaExport, {
streams: {
value: mapValues(deltaExport.streams, forkStreamUnpipe),
},
})
exports.VmBackup = class VmBackup {
constructor({ config, getSnapshotNameLabel, job, remoteAdapters, remotes, schedule, settings, srs, vm }) {
this.config = config
this.job = job
this.remoteAdapters = remoteAdapters
this.remotes = remotes
this.scheduleId = schedule.id
this.timestamp = undefined
// VM currently backed up
this.vm = vm
const { tags } = this.vm
// VM (snapshot) that is really exported
this.exportedVm = undefined
this._fullVdisRequired = undefined
this._getSnapshotNameLabel = getSnapshotNameLabel
this._isDelta = job.mode === 'delta'
this._jobId = job.id
this._jobSnapshots = undefined
this._xapi = vm.$xapi
// Base VM for the export
this._baseVm = undefined
// Settings for this specific run (job, schedule, VM)
if (tags.includes('xo-memory-backup')) {
settings.checkpointSnapshot = true
}
if (tags.includes('xo-offline-backup')) {
settings.offlineSnapshot = true
}
this._settings = settings
// Create writers
{
const writers = []
this._writers = writers
const [BackupWriter, ReplicationWriter] = this._isDelta
? [DeltaBackupWriter, ContinuousReplicationWriter]
: [FullBackupWriter, DisasterRecoveryWriter]
const allSettings = job.settings
Object.keys(remoteAdapters).forEach(remoteId => {
const targetSettings = {
...settings,
...allSettings[remoteId],
}
if (targetSettings.exportRetention !== 0) {
writers.push(new BackupWriter(this, remoteId, targetSettings))
}
})
srs.forEach(sr => {
const targetSettings = {
...settings,
...allSettings[sr.uuid],
}
if (targetSettings.copyRetention !== 0) {
writers.push(new ReplicationWriter(this, sr, targetSettings))
}
})
}
}
// ensure the VM itself does not have any backup metadata which would be
// copied on manual snapshots and interfere with the backup jobs
async _cleanMetadata() {
const { vm } = this
if ('xo:backup:job' in vm.other_config) {
await vm.update_other_config({
'xo:backup:datetime': null,
'xo:backup:deltaChainLength': null,
'xo:backup:exported': null,
'xo:backup:job': null,
'xo:backup:schedule': null,
'xo:backup:vm': null,
})
}
}
async _snapshot() {
const { vm } = this
const xapi = this._xapi
const settings = this._settings
const doSnapshot = this._isDelta || vm.power_state === 'Running' || settings.snapshotRetention !== 0
if (doSnapshot) {
await Task.run({ name: 'snapshot' }, async () => {
if (!settings.bypassVdiChainsCheck) {
await vm.$assertHealthyVdiChains()
}
const snapshotRef = await vm[settings.checkpointSnapshot ? '$checkpoint' : '$snapshot']({
name_label: this._getSnapshotNameLabel(vm),
})
this.timestamp = Date.now()
await xapi.setFieldEntries('VM', snapshotRef, 'other_config', {
'xo:backup:datetime': formatDateTime(this.timestamp),
'xo:backup:job': this._jobId,
'xo:backup:schedule': this.scheduleId,
'xo:backup:vm': vm.uuid,
})
this.exportedVm = await xapi.getRecord('VM', snapshotRef)
return this.exportedVm.uuid
})
} else {
this.exportedVm = vm
this.timestamp = Date.now()
}
}
async _copyDelta() {
const { exportedVm } = this
const baseVm = this._baseVm
await asyncMap(this._writers, writer => writer.prepare && writer.prepare())
const deltaExport = await exportDeltaVm(exportedVm, baseVm, {
fullVdisRequired: this._fullVdisRequired,
})
const sizeContainers = mapValues(deltaExport.streams, watchStreamSize)
const timestamp = Date.now()
await asyncMap(this._writers, async writer => {
try {
await writer.transfer({
deltaExport: forkDeltaExport(deltaExport),
sizeContainers,
timestamp,
})
} catch (error) {
warn('copy failure', {
error,
target: writer.target,
vm: this.vm,
})
}
})
this._baseVm = exportedVm
if (baseVm !== undefined) {
await exportedVm.update_other_config(
'xo:backup:deltaChainLength',
String(+(baseVm.other_config['xo:backup:deltaChainLength'] ?? 0) + 1)
)
}
// not the case if offlineBackup
if (exportedVm.is_a_snapshot) {
await exportedVm.update_other_config('xo:backup:exported', 'true')
}
const size = Object.values(sizeContainers).reduce((sum, { size }) => sum + size, 0)
const end = Date.now()
const duration = end - timestamp
debug('transfer complete', {
duration,
speed: duration !== 0 ? (size * 1e3) / 1024 / 1024 / duration : 0,
size,
})
await asyncMap(this._writers, writer => writer.cleanup && writer.cleanup())
}
async _copyFull() {
const { compression } = this.job
const stream = await this._xapi.VM_export(this.exportedVm.$ref, {
compress: Boolean(compression) && (compression === 'native' ? 'gzip' : 'zstd'),
useSnapshot: false,
})
const sizeContainer = watchStreamSize(stream)
const timestamp = Date.now()
await asyncMap(this._writers, async writer => {
try {
await writer.run({
sizeContainer,
stream: forkStreamUnpipe(stream),
timestamp,
})
} catch (error) {
warn('copy failure', {
error,
target: writer.target,
vm: this.vm,
})
}
})
const { size } = sizeContainer
const end = Date.now()
const duration = end - timestamp
debug('transfer complete', {
duration,
speed: duration !== 0 ? (size * 1e3) / 1024 / 1024 / duration : 0,
size,
})
}
async _fetchJobSnapshots() {
const jobId = this._jobId
const vmRef = this.vm.$ref
const xapi = this._xapi
const snapshotsRef = await xapi.getField('VM', vmRef, 'snapshots')
const snapshotsOtherConfig = await asyncMap(snapshotsRef, ref => xapi.getField('VM', ref, 'other_config'))
const snapshots = []
snapshotsOtherConfig.forEach((other_config, i) => {
if (other_config['xo:backup:job'] === jobId) {
snapshots.push({ other_config, $ref: snapshotsRef[i] })
}
})
snapshots.sort((a, b) => (a.other_config['xo:backup:datetime'] < b.other_config['xo:backup:datetime'] ? -1 : 1))
this._jobSnapshots = snapshots
}
async _removeUnusedSnapshots() {
// TODO: handle all schedules (no longer existing schedules default to 0 retention)
const { scheduleId } = this
const scheduleSnapshots = this._jobSnapshots.filter(_ => _.other_config['xo:backup:schedule'] === scheduleId)
const baseVmRef = this._baseVm?.$ref
const xapi = this._xapi
await asyncMap(getOldEntries(this._settings.snapshotRetention, scheduleSnapshots), ({ $ref }) => {
if ($ref !== baseVmRef) {
return xapi.VM_destroy($ref)
}
})
}
async _selectBaseVm() {
const xapi = this._xapi
let baseVm = findLast(this._jobSnapshots, _ => 'xo:backup:exported' in _.other_config)
if (baseVm === undefined) {
return
}
const fullInterval = this._settings.fullInterval
const deltaChainLength = +(baseVm.other_config['xo:backup:deltaChainLength'] ?? 0) + 1
if (!(fullInterval === 0 || fullInterval > deltaChainLength)) {
return
}
const srcVdis = keyBy(await xapi.getRecords('VDI', await this.vm.$getDisks()), '$ref')
// resolve full record
baseVm = await xapi.getRecord('VM', baseVm.$ref)
const baseUuidToSrcVdi = new Map()
await asyncMap(await baseVm.$getDisks(), async baseRef => {
const snapshotOf = await xapi.getField('VDI', baseRef, 'snapshot_of')
const srcVdi = srcVdis[snapshotOf]
if (srcVdi !== undefined) {
baseUuidToSrcVdi.set(await xapi.getField('VDI', baseRef, 'uuid'), srcVdi)
}
})
const presentBaseVdis = new Map(baseUuidToSrcVdi)
const writers = this._writers
for (let i = 0, n = writers.length; presentBaseVdis.size !== 0 && i < n; ++i) {
await writers[i].checkBaseVdis(presentBaseVdis, baseVm)
}
if (presentBaseVdis.size === 0) {
return
}
const fullVdisRequired = new Set()
baseUuidToSrcVdi.forEach((srcVdi, baseUuid) => {
if (!presentBaseVdis.has(baseUuid)) {
fullVdisRequired.add(srcVdi.uuid)
}
})
this._baseVm = baseVm
this._fullVdisRequired = fullVdisRequired
}
async run() {
await this._fetchJobSnapshots()
if (this._isDelta) {
await this._selectBaseVm()
}
await this._cleanMetadata()
await this._removeUnusedSnapshots()
const { _settings: settings, vm } = this
const isRunning = vm.power_state === 'Running'
const startAfter = isRunning && (settings.offlineBackup ? 'backup' : settings.offlineSnapshot && 'snapshot')
if (startAfter) {
await vm.$callAsync('clean_shutdown')
}
try {
await this._snapshot()
if (startAfter === 'snapshot') {
ignoreErrors.call(vm.$callAsync('start', false, false))
}
if (this._writers.length !== 0) {
await (this._isDelta ? this._copyDelta() : this._copyFull())
}
} finally {
if (startAfter) {
ignoreErrors.call(vm.$callAsync('start', false, false))
}
await this._fetchJobSnapshots()
await this._removeUnusedSnapshots()
}
}
}

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