Compare commits

...

289 Commits

Author SHA1 Message Date
Julien Fontanet
95e33ee612 feat(xo-server): 5.29.3 2018-11-05 09:57:01 +01:00
Julien Fontanet
093bda7039 feat(fs): 0.4.1 2018-11-05 09:53:43 +01:00
Julien Fontanet
4e35b19ac5 fix(xo-web/xoa/update): fix re-registration 2018-11-05 09:46:22 +01:00
Julien Fontanet
244d8a51e8 chore(xo-web/xoa/update): dont hide error 2018-11-05 09:42:59 +01:00
Julien Fontanet
9d6cc77cc8 chore(changelog): add timeout entry 2018-11-03 17:45:50 +01:00
Julien Fontanet
d5e0150880 chore: update promise-toolbox to v0.11
`timeout()` now provides better stack traces and support 0 delay to
disable the timeout.
2018-11-03 17:45:50 +01:00
Julien Fontanet
5cf29a98b3 feat(fs): configurable timeout 2018-11-03 17:45:50 +01:00
Julien Fontanet
165c2262c0 fix(nfs): default timeout 10s → 10m 2018-11-03 17:45:50 +01:00
badrAZ
74f5d2e0cd fix(xo-server/backup-ng): "vms" should be a dictionary (#3635) 2018-11-02 16:29:44 +01:00
badrAZ
2d93456f52 feat(xo-server/backup-ng): log scheduled VMs if concurrency above 0 (#3633)
See #3603
2018-11-02 14:56:46 +01:00
Julien Fontanet
fd401ca335 fix(fs/nfs): opts param is optional 2018-11-02 10:09:19 +01:00
Julien Fontanet
97ba93a9ad chore(fs): configurable mounts dir (#3413) 2018-11-02 09:37:35 +01:00
Pierre Donias
0788c25710 feat(xo-web): 5.29.1 2018-10-31 17:20:15 +01:00
Julien Fontanet
82bba951db feat(xo-web/xoa-updater/logs): from old to new
Fixes #2708
2018-10-31 17:18:53 +01:00
Julien Fontanet
6efd611b80 feat(xo-web/xoa/update): use pre for updater logs 2018-10-31 17:18:53 +01:00
Julien Fontanet
b7d43b42b9 fix(xo-web/xoa/update): toggleState not compatible with ActionButton 2018-10-31 17:18:52 +01:00
Julien Fontanet
801b71d9ae fix(xo-web/xoa/update): typo 2018-10-31 17:18:52 +01:00
Pierre Donias
0ff7c2188a feat(xo-server): 5.29.2 2018-10-31 16:56:23 +01:00
Julien Fontanet
bc1667440f feat(log): 0.1.4 2018-10-31 16:55:11 +01:00
Julien Fontanet
227b464a8e fix(log): paths in transports/* 2018-10-31 16:54:35 +01:00
Pierre Donias
f6c43650b4 feat(xo-server): 5.29.1 2018-10-31 16:43:29 +01:00
Pierre Donias
597689fde0 chore(xo-server): use log 0.1.3 2018-10-31 16:42:03 +01:00
Julien Fontanet
da6b71fde8 feat(log): 0.1.3 2018-10-31 16:39:20 +01:00
Julien Fontanet
5f2590c858 fix(log): remove transports symlink
Issue with publication.
2018-10-31 16:38:23 +01:00
Julien Fontanet
37b0867151 feat(log): 0.1.1 2018-10-31 16:31:22 +01:00
Julien Fontanet
85031cfb9d fix(log): publish configure.js and transports 2018-10-31 16:30:55 +01:00
Pierre Donias
a13f86fb7c chore(CHANGELOG): 5.28.0 2018-10-31 16:03:35 +01:00
Pierre Donias
7cbc5e642f feat(xo-web): 5.29.0 2018-10-31 15:59:53 +01:00
Pierre Donias
48d4abc259 feat(xo-server): 5.29.0 2018-10-31 15:58:52 +01:00
Pierre Donias
c805f3b1a7 feat(xo-server-usage-report): 0.7.0 2018-10-31 15:55:26 +01:00
Pierre Donias
40212582a9 feat(xen-api): 0.20.0 2018-10-31 15:53:40 +01:00
Pierre Donias
2c875928de feat(vhd-lib): 0.4.0 2018-10-31 15:51:49 +01:00
Pierre Donias
c24eb9778e feat(complex-matcher): 0.5.0 2018-10-31 15:48:31 +01:00
Pierre Donias
eb079b1360 feat(fs): 0.4.0 2018-10-31 15:46:49 +01:00
Pierre Donias
120a519303 chore(@xen-orchestra/log): packages should not be private 2018-10-31 15:32:17 +01:00
Pierre Donias
95def95678 feat(log): 0.1.0 2018-10-31 15:26:58 +01:00
Pierre Donias
8274a00f91 feat(xo-common): 0.1.2 2018-10-31 15:21:09 +01:00
Pierre Donias
3c6c4976cd feat(xo-server-backup-reports): 0.15.0 2018-10-31 15:19:21 +01:00
Julien Fontanet
0fd35b1679 feat(xo-web/xoa): product version and pkgs list (#3621)
Fixes #3560
2018-10-31 10:49:10 +01:00
Enishowk
3c931604be fix(xo-web/backup-ng/utils/FormFeedback): fix incorrect prop type (#3604)
See #2578
2018-10-31 10:14:41 +01:00
Julien Fontanet
02dddbd662 chore(xo-web/xoa/update): rewrite (#3620) 2018-10-31 09:46:44 +01:00
Julien Fontanet
675763d039 chore(xo-web): mutualize chain decoration 2018-10-30 18:40:21 +01:00
Julien Fontanet
63acc7ef32 fix(xo-server/bin): import → require 2018-10-30 17:45:02 +01:00
Enishowk
cbd78bdfef chore(xo-server): use @xen-orchestra/log to handle global errors (#3618)
Fixes #3616
2018-10-30 17:38:24 +01:00
Julien Fontanet
a09a2ed6c3 chore(log/configure): explicit test 2018-10-30 17:14:44 +01:00
Enishowk
7d18a6d8a9 fix(xo-web/Home/NoObjects): only subscribe to servers if admin (#3613)
Fixes #2335
2018-10-30 15:58:06 +01:00
Julien Fontanet
65a5984d4c fix(cron): prevent exceptions from breaking the job (#3617)
Fixes support#1038
2018-10-30 15:26:46 +01:00
badrAZ
d5f519bf5a feat(backup-ng): display logs for backup restoration (#3609)
Fixes #2511
2018-10-30 15:26:26 +01:00
badrAZ
bede39c8f3 fix(xo-server-usage-report): handle SR without container (#3614)
This is a work-around for an XO bug.

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

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

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

Fixes #3132
2018-09-21 17:37:49 +02:00
badrAZ
82956af785 fix(xo-web/logs): fix @xen-orchestra/log import (#3441) 2018-09-21 15:36:54 +02:00
badrAZ
6ca09dc9fe feat(xo-web/vm/tab-advanced): ability to set the NIC type (#3440)
Fixes #3423
2018-09-21 15:30:23 +02:00
badrAZ
e0ecbab841 feat(xo-server/vm): ability to change NIC type (#3437)
See #3423
2018-09-21 14:42:43 +02:00
Julien Fontanet
83d1a5ff13 feat(defined): helpers to deal with undefined (#3436)
Extracted from xo-web.
2018-09-21 11:56:33 +02:00
badrAZ
a71740d49a feat(backup-ng): ability to restart all failed VMs (#3420)
Fixes #3339
2018-09-20 17:05:57 +02:00
Julien Fontanet
0215c19d1d feat(xo-server/proxy-console): work around missing host (#3435)
Fixes #3432
2018-09-20 14:47:39 +02:00
Julien Fontanet
ea1c3ab54a fix(xo-server/delta NG): dont start export before all targets ready (#3427)
Fixes #3424
2018-09-20 14:38:45 +02:00
badrAZ
b98b618be8 fix(xo-server-backup-reports): handle log not found (#3430) 2018-09-20 14:34:29 +02:00
Julien Fontanet
5e363761a2 fix(xo-server/proxy-console): pass hostname to net.connect 2018-09-20 10:25:47 +02:00
Julien Fontanet
62d48bd59d fix(async-map): apply → call 😖
Fixes #3431
2018-09-20 10:04:09 +02:00
badrAZ
a0049bae8d fix(xo-web/backup-ng): make log value dynamic in copy/report buttons (#3360)
Fixes #3273
2018-09-18 15:15:59 +02:00
Julien Fontanet
18660cb0e1 fix(async-map): accept objects as collection 2018-09-18 12:10:44 +02:00
Julien Fontanet
e3c6c1c1ca chore(async-map): use lodash/map import 2018-09-18 12:10:43 +02:00
badrAZ
56114a7d18 feat(xo-server-backup-report): ability to test the plugin (#3421) 2018-09-17 17:23:45 +02:00
Julien Fontanet
0fe70b1a91 feat(log): new lib to help logging (#3414)
Fixes #2414
Related to #1669
2018-09-17 17:02:27 +02:00
Julien Fontanet
527eb0b1e6 feat(async-map): better Promise.all + map (#3422) 2018-09-17 16:37:49 +02:00
Julien Fontanet
cfd6fd722a feat(mixin): split a class in independant parts (#3415) 2018-09-17 16:33:21 +02:00
badrAZ
42591bd4bd feat(xo-web/VM): display the PVHVM status (#3418)
Fixes #3014
2018-09-17 11:06:19 +02:00
Julien Fontanet
8689cff26b feat(emit-async): handle async listeners (#3416)
Extracted from xo-server.
2018-09-17 10:42:22 +02:00
badrAZ
b4e4d32255 feat(xo-web/backup-ng/overview): display transferred and merged data size (#3408) 2018-09-14 17:21:06 +02:00
Julien Fontanet
18c88ba770 feat(cr-seed-cli): CLI to help CR seeding (#3346) 2018-09-14 17:04:55 +02:00
badrAZ
d212168f59 feat(xo-web/backup-ng/new): use a modal for creating/editing a schedule (#3359)
Fixes #3138
2018-09-14 15:53:28 +02:00
Julien Fontanet
3625477187 fix(xo-web/xoa-updater): wait trial request before checking state (#3412)
Fixes #3407
2018-09-14 12:26:41 +02:00
Pierre Donias
9be8525439 await xoaState 2018-09-14 12:04:19 +02:00
Pierre Donias
6e013a0dc5 PR 2018-09-14 11:54:39 +02:00
Pierre Donias
06d555d4f9 fix(xo-web/xoa-updater): wait for trial request before checking trial state
Fixes #3407
2018-09-14 11:51:43 +02:00
Julien Fontanet
63fe0f2c88 fix(xo-server/importDeltaVm): dont fail on empty VBDs (#3410) 2018-09-14 10:37:09 +02:00
Julien Fontanet
2a276dfb99 changelog 2018-09-14 10:36:49 +02:00
Julien Fontanet
1f009ca954 fix(xo-server/importDeltaVm): dont fail on empty VBDs 2018-09-13 17:27:11 +02:00
badrAZ
5cf1ba41f3 feat(xo-web/modal#form): support an additional icon to the title (#3409) 2018-09-13 16:58:03 +02:00
badrAZ
1ef205cb74 feat(xo-web/backup-ng/new): tip for creating vms on a thin-provisioned storage (#3402)
Fixes #3334
2018-09-13 16:23:51 +02:00
Julien Fontanet
1e59af3ab2 fix(xo-server/deleteVm): dont delete VDIs/VIFs if destroy is blocked (#3406) 2018-09-13 11:44:17 +02:00
badrAZ
bb88b420c1 chore(xo-web/backup-ng/new): make "setSchedule" support multiple params (#3404) 2018-09-12 13:53:37 +02:00
badrAZ
e8475d144c chore(xo-web/backup-ng/new): improve new schedule implementation (#3348) 2018-09-12 13:43:02 +02:00
badrAZ
dedac62269 feat(xo-web): implementation of the modal dedicated to forms (#3358) 2018-09-11 17:31:20 +02:00
badrAZ
f8fdd888c4 fix(xo-web/remotes): error appears twice on testing (#3399) 2018-09-11 17:27:10 +02:00
Julien Fontanet
d8bd30e355 feat(xo-web/remotes): use WORKGROUP as default SMB domain (#3398) 2018-09-11 10:46:52 +02:00
badrAZ
4fec816274 feat(xo-web/settings/remotes): test on creation/edition (#3397)
See #3323
2018-09-11 10:09:16 +02:00
badrAZ
1211447e81 fix(xo-web/settings/remotes): rename connect(ed)/disconnect(ed) to enable(d)/disable(d) (#3396)
See #3323
2018-09-10 17:15:33 +02:00
Julien Fontanet
ed78f4c5ee fix(xo-server/backup NG): third time is the charm (sigh) 2018-09-10 10:59:56 +02:00
Julien Fontanet
4a2e9d4c88 fix(xo-server/backup NG): CancelToken.race → .source 2018-09-10 10:51:05 +02:00
Julien Fontanet
1807917204 fix(xo-server/backup NG): CancelToken#fork() no longer exists
Issue introduced in b3004a38a
2018-09-10 10:47:53 +02:00
Julien Fontanet
b3004a38aa chore: update promise-toolbox to v0.10 (#3392) 2018-09-07 14:34:47 +02:00
Pierre Donias
0ad6c073ee chore(CHANGELOG): 5.26.0 2018-09-07 11:39:26 +02:00
Pierre Donias
0abaadb391 feat(xo-web): 5.26.0 2018-09-07 11:37:12 +02:00
Pierre Donias
b43cf27479 feat(xo-server): 5.26.0 2018-09-07 11:36:20 +02:00
Julien Fontanet
bcab6bb584 fix(xo-server/listReplicatedVms): dont use snapshot_time (#3394)
It is not always set on VMs imported from snapshots, add a new metadata in `other_config` and use the timestamp in the name_label a fallback.

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,18 +22,16 @@ const configs = {
// loose: true,
shippedProposals: true,
targets: __PROD__
? (() => {
let node = (pkg.engines || {}).node
if (node !== undefined) {
const trimChars = '^=>~'
while (trimChars.includes(node[0])) {
node = node.slice(1)
}
return { node: node }
}
})()
: { browsers: '', node: 'current' },
targets: (() => {
let node = (pkg.engines || {}).node
if (node !== undefined) {
const trimChars = '^=>~'
while (trimChars.includes(node[0])) {
node = node.slice(1)
}
}
return { browsers: pkg.browserslist, node }
})(),
useBuiltIns: '@babel/polyfill' in (pkg.dependencies || {}) && 'usage',
}
},

View File

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

View File

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

View File

@@ -41,10 +41,10 @@
"moment-timezone": "^0.5.14"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/preset-flow": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"cross-env": "^5.1.3",
"rimraf": "^2.6.2"
},

View File

@@ -8,7 +8,12 @@ const MAX_DELAY = 2 ** 31 - 1
class Job {
constructor (schedule, fn) {
const wrapper = () => {
const result = fn()
let result
try {
result = fn()
} catch (_) {
// catch any thrown value to ensure it does not break the job
}
let then
if (result != null && typeof (then = result.then) === 'function') {
then.call(result, scheduleNext, scheduleNext)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@xen-orchestra/fs",
"version": "0.3.0",
"version": "0.4.1",
"license": "AGPL-3.0",
"description": "The File System for Xen Orchestra backups.",
"keywords": [],
@@ -20,24 +20,22 @@
"node": ">=6"
},
"dependencies": {
"@babel/runtime": "7.0.0-rc.1",
"@marsaud/smb2": "^0.9.0",
"execa": "^0.10.0",
"execa": "^1.0.0",
"fs-extra": "^7.0.0",
"get-stream": "^4.0.0",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"through2": "^2.0.3",
"tmp": "^0.0.33",
"xo-remote-parser": "^0.5.0"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/plugin-proposal-function-bind": "7.0.0-rc.1",
"@babel/plugin-transform-runtime": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/preset-flow": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-function-bind": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"index-modules": "^0.3.0",

View File

@@ -2,7 +2,7 @@
import getStream from 'get-stream'
import { randomBytes } from 'crypto'
import { fromCallback, fromEvent, ignoreErrors } from 'promise-toolbox'
import { fromCallback, fromEvent, ignoreErrors, timeout } from 'promise-toolbox'
import { type Readable, type Writable } from 'stream'
import { parse } from 'xo-remote-parser'
@@ -17,13 +17,22 @@ type File = FileDescriptor | string
const checksumFile = file => file + '.checksum'
const DEFAULT_TIMEOUT = 6e5 // 10 min
export default class RemoteHandlerAbstract {
_remote: Object
constructor (remote: any) {
this._remote = { ...remote, ...parse(remote.url) }
if (this._remote.type !== this.type) {
throw new Error('Incorrect remote type')
_timeout: number
constructor (remote: any, options: Object = {}) {
if (remote.url === 'test://') {
this._remote = remote
} else {
this._remote = { ...remote, ...parse(remote.url) }
if (this._remote.type !== this.type) {
throw new Error('Incorrect remote type')
}
}
;({ timeout: this._timeout = DEFAULT_TIMEOUT } = options)
}
get type (): string {
@@ -121,7 +130,7 @@ export default class RemoteHandlerAbstract {
newPath: string,
{ checksum = false }: Object = {}
) {
let p = this._rename(oldPath, newPath)
let p = timeout.call(this._rename(oldPath, newPath), this._timeout)
if (checksum) {
p = Promise.all([
p,
@@ -142,7 +151,7 @@ export default class RemoteHandlerAbstract {
prependDir = false,
}: { filter?: (name: string) => boolean, prependDir?: boolean } = {}
): Promise<string[]> {
let entries = await this._list(dir)
let entries = await timeout.call(this._list(dir), this._timeout)
if (filter !== undefined) {
entries = entries.filter(filter)
}
@@ -165,28 +174,30 @@ export default class RemoteHandlerAbstract {
{ checksum = false, ignoreMissingChecksum = false, ...options }: Object = {}
): Promise<LaxReadable> {
const path = typeof file === 'string' ? file : file.path
const streamP = this._createReadStream(file, options).then(stream => {
// detect early errors
let promise = fromEvent(stream, 'readable')
const streamP = timeout
.call(this._createReadStream(file, options), this._timeout)
.then(stream => {
// detect early errors
let promise = fromEvent(stream, 'readable')
// try to add the length prop if missing and not a range stream
if (
stream.length === undefined &&
options.end === undefined &&
options.start === undefined
) {
promise = Promise.all([
promise,
ignoreErrors.call(
this.getSize(file).then(size => {
stream.length = size
})
),
])
}
// try to add the length prop if missing and not a range stream
if (
stream.length === undefined &&
options.end === undefined &&
options.start === undefined
) {
promise = Promise.all([
promise,
ignoreErrors.call(
this.getSize(file).then(size => {
stream.length = size
})
),
])
}
return promise.then(() => stream)
})
return promise.then(() => stream)
})
if (!checksum) {
return streamP
@@ -224,7 +235,10 @@ export default class RemoteHandlerAbstract {
}
async openFile (path: string, flags?: string): Promise<FileDescriptor> {
return { fd: await this._openFile(path, flags), path }
return {
fd: await timeout.call(this._openFile(path, flags), this._timeout),
path,
}
}
async _openFile (path: string, flags?: string): Promise<mixed> {
@@ -232,7 +246,7 @@ export default class RemoteHandlerAbstract {
}
async closeFile (fd: FileDescriptor): Promise<void> {
await this._closeFile(fd.fd)
await timeout.call(this._closeFile(fd.fd), this._timeout)
}
async _closeFile (fd: mixed): Promise<void> {
@@ -252,10 +266,13 @@ export default class RemoteHandlerAbstract {
{ checksum = false, ...options }: Object = {}
): Promise<LaxWritable> {
const path = typeof file === 'string' ? file : file.path
const streamP = this._createOutputStream(file, {
flags: 'wx',
...options,
})
const streamP = timeout.call(
this._createOutputStream(file, {
flags: 'wx',
...options,
}),
this._timeout
)
if (!checksum) {
return streamP
@@ -290,7 +307,7 @@ export default class RemoteHandlerAbstract {
ignoreErrors.call(this._unlink(checksumFile(file)))
}
await this._unlink(file)
await timeout.call(this._unlink(file), this._timeout)
}
async _unlink (file: mixed): Promise<void> {
@@ -298,7 +315,7 @@ export default class RemoteHandlerAbstract {
}
async getSize (file: mixed): Promise<number> {
return this._getSize(file)
return timeout.call(this._getSize(file), this._timeout)
}
async _getSize (file: mixed): Promise<number> {

View File

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

View File

@@ -2,8 +2,8 @@
import rimraf from 'rimraf'
import tmp from 'tmp'
import { pFromCallback } from 'promise-toolbox'
import { fromCallback as pFromCallback } from 'promise-toolbox'
import { getHandler } from '.'
const initialDir = process.cwd()

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import Smb2 from '@marsaud/smb2'
import { lastly as pFinally } from 'promise-toolbox'
import { pFinally } from 'promise-toolbox'
import RemoteHandlerAbstract from './abstract'
@@ -23,8 +23,8 @@ const normalizeError = error => {
}
export default class SmbHandler extends RemoteHandlerAbstract {
constructor (remote) {
super(remote)
constructor (remote, opts) {
super(remote, opts)
this._forget = noop
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,12 +6,180 @@
### Bug fixes
- [Backup NG] Increase timeout in stale remotes detection to limit false positives (PR [#3632](https://github.com/vatesfr/xen-orchestra/pull/3632))
### Released packages
- xo-server v5.30.0
- xo-web v5.30.0
## **5.28.0** (2018-10-31)
### Enhancements
- [Usage Report] Add IOPS read/write/total per VM [#3309](https://github.com/vatesfr/xen-orchestra/issues/3309) (PR [#3455](https://github.com/vatesfr/xen-orchestra/pull/3455))
- [Self service] Sort resource sets by name (PR [#3507](https://github.com/vatesfr/xen-orchestra/pull/3507))
- [Usage Report] Add top 3 SRs which use the most IOPS read/write/total [#3306](https://github.com/vatesfr/xen-orchestra/issues/3306) (PR [#3508](https://github.com/vatesfr/xen-orchestra/pull/3508))
- [New VM] Display a warning when the memory is below the template memory static min [#3496](https://github.com/vatesfr/xen-orchestra/issues/3496) (PR [#3513](https://github.com/vatesfr/xen-orchestra/pull/3513))
- [Backup NG form] Add link to plugins setting [#3457](https://github.com/vatesfr/xen-orchestra/issues/3457) (PR [#3514](https://github.com/vatesfr/xen-orchestra/pull/3514))
- [Backup reports] Add job and run ID [#3488](https://github.com/vatesfr/xen-orchestra/issues/3488) (PR [#3516](https://github.com/vatesfr/xen-orchestra/pull/3516))
- [Usage Report] Add top 3 VMs which use the most IOPS read/write/total [#3308](https://github.com/vatesfr/xen-orchestra/issues/3308) (PR [#3463](https://github.com/vatesfr/xen-orchestra/pull/3463))
- [Settings/logs] Homogenize action buttons in table and enable bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3528](https://github.com/vatesfr/xen-orchestra/pull/3528))
- [Settings/acls] Add bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3536](https://github.com/vatesfr/xen-orchestra/pull/3536))
- [Home] Improve search usage: raw numbers also match in names [#2906](https://github.com/vatesfr/xen-orchestra/issues/2906) (PR [#3552](https://github.com/vatesfr/xen-orchestra/pull/3552))
- [Backup NG] Timeout of a job is now in hours [#3550](https://github.com/vatesfr/xen-orchestra/issues/3550) (PR [#3553](https://github.com/vatesfr/xen-orchestra/pull/3553))
- [Backup NG] Explicit error if a VM is missing [#3434](https://github.com/vatesfr/xen-orchestra/issues/3434) (PR [#3522](https://github.com/vatesfr/xen-orchestra/pull/3522))
- [Backup NG] Show all advanced settings with non-default values in overview [#3549](https://github.com/vatesfr/xen-orchestra/issues/3549) (PR [#3554](https://github.com/vatesfr/xen-orchestra/pull/3554))
- [Backup NG] Collapse advanced settings by default [#3551](https://github.com/vatesfr/xen-orchestra/issues/3551) (PR [#3559](https://github.com/vatesfr/xen-orchestra/pull/3559))
- [Scheduling] Merge selection and interval tabs [#1902](https://github.com/vatesfr/xen-orchestra/issues/1902) (PR [#3519](https://github.com/vatesfr/xen-orchestra/pull/3519))
- [Backup NG/Restore] The backup selector now also shows the job name [#3366](https://github.com/vatesfr/xen-orchestra/issues/3366) (PR [#3564](https://github.com/vatesfr/xen-orchestra/pull/3564))
- Sort buttons by criticality in tables [#3168](https://github.com/vatesfr/xen-orchestra/issues/3168) (PR [#3545](https://github.com/vatesfr/xen-orchestra/pull/3545))
- [Usage Report] Ability to send a daily report [#3544](https://github.com/vatesfr/xen-orchestra/issues/3544) (PR [#3582](https://github.com/vatesfr/xen-orchestra/pull/3582))
- [Backup NG logs] Disable state filters with no entries [#3438](https://github.com/vatesfr/xen-orchestra/issues/3438) (PR [#3442](https://github.com/vatesfr/xen-orchestra/pull/3442))
- [ACLs] Global performance improvement on UI for non-admin users [#3578](https://github.com/vatesfr/xen-orchestra/issues/3578) (PR [#3584](https://github.com/vatesfr/xen-orchestra/pull/3584))
- [Backup NG] Improve the Schedule's view (Replace table by list) [#3491](https://github.com/vatesfr/xen-orchestra/issues/3491) (PR [#3586](https://github.com/vatesfr/xen-orchestra/pull/3586))
- ([Host/Storage], [Sr/hosts]) add bulk deletion [#3179](https://github.com/vatesfr/xen-orchestra/issues/3179) (PR [#3539](https://github.com/vatesfr/xen-orchestra/pull/3539))
- [xo-server] Use @xen-orchestra/log for basic logging [#3555](https://github.com/vatesfr/xen-orchestra/issues/3555) (PR [#3579](https://github.com/vatesfr/xen-orchestra/pull/3579))
- [Backup Report] Log error when job failed [#3458](https://github.com/vatesfr/xen-orchestra/issues/3458) (PR [#3593](https://github.com/vatesfr/xen-orchestra/pull/3593))
- [Backup NG] Display logs for backup restoration [#2511](https://github.com/vatesfr/xen-orchestra/issues/2511) (PR [#3609](https://github.com/vatesfr/xen-orchestra/pull/3609))
- [XOA] Display product version and list of all installed packages [#3560](https://github.com/vatesfr/xen-orchestra/issues/3560) (PR [#3621](https://github.com/vatesfr/xen-orchestra/pull/3621))
### Bug fixes
- [Remotes] Fix removal of broken remotes [#3327](https://github.com/vatesfr/xen-orchestra/issues/3327) (PR [#3521](https://github.com/vatesfr/xen-orchestra/pull/3521))
- [Backups] Fix stuck backups due to broken NFS remotes [#3467](https://github.com/vatesfr/xen-orchestra/issues/3467) (PR [#3534](https://github.com/vatesfr/xen-orchestra/pull/3534))
- [New VM] Fix missing cloud config when creating multiple VMs at once in some cases [#3532](https://github.com/vatesfr/xen-orchestra/issues/3532) (PR [#3535](https://github.com/vatesfr/xen-orchestra/pull/3535))
- [VM] Fix an error when an admin tried to add a disk on a Self VM whose resource set had been deleted [#2814](https://github.com/vatesfr/xen-orchestra/issues/2814) (PR [#3530](https://github.com/vatesfr/xen-orchestra/pull/3530))
- [Self/Create VM] Fix some quotas based on the template instead of the user inputs [#2683](https://github.com/vatesfr/xen-orchestra/issues/2683) (PR [#3546](https://github.com/vatesfr/xen-orchestra/pull/3546))
- [Self] Ignore DR and CR VMs when computing quotas [#3064](https://github.com/vatesfr/xen-orchestra/issues/3064) (PR [#3561](https://github.com/vatesfr/xen-orchestra/pull/3561))
- [Patches] Wrongly requiring to eject CDs from halted VMs and snapshots before installing patches (PR [#3611](https://github.com/vatesfr/xen-orchestra/pull/3611))
- [Jobs] Ensure the scheduling is not interrupted in rare cases (PR [#3617](https://github.com/vatesfr/xen-orchestra/pull/3617))
- [Home] Fix `server.getAll` error at login when user is not admin [#2335](https://github.com/vatesfr/xen-orchestra/issues/2335) (PR [#3613](https://github.com/vatesfr/xen-orchestra/pull/3613))
### Released packages
- xo-server-backup-reports v0.15.0
- xo-common v0.1.2
- @xen-orchestra/log v0.1.0
- @xen-orchestra/fs v0.4.0
- complex-matcher v0.5.0
- vhd-lib v0.4.0
- xen-api v0.20.0
- xo-server-usage-report v0.7.0
- xo-server v5.29.0
- xo-web v5.29.0
## **5.27.2** (2018-10-05)
### Enhancements
- [Host/Networks] Remove "Add network" button [#3386](https://github.com/vatesfr/xen-orchestra/issues/3386) (PR [#3478](https://github.com/vatesfr/xen-orchestra/pull/3478))
- [Host/networks] Private networks table [#3387](https://github.com/vatesfr/xen-orchestra/issues/3387) (PR [#3481](https://github.com/vatesfr/xen-orchestra/pull/3481))
- [Home/pool] Patch count pill now shows the number of unique patches in the pool [#3321](https://github.com/vatesfr/xen-orchestra/issues/3321) (PR [#3483](https://github.com/vatesfr/xen-orchestra/pull/3483))
- [Patches] Pre-install checks to avoid errors [#3252](https://github.com/vatesfr/xen-orchestra/issues/3252) (PR [#3484](https://github.com/vatesfr/xen-orchestra/pull/3484))
- [Vm/Snapshots] Allow VM operators to create snapshots and delete those they created [#3443](https://github.com/vatesfr/xen-orchestra/issues/3443) (PR [#3482](https://github.com/vatesfr/xen-orchestra/pull/3482))
- [VM/clone] Handle ACLs and Self Service [#3139](https://github.com/vatesfr/xen-orchestra/issues/3139) (PR [#3493](https://github.com/vatesfr/xen-orchestra/pull/3493))
### Bug fixes
- [Backup NG] Fix `Cannot read property 'uuid' of undefined` when a disk is removed from a VM to backup (PR [#3479](https://github.com/vatesfr/xen-orchestra/pull/3479))
- [Backup NG] Fix unexpected full after failure, interruption or basic rolling snapshot (PR [#3485](https://github.com/vatesfr/xen-orchestra/pull/3485))
- [Usage report] Display top 3 used SRs instead of top 3 biggest SRs [#3307](https://github.com/vatesfr/xen-orchestra/issues/3307) (PR [#3475](https://github.com/vatesfr/xen-orchestra/pull/3475))
### Released packages
- vhd-lib v0.3.2
- xo-vmdk-to-vhd v0.1.5
- xo-server-usage-report v0.6.0
- xo-acl-resolver v0.3.0
- xo-server v5.28.0
- xo-web v5.28.0
## **5.27.1** (2018-09-28)
### Enhancements
### Bug fixes
- [OVA Import] Allow import of files bigger than 127GB (PR [#3451](https://github.com/vatesfr/xen-orchestra/pull/3451))
- [File restore] Fix a path issue when going back to the parent folder (PR [#3446](https://github.com/vatesfr/xen-orchestra/pull/3446))
- [File restore] Fix a minor issue when showing which selected files are redundant (PR [#3447](https://github.com/vatesfr/xen-orchestra/pull/3447))
- [Memory] Fix a major leak [#2580](https://github.com/vatesfr/xen-orchestra/issues/2580) [#2820](https://github.com/vatesfr/xen-orchestra/issues/2820) (PR [#3453](https://github.com/vatesfr/xen-orchestra/pull/3453))
- [NFS Remotes] Fix `already mounted` race condition [#3380](https://github.com/vatesfr/xen-orchestra/issues/3380) (PR [#3460](https://github.com/vatesfr/xen-orchestra/pull/3460))
- Fix `Cannot read property 'type' of undefined` when deleting a VM (PR [#3465](https://github.com/vatesfr/xen-orchestra/pull/3465))
### Released packages
- @xen-orchestra/fs v0.3.1
- vhd-lib v0.3.1
- xo-vmdk-to-vhd v0.1.4
- xo-server v5.27.2
- xo-web v5.27.1
## **5.27.0** (2018-09-24)
### Enhancements
- [Remotes] Test the remote automatically on changes [#3323](https://github.com/vatesfr/xen-orchestra/issues/3323) (PR [#3397](https://github.com/vatesfr/xen-orchestra/pull/3397))
- [Remotes] Use *WORKGROUP* as default domain for new SMB remote (PR [#3398](https://github.com/vatesfr/xen-orchestra/pull/3398))
- [Backup NG form] Display a tip to encourage users to create vms on a thin-provisioned storage [#3334](https://github.com/vatesfr/xen-orchestra/issues/3334) (PR [#3402](https://github.com/vatesfr/xen-orchestra/pull/3402))
- [Backup NG form] improve schedule's form [#3138](https://github.com/vatesfr/xen-orchestra/issues/3138) (PR [#3359](https://github.com/vatesfr/xen-orchestra/pull/3359))
- [Backup NG Overview] Display transferred and merged data size for backup jobs [#3340](https://github.com/vatesfr/xen-orchestra/issues/3340) (PR [#3408](https://github.com/vatesfr/xen-orchestra/pull/3408))
- [VM] Display the PVHVM status [#3014](https://github.com/vatesfr/xen-orchestra/issues/3014) (PR [#3418](https://github.com/vatesfr/xen-orchestra/pull/3418))
- [Backup reports] Ability to test the plugin (PR [#3421](https://github.com/vatesfr/xen-orchestra/pull/3421))
- [Backup NG] Ability to restart failed VMs' backup [#3339](https://github.com/vatesfr/xen-orchestra/issues/3339) (PR [#3420](https://github.com/vatesfr/xen-orchestra/pull/3420))
- [VM] Ability to change the NIC type [#3423](https://github.com/vatesfr/xen-orchestra/issues/3423) (PR [#3440](https://github.com/vatesfr/xen-orchestra/pull/3440))
- [Backup NG Overview] Display the schedule's name [#3444](https://github.com/vatesfr/xen-orchestra/issues/3444) (PR [#3445](https://github.com/vatesfr/xen-orchestra/pull/3445))
### Bug fixes
- [Remotes] Rename connect(ed)/disconnect(ed) to enable(d)/disable(d) [#3323](https://github.com/vatesfr/xen-orchestra/issues/3323) (PR [#3396](https://github.com/vatesfr/xen-orchestra/pull/3396))
- [Remotes] Fix error appears twice on testing (PR [#3399](https://github.com/vatesfr/xen-orchestra/pull/3399))
- [Backup NG] Don't fail on VMs with empty VBDs (like CDs or floppy disks) (PR [#3410](https://github.com/vatesfr/xen-orchestra/pull/3410))
- [XOA updater] Fix issue where trial request would fail [#3407](https://github.com/vatesfr/xen-orchestra/issues/3407) (PR [#3412](https://github.com/vatesfr/xen-orchestra/pull/3412))
- [Backup NG logs] Fix log's value not being updated in the copy and report button [#3273](https://github.com/vatesfr/xen-orchestra/issues/3273) (PR [#3360](https://github.com/vatesfr/xen-orchestra/pull/3360))
- [Backup NG] Fix issue when *Delete first* was enabled for some of the remotes [#3424](https://github.com/vatesfr/xen-orchestra/issues/3424) (PR [#3427](https://github.com/vatesfr/xen-orchestra/pull/3427))
- [VM/host consoles] Work around a XenServer/XCP-ng issue which lead to some consoles not working [#3432](https://github.com/vatesfr/xen-orchestra/issues/3432) (PR [#3435](https://github.com/vatesfr/xen-orchestra/pull/3435))
- [Backup NG] Remove extraneous snapshots in case of multiple schedules [#3132](https://github.com/vatesfr/xen-orchestra/issues/3132) (PR [#3439](https://github.com/vatesfr/xen-orchestra/pull/3439))
- [Backup NG] Fix page reloaded on creating a schedule [#3461](https://github.com/vatesfr/xen-orchestra/issues/3461) (PR [#3462](https://github.com/vatesfr/xen-orchestra/pull/3462))
### Released packages
- xo-server-backup-reports v0.14.0
- @xen-orchestra/async-map v0.0.0
- @xen-orchestra/defined v0.0.0
- @xen-orchestra/emit-async v0.0.0
- @xen-orchestra/mixin v0.0.0
- xo-server v5.27.0
- xo-web v5.27.0
## **5.26.0** (2018-09-07)
### Enhancements
- [Backup (file) restore] Order backups by date in selector [#3294](https://github.com/vatesfr/xen-orchestra/issues/3294) (PR [#3374](https://github.com/vatesfr/xen-orchestra/pull/3374))
- [Self] Hide Tasks entry in menu for self users [#3311](https://github.com/vatesfr/xen-orchestra/issues/3311) (PR [#3373](https://github.com/vatesfr/xen-orchestra/pull/3373))
- [Tasks] Show previous tasks [#3266](https://github.com/vatesfr/xen-orchestra/issues/3266) (PR [#3377](https://github.com/vatesfr/xen-orchestra/pull/3377))
- [Backup NG] Add job name in names of replicated VMs (PR [#3379](https://github.com/vatesfr/xen-orchestra/pull/3379))
- [Backup NG] Restore directories [#1924](https://github.com/vatesfr/xen-orchestra/issues/1924) (PR [#3384](https://github.com/vatesfr/xen-orchestra/pull/3384))
- [VM] Start a VM on a specific host [#3191](https://github.com/vatesfr/xen-orchestra/issues/3191) (PR [#3389](https://github.com/vatesfr/xen-orchestra/pull/3389))
### Bug fixes
- [Self] Fix Self Service quotas not being correctly updated when deleting multiple VMs at a time (PR [#3368](https://github.com/vatesfr/xen-orchestra/pull/3368))
- [Backup NG] Don't fail listing backups when a remote is broken [#3365](https://github.com/vatesfr/xen-orchestra/issues/3365) (PR [#3367](https://github.com/vatesfr/xen-orchestra/pull/3367))
- [New XOSAN] Fix error sometimes occurring when selecting the pool (PR [#3370](https://github.com/vatesfr/xen-orchestra/pull/3370))
- [New VM] Selecting multiple VMs and clicking Create then Cancel used to redirect to Home [#3268](https://github.com/vatesfr/xen-orchestra/issues/3268) (PR [#3371](https://github.com/vatesfr/xen-orchestra/pull/3371))
- [Remotes] `cannot read 'properties' of undefined` error (PR [#3382](https://github.com/vatesfr/xen-orchestra/pull/3382))
- [Servers] Various issues when adding a new server [#3385](https://github.com/vatesfr/xen-orchestra/issues/3385) (PR [#3388](https://github.com/vatesfr/xen-orchestra/pull/3388))
- [Backup NG] Always delete the correct old replications [#3391](https://github.com/vatesfr/xen-orchestra/issues/3391) (PR [#3394](https://github.com/vatesfr/xen-orchestra/pull/3394))
### Released packages
- xo-server v5.26.0
- xo-web v5.26.0
## **5.25.1** (2018-08-27)
## **5.25.2** (2018-08-27)
### Enhancements
@@ -24,7 +192,7 @@
### Released packages
- xo-server v5.25.1
- xo-server v5.25.2
- xo-web v5.25.1
## **5.25.0** (2018-08-23)

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -39,6 +39,10 @@ You can check if a coalesce job is currently active by running `ps axf | grep vh
If you don't see any running coalesce jobs, and can't find any other reason that XenServer has not started one, you can attempt to make it start a coalesce job by rescanning the SR. This is harmless to try, but will not always result in a coalesce. Visit the problematic SR in the XOA UI, then click the "Rescan All Disks" button towards the top right: it looks like a refresh circle icon. This should begin the coalesce process - if you click the Advanced tab in the SR view, the "disks needing to be coalesced" list should become smaller and smaller.
### Parse Error
This is most likely due to running a backup job that uses Delta functionality (eg: delta backups, or continuous replication) on a version of XenServer older than 6.5. To use delta functionality you must run [XenServer 6.5 or later](https://xen-orchestra.com/docs/supported-version.html).
### SR_BACKEND_FAILURE_44 (insufficient space)
> This message can be triggered by any backup method.
@@ -72,4 +76,4 @@ To check your free space, enter your XOA and run `xoa check` to check free syste
This is happening when you have a *smart backup job* that doesn't match any VMs. For example: you created a job to backup all running VMs. If no VMs are running on backup schedule, you'll have this message. This could also happen if you lost connection with your pool master (the VMs aren't visible anymore from Xen Orchestra).
Edit your job and try to see matching VMs or check if your pool is connected to XOA.
Edit your job and try to see matching VMs or check if your pool is connected to XOA.

View File

@@ -29,13 +29,19 @@ You also have a filter to search anything related to these logs.
> Logs are not "live" tasks. If you restart XOA during a backup, the log associated with the job will stay in orange (in progress), because it wasn't finished. It will stay forever unfinished because the job was cut in the middle.
## Backups execution
Each backups' job execution is identified by a `runId`. You can find this `runId` in its detailed log.
![](./assets/log-runId.png)
## Consistent backup (with quiesce snapshots)
All backup rely on snapshots. But what about data consistency? By default, Xen Orchestra will try to make a **quiesce snapshot** every time a snapshot is done (and fallback to normal snapshot if it's not possible).
All backup types rely on snapshots. But what about data consistency? By default, Xen Orchestra will try to take a **quiesced snapshot** every time a snapshot is done (and fall back to normal snapshots if it's not possible).
All your Windows VMs can be protected (especially MS SQL or Exchange services) after you have installed Xen Tools in your VMs. A quiesce snapshots means the operating system will be notified and the cache will be flushed to disks. This way, your backups will always be consistent.
Snapshots of Windows VMs can be quiesced (especially MS SQL or Exchange services) after you have installed Xen Tools in your VMs. However, [there is an extra step to install the VSS provider on windows](quiesce). A quiesced snapshot means the operating system will be notified and the cache will be flushed to disks. This way, your backups will always be consistent.
To see if you have quiesced snapshots for a VM, just go into its snapshot tab, the "info" icon means it is a quiesced snapshot:
To see if you have quiesced snapshots for a VM, just go into its snapshot tab, then the "info" icon means it is a quiesced snapshot:
![](./assets/quiesced1.png)

View File

@@ -1,10 +1,10 @@
# File level restore
You can also restore individual files inside a VM. It works with all your existing delta backups.
You can also restore specific files and directories inside a VM. It works with all your existing delta backups.
> You must use the latest XOA release. When you connect with the console, you should see `Build number: 16.12.20`. If you have this or higher (eg `17.*`), it means that's OK! Otherwise, please update your XOA.
> Restoring individual files from an SMB remote is not yet possible, but it's planned for the future!
> Restoring individual files from an SMB remote is not possible yet, but it's planned for the future!
> File level restore **is only possible on delta backups**

View File

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

View File

@@ -1,33 +1,39 @@
{
"devDependencies": {
"@babel/core": "7.0.0-rc.1",
"@babel/register": "7.0.0-rc.1",
"@babel/core": "^7.0.0",
"@babel/register": "^7.0.0",
"babel-core": "^7.0.0-0",
"babel-eslint": "^8.1.2",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.0.1",
"benchmark": "^2.1.4",
"eslint": "^5.1.0",
"eslint-config-standard": "12.0.0-alpha.0",
"eslint-config-standard-jsx": "^5.0.0",
"eslint-config-standard": "12.0.0",
"eslint-config-standard-jsx": "^6.0.2",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-react": "^7.6.1",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-standard": "^4.0.0",
"exec-promise": "^0.7.0",
"flow-bin": "^0.78.0",
"flow-bin": "^0.82.0",
"globby": "^8.0.0",
"husky": "^0.14.3",
"husky": "^1.0.0-rc.15",
"jest": "^23.0.1",
"lodash": "^4.17.4",
"prettier": "^1.10.2",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"sorted-object": "^2.0.1"
},
"engines": {
"yarn": "^1.7.0"
},
"husky": {
"hooks": {
"pre-commit": "scripts/lint-staged"
}
},
"jest": {
"timers": "fake",
"collectCoverage": true,
"projects": [
"<rootDir>"
@@ -49,11 +55,11 @@
"dev": "scripts/run-script --parallel dev",
"dev-test": "jest --bail --watch \"^(?!.*\\.integ\\.spec\\.js$)\"",
"posttest": "scripts/run-script test",
"precommit": "scripts/lint-staged",
"prepare": "scripts/run-script prepare",
"pretest": "eslint --ignore-path .gitignore .",
"test": "jest \"^(?!.*\\.integ\\.spec\\.js$)\"",
"test-integration": "jest \".integ\\.spec\\.js$\""
"test-integration": "jest \".integ\\.spec\\.js$\"",
"travis-tests": "scripts/travis-tests"
},
"workspaces": [
"@xen-orchestra/*",

View File

@@ -1,6 +1,6 @@
{
"name": "complex-matcher",
"version": "0.4.0",
"version": "0.5.0",
"license": "ISC",
"description": "",
"keywords": [],
@@ -30,9 +30,9 @@
"lodash": "^4.17.4"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.1",
"rimraf": "^2.6.2"

View File

@@ -11,7 +11,7 @@ export const ast = new CM.And([
new CM.Or([new CM.String('wonderwoman'), new CM.String('batman')])
),
new CM.TruthyProperty('hasCape'),
new CM.Property('age', new CM.Number(32)),
new CM.Property('age', new CM.NumberOrStringNode('32')),
new CM.GlobPattern('chi*go'),
new CM.RegExp('^foo/bar\\.', 'i'),
])

View File

@@ -153,6 +153,34 @@ export class NumberNode extends Node {
}
export { NumberNode as Number }
export class NumberOrStringNode extends Node {
constructor (value) {
super()
this.value = value
// should not be enumerable for the tests
Object.defineProperty(this, 'match', {
value: this.match.bind(this, value.toLowerCase(), +value),
})
}
match (lcValue, numValue, value) {
return (
value === numValue ||
(typeof value === 'string'
? value.toLowerCase().indexOf(lcValue) !== -1
: (Array.isArray(value) || isPlainObject(value)) &&
some(value, this.match))
)
}
toString () {
return this.value
}
}
export { NumberOrStringNode as NumberOrString }
export class Property extends Node {
constructor (name, child) {
super()
@@ -564,7 +592,7 @@ const parser = P.grammar({
const asNum = +str
return Number.isNaN(asNum)
? new GlobPattern(str)
: new NumberNode(asNum)
: new NumberOrStringNode(str)
})
),
ws: P.regex(/\s*/),

View File

@@ -6,6 +6,7 @@ import {
GlobPattern,
Null,
NumberNode,
NumberOrStringNode,
parse,
setPropertyClause,
} from './'
@@ -32,7 +33,7 @@ describe('parse', () => {
node = parse('32')
expect(node.match(32)).toBe(true)
expect(node.match('32')).toBe(false)
expect(node.match('32')).toBe(true)
expect(node.toString()).toBe('32')
node = parse('"32"')
@@ -54,6 +55,12 @@ describe('Number', () => {
})
})
describe('NumberOrStringNode', () => {
it('match a string', () => {
expect(new NumberOrStringNode('123').match([{ foo: '123' }])).toBe(true)
})
})
describe('setPropertyClause', () => {
it('creates a node if none passed', () => {
expect(setPropertyClause(undefined, 'foo', 'bar').toString()).toBe(

View File

@@ -28,10 +28,10 @@
},
"dependencies": {},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/preset-flow": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"cross-env": "^5.1.3",
"rimraf": "^2.6.2"
},

View File

@@ -26,22 +26,21 @@
"node": ">=6"
},
"dependencies": {
"@xen-orchestra/fs": "^0.3.0",
"@xen-orchestra/fs": "^0.4.1",
"cli-progress": "^2.0.0",
"exec-promise": "^0.7.0",
"struct-fu": "^1.2.0",
"vhd-lib": "^0.3.0"
"vhd-lib": "^0.4.0"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/plugin-transform-runtime": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"execa": "^0.10.0",
"execa": "^1.0.0",
"index-modules": "^0.3.0",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"rimraf": "^2.6.1",
"tmp": "^0.0.33"
},

View File

@@ -9,13 +9,12 @@ export default async function main (args) {
}
const handler = getHandler({ url: 'file:///' })
const stream = await createSyntheticStream(handler, path.resolve(args[0]))
return new Promise((resolve, reject) => {
createSyntheticStream(handler, path.resolve(args[0]))
.on('error', reject)
.pipe(
createWriteStream(args[1])
.on('error', reject)
.on('finish', resolve)
)
stream.on('error', reject).pipe(
createWriteStream(args[1])
.on('error', reject)
.on('finish', resolve)
)
})
}

View File

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

View File

@@ -5,15 +5,13 @@ import fs from 'fs-extra'
import getStream from 'get-stream'
import rimraf from 'rimraf'
import tmp from 'tmp'
import { fromEvent, pFromCallback } from 'promise-toolbox'
import { getHandler } from '@xen-orchestra/fs'
import { randomBytes } from 'crypto'
import { fromEvent, fromCallback as pFromCallback } from 'promise-toolbox'
import chainVhd from './chain'
import createReadStream from './createSyntheticStream'
import Vhd from './vhd'
import vhdMerge from './merge'
import { SECTOR_SIZE } from './_constants'
import Vhd, { chainVhd, createSyntheticStream, mergeVhd as vhdMerge } from './'
import { SECTOR_SIZE } from './src/_constants'
const initialDir = process.cwd()
@@ -270,14 +268,18 @@ test('coalesce works in normal cases', async () => {
test('createSyntheticStream passes vhd-util check', async () => {
const initalSize = 4
const expectedVhdSize = 4197888
await createRandomFile('randomfile', initalSize)
await convertFromRawToVhd('randomfile', 'randomfile.vhd')
const handler = getHandler({ url: 'file://' + process.cwd() })
const stream = createReadStream(handler, 'randomfile.vhd')
const stream = await createSyntheticStream(handler, 'randomfile.vhd')
expect(stream.length).toEqual(expectedVhdSize)
await fromEvent(
stream.pipe(await fs.createWriteStream('recovered.vhd')),
'finish'
)
await checkFile('recovered.vhd')
const stats = await fs.stat('recovered.vhd')
expect(stats.size).toEqual(expectedVhdSize)
await execa('qemu-img', ['compare', 'recovered.vhd', 'randomfile'])
})

View File

@@ -1,6 +1,6 @@
{
"name": "vhd-lib",
"version": "0.3.0",
"version": "0.4.0",
"license": "AGPL-3.0",
"description": "Primitives for VHD file handling",
"keywords": [],
@@ -20,25 +20,24 @@
"node": ">=6"
},
"dependencies": {
"@babel/runtime": "7.0.0-rc.1",
"async-iterator-to-stream": "^1.0.2",
"core-js": "3.0.0-beta.3",
"from2": "^2.3.0",
"fs-extra": "^7.0.0",
"limit-concurrency-decorator": "^0.4.0",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"struct-fu": "^1.2.0",
"uuid": "^3.0.1"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/plugin-transform-runtime": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/preset-flow": "7.0.0-rc.1",
"@xen-orchestra/fs": "^0.3.0",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"@xen-orchestra/fs": "^0.4.1",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"execa": "^0.10.0",
"execa": "^1.0.0",
"fs-promise": "^2.0.0",
"get-stream": "^4.0.0",
"index-modules": "^0.3.0",

View File

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

View File

@@ -17,7 +17,7 @@ import { set as setBitmap } from './_bitmap'
const VHD_BLOCK_SIZE_SECTORS = VHD_BLOCK_SIZE_BYTES / SECTOR_SIZE
/**
* @returns {Array} an array of occupation bitmap, each bit mapping an input block size of bytes
* @returns currentVhdPositionSector the first free sector after the data
*/
function createBAT (
firstBlockPosition,
@@ -36,9 +36,10 @@ function createBAT (
(bitmapSize + VHD_BLOCK_SIZE_BYTES) / SECTOR_SIZE
}
})
return currentVhdPositionSector
}
export default asyncIteratorToStream(async function * (
export default async function createReadableStream (
diskSize,
incomingBlockSize,
blockAddressList,
@@ -79,7 +80,14 @@ export default asyncIteratorToStream(async function * (
const bitmapSize =
Math.ceil(VHD_BLOCK_SIZE_SECTORS / 8 / SECTOR_SIZE) * SECTOR_SIZE
const bat = Buffer.alloc(tablePhysicalSizeBytes, 0xff)
createBAT(firstBlockPosition, blockAddressList, ratio, bat, bitmapSize)
const endOfData = createBAT(
firstBlockPosition,
blockAddressList,
ratio,
bat,
bitmapSize
)
const fileSize = endOfData * SECTOR_SIZE + FOOTER_SIZE
let position = 0
function * yieldAndTrack (buffer, expectedPosition) {
if (expectedPosition !== undefined) {
@@ -120,9 +128,16 @@ export default asyncIteratorToStream(async function * (
}
yield * yieldAndTrack(currentBlockWithBitmap)
}
yield * yieldAndTrack(footer, 0)
yield * yieldAndTrack(header, FOOTER_SIZE)
yield * yieldAndTrack(bat, FOOTER_SIZE + HEADER_SIZE)
yield * generateFileContent(blockIterator, bitmapSize, ratio)
yield * yieldAndTrack(footer)
})
async function * iterator () {
yield * yieldAndTrack(footer, 0)
yield * yieldAndTrack(header, FOOTER_SIZE)
yield * yieldAndTrack(bat, FOOTER_SIZE + HEADER_SIZE)
yield * generateFileContent(blockIterator, bitmapSize, ratio)
yield * yieldAndTrack(footer)
}
const stream = asyncIteratorToStream(iterator())
stream.length = fileSize
return stream
}

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "xen-api",
"version": "0.18.0",
"version": "0.20.0",
"license": "ISC",
"description": "Connector to the Xen API",
"keywords": [
@@ -33,10 +33,10 @@
},
"dependencies": {
"blocked": "^1.2.1",
"debug": "^3.1.0",
"debug": "^4.0.1",
"event-to-promise": "^0.8.0",
"exec-promise": "^0.7.0",
"http-request-plus": "^0.5.0",
"http-request-plus": "^0.6.0",
"iterable-backoff": "^0.0.0",
"jest-diff": "^23.5.0",
"json-rpc-protocol": "^0.12.0",
@@ -45,16 +45,16 @@
"make-error": "^1.3.0",
"minimist": "^1.2.0",
"ms": "^2.1.1",
"promise-toolbox": "^0.9.5",
"promise-toolbox": "^0.11.0",
"pw": "0.0.4",
"xmlrpc": "^1.3.2",
"xo-collection": "^0.4.1"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/plugin-proposal-decorators": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-decorators": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"

View File

@@ -11,7 +11,6 @@ import {
forEach,
isArray,
isInteger,
isObject,
map,
noop,
omit,
@@ -21,12 +20,12 @@ import {
import {
Cancel,
cancelable,
catchPlus as pCatch,
defer,
delay as pDelay,
fromEvents,
lastly,
timeout as pTimeout,
pCatch,
pDelay,
pFinally,
pTimeout,
TimeoutError,
} from 'promise-toolbox'
@@ -137,8 +136,8 @@ const parseUrl = url => {
const {
create: createObject,
defineProperties,
defineProperty,
freeze: freezeObject,
keys: getKeys,
} = Object
// -------------------------------------------------------------------
@@ -190,10 +189,6 @@ const getKey = o => o.$id
// -------------------------------------------------------------------
const EMPTY_ARRAY = freezeObject([])
// -------------------------------------------------------------------
const getTaskResult = task => {
const { status } = task
if (status === 'cancelled') {
@@ -215,6 +210,15 @@ const getTaskResult = task => {
// -------------------------------------------------------------------
const RESERVED_FIELDS = {
id: true,
pool: true,
ref: true,
type: true,
}
// -------------------------------------------------------------------
const CONNECTED = 'connected'
const CONNECTING = 'connecting'
const DISCONNECTED = 'disconnected'
@@ -229,6 +233,7 @@ export class Xapi extends EventEmitter {
this._auth = opts.auth
this._pool = null
this._readOnly = Boolean(opts.readOnly)
this._RecordsByType = createObject(null)
this._sessionId = null
const url = (this._url = parseUrl(opts.url))
@@ -259,8 +264,8 @@ export class Xapi extends EventEmitter {
const objects = (this._objects = new Collection())
objects.getKey = getKey
this._objectsByRefs = createObject(null)
this._objectsByRefs[NULL_REF] = undefined
this._objectsByRef = createObject(null)
this._objectsByRef[NULL_REF] = undefined
this._taskWatchers = Object.create(null)
@@ -428,7 +433,7 @@ export class Xapi extends EventEmitter {
this._sessionCall('task.cancel', [taskRef]).catch(noop)
})
return lastly.call(this.watchTask(taskRef), () => {
return pFinally.call(this.watchTask(taskRef), () => {
this._sessionCall('task.destroy', [taskRef]).catch(noop)
})
})
@@ -462,7 +467,7 @@ export class Xapi extends EventEmitter {
}
const object =
this._objects.all[idOrUuidOrRef] || this._objectsByRefs[idOrUuidOrRef]
this._objects.all[idOrUuidOrRef] || this._objectsByRef[idOrUuidOrRef]
if (object !== undefined) return object
@@ -474,7 +479,7 @@ export class Xapi extends EventEmitter {
// Returns the object for a given opaque reference (internal to
// XAPI).
getObjectByRef (ref, defaultValue) {
const object = this._objectsByRefs[ref]
const object = this._objectsByRef[ref]
if (object !== undefined) return object
@@ -497,16 +502,9 @@ export class Xapi extends EventEmitter {
}
async getRecord (type, ref) {
const record = await this._sessionCall(`${type}.get_record`, [ref])
// All custom properties are read-only and non enumerable.
defineProperties(record, {
$id: { value: record.uuid || ref },
$ref: { value: ref },
$type: { value: type },
})
return record
return this._wrapRecord(
await this._sessionCall(`${type}.get_record`, [ref])
)
}
async getRecordByUuid (type, uuid) {
@@ -669,7 +667,7 @@ export class Xapi extends EventEmitter {
setFieldEntries (record, field, entries) {
return Promise.all(
Object.keys(entries).map(entry => {
getKeys(entries).map(entry => {
const value = entries[entry]
if (value !== undefined) {
return value === null
@@ -710,7 +708,7 @@ export class Xapi extends EventEmitter {
let watcher = watchers[ref]
if (watcher === undefined) {
// sync check if the task is already settled
const task = this._objectsByRefs[ref]
const task = this._objectsByRef[ref]
if (task !== undefined) {
const result = getTaskResult(task)
if (result !== undefined) {
@@ -775,78 +773,29 @@ export class Xapi extends EventEmitter {
}
_addObject (type, ref, object) {
const { _objectsByRefs: objectsByRefs } = this
const reservedKeys = {
id: true,
pool: true,
ref: true,
type: true,
}
const getKey = (key, obj) =>
reservedKeys[key] && obj === object ? `$$${key}` : `$${key}`
// Creates resolved properties.
forEach(object, function resolveObject (value, key, object) {
if (isArray(value)) {
if (!value.length) {
// If the array is empty, it isn't possible to be sure that
// it is not supposed to contain links, therefore, in
// benefice of the doubt, a resolved property is defined.
defineProperty(object, getKey(key, object), {
value: EMPTY_ARRAY,
})
// Minor memory optimization, use the same empty array for
// everyone.
object[key] = EMPTY_ARRAY
} else if (isOpaqueRef(value[0])) {
// This is an array of refs.
defineProperty(object, getKey(key, object), {
get: () => freezeObject(map(value, ref => objectsByRefs[ref])),
})
freezeObject(value)
}
} else if (isObject(value)) {
forEach(value, resolveObject)
freezeObject(value)
} else if (isOpaqueRef(value)) {
defineProperty(object, getKey(key, object), {
get: () => objectsByRefs[value],
})
}
})
// All custom properties are read-only and non enumerable.
defineProperties(object, {
$id: { value: object.uuid || ref },
$pool: { get: this._getPool },
$ref: { value: ref },
$type: { value: type },
})
object = this._wrapRecord(type, ref, object)
// Finally freezes the object.
freezeObject(object)
const objects = this._objects
const objectsByRef = this._objectsByRef
// An object's UUID can change during its life.
const prev = objectsByRefs[ref]
const prev = objectsByRef[ref]
let prevUuid
if (prev && (prevUuid = prev.uuid) && prevUuid !== object.uuid) {
objects.remove(prevUuid)
}
this._objects.set(object)
objectsByRefs[ref] = object
objectsByRef[ref] = object
if (type === 'pool') {
this._pool = object
const eventWatchers = this._eventWatchers
Object.keys(object.other_config).forEach(key => {
getKeys(object.other_config).forEach(key => {
const eventWatcher = eventWatchers[key]
if (eventWatcher !== undefined) {
delete eventWatchers[key]
@@ -871,7 +820,7 @@ export class Xapi extends EventEmitter {
}
_removeObject (type, ref) {
const byRefs = this._objectsByRefs
const byRefs = this._objectsByRef
const object = byRefs[ref]
if (object !== undefined) {
this._objects.unset(object.$id)
@@ -1028,6 +977,90 @@ export class Xapi extends EventEmitter {
return getAllObjects().then(watchEvents)
}
_wrapRecord (type, ref, data) {
const RecordsByType = this._RecordsByType
let Record = RecordsByType[type]
if (Record === undefined) {
const fields = getKeys(data)
const nFields = fields.length
const xapi = this
const objectsByRef = this._objectsByRef
const getObjectByRef = ref => objectsByRef[ref]
Record = function (ref, data) {
defineProperties(this, {
$id: { value: data.uuid || ref },
$ref: { value: ref },
})
for (let i = 0; i < nFields; ++i) {
const field = fields[i]
this[field] = data[field]
}
}
const getters = { $pool: this._getPool }
const props = { $type: type }
fields.forEach(field => {
props[`set_${field}`] = function (value) {
return xapi.setField(this, field, value)
}
const $field = (field in RESERVED_FIELDS ? '$$' : '$') + field
const value = data[field]
if (isArray(value)) {
if (value.length === 0 || isOpaqueRef(value[0])) {
getters[$field] = function () {
const value = this[field]
return value.length === 0 ? value : value.map(getObjectByRef)
}
}
props[`add_to_${field}`] = function (...values) {
return xapi
.call(`${type}.add_${field}`, this.$ref, values)
.then(noop)
}
} else if (value !== null && typeof value === 'object') {
getters[$field] = function () {
const value = this[field]
const result = {}
getKeys(value).forEach(key => {
result[key] = objectsByRef[value[key]]
})
return result
}
props[`update_${field}`] = function (entries) {
return xapi.setFieldEntries(this, field, entries)
}
} else if (isOpaqueRef(value)) {
getters[$field] = function () {
return objectsByRef[this[field]]
}
}
})
const descriptors = {}
getKeys(getters).forEach(key => {
descriptors[key] = {
configurable: true,
get: getters[key],
}
})
getKeys(props).forEach(key => {
descriptors[key] = {
configurable: true,
value: props[key],
writable: true,
}
})
defineProperties(Record.prototype, descriptors)
RecordsByType[type] = Record
}
return new Record(ref, data)
}
}
Xapi.prototype._transportCall = reduce(

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
import { delay as pDelay } from 'promise-toolbox'
import { pDelay } from 'promise-toolbox'
import { createClient } from './'

View File

@@ -86,7 +86,7 @@ const checkAuthorizationByTypes = {
'VM-controller': checkMember('$container'),
'VM-snapshot': checkMember('$snapshot_of'),
'VM-snapshot': or(checkSelf, checkMember('$snapshot_of')),
'VM-template': or(checkSelf, checkMember('$pool')),
}

View File

@@ -1,6 +1,6 @@
{
"name": "xo-acl-resolver",
"version": "0.2.4",
"version": "0.3.0",
"license": "ISC",
"description": "Xen-Orchestra internal: do ACLs resolution",
"keywords": [],

View File

@@ -28,31 +28,32 @@
"node": ">=6"
},
"dependencies": {
"@babel/polyfill": "7.0.0-rc.1",
"@babel/polyfill": "^7.0.0",
"bluebird": "^3.5.1",
"chalk": "^2.2.0",
"event-to-promise": "^0.8.0",
"exec-promise": "^0.7.0",
"fs-promise": "^2.0.3",
"got": "^8.0.1",
"http-request-plus": "^0.6.0",
"human-format": "^0.10.0",
"l33teral": "^3.0.3",
"lodash": "^4.17.4",
"micromatch": "^3.1.3",
"mkdirp": "^0.5.1",
"nice-pipe": "0.0.0",
"pretty-ms": "^3.0.1",
"pretty-ms": "^4.0.0",
"progress-stream": "^2.0.0",
"promise-toolbox": "^0.11.0",
"pump": "^3.0.0",
"pw": "^0.0.4",
"strip-indent": "^2.0.0",
"xdg-basedir": "^3.0.0",
"xo-lib": "^0.9.0"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/preset-flow": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"rimraf": "^2.6.2"

View File

@@ -11,10 +11,10 @@ const resolveUrl = require('url').resolve
const stat = require('fs-promise').stat
const chalk = require('chalk')
const eventToPromise = require('event-to-promise')
const forEach = require('lodash/forEach')
const fromCallback = require('promise-toolbox/fromCallback')
const getKeys = require('lodash/keys')
const got = require('got')
const hrp = require('http-request-plus').default
const humanFormat = require('human-format')
const identity = require('lodash/identity')
const isArray = require('lodash/isArray')
@@ -23,6 +23,7 @@ const micromatch = require('micromatch')
const nicePipe = require('nice-pipe')
const pairs = require('lodash/toPairs')
const pick = require('lodash/pick')
const pump = require('pump')
const startsWith = require('lodash/startsWith')
const prettyMs = require('pretty-ms')
const progressStream = require('progress-stream')
@@ -362,50 +363,43 @@ async function call (args) {
ensurePathParam(method, file)
url = resolveUrl(baseUrl, result[key])
const output = createWriteStream(file)
const response = await hrp(url)
const progress = progressStream({ time: 1e3 }, printProgress)
return eventToPromise(
nicePipe([
got.stream(url).on('response', function (response) {
const length = response.headers['content-length']
if (length !== undefined) {
progress.length(length)
}
}),
progress,
output,
]),
'finish'
const progress = progressStream(
{
length: response.headers['content-length'],
time: 1e3,
},
printProgress
)
return fromCallback(cb => pump(response, progress, output, cb))
}
if (key === '$sendTo') {
ensurePathParam(method, file)
url = resolveUrl(baseUrl, result[key])
const stats = await stat(file)
const length = stats.size
const { size: length } = await stat(file)
const input = nicePipe([
createReadStream(file),
progressStream(
{
length: length,
length,
time: 1e3,
},
printProgress
),
])
const response = await got.post(url, {
body: input,
headers: {
'content-length': length,
},
method: 'POST',
})
return response.body
return hrp
.post(url, {
body: input,
headers: {
'content-length': length,
},
})
.readAll('utf-8')
}
}

View File

@@ -25,16 +25,14 @@
"node": ">=6"
},
"dependencies": {
"@babel/runtime": "7.0.0-rc.1",
"kindof": "^2.0.0",
"lodash": "^4.17.2",
"make-error": "^1.0.2"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/plugin-transform-runtime": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"cross-env": "^5.1.3",
"event-to-promise": "^0.8.0",
"rimraf": "^2.6.1"

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "xo-common",
"version": "0.1.1",
"version": "0.1.2",
"license": "AGPL-3.0",
"description": "Code shared between [XO](https://xen-orchestra.com) server and clients",
"keywords": [],
@@ -20,20 +20,21 @@
"dist/",
"*.js"
],
"browserslist": [
"> 1%"
],
"engines": {
"node": ">=6"
},
"dependencies": {
"babel-runtime": "^6.18.0",
"lodash": "^4.16.6",
"make-error": "^1.2.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
@@ -44,22 +45,5 @@
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"browsers": "> 1%",
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -20,7 +20,8 @@ class XoError extends BaseError {
const create = (code, getProps) => {
const factory = (...args) => new XoError({ ...getProps(...args), code })
factory.is = (error, predicate) =>
error.code === code && iteratee(predicate)(error)
error.code === code &&
(predicate === undefined || iteratee(predicate)(error))
return factory
}
@@ -33,7 +34,7 @@ export const notImplemented = create(0, () => ({
export const noSuchObject = create(1, (id, type) => ({
data: { id, type },
message: 'no such object',
message: `no such ${type || 'object'} ${id}`,
}))
export const unauthorized = create(2, () => ({

View File

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

View File

@@ -24,6 +24,9 @@
"files": [
"dist/"
],
"browserslist": [
"> 2%"
],
"engines": {
"node": ">=6"
},
@@ -33,10 +36,10 @@
"make-error": "^1.0.4"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
@@ -46,28 +49,5 @@
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
},
"babel": {
"env": {
"test": {
"ignore": null
}
},
"ignore": "*.spec.js",
"plugins": [
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"browsers": "> 2%",
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -27,9 +27,9 @@
"lodash": "^4.13.1"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"deep-freeze": "^0.0.1",

View File

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

View File

@@ -29,32 +29,16 @@
"node": ">=6"
},
"dependencies": {
"babel-runtime": "^6.11.6",
"passport-github": "^1.1.0"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-env": "^1.6.1"
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0"
},
"scripts": {
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"transform-runtime"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
]
]
}
}

View File

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

View File

@@ -32,15 +32,13 @@
"node": ">=6"
},
"dependencies": {
"babel-runtime": "^6.23.0",
"passport-google-oauth20": "^1.0.0"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.3.3",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
@@ -50,28 +48,5 @@
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
},
"babel": {
"env": {
"test": {
"ignore": null
}
},
"ignore": "*.spec.js",
"plugins": [
"transform-runtime",
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -38,12 +38,12 @@
"inquirer": "^6.0.0",
"ldapjs": "^1.0.1",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5"
"promise-toolbox": "^0.11.0"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"

View File

@@ -1,5 +1,5 @@
import { forEach, isFinite, isInteger } from 'lodash'
import { forOwn as forOwnAsync } from 'promise-toolbox'
import { pforOwn } from 'promise-toolbox'
import { prompt } from 'inquirer'
// ===================================================================
@@ -160,7 +160,7 @@ const promptByType = {
}
}
await forOwnAsync.call(schema.properties || {}, promptProperty)
await pforOwn.call(schema.properties || {}, promptProperty)
return value
},

View File

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

View File

@@ -32,14 +32,13 @@
"node": ">=6"
},
"dependencies": {
"babel-runtime": "^6.11.6",
"passport-saml": "^0.33.0"
"passport-saml": "^0.35.0"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
@@ -50,21 +49,5 @@
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"transform-runtime"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "xo-server-backup-reports",
"version": "0.13.0",
"version": "0.15.0",
"license": "AGPL-3.0",
"description": "Backup reports plugin for XO-Server",
"keywords": [
@@ -40,9 +40,9 @@
"moment-timezone": "^0.5.13"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"

View File

@@ -30,12 +30,27 @@ export const configurationSchema = {
},
}
export const testSchema = {
type: 'object',
properties: {
runId: {
type: 'string',
description: `<a href="https://xen-orchestra.com/docs/backups.html#backups-execution" rel="noopener noreferrer" target="_blank">job's runId</a>`,
},
},
additionalProperties: false,
required: ['runId'],
}
// ===================================================================
const ICON_FAILURE = '🚨'
const ICON_INTERRUPTED = '⚠️'
const ICON_SKIPPED = '⏩'
const ICON_SUCCESS = '✔'
const ICON_WARNING = '⚠️'
const STATUS_ICON = {
failure: ICON_FAILURE,
@@ -85,12 +100,13 @@ const isSkippedError = error =>
error.message === UNHEALTHY_VDI_CHAIN_ERROR ||
error.message === NO_SUCH_OBJECT_ERROR
const INDENT = ' '
const createGetTemporalDataMarkdown = formatDate => (
start,
end,
nbIndent = 0
) => {
const indent = ' '.repeat(nbIndent)
const indent = INDENT.repeat(nbIndent)
const markdown = [`${indent}- **Start time**: ${formatDate(start)}`]
if (end !== undefined) {
@@ -103,6 +119,17 @@ const createGetTemporalDataMarkdown = formatDate => (
return markdown
}
const addWarnings = (text, warnings, nbIndent = 0) => {
if (warnings === undefined) {
return
}
const indent = INDENT.repeat(nbIndent)
warnings.forEach(({ message }) => {
text.push(`${indent}- **${ICON_WARNING} ${message}**`)
})
}
class BackupReportsXoPlugin {
constructor (xo) {
this._xo = xo
@@ -118,6 +145,10 @@ class BackupReportsXoPlugin {
this._xo.on('job:terminated', this._report)
}
test ({ runId }) {
return this._backupNgListener(undefined, undefined, undefined, runId)
}
unload () {
this._xo.removeListener('job:terminated', this._report)
}
@@ -135,6 +166,9 @@ class BackupReportsXoPlugin {
async _backupNgListener (_1, _2, schedule, runJobId) {
const xo = this._xo
const log = await xo.getBackupNgLogs(runJobId)
if (log === undefined) {
throw new Error(`no log found with runId=${JSON.stringify(runJobId)}`)
}
const { reportWhen, mode } = log.data || {}
if (
@@ -159,13 +193,14 @@ class BackupReportsXoPlugin {
let markdown = [
`## Global status: ${log.status}`,
'',
`- **Job ID**: ${log.jobId}`,
`- **Run ID**: ${runJobId}`,
`- **mode**: ${mode}`,
...getTemporalDataMarkdown(log.start, log.end),
`- **Error**: ${log.result.message}`,
'---',
'',
`*${pkg.name} v${pkg.version}*`,
]
addWarnings(markdown, log.warnings)
markdown.push('---', '', `*${pkg.name} v${pkg.version}*`)
markdown = markdown.join('\n')
return this._sendReport({
@@ -207,6 +242,7 @@ class BackupReportsXoPlugin {
`- **UUID**: ${vm !== undefined ? vm.uuid : vmId}`,
...getTemporalDataMarkdown(taskLog.start, taskLog.end),
]
addWarnings(text, taskLog.warnings)
const failedSubTasks = []
const snapshotText = []
@@ -241,6 +277,7 @@ class BackupReportsXoPlugin {
}** (${id}) ${icon}`,
...getTemporalDataMarkdown(subTaskLog.start, subTaskLog.end, 2)
)
addWarnings(remotesText, subTaskLog.warnings, 2)
if (subTaskLog.status === 'failure') {
failedSubTasks.push(remote !== undefined ? remote.name : id)
remotesText.push('', errorMessage)
@@ -257,6 +294,7 @@ class BackupReportsXoPlugin {
` - **${srName}** (${srUuid}) ${icon}`,
...getTemporalDataMarkdown(subTaskLog.start, subTaskLog.end, 2)
)
addWarnings(srsText, subTaskLog.warnings, 2)
if (subTaskLog.status === 'failure') {
failedSubTasks.push(sr !== undefined ? sr.name_label : id)
srsText.push('', errorMessage)
@@ -272,6 +310,7 @@ class BackupReportsXoPlugin {
}
const operationInfoText = []
addWarnings(operationInfoText, operationLog.warnings, 3)
if (operationLog.status === 'success') {
const size = operationLog.result.size
if (operationLog.message === 'merge') {
@@ -374,6 +413,8 @@ class BackupReportsXoPlugin {
let markdown = [
`## Global status: ${log.status}`,
'',
`- **Job ID**: ${log.jobId}`,
`- **Run ID**: ${runJobId}`,
`- **mode**: ${mode}`,
...getTemporalDataMarkdown(log.start, log.end),
`- **Successes**: ${nSuccesses} / ${nVms}`,
@@ -385,6 +426,7 @@ class BackupReportsXoPlugin {
if (globalMergeSize !== 0) {
markdown.push(`- **Merge size**: ${formatSize(globalMergeSize)}`)
}
addWarnings(markdown, log.warnings)
markdown.push('')
if (nFailures !== 0) {

View File

@@ -31,14 +31,13 @@
"node": ">=6"
},
"dependencies": {
"event-to-promise": "^0.8.0",
"jsonrpc-websocket-client": "^0.4.1",
"superagent": "^3.8.2"
"http-request-plus": "^0.6.0",
"jsonrpc-websocket-client": "^0.4.1"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"cross-env": "^5.1.3",
"rimraf": "^2.5.4"
},

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