Compare commits

..

1257 Commits

Author SHA1 Message Date
Julien Fontanet
bc02d51882 feat(xen-api): 0.16.0 2017-12-15 13:59:34 +01:00
Julien Fontanet
aada9e4a33 feat(xen-api): XenServer 7.3 support (#27)
XenServer 7.3 has changed the error format.
2017-12-15 13:58:31 +01:00
Julien Fontanet
abf526f91a chore(normalize-package): remove eslint 2017-12-12 12:31:39 +01:00
Julien Fontanet
c06020745e chore(import-packages): support git URLs 2017-12-12 12:08:21 +01:00
Julien Fontanet
431b85c98f chore(package): split pretest into pretest and test 2017-12-12 12:07:06 +01:00
Julien Fontanet
299fdc19d6 chore(scripts): coding style 2017-12-12 12:06:32 +01:00
Julien Fontanet
bf3f9b4ac2 chore(ci): test on old-LTS, LTS and stable 2017-12-05 18:37:03 +01:00
Julien Fontanet
4c91667d2c chore: remove dependency-check (#25) 2017-12-05 11:46:02 +01:00
Julien Fontanet
ed45888d7f fix(xen-api): handle RO in createTask() and putResource() 2017-12-03 18:10:57 +01:00
Julien Fontanet
dbadc487ae chore(package): update dependencies 2017-12-03 18:10:57 +01:00
Julien Fontanet
a5b80655da chore: clean Babel configs (#24)
- avoid using stage-0
- remove deprecated preset-latest in favor of preset-env
2017-11-27 12:59:04 +00:00
Julien Fontanet
99d4789049 chore(xen-api): properly logout on disconnect() 2017-11-26 20:07:28 +00:00
Julien Fontanet
2de4163553 fix(xen-api): handle RO in callAsync() 2017-11-26 20:00:40 +00:00
Julien Fontanet
0fc0be19b2 feat(xen-api): XenApi#getRecord() 2017-11-26 20:00:40 +00:00
Julien Fontanet
3c271ffffd feat(xo-cli): prepare params 2017-11-26 20:00:40 +00:00
Julien Fontanet
1aa793886b feat(xo-server-transport-email): 0.5.0 2017-11-24 14:03:59 +01:00
Julien Fontanet
46cd2f10a5 feat(xo-server-auth-saml): 0.5.0 2017-11-24 14:02:06 +01:00
Julien Fontanet
337abe2a2b feat(xo-server-auth-ldap): 0.6.4 2017-11-24 14:00:37 +01:00
Julien Fontanet
0441103f0c feat(xo-cli): 0.10.1 2017-11-24 13:57:36 +01:00
Julien Fontanet
b5829e2484 chore(package): update dependencies 2017-11-24 13:35:51 +01:00
Julien Fontanet
e24cba9684 feat: more normalization
- remove config.commitizen
- remove left over devDependencies[babel-eslint]
- remove scripts.commitmsg
2017-11-21 14:55:35 +01:00
Julien Fontanet
084e96be0e chore: eslint instead of standard (#22)
Custom rules:

- comma dangle
- no `var`s
- use `const` AMAP
2017-11-21 14:35:44 +01:00
Julien Fontanet
5dfefe8d35 chore: eslint instead of standard 2017-11-21 11:47:21 +01:00
Julien Fontanet
4a6ba998d4 feat(xo-server-backup-reports): 0.9.1 2017-11-20 13:55:52 +01:00
Julien Fontanet
7f953c62af chore: update human-format to 0.9.0 2017-11-20 13:53:28 +01:00
Julien Fontanet
c9412b02af chore: update dev dependencies 2017-11-20 13:52:04 +01:00
Julien Fontanet
536a9d6e50 chore(xo-server-backup-reports): remove unnecessary babel-preset-stage-0 2017-11-20 13:47:45 +01:00
Julien Fontanet
4ee0944037 feat(xo-server-backup-reports): add plugin version in footer (#23) 2017-11-20 10:20:40 +01:00
Julien Fontanet
98f5db9fbd feat: standard and jest only on committed files 2017-11-17 15:45:15 +01:00
Julien Fontanet
575fe300fa feat(xo-server-backup-reports): 0.9.0 2017-11-17 14:51:21 +01:00
badrAZ
4749e452c4 feat(xo-server-backup-reports): add merge size and speed (#21)
See vatesfr/xo-web#2426
2017-11-17 14:17:30 +01:00
Julien Fontanet
24b7dc1c30 feat(xen-api): 0.15.1 2017-11-13 17:25:54 +01:00
Julien Fontanet
2c99a4db0f feat(xen-api): expose putResource URL on failure 2017-11-13 16:03:58 +01:00
Julien Fontanet
88d9111196 feat: run jest at root level 2017-11-08 15:04:07 +01:00
Julien Fontanet
17e9885586 feat(xen-api): 0.15.0 2017-11-08 11:02:33 +01:00
Julien Fontanet
826628b18f chore(normalize-packages): remove commitizen 2017-11-08 10:56:18 +01:00
Julien Fontanet
c84f61d388 feat: standard run at root level
Simplify & uniformize standard confirmation.
Single pass for all packages, which is faster.

Unfortunately, it breaks atom's linter-js-standard.
2017-10-30 10:59:35 +01:00
Julien Fontanet
11b1ff478e feat: use Yarn workspaces
Much faster installation which is very important for CI.

We'll see if it proves difficult for deps management.
2017-10-30 10:59:35 +01:00
Julien Fontanet
ea7c44b544 feat(xen-api): implements Fibonacci backoff (#17) 2017-10-28 13:14:15 +02:00
Julien Fontanet
50581c539c chore(xo-cli): use modern ES 2017-10-23 11:14:46 +02:00
Julien Fontanet
37f0bffcaa feat(xo-cli): 0.10.0 2017-10-17 17:14:38 +02:00
Julien Fontanet
0e6a460332 feat(xo-cli): register supports expiresIn flag
See vatesfr/xo-web#1769
2017-10-17 17:13:33 +02:00
Julien Fontanet
0c9f6e0923 feat(xen-api): 0.14.3 2017-10-09 12:02:11 +02:00
Julien Fontanet
56d6347209 fix(xen-api): barrier compat with XS < 6.1 (#15)
New implementation needs to modify the pool record but is simpler and does not rely on `event.inject`.

Fixes vatesfr/xo-web#2418.
2017-10-09 11:58:02 +02:00
Julien Fontanet
2db582339a feat(xo-cli): 0.9.1 2017-10-05 20:48:23 +02:00
Julien Fontanet
7f6386dd8c fix(xo-cli): remove incorrect change 2017-10-05 20:47:47 +02:00
Julien Fontanet
92e33efc04 Merge remote-tracking branch 'xo-server-transport-slack/master' 2017-10-05 18:18:25 +02:00
Julien Fontanet
805409cb48 chore: remove husky from packages deps 2017-10-05 18:01:00 +02:00
Julien Fontanet
93400352ca Merge remote-tracking branch 'xo-vmdk-to-vhd/master' 2017-10-05 17:58:30 +02:00
Julien Fontanet
585b943a52 Merge remote-tracking branch 'xo-server-auth-google/master' 2017-10-05 17:44:35 +02:00
Julien Fontanet
a0bf1bca8b Merge remote-tracking branch 'xen-api/master' 2017-10-05 17:36:11 +02:00
Julien Fontanet
738e3a0fbe feat(normalize-packages): remove .editorconfig and .gitignore 2017-10-05 17:32:32 +02:00
Julien Fontanet
05f3171910 feat(import-packages): script to import packages into monorepo 2017-10-05 17:30:39 +02:00
Julien Fontanet
f0771e4fed chore: update root deps
Do not use yarn workspaces for the moment because it's still quite buggy.
2017-10-05 16:15:00 +02:00
Julien Fontanet
a3cd3b3fb1 feat(xo-cli): 0.9.0 2017-10-05 15:41:14 +02:00
Julien Fontanet
323577f1ca chore(package): update dependencies 2017-10-05 15:38:49 +02:00
Julien Fontanet
d22619adc7 feat(list-objects): move filter to server-side 2017-10-05 13:30:20 +02:00
Julien Fontanet
025cb6c6ca chore: update yarn.lock
Due to new releases of yarn
2017-09-28 14:56:53 +02:00
Julien Fontanet
15809333c1 0.14.2 2017-08-09 16:17:51 +02:00
Julien Fontanet
815855964e chore(package): update dev dependencies 2017-08-09 16:17:28 +02:00
Julien Fontanet
5649a10c44 fix(Xapi#barrier): handle merged events 2017-08-09 16:15:38 +02:00
Julien Fontanet
5bb27131a3 0.14.1 2017-08-08 12:57:04 +02:00
Julien Fontanet
d06aacbe25 feat(xapi#barrier): can be done on a specific object
The function now returns an up-to-date record of the object.
2017-08-08 12:56:35 +02:00
Julien Fontanet
d9a5764dda 0.14.0 2017-07-31 18:04:32 +02:00
Julien Fontanet
9859afe8b7 feat(Xapi#barrier): waits for all events to have been received 2017-07-31 18:04:00 +02:00
Julien Fontanet
a4087530c5 0.13.7 2017-07-05 18:05:28 +02:00
Julien Fontanet
d5fae68b98 fix(cli): fix URL handling 2017-07-05 18:05:24 +02:00
Julien Fontanet
9f7c01599e chore(Xapi#connect): use auth var directly 2017-07-05 17:17:54 +02:00
Julien Fontanet
9a0090570c 0.13.6 2017-07-05 10:35:20 +02:00
Julien Fontanet
5ef22f9921 fix(Xapi#watchTask): do not parse task result
It is not always a XML-RPC value.
2017-07-05 10:35:00 +02:00
Julien Fontanet
1239f80018 feat(cli): accept credentials as part of the URL 2017-07-05 10:08:34 +02:00
Julien Fontanet
49d1d060d6 chore(README): various changes 2017-07-05 10:07:38 +02:00
Julien Fontanet
2cfa31609e chore(*): update dev dependencies 2017-07-04 11:07:35 +02:00
Julien Fontanet
64101c8337 chore: update dependencies 2017-07-04 10:42:44 +02:00
Julien Fontanet
241fed25a2 chore(xo-server-auth-github): remove trace dep
Conflicts with Node 8.
2017-07-04 10:42:44 +02:00
Julien Fontanet
209b34e451 Update README.md 2017-07-03 09:25:58 +02:00
Julien Fontanet
5e50ed15bd feat(xo-server-cloud): 0.2.1 2017-06-29 17:49:55 +02:00
Julien Fontanet
b0bff55dac fix(xo-server-cloud): reopen connection on close 2017-06-29 17:49:09 +02:00
Julien Fontanet
81cad34170 chore(xo-server-cloud): add yarn.lock 2017-06-29 17:47:08 +02:00
Julien Fontanet
c38379e229 chore(xo-server-cloud): update dependencies 2017-06-29 17:46:57 +02:00
Julien Fontanet
07945ef8a6 0.13.5 2017-06-29 15:09:17 +02:00
Julien Fontanet
35d7fa4a85 fix(Xapi#putResource): behave if close occurs before finish 2017-06-29 15:07:17 +02:00
Julien Fontanet
fbb6870310 0.13.4 2017-06-27 09:51:08 +02:00
Julien Fontanet
81efe25fbc chore(package): update http-request-plus to v0.4.0 2017-06-27 09:50:32 +02:00
Julien Fontanet
63d440677b fix(Xapi#*Resource): correctly link cancel token to tasks 2017-06-27 09:49:26 +02:00
Julien Fontanet
2e0df47581 chore(Xapi#putResource): no need to inject session_id in query again 2017-06-26 12:19:52 +02:00
Julien Fontanet
a84ced70a6 chore(package): update dependencies 2017-06-26 12:17:04 +02:00
Julien Fontanet
4a9498e25e 0.13.3 2017-06-26 10:03:34 +02:00
Julien Fontanet
1978e218f4 feat(Xapi#*Resource): task can be a ref to an existing task
Or a promise to a ref.
2017-06-23 21:24:27 +02:00
Julien Fontanet
2ab912c4f4 feat(Xapi#*Resource): link the cancel token with the task 2017-06-23 21:24:27 +02:00
Julien Fontanet
ca329fa2ff feat(Xapi#*Resource): use task by default if watching events 2017-06-23 21:24:27 +02:00
Julien Fontanet
db122ed8c5 0.13.2 2017-06-23 11:06:48 +02:00
Julien Fontanet
28d7f106b2 feat(Xapi#putResource): opt-in task creation and watching 2017-06-23 11:05:19 +02:00
Julien Fontanet
d6e46adfde feat(Xapi#getResource): opt-in task creation and watching 2017-06-23 11:05:19 +02:00
Julien Fontanet
93109fe9b8 chore(Xapi#watchTask): reject if task has been destroyed before settling 2017-06-22 19:03:52 +02:00
Julien Fontanet
439992d96f feat(Xapi#createTask): create an ephemeral task
Ephemeral means the task is automatically destroyed when settled.
2017-06-22 18:33:22 +02:00
Julien Fontanet
f94fe0b325 0.13.1 2017-06-22 15:39:31 +02:00
Julien Fontanet
cc543ae33a chore(examples): usage for --raw 2017-06-22 15:34:08 +02:00
Julien Fontanet
5df1e62d53 chore(Xapi#putResource): always close the connection when data has been sent 2017-06-22 15:29:08 +02:00
Julien Fontanet
1dff6f81fc feat(examples): also accept UUID and name labels 2017-06-21 14:33:15 +02:00
Julien Fontanet
ecf2d95318 fix(Xapi#watchTask): correctly handle already settled tasks 2017-06-20 15:06:43 +02:00
Julien Fontanet
a1af272180 chore(Xapi#putResource): avoid unnecessary function creation 2017-06-20 14:52:24 +02:00
Julien Fontanet
72f0793aeb fix(Xapi#putResource): consume the response stream 2017-06-20 14:42:36 +02:00
Julien Fontanet
e150be6a3e 0.13.0 2017-06-14 14:05:01 +02:00
Julien Fontanet
d71b13f2a2 feat(Xapi#putResource): handle redirects 2017-06-14 14:04:43 +02:00
Julien Fontanet
66c88b37a4 chore(package): update http-request-plus to v0.3.0 2017-06-13 17:43:30 +02:00
Julien Fontanet
a7198d2e83 chore(examples): various improvements and fixes 2017-06-13 17:40:50 +02:00
Julien Fontanet
d3584b5820 feat: expose isOpaqueRef() 2017-06-13 14:03:04 +02:00
Julien Fontanet
e2e6267dd7 fix(Xapi#putResource): handle aborted connection 2017-06-13 14:03:00 +02:00
Julien Fontanet
aa5baecdc3 feat(examples): moved into their own directory 2017-06-12 17:53:52 +02:00
Julien Fontanet
601b2aa6bb fix(Xapi#putResource): do not fail on early cancel hack 2017-06-12 17:36:48 +02:00
Julien Fontanet
d09ba38dbd feat(Xapi#constructor): url can contain credentials 2017-06-12 16:53:06 +02:00
Julien Fontanet
b847c2b7b5 chore(resources): add TODO 2017-06-12 16:53:06 +02:00
Julien Fontanet
30e44cb533 chore(Xapi#callAsync): use _sessionCall instead of call 2017-06-12 16:18:00 +02:00
Julien Fontanet
d43b83081b 0.12.3 2017-06-08 17:23:54 +02:00
Julien Fontanet
699363c548 chore(package): update promise-toolbox to v0.9.4
Fix an minor issue regarding Cancel#toString().
2017-06-08 17:21:16 +02:00
Julien Fontanet
6ea3eb4ba6 feat(Xapi#callAsync): cancellable async call 2017-06-08 17:21:16 +02:00
Julien Fontanet
4aa20b6f6a fix(Xapi#watchTask): handle already settled tasks 2017-06-08 17:21:16 +02:00
Julien Fontanet
9c8ea27238 feat(cli): auto Promise.all 2017-06-08 17:21:16 +02:00
Julien Fontanet
22c515b0e7 feat(Xapi#watchTask): returns a promise which settled when the task finishes 2017-06-08 17:21:16 +02:00
Julien Fontanet
72a53c0c09 Revert "fix: ensure a scope is used"
This reverts commit 3fbfbb1b2687de3d7c056a6790ad735b19eb9254.

No longer necessary, this is now fixed in vatesfr/xo-server@8c7d254244
2017-06-02 16:02:36 +02:00
Julien Fontanet
9a5a0d7a2b fix: ensure a scope is used 2017-06-02 10:20:20 +02:00
Julien Fontanet
a18d88a3f1 chore(configuration): typo 2017-06-02 09:59:53 +02:00
Julien Fontanet
7e2bd52f25 0.12.2 2017-05-31 18:06:27 +02:00
Julien Fontanet
e65dd15edc feat(transports): add plain XML-RPC as last fallback (#65) 2017-05-31 18:05:46 +02:00
Julien Fontanet
781ffa5574 0.12.1 2017-05-26 17:50:47 +02:00
Julien Fontanet
8fe3b1a368 feat(Xapi#putResource): accept buffers 2017-05-26 17:49:14 +02:00
Julien Fontanet
e64dc51a17 feat(xo-cli): 0.8.4 2017-05-25 13:16:13 +02:00
Julien Fontanet
7f4df49933 chore(xo-cli): update all dependencies 2017-05-25 13:15:47 +02:00
Julien Fontanet
2bc77ee0cd feat(xo-lib): 0.9.0 2017-05-25 13:14:33 +02:00
Julien Fontanet
b25adf7f57 chore(xo-lib): update all dependencies 2017-05-25 13:08:57 +02:00
Julien Fontanet
69c48e2770 0.12.0 2017-05-23 15:47:01 +02:00
Julien Fontanet
aa934ad725 chore(package): update all dependencies 2017-05-23 15:46:53 +02:00
Julien Fontanet
8596ca607d fix(Xapi#call): mark as disconnected on network failures (#64)
Related to vatesfr/xo-web#2099
2017-05-23 15:45:16 +02:00
Julien Fontanet
643ea9e523 feat: methods to get/put an HTTP resource (#63) 2017-05-22 15:51:41 +02:00
Julien Fontanet
ce2a08373e feat(xo-server-usage-report): 0.3.2 2017-05-18 17:04:19 +02:00
Julien Fontanet
d9e2229335 fix(xo-server-usage-report): handle missing UUIDs (#12)
Should not happen but the plugin is no longer failing.
2017-05-18 16:57:13 +02:00
badrAZ
61454e2030 fix errors 2017-05-18 15:49:27 +02:00
badrAZ
6f79e8145e fix(xo-server-usage-report): test if uuid exists 2017-05-18 15:25:47 +02:00
Julien Fontanet
60c4673992 feat(xo-server-backup-reports): 0.8.0 2017-05-17 10:32:56 +02:00
Julien Fontanet
7c597539ff feat(xo-server-backup-reports): clearer sections separation 2017-05-17 10:28:53 +02:00
Julien Fontanet
8b80c12aa4 feat(xo-server-backup-reports): better date format 2017-05-17 10:28:25 +02:00
Julien Fontanet
9c802c7b0c feat(xo-server-backup-reports): improve mail subject 2017-05-17 10:18:04 +02:00
Julien Fontanet
00c0ba9863 feat(xo-server-backup-reports): simplify section titles 2017-05-17 10:17:48 +02:00
Julien Fontanet
ca11656844 chore(xo-server-backup-reports): use globalSuccess 2017-05-17 10:17:14 +02:00
Julien Fontanet
9d27730a4a feat(xo-server-backup-reports): emphasise dt 2017-05-17 10:15:54 +02:00
Julien Fontanet
4ce0ca702c fix(xo-server-backup-reports): second symbol is lowercase 2017-05-17 10:14:06 +02:00
Julien Fontanet
242e28ddb9 feat(xo-server-backup-reports): better icons 2017-05-17 10:13:39 +02:00
Julien Fontanet
40d20b0f9c fix(xo-server-backup-reports): report on failure by default 2017-05-17 09:49:22 +02:00
Julien Fontanet
c0cf6b2551 fix(xo-server-backup-reports): remove log 2017-05-16 17:15:02 +02:00
badrAZ
eff3221f7d feat(xo-server-backup-reports): use related schedule's timezone (#11)
Fixes vatesfr/xo-web#2133
2017-05-16 16:19:49 +02:00
Julien Fontanet
be57004257 chore(xo-server-backup-reports): code cleanup (#10) 2017-05-16 12:48:47 +02:00
badrAZ
730ed8bc69 feat(xo-server-backup-reports): add the transmission data speed (#9)
See vatesfr/xo-web#2096
2017-05-12 11:33:18 +02:00
Julien Fontanet
26f630d9d6 0.11.0 2017-05-11 16:36:01 +02:00
Julien Fontanet
7481874ba2 feat(allowUnauthorized): accept self-signed certificates (#62)
Related to vatesfr/xo-web#2138
2017-05-11 16:35:02 +02:00
badrAZ
52a5ce95b6 feat(xo-server-backup-reports): ability to report only failed backups (#8)
Fixes vatesfr/xo-web#2095
2017-05-09 18:28:17 +02:00
Julien Fontanet
a348585e76 chore(package): update all dependencies 2017-05-08 18:34:58 +02:00
Julien Fontanet
8d135bc1fc chore: remove invoke() 2017-05-03 11:02:59 +02:00
Julien Fontanet
2c00f0ffab chore: inline isString() 2017-05-03 11:02:46 +02:00
Julien Fontanet
5e1d627834 0.10.0 2017-04-28 16:26:08 +02:00
Julien Fontanet
37b23bdc47 feat: JSON-RPC with fallback to XML-RPC (#50)
Better performance for XS >=7.0
2017-04-28 16:22:35 +02:00
Julien Fontanet
9e1f97811e feat(xo-server-usage-report): 0.3.1 2017-04-28 16:18:25 +02:00
Julien Fontanet
665fc2b7f9 fix: no longer compatible with Node 4 2017-04-25 15:01:09 +02:00
badrAZ
dfd17bc96c fix(xo-server-usage-report): hosts missing patches (#7)
Fixes vatesfr/xo-web#2106
2017-04-25 14:57:17 +02:00
Julien Fontanet
abf73193fd feat(xo-cli): 0.8.3 2017-04-13 13:57:57 +02:00
Julien Fontanet
5ae1488864 0.2.0 2017-04-12 10:01:34 +02:00
Julien Fontanet
fb3ff165db fix: fix URLs 2017-04-12 10:01:21 +02:00
Julien Fontanet
cdf59bb877 chore(package): update all dependencies 2017-04-12 10:00:30 +02:00
Julien Fontanet
b6371dde23 feat: scope is now configurable (#23)
Fixes #1 as well

Thanks @Maelstrom96
2017-04-12 09:52:17 +02:00
Julien Fontanet
8f4a43de75 feat(xo-server-transport-email): 0.4.2 2017-04-10 11:32:29 +02:00
Julien Fontanet
e2d4a41d9a fix(xo-server-transport-email): fix From header
Fix vatesfr/xo-web#2074
2017-04-10 11:32:13 +02:00
Julien Fontanet
d2af0152d0 feat(xo-server-transport-email): 0.4.1 2017-04-06 09:38:34 +02:00
Julien Fontanet
37f825ab6d chore(package): use passport-google-oauth20 directly 2017-04-03 10:59:11 +02:00
Julien Fontanet
92104f4ab7 feat(xo-server-usage-report): 0.3.0 2017-03-31 17:03:14 +02:00
Julien Fontanet
408a2498d0 feat(xo-server-backup-reports): 0.7.0 2017-03-31 17:02:23 +02:00
Julien Fontanet
8d473001aa feat(xo-server-transport-email): 0.4.0 2017-03-31 15:03:44 +02:00
Julien Fontanet
ceaf0bd745 feat(xo-server-transport-email): finer SSL/TLS config 2017-03-31 15:01:11 +02:00
Julien Fontanet
a96849bc89 chore(xo-server-transport-email): update all deps 2017-03-31 15:00:38 +02:00
Julien Fontanet
182b7ae84d fix: commitmsg instead of commit-msg 2017-03-29 13:40:10 +02:00
Julien Fontanet
e6e254bd86 chore(xo-cli): update dependencies 2017-03-29 13:39:31 +02:00
Julien Fontanet
3b4e7a0bfe feat(xo-cli): ask password if not provided 2017-03-29 13:30:33 +02:00
Julien Fontanet
a0887bb5b3 fix: do not use yarn test 2017-03-29 13:19:20 +02:00
Julien Fontanet
5c6a592aba chore: update dependencies 2017-03-29 13:18:09 +02:00
Julien Fontanet
c8c0b24420 feat(xo-server-auth-ldap): 0.6.3 2017-03-28 16:21:42 +02:00
Julien Fontanet
0da5d4376d feat(xo-server-auth-ldap): example with group filtering 2017-03-28 16:21:42 +02:00
greenkeeper[bot]
47f3788f55 fix(package): update ms to version 1.0.0 (#61)
https://greenkeeper.io/
2017-03-19 22:48:05 +01:00
badrAZ
840a236e61 feat(xo-server-backup-reports): group failed/successful VMs (#6)
Fixes vatesfr/xo-web#1950
2017-03-17 17:01:05 +01:00
badrAZ
b07b998304 fix(xo-server-usage-report): add name for all objects (#5)
Fixes vatesfr/xo-web#2017
2017-03-16 18:00:23 +01:00
Julien Fontanet
e820b13f9f fix(xo-cli): add final newline for --list-objects 2017-03-08 18:02:12 +01:00
Julien Fontanet
63be8afd35 chore(README): reverse XenServer versions order 2017-03-08 15:00:01 +01:00
Olivier Lambert
1166db7834 chore(README): add 7.1 and fix XenServer (#60) 2017-03-08 14:59:03 +01:00
badrAZ
3662f4f256 fix(xo-server-usage-report): handle renamed VMs (#3) 2017-03-08 09:56:45 +01:00
Julien Fontanet
b03335eb94 feat(xo-common): 0.1.1 2017-03-04 16:11:32 +01:00
Julien Fontanet
31e3677111 fix(api-errors): correctly forwards all params 2017-03-04 16:10:37 +01:00
badrAZ
9bc1a51def feat(xo-server-usage-report): add VM names (#2) 2017-03-03 13:26:24 +01:00
Julien Fontanet
d215885dab chore(xo-server-usage-report): remove unused files 2017-03-02 21:04:49 +01:00
Julien Fontanet
68a1c93fbb chore(xo-server-usage-report): fix URLs 2017-03-02 16:36:02 +01:00
Julien Fontanet
9c6302fa50 Merge remote-tracking branch 'xo-server-usage-report/master' 2017-03-02 16:30:23 +01:00
Julien Fontanet
d60440e512 chore(xo-server-cloud): fix homepage URL 2017-03-02 16:27:55 +01:00
Julien Fontanet
957b9319cb fix: coding style 2017-03-02 16:21:30 +01:00
Julien Fontanet
9a28b9d1f1 0.2.2 2017-03-02 16:20:36 +01:00
Julien Fontanet
543d061251 chore: add headers to template 2017-03-02 16:20:36 +01:00
Julien Fontanet
acba90b293 feat: minify HTML 2017-03-02 16:20:36 +01:00
Julien Fontanet
8992e9988f chore: various updates 2017-03-02 16:20:36 +01:00
Julien Fontanet
3ce87b9cb3 fix: inject image directly in HTML 2017-03-02 16:20:36 +01:00
Julien Fontanet
cbe7b3046c chore: better compressed xo.png 2017-03-02 16:20:36 +01:00
Julien Fontanet
62344cb814 chore: remove unused logo.png 2017-03-02 16:20:36 +01:00
Julien Fontanet
72d45a5fd2 0.2.1 2017-03-02 16:20:36 +01:00
Julien Fontanet
c4e4dd501d 0.2.0 2017-03-02 16:20:36 +01:00
greenkeeper[bot]
3d96765b1f fix(package): update event-to-promise to version 0.8.0 (#57)
https://greenkeeper.io/
2017-03-01 11:15:07 +01:00
greenkeeper[bot]
02d052c071 chore(package): update standard to version 9.0.0 (#19)
https://greenkeeper.io/
2017-03-01 00:53:36 +01:00
greenkeeper[bot]
4c52fe11d3 chore(package): update standard to version 9.0.0 (#56)
https://greenkeeper.io/
2017-03-01 00:18:56 +01:00
Julien Fontanet
c70d92cf09 chore(package): update all dependencies 2017-02-28 11:07:23 +01:00
Julien Fontanet
4590bed56e chore(package): use stage-3 instead of stage-0 2017-02-28 11:06:25 +01:00
Julien Fontanet
7c00d118f3 feat: remove html-pdf
Simply send HTML reports for now.
2017-02-28 11:04:48 +01:00
Julien Fontanet
68e37fff79 feat(xo-server-transport-email): 0.3.3 2017-02-27 15:43:18 +01:00
Julien Fontanet
6958e71efd chore(xo-server-transport-email): no longer needs lodash 2017-02-27 15:42:04 +01:00
Julien Fontanet
d0618182d1 feat(xo-server-transport-email): test also sends an attachment 2017-02-27 15:42:04 +01:00
Julien Fontanet
e8891e27dd fix(typo): iterface → interface 2017-02-27 15:42:04 +01:00
Julien Fontanet
adb80933ee chore(package): make package publishable 2017-02-27 14:03:24 +01:00
Julien Fontanet
23b942c7da fix(package): use julien.fontanet@vates.fr as author 2017-02-27 14:03:16 +01:00
Julien Fontanet
7eef7d4636 fix(package): license is AGPL3 2017-02-27 14:02:54 +01:00
Julien Fontanet
72baa1a786 feat(xo-server-cloud): 0.2.0 2017-02-23 19:58:22 +01:00
Julien Fontanet
4d72569030 feat(xo-server-cloud): retry connecting to xoa-updater 2017-02-23 19:42:53 +01:00
Pierre Donias
532a368606 feat(xo-server-cloud): new plugin (#1) 2017-02-23 18:04:03 +01:00
greenkeeper[bot]
6c4178e107 chore(package): update jest to version 19.0.0 (#54)
https://greenkeeper.io/
2017-02-21 10:54:36 +01:00
Julien Fontanet
05a68030b6 feat(xo-cli): use | between types in commands listing 2017-02-17 11:28:51 +01:00
Julien Fontanet
c5f3228ba7 chore(package): use husky instead of ghooks 2017-02-15 10:50:00 +01:00
Julien Fontanet
db3728f9e4 feat(xo-server-auth-ldap): 0.6.2 2017-02-13 17:45:53 +01:00
Julien Fontanet
ca2d3f5b48 fix(xo-server-auth-ldap): correctly use previous values for booleans 2017-02-13 17:44:34 +01:00
Julien Fontanet
f3a992a55f chore(xo-server-auth-ldap): use lodash and promise-toolbox utils 2017-02-13 17:44:09 +01:00
Julien Fontanet
b5d1e7c459 chore(xo-server-auth-ldap): clarify bind.dn description 2017-02-13 17:09:41 +01:00
Julien Fontanet
eecd89772f chore(xo-server-auth-ldap): remember optional inputs 2017-02-13 16:58:27 +01:00
Julien Fontanet
02d1fb436d 0.10.0-2 2017-02-13 15:52:26 +01:00
Julien Fontanet
1c97bf9019 0.10.0-1 2017-02-13 15:50:45 +01:00
Julien Fontanet
bfc4bb1f4c fix(package): commitmsg hook 2017-02-13 15:50:22 +01:00
Julien Fontanet
ccdce32562 chore(package): update all dependencies 2017-02-13 15:50:09 +01:00
Julien Fontanet
87fdcf14e5 feat(Xapi#_rawCall): add method to error object 2017-02-13 15:47:09 +01:00
greenkeeper[bot]
a0570f41fb fix(package): update fs-promise to version 2.0.0 (#27)
https://greenkeeper.io/
2017-02-13 10:36:44 +01:00
Julien Fontanet
5fd42bf216 feat: new vhd-cli package 2017-02-07 19:24:07 +01:00
Julien Fontanet
3e55d8d9df fix(README): issues should be reported on xo-web's tracker 2017-02-02 16:00:21 +01:00
Julien Fontanet
d9f4a196d0 feat(xo-server-auth-ldap): 0.6.1 2017-02-02 15:55:19 +01:00
Julien Fontanet
768ef8a316 chore(xo-server-auth-ldap): update all deps 2017-02-02 15:49:29 +01:00
Julien Fontanet
a478d02e5d fix(xo-server-auth-ldap): handle connection failures 2017-02-02 15:49:27 +01:00
Julien Fontanet
b98a7de3ae chore(xo-server-auth-ldap): use promise-toolbox 2017-02-02 15:49:25 +01:00
Julien Fontanet
2e31ff42ed 0.1.2 2017-01-27 19:25:22 +01:00
Julien Fontanet
9a3b8f2b10 fix: template path 2017-01-27 19:25:18 +01:00
Julien Fontanet
079790bce6 fix(package): add images/ to files list 2017-01-27 19:25:09 +01:00
Julien Fontanet
d99c4c6d14 0.1.1 2017-01-27 19:16:08 +01:00
Julien Fontanet
6839b4d8a4 fix(package): add template to files list 2017-01-27 19:16:04 +01:00
Julien Fontanet
4c47cc6ea8 0.1.0 2017-01-27 18:24:17 +01:00
Julien Fontanet
de81649a98 chore: add yarn.lock 2017-01-27 18:24:14 +01:00
Julien Fontanet
d31eab673e chore: remove ghooks 2017-01-27 18:24:05 +01:00
badrAZ
31189be2c8 Implementation (#15) 2017-01-27 18:22:01 +01:00
Julien Fontanet
45dd2c6519 chore: limit concurrency at 1 for installation 2017-01-27 17:05:48 +01:00
Julien Fontanet
5176d2000e chore: upgrade husky to v0.13.1 and lerna to v2.0.0-beta.34 2017-01-27 17:01:53 +01:00
Julien Fontanet
6f606761e4 chore(xo-server-test-plugin): add yarn.lock 2017-01-27 16:55:02 +01:00
Julien Fontanet
cfabadffe4 chore: exclusively use yarn instead of npm 2017-01-27 16:54:32 +01:00
Julien Fontanet
7523cb3489 chore: improve filter description 2017-01-25 12:43:29 +01:00
Julien Fontanet
505a9d7c70 chore(package): update inquirer to v3.0.1 2017-01-25 11:12:08 +01:00
Julien Fontanet
9581764cc8 feat: add normalize-package script 2017-01-16 12:01:32 +01:00
Julien Fontanet
f03493a252 chore: integration of imported packages 2017-01-13 18:54:27 +01:00
Julien Fontanet
22f2a05c8a Merge remote-tracking branch 'xo-server-test-plugin/master' 2017-01-13 14:00:47 +01:00
Julien Fontanet
a703ecc7e1 Merge remote-tracking branch 'xo-cli/master' 2017-01-13 14:00:45 +01:00
Julien Fontanet
c90a687179 Merge remote-tracking branch 'xo-server-load-balancer/master' 2017-01-13 14:00:41 +01:00
Julien Fontanet
e08689ff0e Merge remote-tracking branch 'xo-server-auth-ldap/master' 2017-01-13 14:00:37 +01:00
Julien Fontanet
80ffd811c9 Merge remote-tracking branch 'xo-server-auth-saml/master' 2017-01-13 14:00:33 +01:00
Julien Fontanet
56b583fc99 Merge remote-tracking branch 'xo-server-backup-reports/master' 2017-01-13 14:00:30 +01:00
Julien Fontanet
37a4e108be Merge remote-tracking branch 'xo-server-transport-nagios/master' 2017-01-13 14:00:26 +01:00
Julien Fontanet
9ca531541d Merge remote-tracking branch 'xo-server-transport-email/master' 2017-01-13 14:00:23 +01:00
Julien Fontanet
e304c554d0 Merge remote-tracking branch 'xo-server-auth-github/master' 2017-01-13 14:00:19 +01:00
Julien Fontanet
a7dfa7a381 Merge remote-tracking branch 'xo-server-transport-xmpp/master' 2017-01-13 14:00:09 +01:00
Julien Fontanet
090d48b636 0.3.2 2017-01-12 17:13:55 +01:00
Julien Fontanet
55567bf666 fix(package): use nodemailer 2.6.4
nodemailer 2.7.0 breaks Node 4 compatibility
2017-01-12 17:13:51 +01:00
Julien Fontanet
5867d84eaa 0.3.1 2017-01-12 16:24:45 +01:00
Julien Fontanet
4d8f1ab169 chore(package): update all dependencies 2017-01-12 16:23:21 +01:00
Julien Fontanet
6a2963be41 chore(configuration schema): add description for from.name and from.address 2017-01-12 16:17:55 +01:00
Julien Fontanet
b10f7b35ee chore(xo-collection): update all deps 2017-01-11 16:50:48 +01:00
Julien Fontanet
dc1bb8992f chore(xo-collection): use jest instead of mocha/must/sinon 2017-01-11 16:32:10 +01:00
Julien Fontanet
5eb7119821 chore: global gitignore 2017-01-11 14:07:49 +01:00
Julien Fontanet
3fa8fc19dc chore: global EditorConfig 2017-01-11 12:03:14 +01:00
Julien Fontanet
341d98e00d chore(xo-acl-resolver): minor fixes regarding monorepo 2017-01-10 18:29:52 +01:00
Julien Fontanet
7eee0f4341 Merge remote-tracking branch 'xo-acl-resolver/master' 2017-01-10 18:11:47 +01:00
Julien Fontanet
0b71b7862a feat: basic README 2017-01-10 18:07:05 +01:00
Julien Fontanet
e2893a0eba chore: fix packages URLs 2017-01-10 18:05:01 +01:00
Julien Fontanet
0c39a4e193 chore: add yarn.lock for all packages 2017-01-10 17:51:50 +01:00
Julien Fontanet
066072b22d chore: configure Travis globally 2017-01-10 17:51:31 +01:00
Julien Fontanet
d548503590 chore(xo-remote-parser): update all deps 2017-01-10 16:24:28 +01:00
Julien Fontanet
3a02fc99a2 chore: global husky instead of local ghooks 2017-01-10 16:13:37 +01:00
Julien Fontanet
d8ae943d8a chore: lerna is a dev dep 2017-01-10 15:54:29 +01:00
Julien Fontanet
63dae9ed70 chore: ignore log files
- lerna-debug.log{,.*}
- yerna-error.log{,.*}
2017-01-10 15:54:14 +01:00
Julien Fontanet
97f57f1f2b Merge remote-tracking branch 'xo-remote-parser/master' 2017-01-10 15:20:28 +01:00
Julien Fontanet
060ba6423e Merge remote-tracking branch 'xo-lib/master' 2017-01-10 15:20:25 +01:00
Julien Fontanet
f31bbcdaab Merge remote-tracking branch 'xo-common/master' 2017-01-10 15:20:14 +01:00
Julien Fontanet
2a4bae54ab Merge remote-tracking branch 'xo-collection/master' 2017-01-10 15:20:08 +01:00
Julien Fontanet
95df2f66a3 initial commit 2017-01-10 12:21:59 +01:00
Julien Fontanet
c0c63f49b1 0.10.0-0 2017-01-09 18:30:20 +01:00
Julien Fontanet
4e1bef9537 feat: redirect event 2017-01-09 18:29:54 +01:00
Julien Fontanet
8b78727d20 chore: add yarn.lock 2017-01-09 18:29:38 +01:00
Julien Fontanet
4cd7025be4 chore(package): update all dependencies 2017-01-09 17:30:52 +01:00
Julien Fontanet
015ce2690d chore(package): simplify scripts 2017-01-09 15:12:04 +01:00
Julien Fontanet
3e7f552a63 chore(package): use husky instead of ghooks 2017-01-09 15:05:36 +01:00
greenkeeper[bot]
128b169ae2 fix(package): update promise-toolbox to version 0.8.0 (#16)
https://greenkeeper.io/
2017-01-06 11:32:14 +01:00
greenkeeper[bot]
fdd33abe91 fix(package): update promise-toolbox to version 0.8.0 (#14)
https://greenkeeper.io/
2017-01-06 11:31:53 +01:00
greenkeeper[bot]
22882b1ff2 fix(package): update promise-toolbox to version 0.8.0 (#53)
https://greenkeeper.io/
2017-01-06 11:31:23 +01:00
greenkeeper[bot]
213898987d fix(package): update promise-toolbox to version 0.8.0 (#7)
https://greenkeeper.io/
2017-01-06 11:28:52 +01:00
Julien Fontanet
044c9bed4c Update README.md 2016-12-23 11:01:25 +01:00
badrAZ
81062638eb feat: attachments support (#13) 2016-12-22 10:09:15 +01:00
Julien Fontanet
8c0028055a chore(package): use husky instead of ghooks 2016-12-20 15:06:29 +01:00
Julien Fontanet
74f060b309 fix(package): rename to xo-server-transport-nagios 2016-12-20 11:50:49 +01:00
Julien Fontanet
12de0ca463 0.1.0 2016-12-20 11:45:11 +01:00
Julien Fontanet
72ed59b65b chore(package): make package publishable 2016-12-20 11:45:05 +01:00
Julien Fontanet
fe369bfa18 chore(package): update all dependencies 2016-12-20 11:43:45 +01:00
Julien Fontanet
116661d958 chore: add yarn.lock 2016-12-16 09:48:35 +01:00
Julien Fontanet
0dd70c57cd chore: factory function pass options directly 2016-12-16 09:48:19 +01:00
Julien Fontanet
df0aa1c46d fix(configure): do not modify passed configuration 2016-12-16 09:47:56 +01:00
Julien Fontanet
9b93a47e45 chore(package): update all dependencies 2016-12-16 09:47:26 +01:00
Julien Fontanet
06f6912fb9 chore(package): add mattermost as a keyword 2016-12-16 09:42:22 +01:00
badrAZ
c708fd65d7 feat: integration with xo-server-transport-nagios (#17) 2016-12-14 17:24:55 +01:00
badrAZ
0c497900a2 implementation (#1) 2016-12-14 16:33:40 +01:00
greenkeeper[bot]
0b92ceec90 chore(package): update babel-preset-env to version 1.0.1 (#23)
https://greenkeeper.io/
2016-12-10 17:36:47 +01:00
greenkeeper[bot]
731bcc68ef chore(package): update babel-preset-env to version 1.0.0 (#5)
https://greenkeeper.io/
2016-12-09 22:56:24 +01:00
greenkeeper[bot]
7e5131c4d2 chore(package): update babel-preset-env to version 1.0.0 (#6)
https://greenkeeper.io/
2016-12-09 22:55:37 +01:00
greenkeeper[bot]
f0554fc77c chore(package): update babel-preset-env to version 1.0.0 (#14)
https://greenkeeper.io/
2016-12-09 22:55:29 +01:00
greenkeeper[bot]
c7814b1f04 chore(package): update babel-preset-env to version 1.0.0 (#14)
https://greenkeeper.io/
2016-12-09 22:55:16 +01:00
greenkeeper[bot]
d585b47360 chore(package): update babel-preset-env to version 1.0.0 (#16)
https://greenkeeper.io/
2016-12-09 22:55:06 +01:00
greenkeeper[bot]
4530d95f48 chore(package): update babel-preset-env to version 1.0.0 (#11)
https://greenkeeper.io/
2016-12-09 22:52:58 +01:00
Julien Fontanet
c7e6e72ce8 fix(events watching): stop if not connected
Fixes vatesfr/xo-web#1799
2016-12-07 23:18:05 +01:00
greenkeeper[bot]
013d4b9411 fix(package): update inquirer to version 2.0.0 (#23)
https://greenkeeper.io/
2016-12-06 23:18:15 +01:00
Julien Fontanet
0a8fed1950 0.2.3 2016-12-02 14:57:24 +01:00
Julien Fontanet
6dc4b4dc1b fix: workaround for VDI snapshots with $snaphot_of which point to themselves 2016-12-02 14:56:19 +01:00
Julien Fontanet
959b8863eb chore(package): update all dependencies 2016-12-02 10:03:49 +01:00
greenkeeper[bot]
b25f418411 chore(package): update dependencies (#20)
https://greenkeeper.io/
2016-11-29 13:41:32 +01:00
greenkeeper[bot]
985a4780f5 chore(package): update dependencies (#26)
https://greenkeeper.io/
2016-11-29 13:41:04 +01:00
Olivier Lambert
92a1f2c6d5 feat(description): better explanation of checkCertificate (#22) 2016-11-29 10:51:24 +01:00
greenkeeper[bot]
81b9348e50 chore(package): update dependencies (#5)
https://greenkeeper.io/
2016-11-28 15:06:32 +01:00
Julien Fontanet
04e7d54620 v0.8.2 2016-11-25 11:50:31 +01:00
Julien Fontanet
729dbe16c0 fix(list objects): fix without filtering
Fixes #19
2016-11-25 11:49:28 +01:00
greenkeeper[bot]
974650bc56 chore(package): update babel-preset-env to version 0.0.9 (#22)
https://greenkeeper.io/
2016-11-24 22:42:22 +01:00
greenkeeper[bot]
0899a16333 chore(package): update babel-preset-env to version 0.0.9 (#11)
https://greenkeeper.io/
2016-11-24 22:06:00 +01:00
greenkeeper[bot]
8c9ed833c3 chore(package): update babel-preset-env to version 0.0.9 (#5)
https://greenkeeper.io/
2016-11-24 22:05:47 +01:00
greenkeeper[bot]
5e86e64e18 chore(package): update babel-preset-env to version 0.0.9 (#10)
https://greenkeeper.io/
2016-11-24 22:04:27 +01:00
greenkeeper[bot]
235c789c5e chore(package): update babel-preset-env to version 0.0.9 (#15)
https://greenkeeper.io/
2016-11-24 22:04:18 +01:00
greenkeeper[bot]
3f69a22229 chore(package): update babel-preset-env to version 0.0.9 (#3)
https://greenkeeper.io/
2016-11-24 22:03:23 +01:00
Julien Fontanet
ab48d06967 feat(README): update usage 2016-11-24 12:39:14 +01:00
Julien Fontanet
18ca950cd2 0.8.1 2016-11-24 12:35:02 +01:00
Julien Fontanet
82489b36c8 0.8.0 2016-11-24 12:34:04 +01:00
Julien Fontanet
a67b6130f8 feat(list objects): ability to select displayed properties
Fixes #18
2016-11-24 12:33:24 +01:00
Julien Fontanet
eab007db6e 0.6.0 2016-11-23 11:15:55 +01:00
Julien Fontanet
889eae276e 0.6.0 2016-11-23 10:17:38 +01:00
Julien Fontanet
2b2a72252b chore(package): update all dependencies 2016-11-23 10:12:48 +01:00
Olivier Lambert
13e4568d3b feat(Slack): support Slack transport (#14) 2016-11-23 10:02:01 +01:00
Julien Fontanet
92c4dda801 0.3.0 2016-11-23 09:57:41 +01:00
Julien Fontanet
3e59ba4563 chore(package): update all dependencies 2016-11-23 09:57:23 +01:00
Julien Fontanet
99c95626df chore: pass options directly to the constructor 2016-11-23 09:49:27 +01:00
Julien Fontanet
20a9fc2497 chore(package): add description 2016-11-23 09:48:07 +01:00
Julien Fontanet
27e0621ad8 0.9.6 2016-11-23 09:46:33 +01:00
Julien Fontanet
abecff222c chore(package): make package publishable 2016-11-23 09:46:16 +01:00
Julien Fontanet
3f0d91101a chore(package): add description 2016-11-23 09:46:06 +01:00
Julien Fontanet
252be59ac3 chore(package): update babel-preset-env to v0.0.8 2016-11-23 09:43:19 +01:00
Julien Fontanet
1987a8c68a chore(events): rely on event.from timeout instead of inject
Should be enough.
2016-11-23 09:42:11 +01:00
Julien Fontanet
5088fd0e82 fix(README): dev section 2016-11-23 09:39:50 +01:00
badrAZ
2de4d36a0f implementation (#2) 2016-11-23 09:37:39 +01:00
Julien Fontanet
fb32aeeeb6 0.8.2 2016-11-18 18:32:35 +01:00
Julien Fontanet
76cb4037d4 fix: build for > 2% browsers
Fixes #21
2016-11-18 18:32:27 +01:00
Julien Fontanet
af1530db36 0.8.1 2016-11-18 11:51:23 +01:00
Julien Fontanet
9d6560aece chore(package): update all dependencies 2016-11-18 11:50:26 +01:00
Julien Fontanet
caba246e0b fix(README): update doc 2016-11-18 10:46:51 +01:00
Julien Fontanet
731e2dc4c4 0.7.4 2016-11-17 17:33:08 +01:00
Julien Fontanet
815bdf3454 fix(package): downgrade nice-pipe to 0.0.0 2016-11-17 17:33:04 +01:00
Julien Fontanet
b36ef9fdb1 0.7.3 2016-11-17 15:40:29 +01:00
Julien Fontanet
fee3f7a716 fix(list objects): writes one objects at a time
Fixes vatesfr/xo-web#1356
2016-11-17 14:56:43 +01:00
Julien Fontanet
dd00d6581f 0.1.0 2016-11-16 11:52:36 +01:00
Pierre Donias
f4fc7acf4d feat(api-errors): 10 new errors (#3)
See vatesfr/xo-web#1481
2016-11-16 11:21:46 +01:00
Julien Fontanet
5b8cdf06b3 feat(test): new protocol (#2)
Plugins are now testable.
2016-11-15 11:01:50 +01:00
Julien Fontanet
682315e672 fix(package): update fs-promise to version 1.0.0 (#7)
https://greenkeeper.io/
2016-11-15 09:50:26 +01:00
Julien Fontanet
e33c7fd1c1 initial commit 2016-11-10 15:48:14 +01:00
Julien Fontanet
9b3668423e 0.2.0 2016-11-10 10:47:43 +01:00
greenkeeper[bot]
7227af9aac fix(package): update fs-promise to version 1.0.0 (#21)
https://greenkeeper.io/
2016-11-10 08:57:20 +01:00
greenkeeper[bot]
7c8194307e fix(package): update fs-promise to version 1.0.0 (#17)
https://greenkeeper.io/
2016-11-10 08:56:51 +01:00
greenkeeper[bot]
36c38063f2 fix(package): update fs-promise to version 1.0.0
https://greenkeeper.io/
2016-11-10 02:17:39 +00:00
Julien Fontanet
9b5fac9e2b feat(test): throw an error when authentication fails 2016-11-09 17:08:17 +01:00
badrAZ
47991b7d1a feat(test): this plugin is now testable (#20)
See vatesfr/xo-web#1749
2016-11-09 16:41:53 +01:00
Badr AZIZBI
a5ea24311a Errors fixed 2016-11-09 15:53:16 +01:00
badrAZ
3c11f0acda feat(test): this plugin is now testable (#9)
See vatesfr/xo-web#1749
2016-11-09 14:55:08 +01:00
Julien Fontanet
4a5222fa3b chore(package): update all dependencies 2016-11-08 11:11:13 +01:00
greenkeeper[bot]
4294dfd8fe chore(package): update dependencies (#18)
https://greenkeeper.io/
2016-11-07 21:20:54 +01:00
Pierre Donias
e7b739bb3b feat(api-errors): error factories (#1) 2016-11-07 12:30:29 +01:00
Julien Fontanet
baf5f7491d 0.7.2 2016-11-04 17:24:00 +01:00
Badr AZIZBI
a2afe2fa1a Error correction 2016-11-04 15:47:45 +01:00
Badr AZIZBI
8496a9bebd Initial commit 2016-11-04 13:42:23 +01:00
greenkeeper[bot]
0d6b7d6f04 chore(package): update babel-preset-env to version 0.0.7 (#2)
https://greenkeeper.io/
2016-11-03 08:59:51 +01:00
Julien Fontanet
59d6a51635 fix(package): fix depcheck (again) 2016-11-02 17:23:40 +01:00
Julien Fontanet
b4693019f7 fix(package): fix depcheck 2016-11-02 17:02:14 +01:00
Julien Fontanet
320d7a89ca fix(package): fix build 2016-11-02 17:02:07 +01:00
Julien Fontanet
1016f5f26f initial commit 2016-11-02 16:43:19 +01:00
Julien Fontanet
9284aee3fa 0.4.1 2016-11-02 15:12:23 +01:00
Julien Fontanet
32726efe88 Revert "Temporary work around for eslint/eslint#3946."
This reverts commit 63426818e6d43181ae635819fd1697fc9ea0a3d8.
2016-11-02 15:10:29 +01:00
Julien Fontanet
87daf0271c chore(package): update all dependencies 2016-11-02 15:03:45 +01:00
Julien Fontanet
70e73a5a65 fix(Collection#remove): properly report the key in related event 2016-11-02 15:00:04 +01:00
Julien Fontanet
1642798aa6 0.9.5 2016-11-02 14:04:15 +01:00
Julien Fontanet
588c369615 fix(package): fix URLs 2016-11-02 11:45:31 +01:00
greenkeeper[bot]
9aa9d4452c chore(package): update dependencies (#16)
https://greenkeeper.io/
2016-11-02 09:30:20 +01:00
greenkeeper[bot]
d7fe25c4fc chore(package): update dependencies (#17)
https://greenkeeper.io/
2016-11-01 22:01:39 +01:00
greenkeeper[bot]
990c5e9f08 chore(package): update dependencies (#25)
https://greenkeeper.io/
2016-11-01 22:01:31 +01:00
Julien Fontanet
7e2f2f6102 chore(package): various updates 2016-10-28 13:47:21 +02:00
Julien Fontanet
51def6535f chore(package): various updates 2016-10-28 12:10:31 +02:00
Julien Fontanet
14156b0911 chore(package): update all dependencies 2016-10-28 11:54:51 +02:00
Julien Fontanet
e2ba1fa7f8 initial commit 2016-10-28 11:48:06 +02:00
Julien Fontanet
36b589c2db chore(package): various updates 2016-10-28 11:44:11 +02:00
Julien Fontanet
cdf1a5fe47 fix(package): do not use babel-preset-babili 2016-10-28 11:14:17 +02:00
Julien Fontanet
abf146707f chore(package): update all dependencies 2016-10-28 11:08:32 +02:00
Julien Fontanet
c24a4009c8 chore(package): use full lodash package 2016-10-24 17:29:34 +02:00
Julien Fontanet
7b928c4d41 chore(package): add Travis CI conf 2016-10-24 16:03:04 +02:00
Julien Fontanet
4d50eae3c9 0.7.1 2016-10-24 15:58:32 +02:00
Julien Fontanet
c3a5e0592d chore(package): add --unregister doc 2016-10-24 15:58:14 +02:00
Julien Fontanet
207aef7cb3 fix(--list-commands): fix --json flag 2016-10-24 15:55:22 +02:00
Julien Fontanet
fbbd9ae249 chore(package): update (almost) all dependencies 2016-10-24 15:55:01 +02:00
Julien Fontanet
7aa591ffbd fix(package): handle multiline params 2016-10-24 15:33:40 +02:00
Greenkeeper
1fd91de50d chore(package): update promise-toolbox to version 0.7.0 (#8)
https://greenkeeper.io/
2016-10-24 15:03:35 +02:00
Greenkeeper
8492ceee09 chore(package): update promise-toolbox to version 0.7.0 (#13)
https://greenkeeper.io/
2016-10-24 15:01:03 +02:00
Greenkeeper
1f710b9b78 chore(package): update promise-toolbox to version 0.7.0 (#49)
https://greenkeeper.io/
2016-10-24 15:00:35 +02:00
Julien Fontanet
b87ad2df54 0.5.1 2016-10-20 16:18:00 +02:00
Julien Fontanet
c9e2f94daf fix(markdown): insert a blank line after lists 2016-10-20 15:19:17 +02:00
Julien Fontanet
52774c7d6d 0.3.1 2016-10-19 13:17:10 +02:00
Julien Fontanet
01686b8e60 fix(package): behave with missing thresholds object 2016-10-18 17:34:06 +02:00
Julien Fontanet
0317d6a862 chore(package): remove unused test env 2016-10-18 17:33:29 +02:00
Julien Fontanet
2a316b1ffa chore(package): use constants in configuration schema 2016-10-18 17:31:46 +02:00
Julien Fontanet
9b9f0e5607 fix(package): fix various URLs 2016-10-14 11:57:37 +02:00
Julien Fontanet
590f6cb7b3 chore(package): update all dependencies 2016-10-14 11:53:39 +02:00
Julien Fontanet
b120146cdc chore(package): lots of comments 2016-10-12 17:21:45 +02:00
Greenkeeper
7587458f99 Update promise-toolbox to version 0.6.0 🚀 (#7)
https://greenkeeper.io/
2016-10-12 09:12:14 +02:00
Greenkeeper
d1e69f5957 chore(package): update promise-toolbox to version 0.6.0 (#12)
https://greenkeeper.io/
2016-10-11 17:34:11 +02:00
Julien Fontanet
2c35ee11b3 fix(Xapi#_init): fix promisify() usage 2016-10-11 17:22:43 +02:00
Greenkeeper
bd0c747d98 chore(package): update promise-toolbox to version 0.6.0 (#48)
https://greenkeeper.io/
2016-10-11 17:20:32 +02:00
Julien Fontanet
f5fb066975 chore(package): babel-preset-es2015 → babel-preset-latest 2016-10-11 10:51:21 +02:00
Julien Fontanet
cd6a0fa678 fix(package): use babel-plugin-transform-runtime 2016-10-11 10:51:04 +02:00
Julien Fontanet
e735420dd8 chore(package): babel-preset-es2015 → babel-preset-latest 2016-10-11 10:49:37 +02:00
Julien Fontanet
bc2f17c840 fix(package): use babel-plugin-transform-runtime 2016-10-11 10:47:05 +02:00
Julien Fontanet
8c459cac0f fix(package): remove babel-plugin-lodash 2016-10-11 10:43:28 +02:00
Julien Fontanet
878d2d9260 fix(package): remove babel-plugin-lodash 2016-10-11 10:38:39 +02:00
Julien Fontanet
14bca4bbf7 0.2.2 2016-10-04 15:16:10 +02:00
Julien Fontanet
ea55c10c4d chore(package): pnpm compatibility 2016-10-04 15:15:39 +02:00
Julien Fontanet
75cde40b0e fix(VM-template): normal permissions handling
Fixes vatesfr/xo-web#1620
2016-10-04 15:13:08 +02:00
Julien Fontanet
5434b4987f chore(package): remove unused deps 2016-10-04 15:09:12 +02:00
Julien Fontanet
54befb2814 chore(package): update all dependencies 2016-09-29 14:43:29 +02:00
Julien Fontanet
74fba76b85 chore(package): update all dependencies 2016-09-29 14:16:47 +02:00
Julien Fontanet
2b8996e965 chore(package): update all dependencies 2016-09-29 14:00:25 +02:00
Julien Fontanet
a9aad547b2 fix(package): use babel-plugin-transform-runtime 2016-09-29 13:37:20 +02:00
Julien Fontanet
4673af6fd8 chore(package): update all dependencies 2016-09-29 12:54:46 +02:00
Julien Fontanet
33ae81ce05 chore(package): update all dependencies 2016-09-29 12:38:18 +02:00
Greenkeeper
075c43ce0e chore(package): update chai-as-promised to version 6.0.0 (#6)
https://greenkeeper.io/
2016-09-28 09:49:21 +02:00
Greenkeeper
10fddc51bb chore(package): update babel-eslint to version 7.0.0 (#11)
https://greenkeeper.io/
2016-09-27 23:40:44 +02:00
Greenkeeper
b32b319198 chore(package): update babel-eslint to version 7.0.0 (#5)
https://greenkeeper.io/
2016-09-27 23:40:10 +02:00
Greenkeeper
2d14fde671 chore(package): update babel-eslint to version 7.0.0 (#23)
https://greenkeeper.io/
2016-09-27 23:39:57 +02:00
Greenkeeper
98cd2746ef chore(package): update babel-eslint to version 7.0.0 (#15)
https://greenkeeper.io/
2016-09-27 23:39:13 +02:00
Greenkeeper
ea18e4129c chore(package): update babel-eslint to version 7.0.0 (#14)
https://greenkeeper.io/
2016-09-27 23:37:26 +02:00
Greenkeeper
6980e2b959 chore(package): update babel-eslint to version 7.0.0 (#4)
https://greenkeeper.io/
2016-09-27 23:37:03 +02:00
Greenkeeper
7e7ec83c12 chore(package): update babel-eslint to version 7.0.0 (#19)
https://greenkeeper.io/
2016-09-27 23:27:37 +02:00
Greenkeeper
b12fd45df1 chore(package): update babel-eslint to version 7.0.0 (#47)
https://greenkeeper.io/
2016-09-27 23:26:52 +02:00
Julien Fontanet
a8340c24c3 chore(package): update all dependencies 2016-09-27 15:29:23 +02:00
Greenkeeper
f49f3fb2a6 Update all dependencies 🌴 (#3)
https://greenkeeper.io/
2016-09-25 16:20:17 +02:00
Greenkeeper
3c9ef8d199 chore(package): update dependencies (#10)
https://greenkeeper.io/
2016-09-25 15:54:44 +02:00
Greenkeeper
669c24ebd5 chore(package): update dependencies (#4)
https://greenkeeper.io/
2016-09-22 07:53:33 +02:00
Greenkeeper
16dde5c772 chore(package): update standard to version 8.1.0 (#12)
https://greenkeeper.io/
2016-09-18 05:17:03 +02:00
Julien Fontanet
96635a98f5 0.5.0 2016-09-09 15:50:38 +02:00
Julien Fontanet
54ef65ced9 feat: display error message
Fixes vatesfr/xo-web#1462
2016-09-05 10:39:54 +02:00
Julien Fontanet
4fb6bef04c feat: express global status in mail subject
New format is: `[Xen Orchestra][Success | Failure] Backup report for $tag`

Fixes vatesfr/xo-web#1463
2016-09-05 10:39:11 +02:00
Nicolas Raynaud
7d5e7f5c73 add some notes 2016-08-30 11:26:35 -07:00
Nicolas Raynaud
15a8b97410 0.0.12 2016-08-30 10:10:21 -07:00
Nicolas Raynaud
874162df87 remove stream declared size 2016-08-30 10:03:57 -07:00
Nicolas Raynaud
0c8b9d8539 0.0.11 2016-08-26 17:37:15 -07:00
Nicolas Raynaud
7c6579b264 allow the reading of some cases of malformed VMDK files
- if the L1 is advertised in the header
 - and the L1 table is right after the descriptor (with or without marker)
 - and the L2 table is just after L1 (with or without marker)
 - and L2 is not fragmented
 - and the grains are in ascending order in the file
 - then we skip all the tables and read the grains directly
2016-08-26 17:36:41 -07:00
Nicolas Raynaud
e3473721f6 0.0.10 2016-08-24 09:42:12 -07:00
Nicolas Raynaud
28799c62a9 spawn only one promise in _read() of the outpur stream
- instrument VirtualBuffer to detect unwanted concurrency
 - move VMDK version detection to header parsing
 - chunk zero pages in the output stage
 - avoid sending too much data at once in the output stage (with a shadow file)
2016-08-24 09:39:02 -07:00
Greenkeeper
224a79840d chore(package): update standard to version 8.0.0 (#14)
https://greenkeeper.io/
2016-08-24 12:23:09 -04:00
Greenkeeper
c21fef7c72 Update standard to version 8.0.0 🚀 (#46)
https://greenkeeper.io/
2016-08-24 11:50:04 -04:00
Greenkeeper
99ae3e0f7f chore(package): update standard to version 8.0.0 (#18)
https://greenkeeper.io/
2016-08-24 11:37:01 -04:00
Greenkeeper
02a4161ecb chore(package): update standard to version 8.0.0 (#22)
https://greenkeeper.io/
2016-08-24 11:33:05 -04:00
Greenkeeper
1a68c3947d chore(package): update standard to version 8.0.0 (#13)
https://greenkeeper.io/
2016-08-24 11:26:48 -04:00
Nicolas Raynaud
4ac58b3f44 0.0.9 2016-08-22 03:53:26 +02:00
Nicolas Raynaud
a9758e6997 fix postion computation in vitual buffer 2016-08-22 03:32:06 +02:00
Nicolas Raynaud
bb546c6bb7 chunk the padding buffers of the output stage 2016-08-22 03:30:16 +02:00
Nicolas Raynaud
d877a6f1ad 0.0.8 2016-08-18 21:40:20 +02:00
Nicolas Raynaud
ef7a563d97 avoid constructing 0 byte long buffer and async stream._read() function 2016-08-18 21:39:23 +02:00
Nicolas Raynaud
df33772eba 0.0.7 2016-08-18 17:11:23 +02:00
Nicolas Raynaud
97c1cc8ed9 remove the custom virtual buffer in favor of pipette 2016-08-18 17:10:14 +02:00
Julien Fontanet
b7f34b9da6 0.9.4 2016-08-18 09:39:28 +02:00
Julien Fontanet
f82a6efda1 feat(Xapi#_watchEvents): keep-alive, inject 1 event per minute (#45)
See vatesfr/xo-web#1384
2016-08-18 09:37:31 +02:00
Nicolas Raynaud
f869c02395 0.0.6 2016-08-17 19:20:09 +02:00
Nicolas Raynaud
43e0217008 check for out of order blocks in vhd output 2016-08-17 19:19:51 +02:00
Julien Fontanet
f89538c6f7 0.0.5 2016-08-17 16:50:07 +02:00
Julien Fontanet
1ee0085c22 fix: correctly export the function 2016-08-17 16:49:16 +02:00
Greenkeeper
d56590c6e6 chore(package): update nyc to version 8.0.0 (#12)
https://greenkeeper.io/
2016-08-16 23:41:08 +02:00
Julien Fontanet
556ca71c3a 0.9.3 2016-08-16 19:02:24 +02:00
Julien Fontanet
42be2d5031 fix(Xapi#_watchEvent): timeouts are in ms 2016-08-16 19:01:49 +02:00
Julien Fontanet
876e22b092 0.9.2 2016-08-16 18:09:47 +02:00
Julien Fontanet
f9f9c16cb0 feat(Xapi#_watchEvents): timeout request after 10mins (#44) 2016-08-16 18:07:44 +02:00
Julien Fontanet
f63f5d0ac6 0.0.4 2016-08-16 15:33:24 +02:00
Nicolas Raynaud
44dbbeb2a3 tests: increase timeout and fix Node 0.12 compat (#3) 2016-08-16 15:25:05 +02:00
Greenkeeper
a881090e65 chore(package): update clarify to version 2.0.0 (#37)
https://greenkeeper.io/
2016-08-16 15:09:59 +02:00
Greenkeeper
3d9fce02a4 chore(package): update promise-toolbox to version 0.5.0 (#43)
https://greenkeeper.io/
2016-08-16 12:22:21 +02:00
Julien Fontanet
dea401fc7c 0.0.3 2016-08-15 19:02:04 +02:00
Julien Fontanet
cca21fcd32 fix(build): work with npm 3 2016-08-15 18:56:33 +02:00
Julien Fontanet
ab6b1e2c32 chore(Xapi#_transportCall): simplify and improve code
It may even fixes some issues.
2016-08-15 16:36:18 +02:00
Greenkeeper
59d9b3c6b4 chore(package): update nyc to version 8.1.0 (#42)
https://greenkeeper.io/
2016-08-14 19:07:12 +02:00
Julien Fontanet
f7b6fcf684 Merge pull request #17 from vatesfr/greenkeeper-nyc-8.1.0
Update nyc to version 8.1.0 🚀
2016-08-14 19:07:04 +02:00
Greenkeeper
df3ec9a629 chore(package): update nyc to version 8.1.0 (#21)
https://greenkeeper.io/
2016-08-14 19:06:15 +02:00
Greenkeeper
6bc4bf308b chore(package): update nyc to version 8.1.0 (#13)
https://greenkeeper.io/
2016-08-14 19:05:44 +02:00
greenkeeperio-bot
2ddb84f457 chore(package): update nyc to version 8.1.0
https://greenkeeper.io/
2016-08-14 10:26:33 +02:00
Olivier Lambert
6b82cc7510 fix(readme): typo 2016-08-12 17:22:28 +02:00
Nicolas Raynaud
2764f4d455 get the npm package ready for use (#2) 2016-08-12 12:02:52 +02:00
Nicolas Raynaud
28fecc428f preliminary parsing of VMDK file (#1)
* initial tests for integration of VMDK inside xo-server
2016-08-11 17:21:16 +02:00
Julien Fontanet
8851a661c0 0.9.1 2016-08-05 10:49:08 +02:00
Julien Fontanet
2d327961da fix(objects): use $$ to avoid conflict with reserverd keys 2016-08-05 10:42:33 +02:00
Greenkeeper
ac6d14113a chore(package): update mocha to version 3.0.0 (#9)
https://greenkeeper.io/
2016-08-02 19:21:37 +02:00
Greenkeeper
398db72b44 chore(package): update mocha to version 3.0.0 (#17)
https://greenkeeper.io/
2016-08-02 19:21:01 +02:00
Olivier Lambert
32777e17c4 Initial commit 2016-08-02 16:37:00 +02:00
Julien Fontanet
a3da3299fa Merge pull request #15 from vatesfr/greenkeeper-mocha-3.0.0
Update mocha to version 3.0.0 🚀
2016-08-02 16:20:59 +02:00
Greenkeeper
3a41efbea8 chore(package): update mocha to version 3.0.0 (#40)
https://greenkeeper.io/
2016-08-02 16:20:00 +02:00
Greenkeeper
e763db7102 chore(package): update mocha to version 3.0.0 (#11)
https://greenkeeper.io/
2016-08-02 16:16:53 +02:00
greenkeeperio-bot
2b504ce5ab chore(package): update mocha to version 3.0.0
https://greenkeeper.io/
2016-08-01 07:33:49 +02:00
Julien Fontanet
39a16f9a7f 0.8.0 2016-07-20 16:53:56 +02:00
Julien Fontanet
f3ea8d012f feat(Xo#refreshUser) 2016-07-20 16:53:56 +02:00
Julien Fontanet
1b9aa63096 Merge pull request #14 from vatesfr/greenkeeper-nyc-7.0.0
Update nyc to version 7.0.0 🚀
2016-07-15 11:29:20 +02:00
Greenkeeper
45b8fd0100 chore(package): update nyc to version 7.0.0 (#38)
https://greenkeeper.io/
2016-07-15 11:29:14 +02:00
Greenkeeper
11bde53069 chore(package): update nyc to version 7.0.0 (#16)
https://greenkeeper.io/
2016-07-15 11:27:31 +02:00
Greenkeeper
f115ee18c4 chore(package): update nyc to version 7.0.0 (#10)
https://greenkeeper.io/
2016-07-15 11:27:11 +02:00
Greenkeeper
162a56232c chore(package): update nyc to version 7.0.0 (#8)
https://greenkeeper.io/
2016-07-15 11:26:53 +02:00
greenkeeperio-bot
de6f0ef8eb chore(package): update nyc to version 7.0.0
https://greenkeeper.io/
2016-07-09 09:13:32 +02:00
wescoeur
cd8a92c30b 0.3.0 2016-07-07 12:00:24 +02:00
wescoeur
10030c4959 Use default value on cpu/memoryFree. 2016-07-07 11:58:12 +02:00
wescoeur
ffc155c341 0.2.1 2016-06-30 14:12:19 +02:00
wescoeur
42ea76eb2a Supports ui schema builder of xo-web. 2016-06-30 14:10:59 +02:00
Julien Fontanet
be503f1341 0.4.1 2016-06-30 10:12:30 +02:00
Julien Fontanet
6f4509c260 fix: report only on failures with xo-web 5 2016-06-30 10:12:07 +02:00
Julien Fontanet
b2331084d1 0.4.0 2016-06-29 11:40:58 +02:00
Julien Fontanet
ab3a594884 chore(package): update all dependencies 2016-06-29 11:34:43 +02:00
Julien Fontanet
592adcf42e 0.1.0 2016-06-29 10:40:03 +02:00
Julien Fontanet
978c881ab7 chore(package): update all dependencies 2016-06-29 10:39:05 +02:00
Julien Fontanet
99727447ef 0.2.1 2016-06-24 14:32:25 +02:00
Julien Fontanet
e02cb56ee0 feat(VM-controller): inherits from its container 2016-06-24 14:32:16 +02:00
Julien Fontanet
02f75fb2e1 0.3.0 2016-06-08 17:17:45 +02:00
Fabrice Marsaud
6cced719dc Handling a missing leading slash in nfs path 2016-06-08 17:08:54 +02:00
Fabrice Marsaud
45ec0b9b01 No more mount matters in nfs remotes + tests 2016-06-08 15:05:09 +02:00
Julien Fontanet
2451ac3ade feat(parse): local type is now file 2016-06-08 14:38:31 +02:00
Julien Fontanet
45a94fe73d feat(parse): only accepts a string 2016-06-08 14:36:10 +02:00
Julien Fontanet
e1173e6565 0.2.0 2016-06-07 16:27:34 +02:00
Julien Fontanet
46f6911ef8 feat(README): add usage 2016-06-07 16:27:05 +02:00
Julien Fontanet
7629bf5be2 feat: simplify use for single object 2016-06-07 16:26:52 +02:00
Julien Fontanet
fb17de7988 0.2.3 2016-06-07 10:10:51 +02:00
Fabrice Marsaud
32f4d42e59 fix(parse): revert to 0.2 API (#10) 2016-06-07 10:10:20 +02:00
Julien Fontanet
0031c2a9b7 0.2.2 2016-06-06 17:57:22 +02:00
Fabrice Marsaud
509e99913a fix(parse): fix tests and many issues (#9) 2016-06-06 17:56:59 +02:00
Julien Fontanet
c639cfcfd9 fix(parse): do not edit the remote object inplace 2016-06-06 17:17:57 +02:00
Julien Fontanet
b140a2ca3e chore(tests): refactor data 2016-06-06 17:17:19 +02:00
Julien Fontanet
aa448e7a41 0.2.1 2016-06-06 13:33:33 +02:00
Fabrice Marsaud
220750f887 fix(SMB): use of @ and : chars in passwords (#8)
Fixes #4.
2016-06-06 12:11:29 +02:00
Fabrice Marsaud
808cc5d8d0 fix: file protocol starts with 3 / (#6)
Fixes #7
2016-06-06 10:57:27 +02:00
Julien Fontanet
02e1a32fae feat(README) 2016-05-27 17:03:03 +02:00
Julien Fontanet
d1690bda81 Initial commit 2016-05-27 17:00:49 +02:00
Julien Fontanet
155f2fc36c fix(Xapi#_sessionCall): must pCatch when there is a predicate 2016-05-17 10:40:59 +02:00
Julien Fontanet
c3dc136de4 0.9.0 2016-05-11 17:44:45 +02:00
Julien Fontanet
4cbc5c4e2f fix(memory leak): due to Bluebird#cancellable()
To be fair, it may be due to an incorrect usage of
Bluebird#cancellable().

Bluebird has been completely removed at the same occasion.
2016-05-11 17:42:01 +02:00
Julien Fontanet
68820aaf59 fix(cli): add babel-polyfill dep 2016-05-11 17:40:56 +02:00
Julien Fontanet
cd65bc7683 chore: simplify _watchEvents() and _watchEventsLegacy() implementation
I finally understood ES6 Temporal Dead Zone!
2016-05-11 15:23:57 +02:00
Julien Fontanet
4d4a2897a5 0.7.0 2016-05-11 12:59:14 +02:00
Julien Fontanet
cd73c8f82f 0.2.0 2016-05-11 12:58:59 +02:00
Julien Fontanet
934e67d146 Document JSON syntax 2016-05-11 12:57:53 +02:00
Julien Fontanet
4c96e44b9b fix(format): use correct type for local 2016-05-11 12:49:29 +02:00
Julien Fontanet
189900549a feat(tests): basic tests for local and smb types 2016-05-11 12:49:06 +02:00
Julien Fontanet
6d18420a5d fix(tests): make them pass 2016-05-11 12:22:28 +02:00
Julien Fontanet
ab3d307393 chore(package): update all dependencies 2016-05-11 12:22:26 +02:00
ABHAMON Ronan
89b2156f61 chore(package): remove unused babel-runtime (#3) 2016-05-11 12:19:09 +02:00
ABHAMON Ronan
2f95da1892 fix(package): lodash.xxx are prod dependencies (#2) 2016-05-11 12:08:34 +02:00
Julien Fontanet
6e0956f09f feat(memory-test): split into inject-events 2016-05-11 11:30:49 +02:00
Julien Fontanet
1191f0ba93 feat(events): it's possible not to watch them 2016-05-10 15:35:43 +02:00
Julien Fontanet
6534ffea26 perf(objects): memoize () => this._pool 2016-05-10 14:55:19 +02:00
Julien Fontanet
f9173c41d1 chore(package): remove unnecessary babel-runtime 2016-05-10 10:12:24 +02:00
Julien Fontanet
a2faedcacb fix(debounce): it can be changed at runtime 2016-05-09 18:40:27 +02:00
Julien Fontanet
cb56b3b9d0 chore: rewrite some code using invoke pattern 2016-05-09 18:11:14 +02:00
Julien Fontanet
a61b50548c feat: memory test 2016-05-09 18:00:09 +02:00
Julien Fontanet
75ad588e0b fix(package): clarify, source-map-support & trace are prod deps 2016-05-09 14:07:55 +02:00
Julien Fontanet
306d5d8fc7 feat(event): authenticationFailure 2016-05-06 12:33:56 +02:00
Julien Fontanet
5553d5fefa Merge pull request #8 from vatesfr/greenkeeper-update-all
Update all dependencies 🌴
2016-05-04 16:21:06 +02:00
Julien Fontanet
9328518bbc chore: various updates 2016-05-04 16:14:50 +02:00
Julien Fontanet
6564edcc32 chore(package): update all dependencies (except Bluebird) 2016-05-04 15:54:04 +02:00
Julien Fontanet
75bb7d5a2d fix(prompt-schema): adapt to inquirer version 1.0.0 2016-05-04 14:48:47 +02:00
Julien Fontanet
ceab4e37cd style(prompt-schema): handle ESLint special cases 2016-05-04 14:45:01 +02:00
greenkeeperio-bot
afb6974cc0 chore(package): update dependencies
https://greenkeeper.io/
2016-05-04 14:35:22 +02:00
Greenkeeper
a4dc965c23 chore(package): update standard to version 7.0.0 (#3)
https://greenkeeper.io/
2016-05-03 09:16:47 +02:00
Greenkeeper
8b65c280a8 chore(package): update standard to version 7.0.0 (#4)
https://greenkeeper.io/
2016-05-03 09:05:49 +02:00
Greenkeeper
c46c0018ea chore(package): update nyc to version 6.4.0 (#9)
https://greenkeeper.io/
2016-04-27 22:20:32 +02:00
Julien Fontanet
4ab24d2fe5 0.1.0 2016-04-27 16:11:12 +02:00
Julien Fontanet
c70ca2ff64 style: add missing parentheses 2016-04-27 15:58:22 +02:00
Julien Fontanet
649ab26da8 feat: gracefully handle missing objects 2016-04-27 15:52:19 +02:00
Julien Fontanet
c921ea6eb7 Merge pull request #1 from vatesfr/greenkeeper-update-all
Update all dependencies 🌴
2016-04-27 15:49:02 +02:00
greenkeeperio-bot
58aed76aa3 chore(package): update dependencies
https://greenkeeper.io/
2016-04-27 15:44:41 +02:00
Julien Fontanet
e286c57ce4 Merge pull request #2 from vatesfr/v0.8.x
Refactor on top of jsonrpc-websocket-client
2016-04-27 15:26:39 +02:00
Julien Fontanet
840f0b6379 chore(package): update babel-eslint to version 6.0.4 2016-04-27 15:18:09 +02:00
Julien Fontanet
538025edd5 chore: remove irrelevant comment 2016-04-27 15:14:33 +02:00
Julien Fontanet
35e8dcc3be 0.8.0 2016-04-26 08:46:22 +02:00
Julien Fontanet
d1600fd058 fix: handle UUID changes (fix #5). (#7) 2016-04-26 08:46:04 +02:00
Julien Fontanet
1416fb0c71 Xapi#call(): only *.get_*() methods in readonly mode. 2016-04-19 16:32:18 +01:00
Julien Fontanet
2975db247d Do not attempt to fix JSON is parsing is successful. 2016-04-19 13:29:51 +01:00
Julien Fontanet
03eaa652ce Optional debounce for events. 2016-04-14 17:55:41 +02:00
Julien Fontanet
1e7d1b1628 0.0.0 2016-04-08 11:05:41 +02:00
Julien Fontanet
defd42f74e VDI: fix fallback on VM. 2016-04-08 10:46:57 +02:00
wescoeur
aa54ab6e51 0.2.0 2016-04-06 16:01:24 +02:00
wescoeur
f0c28c74d8 Try to power on a hosts set. (Performance mode) 2016-04-06 16:00:01 +02:00
wescoeur
3e285d6131 Search destination host per pool. (Performance mode) 2016-04-06 15:24:34 +02:00
Julien Fontanet
c96d94329e Fix using / as url. 2016-03-30 11:34:26 +02:00
wescoeur
627227f2f9 0.1.0 2016-03-24 11:48:49 +01:00
wescoeur
42cef0da88 Performance and density mode work. 2016-03-24 11:44:23 +01:00
wescoeur
06f60b7d92 Make code modular. 2016-03-24 09:46:14 +01:00
wescoeur
3ddb4d2b23 Log VM migrations (density mode). 2016-03-23 17:37:41 +01:00
wescoeur
5a825bd459 Density mode test. 2016-03-23 16:52:05 +01:00
Olivier Lambert
ab1f08f687 0.3.4 2016-03-23 10:35:41 +01:00
Pierre
2f89e3658a Backup type in report e-mail (See vatesfr/xo-web#822) 2016-03-23 10:11:01 +01:00
Julien Fontanet
eac29993d3 0.7.4 2016-03-22 09:37:55 +01:00
Olivier Lambert
f08ab729bd 0.3.3 2016-03-17 14:18:18 +01:00
Pierre
052c974369 Fixed get tag 2016-03-17 14:15:19 +01:00
Olivier Lambert
e760e868c1 0.3.2 2016-03-17 13:47:45 +01:00
Pierre
5e3831a1a4 Added tag in report e-mail 2016-03-17 12:11:49 +01:00
wescoeur
99e046ddea Performance mode: The worst host is removed of hosts array after optimization. 2016-03-17 12:11:34 +01:00
wescoeur
12e0759711 Optimize hosts in order priority. 2016-03-17 11:21:15 +01:00
wescoeur
da0c1cec22 Fix cpu average. Use the cpu number of host and vm. 2016-03-17 11:01:12 +01:00
Julien Fontanet
d23df2ab15 0.0.6 2016-03-16 14:04:14 +01:00
Julien Fontanet
dbe828097c Do not set credentials if they are undefined. 2016-03-16 14:03:28 +01:00
Julien Fontanet
48a0623ded Fix handling of ignoreUnauthorized option. 2016-03-16 14:03:01 +01:00
Julien Fontanet
3527b86ec5 Add empty test file. 2016-03-15 16:25:46 +01:00
Julien Fontanet
fe7a9104a8 Minor package.json changes. 2016-03-15 16:13:49 +01:00
Julien Fontanet
cbfb94afcb Update deps. 2016-03-15 15:42:14 +01:00
Julien Fontanet
74d8eff6d8 Add support for JSON encoded values. 2016-03-14 10:20:15 +01:00
wescoeur
d7ed9ab64e Remove masters from toOptimize and hosts. 2016-03-11 16:48:36 +01:00
wescoeur
3b6c5898fe Plugin can use enum for modes now. 2016-03-11 15:57:29 +01:00
wescoeur
ae22adc920 Density mode in progress. 2016-03-11 15:45:23 +01:00
wescoeur
5dacf9c3f5 The configuration object must not be modified. 2016-03-11 14:42:40 +01:00
wescoeur
9129bfa284 Excluded hosts in options. 2016-03-11 11:32:37 +01:00
wescoeur
96190c21d6 threshold 0 can by used with cpu. 2016-03-09 17:53:38 +01:00
Julien Fontanet
af2a9225b8 Fix invalid \t in JSON. 2016-03-09 17:41:55 +01:00
wescoeur
aa117a0ee3 Fixes hosts top optimize. 2016-03-09 17:29:57 +01:00
wescoeur
39b0ea381b Debug exec plans 2016-03-09 17:24:57 +01:00
wescoeur
021cea0b34 Thresholds are exposed in configuration. 2016-03-09 16:59:36 +01:00
wescoeur
eaad41fe55 Load balancer config use a checkbox. 2016-03-09 16:06:06 +01:00
wescoeur
e25d58d70a Fixes. 2016-03-09 15:34:57 +01:00
wescoeur
9c0967170a Schema update. 2016-03-09 12:23:02 +01:00
wescoeur
abd89df365 Three class: Plan, PerfomancePlan, DensityPlan. 2016-03-03 14:47:56 +01:00
wescoeur
651e4bb775 Fixes (cpus to cpu). Add Low/Critical thresholds. 2016-03-03 11:17:04 +01:00
wescoeur
7f06d6e68c Low threshold are automatically computed. 2016-03-02 16:56:59 +01:00
wescoeur
e5146f7def Many fixes. Cron Job is in a job structure. 2016-03-02 12:16:30 +01:00
wescoeur
d9bf7c7d12 Many fixes. Configure must operate if cron job is running. 2016-03-02 11:20:28 +01:00
wescoeur
b0bf18e235 Debug messages. 2016-03-02 10:36:43 +01:00
wescoeur
f001b2c68f Unstable, plugin can migrate vm. (to test) 2016-03-02 10:24:56 +01:00
Julien Fontanet
a24de7fe3f Fix type case in README. 2016-02-29 18:02:30 +01:00
wescoeur
296141ad3d Unsable: _computeOptimizations 2016-02-26 16:42:11 +01:00
wescoeur
7abba0a69b Many fixes. Compute real cpu usage of vms. 2016-02-26 15:28:00 +01:00
wescoeur
dee7767427 Get only the vms stats of the worst host. 2016-02-26 11:57:41 +01:00
wescoeur
087a71367d Optimizations only on one host ! 2016-02-26 11:07:22 +01:00
wescoeur
bec2e3b4a0 getVms & getVmsStats functions. 2016-02-26 10:20:15 +01:00
wescoeur
274884ef4d Reorder exceeded hosts by priority. 2016-02-25 16:06:46 +01:00
wescoeur
d3f52cdd1a Compute exceeded hosts. 2016-02-25 13:10:27 +01:00
wescoeur
cb97e37c15 Get stats for host. 2016-02-24 15:27:11 +01:00
wescoeur
3013fa86b6 Configure implem. 2016-02-24 12:50:03 +01:00
wescoeur
2593743746 Json schema (prototype). 2016-02-24 10:53:48 +01:00
wescoeur
ab6bd56006 Initial commit. 2016-02-24 10:05:20 +01:00
Julien Fontanet
e02b8522d6 0.0.5 2016-02-22 16:03:54 +01:00
Julien Fontanet
8cf74c88ce Add ignoreUnauthorized option. 2016-02-22 16:03:33 +01:00
Julien Fontanet
4095d6dcf6 0.3.1 2016-02-22 15:45:51 +01:00
Julien Fontanet
cd4221e4f2 0.3.0 2016-02-22 15:44:38 +01:00
Julien Fontanet
e4e65e4576 0.0.4 2016-02-22 15:42:05 +01:00
Julien Fontanet
72e93786df Log errors. 2016-02-22 15:41:03 +01:00
Julien Fontanet
548754821e Merge pull request #8 from vatesfr/conditional-reporting
Implement conditional reporting.
2016-02-19 10:32:17 +01:00
Danp2
77f85579e3 Implement conditional reporting. 2016-02-19 10:23:48 +01:00
Pierre
e3ec03794a Markdown generator first version 2016-02-12 14:29:51 +01:00
Julien Fontanet
05c325d686 Fetch the correct VM even when the param is vm and not id (fix #3). 2016-02-12 09:09:39 +01:00
thehanatos
cf9472eb56 changes 2016-02-11 16:53:28 +01:00
Pierre
bb2085436e Network usage stats 2016-02-11 16:20:04 +01:00
Pierre
d882a4249a Standardized function declarations 2016-02-11 11:38:18 +01:00
thehanatos
a2420e4288 changes 2016-02-11 11:21:43 +01:00
Pierre
3687a230e1 promise-toolbox: all to resolve promises in object 2016-02-10 12:27:55 +01:00
Pierre
c274486057 Improved getHostVmsStats (Promise.all) 2016-02-10 12:13:26 +01:00
Pierre
94e9bbfd63 VM stats for multiple hosts with generateHostsVmsReport.
Renamed generateHostVmReport.
2016-02-10 11:40:21 +01:00
Julien Fontanet
adb71ad174 FIx my previous commit -_-". 2016-02-10 11:06:47 +01:00
Julien Fontanet
4a95f5cd9d Coding style. 2016-02-10 10:48:02 +01:00
Julien Fontanet
f3b368fae4 Merge pull request #2 from Danp2/Danp2-deltacopy
Enable reporting on additional backup types.
2016-02-10 10:45:54 +01:00
Julien Fontanet
20919a8a39 Absence of tests should not prevent CI linting. 2016-02-10 10:31:30 +01:00
Danp2
f2e86efc4d Update index.js 2016-02-09 21:22:17 -06:00
Danp2
98b27d647e Add logic for processing delta copy backup types 2016-02-09 21:10:00 -06:00
Pierre
7a884d1f87 getPoolVmStats returns an object with VM IDs as keys instead of an array 2016-02-05 16:42:00 +01:00
Pierre
d21f5f427c getPoolVmStats to get all the VMs stats from a specific pool 2016-02-05 15:01:55 +01:00
Pierre
9ba2a6628d generateGlobalCpuReport: average CPU usage for each host 2016-02-05 10:59:56 +01:00
Julien Fontanet
739641b857 0.0.3 2016-02-04 18:12:31 +01:00
Julien Fontanet
d02a110f99 Minor comment. 2016-02-04 18:12:16 +01:00
thehanatos
9120db2dc6 get n values max 2016-02-04 09:49:14 +01:00
Julien Fontanet
bf586d0837 0.0.0-0 2016-02-03 11:41:01 +01:00
Julien Fontanet
ed45a9b156 VDI-snapshot inherits from its VDI. 2016-02-03 11:35:48 +01:00
Julien Fontanet
f4ea39b602 Initial commit. 2016-02-03 10:02:39 +01:00
Julien Fontanet
a6dca12bad user and password config entries are optional (fix #2). 2016-02-02 16:49:45 +01:00
Julien Fontanet
e40411ef91 Use live stats. 2016-02-02 11:13:27 +01:00
thehanatos
fd20c21243 granularity added 2016-02-02 10:55:32 +01:00
Julien Fontanet
5c0474ef96 Various updates. 2016-02-02 10:23:20 +01:00
Julien Fontanet
73d1de75f9 Fix computeMin() and computeMax(). 2016-02-02 10:23:11 +01:00
thehanatos
8ea6989ea4 max, min and mean in the same function 2016-02-02 10:02:15 +01:00
Julien Fontanet
16a9f44d4d Xapi#getObject(): behave nicely when a XAPI object is passed. 2016-01-28 10:08:26 +01:00
Fabrice Marsaud
f26fc9a0f0 0.1.0 2016-01-26 17:32:49 +01:00
Fabrice Marsaud
3fd637b3c7 method rename 2016-01-26 17:32:35 +01:00
Fabrice Marsaud
ea605383d5 Various info 2016-01-26 16:08:40 +01:00
Fabrice Marsaud
a4fa670dc5 Initial commit 2016-01-26 16:01:03 +01:00
Julien Fontanet
56f99b2129 Implement a more generic method. 2016-01-26 15:38:03 +01:00
thehanatos
ccb508705b Fonctions computemin and computemax added 2016-01-26 15:06:53 +01:00
Julien Fontanet
6f8fa96150 Mutualize code into functions. 2016-01-26 14:22:35 +01:00
thehanatos
72e1632499 Method to get min, max average values (cpu) 2016-01-26 11:39:39 +01:00
Julien Fontanet
7d7cc56527 0.8.0-1 2016-01-22 17:47:41 +01:00
Julien Fontanet
85d0271b86 Xo#call(method, args).retry(predicate) 2016-01-22 17:47:35 +01:00
Julien Fontanet
749d5e22bb Update jsonrpc-websocket-client to 0.0.1-4. 2016-01-22 17:46:38 +01:00
Julien Fontanet
61c61adea1 Minor code dedup. 2016-01-21 15:42:34 +01:00
Julien Fontanet
c8a6fd19a7 0.8.0-0 2016-01-07 18:04:52 +01:00
Julien Fontanet
0b143b580a Various updates. 2016-01-07 18:01:11 +01:00
Julien Fontanet
ed69fedc0a Initial commit. 2016-01-05 18:13:43 +01:00
Julien Fontanet
8098464f58 Initial commit. 2016-01-05 18:13:43 +01:00
Julien Fontanet
ea0db57388 0.6.0 2016-01-05 18:03:58 +01:00
Julien Fontanet
6fcc148105 0.7.3 2016-01-05 16:09:19 +01:00
Julien Fontanet
3485cb4ec4 Guard against null/undefined error.res. 2016-01-05 16:09:06 +01:00
Julien Fontanet
e6ef6ccccf 0.5.1 2015-12-30 19:16:05 +01:00
Julien Fontanet
4826e14cad Log bind failures. 2015-12-30 19:15:58 +01:00
Julien Fontanet
5515f90147 README updates. 2015-12-30 19:12:24 +01:00
Julien Fontanet
2193c26acb 0.5.0 2015-12-30 19:03:13 +01:00
Julien Fontanet
1974a2c0e4 Use my Vates email. 2015-12-30 19:01:41 +01:00
Julien Fontanet
84fbe9ee06 Do not depend on Bluebird. 2015-12-30 19:00:32 +01:00
Julien Fontanet
b8e2cfc47f CLI for testing. 2015-12-30 17:46:21 +01:00
Julien Fontanet
553fc6f5d9 0.7.4 2015-12-30 12:01:12 +01:00
Julien Fontanet
f5d790b264 Do not automatically retry on connection errors! 2015-12-30 12:00:58 +01:00
Julien Fontanet
641e13496e Return null instead of throwing when no matches. 2015-12-28 10:18:47 +01:00
Julien Fontanet
a6e18819d4 Use arrow funcs where possible. 2015-12-28 10:18:21 +01:00
Julien Fontanet
faf5ff6aa4 Reading certs is now async. 2015-12-28 10:11:17 +01:00
Julien Fontanet
ae20ca5558 Update deps. 2015-12-28 09:20:11 +01:00
Julien Fontanet
22bd87c965 Update ldapjs to 1.0. 2015-12-28 09:20:11 +01:00
Julien Fontanet
2129645f39 Babel 6. 2015-12-28 09:20:11 +01:00
Julien Fontanet
93a07b6207 Typo. 2015-12-28 09:20:09 +01:00
Julien Fontanet
b2a51bd658 0.7.2 2015-12-18 11:29:08 +01:00
Julien Fontanet
e5ab1dc154 Fix opaque ref detection in arrays. 2015-12-18 11:28:58 +01:00
Julien Fontanet
6274969635 0.7.1 2015-12-18 11:23:29 +01:00
Julien Fontanet
069c430346 Fix opaque ref detection. 2015-12-18 11:23:24 +01:00
Julien Fontanet
cbcc4dd21d 0.7.0 2015-12-17 16:33:55 +01:00
Julien Fontanet
b4cdf4d277 Minor optimizations. 2015-12-16 14:19:30 +01:00
Julien Fontanet
716d7bfcf6 Optimize opaque refs detection. 2015-12-16 14:15:37 +01:00
Julien Fontanet
b45a169a2f Read only mode can be altered after initial construction. 2015-12-16 13:58:33 +01:00
Julien Fontanet
d98a457271 0.0.2 2015-12-03 16:34:23 +01:00
Julien Fontanet
ef3a0d881f Add empty file to make Travis not fail. 2015-12-03 16:34:13 +01:00
Julien Fontanet
669a9e5cc3 Merge pull request #1 from Phlogi/patch-1
Fix required field, changed from pass to password
2015-12-03 16:32:31 +01:00
Phlogi
5eb58ae6cc Fix required field, changed from pass to password
this one was missed on 7af5964f13
2015-12-03 16:29:26 +01:00
wescoeur
7fa1923400 0.1.1 2015-12-03 16:24:14 +01:00
wescoeur
4c165bd620 Package is not private. 2015-12-03 16:24:05 +01:00
wescoeur
7c04a455b4 0.1.0 2015-12-03 16:22:16 +01:00
ABHAMON Ronan
06b6061518 Update README.md 2015-12-03 16:19:49 +01:00
wescoeur
3821ee3dcd Update package.json description. 2015-12-03 16:11:58 +01:00
wescoeur
03a33646d6 0.2.1 2015-12-03 15:54:24 +01:00
wescoeur
791183553e Update README 2015-12-03 15:53:19 +01:00
wescoeur
de6ef49043 0.2.0 2015-12-03 15:43:35 +01:00
Julien Fontanet
720b9ef999 0.6.9 2015-12-03 12:40:31 +01:00
Julien Fontanet
9b9e4dddfc Gracefully fails if error.res does not exists. 2015-12-03 12:39:59 +01:00
Julien Fontanet
7434e0352f 0.6.8 2015-12-02 17:37:28 +01:00
Julien Fontanet
26d61af902 Fall back to legacy events on server failure (work around #2). 2015-12-02 17:36:29 +01:00
Julien Fontanet
5bd12c5f9e Fix XML-RPC error display. 2015-12-02 17:31:13 +01:00
Julien Fontanet
e07fae4290 Revert to Babel 5 for now. 2015-12-02 17:30:42 +01:00
wescoeur
28bf7ee90b Update schema with titles/descriptions on mails/xmpp attributes. 2015-12-02 15:47:33 +01:00
wescoeur
4d1ca7ede4 Merge remote-tracking branch 'refs/remotes/origin/master' 2015-12-02 15:42:27 +01:00
wescoeur
f3b46515c5 Update for Xmpp support 2015-12-02 15:40:40 +01:00
wescoeur
0aa5e7ba63 Use {...conf} instead of conf (Frozen object) 2015-12-02 15:39:47 +01:00
wescoeur
8d8bf43b46 to parameter is now an array of xmpp address 2015-12-02 15:27:51 +01:00
wescoeur
9983407c8b Make the client in load. 2015-12-02 15:00:38 +01:00
wescoeur
2471ad4215 Plugin works with few options 2015-12-02 14:33:50 +01:00
wescoeur
f266982560 Use node-xmpp-client for the transport protocol 2015-12-02 10:46:24 +01:00
Julien Fontanet
c059a416f7 0.1.1 2015-12-01 15:54:42 +01:00
Olivier Lambert
82dc0c0593 Update README.md 2015-12-01 14:43:17 +01:00
Olivier Lambert
5faad3db92 Update the documentation 2015-11-30 19:15:01 +01:00
Olivier Lambert
099db6792a Update the documentatioh 2015-11-30 18:58:56 +01:00
wescoeur
74a31f3301 Add schema 2015-11-27 16:47:16 +01:00
wescoeur
f88c0b9b67 Init 2015-11-27 14:51:54 +01:00
wescoeur
61ef313b1c 0.1.0 2015-11-27 14:45:04 +01:00
Julien Fontanet
048cea19b7 0.0.1 2015-11-27 10:59:41 +01:00
Julien Fontanet
3e77c76c34 Make config entry from required. 2015-11-27 10:59:08 +01:00
Julien Fontanet
9b6c5d2ea3 Rename config entry pass to password. 2015-11-27 10:58:56 +01:00
wescoeur
2fa081a4ba Plugin supports now Disaster recovery jobs 2015-11-26 15:47:25 +01:00
Julien Fontanet
5b9a3ca1cb This module is no longer private. 2015-11-26 15:22:32 +01:00
Julien Fontanet
bf7c56a269 Remove unnecessary new line. 2015-11-26 15:21:40 +01:00
Julien Fontanet
d33af742dd Initial commit. 2015-11-26 15:19:09 +01:00
wescoeur
823879e9f9 0.0.2 2015-11-26 11:12:08 +01:00
ABHAMON Ronan
98eb285e14 Update README.md 2015-11-26 11:11:16 +01:00
wescoeur
37fd2e1103 0.0.1 2015-11-26 10:57:16 +01:00
ABHAMON Ronan
56db5dc341 Update README 2015-11-26 10:39:48 +01:00
ABHAMON Ronan
d48fa235b1 Update README 2015-11-25 17:41:17 +01:00
wescoeur
06a111495b Update package.json with plugin infos 2015-11-25 17:23:52 +01:00
wescoeur
f3fb0797bf Schema description fix 2015-11-25 17:14:58 +01:00
wescoeur
561b8f4eed Coding style fixes 2015-11-25 17:13:29 +01:00
wescoeur
8cfc6f0b1d Add schema + Plugin use now an array of receivers 2015-11-25 16:36:08 +01:00
wescoeur
7e04f26f78 Send email to one address 2015-11-25 16:11:27 +01:00
Julien Fontanet
348c30b61e Use Babel 5. 2015-11-25 15:00:41 +01:00
Julien Fontanet
ad1e60e137 Fix transport config. 2015-11-25 14:21:29 +01:00
Julien Fontanet
97e0a983f1 Fix schema. 2015-11-25 14:19:41 +01:00
Julien Fontanet
45681e645b Revert to Babel 5. 2015-11-25 10:21:02 +01:00
Julien Fontanet
a4e9f1a683 Remove unused code. 2015-11-24 11:48:31 +01:00
Julien Fontanet
f8c74daef5 Use object spread instead of altering passed object. 2015-11-24 11:47:53 +01:00
Julien Fontanet
b3a593afd7 Test on Node stable. 2015-11-24 11:47:31 +01:00
Julien Fontanet
d45461bc47 Various updates. 2015-11-23 16:24:47 +01:00
wescoeur
58aa2b6a49 Check backup calls 2015-11-20 11:30:01 +01:00
wescoeur
fb06905c86 Fix NoSuchObject exception 2015-11-20 11:07:47 +01:00
wescoeur
4a2911557d Fix remove listener 2015-11-20 10:31:29 +01:00
wescoeur
99caa5dddc Load/unload implem. 2015-11-20 10:13:12 +01:00
Julien Fontanet
86b42a3716 Initial commit. 2015-11-19 18:22:57 +01:00
wescoeur
12c4680501 Initial commit 2015-11-19 17:22:55 +01:00
Julien Fontanet
b7e05c236f Initial commit. 2015-11-19 16:59:06 +01:00
Julien Fontanet
e304395179 Work around ESLint bug. 2015-11-11 15:58:15 +01:00
Julien Fontanet
6b83130853 Various updates. 2015-11-11 15:52:12 +01:00
Julien Fontanet
9565718699 Update deps. 2015-11-11 15:52:06 +01:00
Julien Fontanet
0f03208aa1 Commands filtering. 2015-10-30 11:30:22 +01:00
Julien Fontanet
d58add18fc Document --list-objects 2015-10-28 14:47:24 +01:00
Julien Fontanet
3a0413d8bb Fix coding style. 2015-10-28 12:54:52 +01:00
Julien Fontanet
9122f9b291 Always use session.signIn(). 2015-10-28 12:46:51 +01:00
Julien Fontanet
d279db2a0e Update deps. 2015-10-28 12:42:20 +01:00
Julien Fontanet
c6657b9619 Sets the length when/if known on download. 2015-10-27 18:47:44 +01:00
Julien Fontanet
80d8388eb6 Update deps. 2015-10-27 16:12:09 +01:00
Julien Fontanet
b1ee4bdc09 Standard coding style. 2015-10-27 12:27:57 +01:00
Julien Fontanet
ac11885379 0.6.7 2015-10-23 16:53:53 +02:00
Julien Fontanet
277669a13c Fix events watching XenServer < 6.0. 2015-10-23 16:53:42 +02:00
Julien Fontanet
fcbc476462 Fix coding style. 2015-10-23 16:53:05 +02:00
Julien Fontanet
4944b415c7 0.6.6 2015-10-23 16:03:02 +02:00
Julien Fontanet
5da7312d2d Correctly publish .mocha.js 2015-10-23 16:02:42 +02:00
Julien Fontanet
954d19fe50 Fix objects collection in read-only mode. 2015-10-23 15:33:22 +02:00
Julien Fontanet
addd86f5d2 Better stack traces in CLI. 2015-10-23 15:33:02 +02:00
Julien Fontanet
1b90223210 0.6.5 2015-10-23 14:54:02 +02:00
Julien Fontanet
95989ff63b Add find() and findAll() in CLI. 2015-10-23 14:51:25 +02:00
Julien Fontanet
799f758dce Document constructor options. 2015-10-20 15:46:50 +02:00
Julien Fontanet
5b782993fd Fix the license. 2015-10-12 15:39:26 +02:00
Julien Fontanet
138e60e77c 0.4.2 2015-10-12 15:38:50 +02:00
Julien Fontanet
9771402c54 No need to reload the plugin after a configure. 2015-10-12 15:38:34 +02:00
Julien Fontanet
30dcb4d8d2 Should be globally installed. 2015-10-12 15:31:09 +02:00
Julien Fontanet
c418c766d8 This test is no longer relevant. 2015-10-12 10:26:45 +02:00
Julien Fontanet
334d843955 Move defaults to the schema. 2015-10-12 10:16:45 +02:00
Julien Fontanet
2e5169eb46 0.4.1 2015-10-09 18:55:23 +02:00
Julien Fontanet
3cdc1c03c3 0.1.1 2015-10-09 18:54:19 +02:00
Julien Fontanet
733c619b1f 0.2.1 2015-10-09 18:53:13 +02:00
Julien Fontanet
2021b644c0 0.4.1 2015-10-09 18:52:57 +02:00
Julien Fontanet
f55ed13bd2 0.2.0 2015-10-09 17:57:27 +02:00
Julien Fontanet
14b7072b5b 0.1.0 2015-10-09 17:56:59 +02:00
Julien Fontanet
98395abc17 0.4.0 2015-10-09 17:56:37 +02:00
Julien Fontanet
5db5c4e52c 0.4.0 2015-10-09 17:56:00 +02:00
Julien Fontanet
b31f55063d New plugin API. 2015-10-08 13:56:03 +02:00
Julien Fontanet
71e77ad45a New plugin API. 2015-10-08 13:55:57 +02:00
Julien Fontanet
25873e0e02 New plugin API. 2015-10-08 13:55:26 +02:00
Julien Fontanet
22638a8147 New plugin API. 2015-10-08 13:54:33 +02:00
Julien Fontanet
e075f1c08b 0.6.4 2015-10-06 14:35:33 +02:00
Julien Fontanet
7e0aa719b4 Use Collection.unset() instead of remove() to avoid exceptions. 2015-10-02 14:05:26 +02:00
Julien Fontanet
ce7bc9f438 Correctly export isObject(). 2015-10-02 12:57:51 +02:00
Julien Fontanet
43a362d0eb Use constants for code robustness & performance. 2015-10-02 12:56:39 +02:00
Julien Fontanet
7d7e6e10b9 Properly use Object.prototype.hasOwnProperty(). 2015-10-02 12:27:18 +02:00
Julien Fontanet
73821b0f12 Isolate isObject(). 2015-10-02 12:26:54 +02:00
Julien Fontanet
2071a7d308 Use kindof instead of typeof where appropriate. 2015-10-01 20:47:32 +02:00
Julien Fontanet
c439daadad Minor opt. 2015-10-01 20:43:27 +02:00
Julien Fontanet
083f325076 Temporary work around for eslint/eslint#3946. 2015-10-01 20:31:43 +02:00
Julien Fontanet
ee53433dcc Use const wherever possible. 2015-10-01 18:25:01 +02:00
Julien Fontanet
ad10d13a75 Update standard. 2015-10-01 18:23:17 +02:00
Julien Fontanet
4fd9639457 0.7.3 2015-09-24 16:40:31 +02:00
Julien Fontanet
2f2ee1f431 Update ws 0.8.0 to support Node 4. 2015-09-24 16:39:54 +02:00
Julien Fontanet
ad06c8147f Initial commit. 2015-09-22 17:27:57 +02:00
Julien Fontanet
a2f5f1cb0e 0.5.0 2015-09-21 18:13:13 +02:00
Julien Fontanet
1fbe7d92eb Add --list-objects (fix #8 and fix #9). 2015-09-21 18:13:04 +02:00
Julien Fontanet
760974c7c7 Clearer explanation for the callback URL. 2015-09-21 10:30:53 +02:00
Julien Fontanet
e1587d11b1 FIXME: certificates reading should be async. 2015-09-21 10:23:48 +02:00
Julien Fontanet
0595360808 Update deps. 2015-09-18 14:51:34 +02:00
Julien Fontanet
1a8149e456 This package should be installed globally. 2015-09-18 14:51:18 +02:00
Julien Fontanet
fd6f92f6b5 0.3.1 2015-09-16 11:57:52 +02:00
Julien Fontanet
ddf7226ba8 Minor fixes in the README. 2015-09-16 11:57:44 +02:00
Julien Fontanet
4ee352fdb2 Always use instances of Error for errors. 2015-09-14 16:45:17 +02:00
Julien Fontanet
96ea3ded4a Better stacktraces in CLI. 2015-09-14 16:02:09 +02:00
Julien Fontanet
8bbc6e9ff5 Initial read only mode (fix #3). 2015-09-14 16:01:46 +02:00
Julien Fontanet
af7029812c 0.6.3 2015-09-11 15:32:16 +02:00
Julien Fontanet
84c9532456 0.1.0 2015-09-11 10:45:02 +02:00
Julien Fontanet
aa7c9bca46 Test with Node 4. 2015-09-11 10:44:32 +02:00
Julien Fontanet
c517b59138 Enable testing on Node 4. 2015-09-10 15:22:09 +02:00
Julien Fontanet
0304d6079d GitHub screenshot. 2015-09-07 17:24:27 +02:00
Julien Fontanet
ae41e64999 Fix passport-github links. 2015-09-07 17:02:57 +02:00
Julien Fontanet
8c9f32c927 Initial commit. 2015-09-07 17:01:37 +02:00
Julien Fontanet
5485e8a322 Test on iojs 3 and use containers on Travis. 2015-09-07 16:23:33 +02:00
Julien Fontanet
2540ac34b3 Be verbose on XML-RPC errors. 2015-08-28 08:51:38 +02:00
Julien Fontanet
76e5d41a34 Upgrade deps. 2015-08-28 08:51:15 +02:00
Julien Fontanet
2c32a4e912 0.6.2 2015-08-10 15:52:55 +02:00
Julien Fontanet
c66f7235b6 Fix master change. 2015-08-10 15:51:45 +02:00
Julien Fontanet
5444381f7d 0.6.1 2015-06-30 17:20:31 +02:00
Julien Fontanet
dc44679031 Optimize and clean _watchEvents() & _watchEventsLegacy(). 2015-06-30 14:24:43 +02:00
Julien Fontanet
2cbd17b745 Better traces. 2015-06-30 11:07:33 +02:00
Julien Fontanet
8de2066634 0.4.0 2015-06-26 12:01:51 +02:00
Julien Fontanet
dfc312c092 Various updates. 2015-06-26 11:57:41 +02:00
Julien Fontanet
ce15dbf31b New Collection#unset(). 2015-06-26 11:20:50 +02:00
Julien Fontanet
9ef13696d8 0.6.0 2015-06-23 10:39:55 +02:00
Julien Fontanet
c3f635fd12 Ask JSON encoded values to XenApi.
The XML is therefore much faster to parse (3-4 ×).
2015-06-23 10:39:04 +02:00
Julien Fontanet
e3d1380435 0.5.7 2015-06-23 10:38:41 +02:00
Julien Fontanet
f83737b538 Make objects immutable. 2015-06-23 09:17:42 +02:00
Julien Fontanet
bb1ea4e4d0 Optimization for empty arrays. 2015-06-23 09:13:43 +02:00
Julien Fontanet
9cb4de2ea8 Inline an only-used-once function. 2015-06-22 23:33:52 +02:00
Julien Fontanet
048cbf60ec Added tested Xen Server versions in README. 2015-06-22 16:23:38 +02:00
Julien Fontanet
36f40b4188 Support Xen Server < 6. 2015-06-22 16:19:32 +02:00
Julien Fontanet
ea1afb260a Update README.md 2015-06-22 14:00:35 +02:00
Julien Fontanet
a3bba92063 Remove duplicate source-map-support. 2015-06-19 15:53:23 +02:00
Julien Fontanet
ebcc6c9341 Remove unused lodash.{find,findkey,size}. 2015-06-19 15:51:22 +02:00
Julien Fontanet
95f765055e Fix invalid session handling. 2015-06-19 15:15:23 +02:00
Julien Fontanet
49aa5ffccc Password can be supplied on the command line. 2015-06-19 15:13:21 +02:00
Julien Fontanet
d09d3fa80b 0.5.6 2015-06-18 13:40:18 +02:00
Julien Fontanet
4c8cd50643 Better build & tests. 2015-06-18 13:15:27 +02:00
Julien Fontanet
eee72f4f27 Test on both io.js v1 & v2. 2015-06-18 13:13:40 +02:00
Julien Fontanet
45f6a7cb4d Minor changes. 2015-06-17 17:09:46 +02:00
Julien Fontanet
8866bd8663 Minor code simplification. 2015-06-17 15:53:48 +02:00
Julien Fontanet
3f9c515f1d Better error messages. 2015-06-17 15:39:33 +02:00
Julien Fontanet
e9e0fdae37 Run linter with npm test. 2015-06-16 17:53:47 +02:00
Julien Fontanet
124f7f43ab Minor changes. 2015-06-16 17:53:47 +02:00
Julien Fontanet
27df44bf44 Use more ES6/7 features. 2015-06-16 16:09:40 +02:00
Julien Fontanet
b934a7de6a npm run lint 2015-06-16 16:00:29 +02:00
Julien Fontanet
d521c75085 Minor changes. 2015-06-16 16:00:17 +02:00
Julien Fontanet
5e18b6b878 Improve Babel configuration. 2015-06-16 15:57:12 +02:00
Julien Fontanet
3183ca02b3 Improve tests execution. 2015-06-16 15:56:36 +02:00
Julien Fontanet
60a278490f Fix coding style. 2015-06-16 12:49:22 +02:00
Julien Fontanet
b78e74cdf6 Use a valid SPDX license. 2015-06-16 12:46:58 +02:00
Julien Fontanet
f61a16074b Update deps. 2015-06-16 12:46:11 +02:00
Julien Fontanet
82766d1645 Update README.md 2015-06-16 12:39:54 +02:00
Julien Fontanet
725f471a6a 0.7.2 2015-05-28 15:36:49 +02:00
Julien Fontanet
0b01a79d9d Implements object.messages. 2015-05-28 15:36:33 +02:00
Julien Fontanet
2653ff6536 Comment currently unused declarations. 2015-05-28 15:36:04 +02:00
Julien Fontanet
0f30cc8e59 Work around linter issue (eslint/espree#136). 2015-05-28 15:35:27 +02:00
Julien Fontanet
e3879cd4d1 Minor style fix. 2015-05-28 15:34:41 +02:00
Julien Fontanet
7a4cdf8688 Fix connection errors handling in Api#connect(). 2015-05-28 15:33:49 +02:00
Julien Fontanet
c92567d4fa 0.5.5 2015-05-27 17:04:04 +02:00
Julien Fontanet
1839bf938a 0.7.1 2015-05-27 17:02:25 +02:00
Julien Fontanet
44d4096a79 Remove a console.log(). 2015-05-27 14:07:14 +02:00
Julien Fontanet
41280c9d38 Various updates. 2015-05-27 12:18:41 +02:00
Julien Fontanet
df3c76fa72 Remove useless statement. 2015-05-26 16:47:14 +02:00
Julien Fontanet
cea4157402 Perf traces in the CLI. 2015-05-26 16:35:51 +02:00
Julien Fontanet
7c54adec9d 0.3.2 2015-05-24 14:41:22 +02:00
Julien Fontanet
68abd91fc2 Api#close() returns a promise. 2015-05-24 14:41:01 +02:00
Julien Fontanet
4d2e42d244 One more Angular work around. 2015-05-24 14:39:48 +02:00
Julien Fontanet
5a87a6c502 This lib is not ES6! 2015-05-22 16:41:33 +02:00
Julien Fontanet
d8ca15ceb3 Fix objects removal. 2015-05-22 15:12:22 +02:00
Julien Fontanet
a17f718517 Update xo-collection to 0.3.1. 2015-05-22 14:53:52 +02:00
Julien Fontanet
3589dda8ee Remove ugly hack. 2015-05-22 14:52:27 +02:00
Julien Fontanet
21f8e4d55b Remove unused UUID index. 2015-05-22 14:52:19 +02:00
Julien Fontanet
811e0123c9 0.3.1 2015-05-22 14:46:46 +02:00
Julien Fontanet
47c4516060 Index & UniqueIndex: Correctly updates even if the hash has not changed. 2015-05-22 14:46:20 +02:00
Julien Fontanet
29ce3bd05e Source maps support in the CLI. 2015-05-22 10:38:38 +02:00
Julien Fontanet
b3d58f4f0c Update to xo-collection 0.3. 2015-05-22 10:38:08 +02:00
Julien Fontanet
13913334b6 0.7.0 2015-05-19 16:48:19 +02:00
Julien Fontanet
7f60725c88 Make it work with current version of xo-web. 2015-05-19 16:48:03 +02:00
Julien Fontanet
d55fb36182 0.3.0 2015-05-19 16:39:54 +02:00
Julien Fontanet
41205aef20 Work around xo-web bug due to Angular. 2015-05-19 16:38:02 +02:00
Julien Fontanet
aeadbc1d58 Minor standardization. 2015-05-19 16:35:23 +02:00
Julien Fontanet
bd12ade426 UniqueIndex only expose the values of contained items. 2015-05-19 16:34:48 +02:00
Julien Fontanet
f9c26089cd Document exceptions. 2015-05-19 15:25:44 +02:00
Julien Fontanet
7ddb57078c Finish event. 2015-05-19 15:25:30 +02:00
Julien Fontanet
3e7f1275d8 Remove unnecessary array. 2015-05-19 14:43:16 +02:00
Julien Fontanet
e963938016 Do not attach collection to index if duplicate. 2015-05-19 14:42:11 +02:00
Julien Fontanet
312fcea5f1 Auto links. 2015-05-15 18:08:21 +02:00
Julien Fontanet
9d05653f5b Use xo-collection. 2015-05-14 17:19:28 +02:00
Julien Fontanet
644ebd0a4f 0.2.1 2015-05-14 17:18:12 +02:00
Julien Fontanet
1033bfcfe5 Add missing files. 2015-05-14 17:16:35 +02:00
Julien Fontanet
d93d234c71 Fix Travis badge. 2015-05-14 15:06:27 +02:00
Julien Fontanet
7fe9ae8a04 Document custom props. 2015-05-14 15:04:55 +02:00
Julien Fontanet
87cf1ed7cb All custom properties are read-only and non enumerable. 2015-05-14 14:58:54 +02:00
Julien Fontanet
a0ba5c8a57 Fix auto links for arrays. 2015-05-14 14:58:27 +02:00
Julien Fontanet
d7208a15d9 Remove unused dep. 2015-05-14 14:46:06 +02:00
Julien Fontanet
debde0c67a Links do not shadow refs. 2015-05-14 14:39:36 +02:00
Julien Fontanet
97db55156a $pool should not be enumerable. 2015-05-13 17:29:24 +02:00
Julien Fontanet
9d3477d465 Fix propagation of Xapi errors. 2015-05-13 17:27:22 +02:00
Julien Fontanet
031af000e6 Alias Object.create() and Object.defineProperty() for perf. 2015-05-06 18:14:43 +02:00
Julien Fontanet
0512fac3aa Better name for the OpaqueRef regex constant. 2015-05-06 18:14:20 +02:00
Julien Fontanet
4272e8196a Bypass Xapi#objects getter internally for perf. 2015-05-06 14:08:59 +02:00
Julien Fontanet
140f9d05df Code style and comments. 2015-05-06 14:06:25 +02:00
Julien Fontanet
9222733243 Xapi#getObject() 2015-05-06 14:06:17 +02:00
Julien Fontanet
5838c56c4e Xapi#getObjectByRef() and Xapi#getObjectByUuid() 2015-05-06 13:27:59 +02:00
Julien Fontanet
1814e0a260 0.5.4 2015-05-05 13:46:03 +02:00
Julien Fontanet
711c5781e6 Export wrapError(). 2015-05-05 13:45:51 +02:00
Julien Fontanet
a688310b95 Remove thisArg param. 2015-05-04 17:34:02 +02:00
Julien Fontanet
e3ffc8784e Use shared module instead of custom isNotEmpty(). 2015-04-30 11:45:14 +02:00
Julien Fontanet
bb35fc3801 Fix README title. 2015-04-30 11:44:53 +02:00
Julien Fontanet
c804630576 0.2.0 2015-04-29 18:17:39 +02:00
Julien Fontanet
e5f3ca1623 Initial UniqueIndex. 2015-04-29 17:51:18 +02:00
Julien Fontanet
0880787d68 new Index() now accepts a computeHash() callback-ish. 2015-04-29 14:33:37 +02:00
Julien Fontanet
cd582e2e3a Rename Index#itemsByHash to Index#items. 2015-04-29 14:12:22 +02:00
Julien Fontanet
aebd9319f5 Comments in Collection#_onUpdate(). 2015-04-29 13:22:36 +02:00
Julien Fontanet
de6cbb0f45 Fix Index#sweep(). 2015-04-29 13:22:36 +02:00
Julien Fontanet
e14dcd0184 Put some utils in their own modules. 2015-04-29 13:22:30 +02:00
Julien Fontanet
17ef653903 0.1.1 2015-04-28 17:10:58 +02:00
Julien Fontanet
f5d5b5efc0 Make View and Index requireable. 2015-04-28 17:10:31 +02:00
Julien Fontanet
59dbee8f28 Minor doc about views. 2015-04-28 17:06:37 +02:00
Julien Fontanet
4db6971cc4 0.3.2 2015-04-28 14:06:08 +02:00
Julien Fontanet
71482bd06c Correctly build the package. 2015-04-28 14:05:47 +02:00
Julien Fontanet
c3acf8341b 0.3.1 2015-04-28 13:25:03 +02:00
Julien Fontanet
1bc48fbf96 0.1.0 2015-04-27 15:27:12 +02:00
Julien Fontanet
d45348c167 Minor fixes. 2015-04-27 15:09:07 +02:00
Julien Fontanet
22caa0ee66 Initial Index implementation (see #1). 2015-04-27 15:05:02 +02:00
Julien Fontanet
e6e8ccc855 Remove standard from npm test.
Currently broken du to due to eslint/espree#123
2015-04-27 15:04:52 +02:00
Julien Fontanet
d78522f5e1 Comment doc for Collection#getId(). 2015-04-27 12:14:30 +02:00
Julien Fontanet
3da2a618b9 Coding style. 2015-04-27 10:55:22 +02:00
Julien Fontanet
047fa5b2db Fix checkCertificate (thx @wrigby). 2015-04-26 11:16:15 +02:00
Julien Fontanet
c763794ef3 Install json-rpc from the npm repository. 2015-04-24 11:21:15 +02:00
Julien Fontanet
7a4dcd52c4 0.3.0 2015-04-21 17:52:07 +02:00
Julien Fontanet
e8e7a92131 Minor README updates. 2015-04-21 17:48:26 +02:00
Julien Fontanet
7e8c2211d8 The REPL waits for promise completion. 2015-04-21 17:20:38 +02:00
Julien Fontanet
99694161e1 The REPL waits for promise completion. 2015-04-21 17:17:00 +02:00
Julien Fontanet
f0858b7d93 0.5.3 2015-04-20 19:14:48 +02:00
Julien Fontanet
3af6c28ab0 Do not swallow all errors. 2015-04-20 19:01:05 +02:00
Julien Fontanet
5c31c7f14c Typo! 2015-04-20 19:00:52 +02:00
Julien Fontanet
2610a9c777 0.5.2 2015-04-20 15:19:06 +02:00
Julien Fontanet
58cf611497 Fix this._sessionId. 2015-04-20 15:18:48 +02:00
Julien Fontanet
00f944f3f4 Remove unused import. 2015-04-19 00:42:02 +02:00
Julien Fontanet
1269411771 Add iteration to the README. 2015-04-18 22:33:32 +02:00
Julien Fontanet
d4d8ea6cf2 Collection #getId() renamed to getKey(). 2015-04-18 22:20:57 +02:00
Julien Fontanet
160522c520 Collection iteration more similar to Map. 2015-04-18 22:04:37 +02:00
Julien Fontanet
7024b5ec1b Reorganization of Collection code. 2015-04-18 21:58:17 +02:00
Julien Fontanet
5b020035d6 Collection is iterable. 2015-04-18 21:34:36 +02:00
Julien Fontanet
fcea7fd4bf mkdir -p is more portable. 2015-04-18 19:37:04 +02:00
Julien Fontanet
37e5bcad61 Minor style fixes. 2015-04-18 12:37:02 +02:00
Julien Fontanet
20679a62fd Merge pull request #1 from wrigby/tls_options
Ability to ignore SSL/TLS certs errors and to specify custom CAs.
2015-04-17 21:15:10 +02:00
Julien Fontanet
bb5a5bf2ed Merge pull request #2 from wrigby/fix-osx-build
`mkdir -p` is more portable
2015-04-17 21:02:36 +02:00
Julien Fontanet
c1db993b92 Fix missing quotes. 2015-04-17 16:25:18 +02:00
Julien Fontanet
61631e405b Coding style. 2015-04-17 16:22:23 +02:00
Julien Fontanet
c19916ff1c Add CLI. 2015-04-17 14:05:49 +02:00
Julien Fontanet
6fa2e79c1c Use standard code style. 2015-04-17 13:35:33 +02:00
Julien Fontanet
185e0849b1 0.5.1 2015-04-17 12:04:06 +02:00
Julien Fontanet
f48b9d364b Shebang and executable mode for cli.js 2015-04-17 12:04:01 +02:00
Julien Fontanet
e4f1a7d4c1 0.5.0 2015-04-17 11:58:08 +02:00
Julien Fontanet
e02f19ff67 Typo. 2015-04-17 11:58:05 +02:00
Julien Fontanet
72a2110845 Add CLI. 2015-04-17 10:38:15 +02:00
Julien Fontanet
9baa415249 0.4.0 2015-04-16 16:44:05 +02:00
Julien Fontanet
22b840af14 Add a description. 2015-04-16 16:43:15 +02:00
Julien Fontanet
61f32d89ca Declare Xapi#_pool in the constructor for perf. 2015-04-16 16:34:09 +02:00
Julien Fontanet
3c7da93dfc Xapi#_poolId is no longer used. 2015-04-16 16:33:45 +02:00
Julien Fontanet
5831616fac Merge branch 'linked-objects' 2015-04-16 16:30:46 +02:00
Julien Fontanet
d7b6d9f124 Objects are now linked together!
```javascript
const {pool} = xapi

console.log(pool.master.name_label)
```
2015-04-16 16:22:01 +02:00
Will Rigby
6e7588e9fc Fix npm scripts on OS X
BSD's mkdir doesn't support the long --parents flag.
2015-04-16 01:03:20 -04:00
Will Rigby
03cc8248bc Update documentation 2015-04-16 00:59:29 -04:00
Will Rigby
068df6f2b1 Support specifying path(s) to CA certificate(s)
Wires the 'ca_certificates' configuration option
through to tls.connect's 'ca' option.
2015-04-16 00:48:04 -04:00
Will Rigby
0966ba909b Allow connecting to SSL-secured servers with self-signed certificates
Adds a new configuration parameter ('check_certificate'),
which gets wired up to the rejectUnauthorized option of
tls.connect.
2015-04-16 00:25:55 -04:00
Julien Fontanet
245978e2b3 0.3.1 2015-04-15 18:27:02 +02:00
Julien Fontanet
3aae60bde9 Better handling of transport call retries. 2015-04-15 18:23:49 +02:00
Julien Fontanet
7941a24d51 Force npm test to be successful before committing. 2015-04-15 15:16:28 +02:00
Julien Fontanet
91d36122eb Rmove unused JSHint conf. 2015-04-15 10:46:25 +02:00
Julien Fontanet
e004ba63f8 Remove unused JSHint conf. 2015-04-15 10:46:09 +02:00
Julien Fontanet
36c1e2cc73 Limit tries in case of transport errors (ugly). 2015-04-14 17:57:31 +02:00
Julien Fontanet
4a0a09ba3e Update to latest make-error. 2015-04-14 17:56:51 +02:00
Julien Fontanet
1f30a19566 Improved view example. 2015-04-14 12:18:29 +02:00
Julien Fontanet
51f4578a41 View handles existing items. 2015-04-14 12:18:24 +02:00
Julien Fontanet
bd3954a5f1 Rename index.* to collection.*. 2015-04-14 10:47:47 +02:00
Julien Fontanet
94967add7c Merge branch 'views' 2015-04-14 10:25:49 +02:00
Julien Fontanet
783ab0b611 Initial view implementation (fix #3). 2015-04-13 20:23:43 +02:00
Julien Fontanet
04b44cff2b 0.3.0 2015-04-13 17:44:55 +02:00
Julien Fontanet
8309755ee3 Expose session identifier. 2015-04-13 17:44:05 +02:00
Julien Fontanet
41a75d404c 0.2.1 2015-04-13 16:48:45 +02:00
Julien Fontanet
8eb63de201 Stupid fix -_-". 2015-04-13 16:48:32 +02:00
Julien Fontanet
1dec134a6b 0.2.0 2015-04-13 16:42:58 +02:00
Julien Fontanet
6f6a8e4bb1 Do not require params to be in an array. 2015-04-13 16:42:30 +02:00
Julien Fontanet
87d216c578 0.1.1 2015-04-13 16:17:53 +02:00
Julien Fontanet
bb0ee61870 Various fixes. 2015-04-13 16:16:30 +02:00
Julien Fontanet
653a9526f5 0.0.1 2015-04-13 14:54:19 +02:00
Julien Fontanet
34ac4b25af Test and fix #clear(). 2015-04-13 14:53:25 +02:00
Julien Fontanet
c7b7f9236f Default to secure if no protocol. 2015-04-13 14:13:56 +02:00
Julien Fontanet
881779744f 0.1.0 2015-04-13 13:34:31 +02:00
Julien Fontanet
ddf3356b5d Expose the pool object. 2015-04-13 13:27:29 +02:00
Julien Fontanet
94e8f3e2c1 Initial doc. 2015-04-10 17:12:59 +02:00
Julien Fontanet
2c4a7f48d1 Update package.json & README. 2015-04-10 16:49:00 +02:00
Julien Fontanet
af47c23ca1 Swallow any errors in #_watchEvents(). 2015-04-10 16:45:40 +02:00
Julien Fontanet
326a001c57 More log. 2015-04-10 16:41:59 +02:00
Julien Fontanet
fe9e548e89 #connect() & #disconnect(). 2015-04-10 16:41:43 +02:00
Julien Fontanet
e58ef1f436 connected/disconnected events. 2015-04-10 16:41:17 +02:00
Julien Fontanet
f249fa6f65 Minor fixes. 2015-04-10 16:40:45 +02:00
Julien Fontanet
68c6b63c9c Host is slave correctly handled. 2015-04-10 16:00:20 +02:00
Julien Fontanet
a249597225 Skip failing test for now. 2015-04-10 15:36:47 +02:00
Julien Fontanet
4906c677af Remove unused code. 2015-04-10 15:35:39 +02:00
Julien Fontanet
2534a9f14a _fetchObjects() is not necessary. 2015-04-10 15:34:10 +02:00
Julien Fontanet
5488c2bdeb Various updates. 2015-04-10 15:33:39 +02:00
Julien Fontanet
e072ff2d77 Update README.md 2015-04-10 11:51:54 +02:00
Julien Fontanet
41dfbc2709 Publish to npm as xo-collection. 2015-04-10 11:41:02 +02:00
Julien Fontanet
964e461597 Better Collection#_resolveItem(). 2015-04-10 10:51:01 +02:00
Julien Fontanet
ef2eec4c4a Minor build updates. 2015-04-10 10:50:23 +02:00
Julien Fontanet
bf1d76d853 Source map support for tests. 2015-04-10 10:49:25 +02:00
Julien Fontanet
0682cbd554 Test for Collection#touch(). 2015-04-10 10:49:08 +02:00
Julien Fontanet
f5191cdd42 Better error classes. 2015-04-10 10:48:47 +02:00
Julien Fontanet
b1c73208c5 Do not return this. 2015-04-08 16:33:28 +02:00
Julien Fontanet
ab221a465b Use lodash.foreach instead of native implementation. 2015-04-08 16:32:46 +02:00
Julien Fontanet
4ecfa0477d Optimize Collection#_touch(). 2015-04-08 14:30:16 +02:00
Julien Fontanet
bab2de36ad Tests for events deduplication. 2015-04-08 14:24:07 +02:00
Julien Fontanet
f479e914bb #getId() should behave even if the item is null or undefined. 2015-04-08 12:00:52 +02:00
Julien Fontanet
45441653f6 #bufferChanges() → #bufferEvents(). 2015-04-08 11:51:18 +02:00
Julien Fontanet
0303558ae1 Prefer the term of item over entry. 2015-04-08 11:49:25 +02:00
Julien Fontanet
b70e0e3e2b Tests for #add(), #update(), #set() and #remove() with an object. 2015-04-08 11:40:07 +02:00
Julien Fontanet
5e0c4d7b7a Avoid Function#apply() for performance. 2015-04-08 10:50:04 +02:00
Julien Fontanet
6c83308451 Optimize Collection#get(). 2015-04-08 10:48:27 +02:00
Julien Fontanet
eeb898179e Minor simplification of buffer flushing. 2015-04-08 10:42:00 +02:00
Julien Fontanet
0ea662d8fe Replace let by const where possible. 2015-04-08 10:05:41 +02:00
Julien Fontanet
ea3219fa10 Typo. 2015-04-08 10:04:58 +02:00
Julien Fontanet
fb9203d396 Fix doc: unset() → remove(). 2015-04-07 18:49:20 +02:00
Julien Fontanet
23e7542871 Document Collection#all. 2015-04-07 18:15:38 +02:00
Julien Fontanet
a40832dffd Typo. 2015-04-07 18:06:17 +02:00
Julien Fontanet
5ba7493613 Better repo architecture. 2015-04-07 18:01:33 +02:00
Julien Fontanet
e7b406c127 Initial commit. 2015-04-02 18:44:16 +02:00
Fabrice Marsaud
dd1d16f91c Fixes and enhancements 2015-04-02 16:49:40 +02:00
Julien Fontanet
82e2a19749 0.2.0 2015-04-02 16:34:21 +02:00
Julien Fontanet
b73126e6c1 README updates [skip ci]. 2015-04-02 16:34:11 +02:00
Fabrice Marsaud
cbd93f450e Implementation seems ok. Deeper tests to come 2015-04-02 15:32:08 +02:00
Fabrice Marsaud
35c64be3d7 All tests for "simple" buffer 2015-04-02 10:14:32 +02:00
Fabrice Marsaud
96ea70c027 Buffer implemented and tested 2015-04-01 18:06:23 +02:00
Julien Fontanet
ea277d0579 Add missing dev dep. 2015-04-01 16:35:51 +02:00
Julien Fontanet
10d7cd1520 Build sources in npm test. 2015-04-01 16:30:51 +02:00
Julien Fontanet
9051322338 Remove erroneous paste. 2015-04-01 16:16:35 +02:00
Julien Fontanet
d26b6103b5 Tweak ESLint. 2015-04-01 16:16:22 +02:00
Julien Fontanet
f2e7963e1f New implementation. 2015-04-01 16:08:40 +02:00
Fabrice Marsaud
a3d7e541d3 Events implemented and tested, without buffering 2015-04-01 15:06:39 +02:00
Julien Fontanet
4617025bd4 Travis CI 2015-04-01 10:47:12 +02:00
Julien Fontanet
0693e19605 Unit tests. 2015-04-01 10:47:02 +02:00
Julien Fontanet
62618acfed ES6 & standard style 2015-04-01 10:46:38 +02:00
Julien Fontanet
1feaa43d2e Update license. 2015-04-01 10:45:37 +02:00
Fabrice Marsaud
92d7d61926 Various fixes and enhancements 2015-03-31 17:08:41 +02:00
Fabrice Marsaud
265d77d776 First main methods. No events, no buffer yet 2015-03-31 16:09:05 +02:00
Fabrice Marsaud
48d9fde3b6 Left over solution 2015-03-30 09:31:42 +02:00
Fabrice Marsaud
a9ea1a02ed Use let and const 2015-03-27 16:43:43 +01:00
Fabrice Marsaud
19cd5c8881 Minor renaming 2015-03-27 15:55:37 +01:00
Fabrice Marsaud
cdb9c661bd A first version without unit tests, but a gross test script 2015-03-27 15:55:37 +01:00
Fabrice Marsaud
c3a01c240b First base 2015-03-27 15:55:37 +01:00
Fabrice Marsaud
e2f748e63d Initial commit 2015-03-27 15:51:06 +01:00
Julien Fontanet
52fa4f11ac 0.1.2 2015-03-18 18:21:44 +01:00
Julien Fontanet
bfcabd30c5 ldapjs is a production dependency! 2015-03-18 17:33:33 +01:00
Julien Fontanet
095ea470a1 0.1.1 2015-03-18 17:24:59 +01:00
Julien Fontanet
c9b502c72b Use YAML in config example. 2015-03-18 17:24:36 +01:00
Julien Fontanet
9390eacb7c Remove ES6 syntax. 2015-03-18 17:23:21 +01:00
Julien Fontanet
f193ce87bf 0.1.0 2015-03-18 17:10:58 +01:00
Julien Fontanet
f6b3f898de Various fixes. 2015-03-18 17:07:19 +01:00
Julien Fontanet
de5ba5d0d3 Initial commit. 2015-03-17 16:28:31 +01:00
Julien Fontanet
c539dd5570 0.6.3 2015-03-04 18:23:21 +01:00
Julien Fontanet
de76afea99 New implementation of Xo (fixes many issues). 2015-03-04 18:22:11 +01:00
Julien Fontanet
1d3616ae71 Minor changes. 2015-03-04 17:42:22 +01:00
Julien Fontanet
d76cb440f9 Move BackOff to its own module. 2015-03-04 17:42:18 +01:00
Julien Fontanet
7c89d658f7 Fix setScheduler(). 2015-03-04 17:37:12 +01:00
Julien Fontanet
292c929117 Split code in to multiple files. 2015-03-04 17:31:14 +01:00
Julien Fontanet
daf42b63c8 Better back off implementation. 2015-03-04 16:18:57 +01:00
Julien Fontanet
07da03618f Use specific version of json-rpc. 2015-03-04 15:51:35 +01:00
Julien Fontanet
dda51f2801 Xo::call() should never through synchronously. 2015-03-04 15:01:30 +01:00
Julien Fontanet
25472bcfa6 Correctly catch some errors. 2015-03-04 15:01:03 +01:00
Julien Fontanet
6ff17d16f0 0.6.2 2015-02-23 14:48:39 +01:00
Julien Fontanet
06b7116692 Never break object refs for Collection:all & Collection:indexes[*]. 2015-02-23 14:48:32 +01:00
Julien Fontanet
3c3ea0f3e1 Fix Collection::data. 2015-02-23 14:47:11 +01:00
Julien Fontanet
db4d6511d6 0.6.1 2015-02-23 12:25:53 +01:00
Julien Fontanet
6e42a67268 Update README. 2015-02-23 12:25:30 +01:00
Julien Fontanet
fd066e5eef Handle credentials directly in constructor. 2015-02-23 12:25:17 +01:00
Julien Fontanet
3dd0c44410 Handle string parameter in Xo constructor. 2015-02-23 12:24:47 +01:00
Julien Fontanet
12b42854e4 Delete useless example. 2015-02-23 12:23:42 +01:00
Julien Fontanet
2fcb6d0c7c 0.6.0 2015-02-23 12:11:53 +01:00
Julien Fontanet
68e863723a Explicitely handle sign in/out. 2015-02-23 12:10:45 +01:00
Julien Fontanet
d0b37d0f9a Update example. 2015-02-20 17:15:00 +01:00
Julien Fontanet
a0a1353445 Update collection API. 2015-02-20 17:14:44 +01:00
Julien Fontanet
6725cc6f61 0.5.2 2015-02-12 12:23:56 +01:00
Julien Fontanet
7e9639052b getCurrentUrl() must not ignore protocol and search parts. 2015-02-12 12:23:35 +01:00
Julien Fontanet
21bd5ba376 fixUrl() properly handles search and hash parts. 2015-02-12 12:22:58 +01:00
Julien Fontanet
28d5fb1822 0.5.1 2015-02-11 15:17:51 +01:00
Julien Fontanet
cd3c031df1 Fix back off reset. 2015-02-11 15:17:42 +01:00
Julien Fontanet
a6db0f6fd9 Fix reconnection. 2015-02-11 15:11:23 +01:00
Julien Fontanet
c80f6e8285 Better status during reconnection. 2015-02-11 15:11:03 +01:00
Julien Fontanet
c6d779853a Remove unused require. 2015-02-11 15:10:52 +01:00
Julien Fontanet
1b720a504c 0.5.0 2015-02-11 14:40:19 +01:00
Julien Fontanet
72f8854a7a Remove factory. 2015-02-11 14:39:52 +01:00
Julien Fontanet
097d195f00 Disable session.*() from high level API. 2015-02-11 14:16:38 +01:00
Julien Fontanet
807da8f696 Make Xo.connect() private. 2015-02-11 14:13:01 +01:00
Julien Fontanet
a8ca6b6fcb Do not use assign for methods. 2015-02-11 14:11:38 +01:00
Julien Fontanet
85e2e14c81 README update. 2015-02-11 14:11:10 +01:00
Julien Fontanet
5ae45ddd55 Do not clear objects on disconnect. 2015-02-10 20:13:11 +01:00
Julien Fontanet
4e0a3da01e 0.4.1 2015-02-10 18:57:03 +01:00
Julien Fontanet
541a99bbc5 Fix item removal. 2015-02-10 18:56:32 +01:00
Julien Fontanet
f62008aba4 0.4.0 2015-02-10 18:22:11 +01:00
Julien Fontanet
74f7415f84 Minor changes. 2015-02-10 18:21:53 +01:00
Julien Fontanet
1e1e079b65 Use current location if URL not provided (for browsers). 2015-02-10 18:21:07 +01:00
Julien Fontanet
0b4f808b2d Only emit disconnected event if previously connected. 2015-02-10 18:13:01 +01:00
Julien Fontanet
ae4af99c59 Xo cannot be closed. 2015-02-10 17:53:56 +01:00
Julien Fontanet
0b17556fa4 Auto reconnect. 2015-02-10 17:53:43 +01:00
Julien Fontanet
e6ebc347e5 setScheduler() to ease integration. 2015-02-10 17:53:33 +01:00
Julien Fontanet
f2323a9d19 Various updates. 2015-02-10 17:12:10 +01:00
Julien Fontanet
ce53fe5e31 Fix module exports. 2015-02-10 14:47:16 +01:00
Julien Fontanet
f1d359b3e7 Smart objects collection. 2015-02-10 14:42:56 +01:00
Julien Fontanet
da99f3bc2a High level interface. 2015-02-10 11:40:11 +01:00
Julien Fontanet
fec8dd74af Use json-rpc. 2015-02-05 17:05:49 +01:00
Julien Fontanet
f598e0d0d5 Not available via Bower. 2015-02-05 14:13:06 +01:00
Julien Fontanet
656d2494b0 Travis CI. 2015-02-05 13:51:50 +01:00
Julien Fontanet
6d07d58f37 0.3.0 2015-02-05 13:45:52 +01:00
Julien Fontanet
dd5270f620 Minimalist browser example. 2015-02-05 13:33:21 +01:00
Julien Fontanet
216f895953 Update deps. 2015-02-05 13:33:21 +01:00
Julien Fontanet
6acb87b7ea Update Bluebird. 2015-02-05 13:33:21 +01:00
Julien Fontanet
acdccd697c Fix browser compatibility. 2015-02-05 13:33:21 +01:00
Julien Fontanet
cecc4b1f6d Fix secure URL check. 2015-02-05 13:33:21 +01:00
Julien Fontanet
f93f115e13 Better and tested fixUrl(). 2015-02-05 13:33:16 +01:00
Julien Fontanet
debcf086b5 Code style. 2015-02-05 11:57:05 +01:00
Julien Fontanet
c6db974962 Update jshintrc. 2015-02-05 11:55:00 +01:00
Julien Fontanet
e9b0b0c42e ws supports browser natively. 2015-02-05 11:53:27 +01:00
Julien Fontanet
ebee1a02fd Promise is already defined in ES6. 2015-02-05 11:46:56 +01:00
Julien Fontanet
de05139dfc Use xdg-basedir instead of xdg. 2014-10-08 09:50:34 +02:00
Julien Fontanet
e88d0579b0 Use nice-pipe instead of own code. 2014-09-29 14:53:11 +02:00
Julien Fontanet
5564e4daa2 Minor update. 2014-09-25 15:23:53 +02:00
Julien Fontanet
4742cd4a03 0.4.0 2014-09-25 14:59:54 +02:00
Julien Fontanet
99985f4fab Better progress stats. 2014-09-24 18:18:17 +02:00
Julien Fontanet
42bb3b5aca Always use the POST method. 2014-09-24 17:22:44 +02:00
Olivier Lambert
70bedaf8dd fix by using PUT instead of POST 2014-09-24 17:09:59 +02:00
Julien Fontanet
38683a7fea Syntax update. 2014-09-23 19:16:11 +02:00
Julien Fontanet
ae1a68500c Remove erroneous require. 2014-09-23 17:57:04 +02:00
Julien Fontanet
578f842eed vm.export. 2014-09-23 17:31:29 +02:00
Julien Fontanet
a7dea95f90 Named functions for better stack traces. 2014-09-22 15:35:12 +02:00
Julien Fontanet
1080562d96 Sort requires. 2014-09-22 15:29:45 +02:00
Julien Fontanet
b8cf5a2347 Use per function lodash modules. 2014-09-22 15:29:28 +02:00
Julien Fontanet
e9706d605a Promise is already defined in ES6. 2014-09-22 15:12:14 +02:00
Julien Fontanet
4b338c56b5 Remove dead code. 2014-09-22 15:11:04 +02:00
Julien Fontanet
2974a96e96 0.3.2 2014-07-28 15:34:19 +02:00
Julien Fontanet
ab7fd3d019 README update. 2014-07-28 15:32:55 +02:00
Julien Fontanet
54c5659496 0.3.1 2014-07-28 15:09:12 +02:00
Julien Fontanet
8c0811885f Handle -h as an alias for --help. 2014-07-28 15:09:00 +02:00
Julien Fontanet
291570dfd7 Add colors to the help message. 2014-07-28 15:06:15 +02:00
Julien Fontanet
f6b9b4cc19 0.3.0 2014-07-28 14:47:08 +02:00
Julien Fontanet
a06403ab7c Update to latest xo-lib. 2014-07-28 13:37:07 +02:00
Julien Fontanet
e64a95d1d7 Handle boolean params. 2014-07-28 13:35:30 +02:00
Julien Fontanet
6939e49643 Enable async long traces. 2014-07-28 13:35:19 +02:00
Julien Fontanet
bb18586484 Update JSHint config. 2014-07-28 13:35:05 +02:00
Julien Fontanet
09b39a4bf9 0.2.0 2014-07-28 13:33:45 +02:00
Julien Fontanet
1d7d639654 Set rejectUnauthorized only for secure connection. 2014-07-28 13:33:29 +02:00
Julien Fontanet
42ec509574 Fix URL fixing. 2014-07-28 13:30:57 +02:00
Julien Fontanet
dcef864c1c Fix URL if necessary. 2014-07-28 13:21:40 +02:00
Julien Fontanet
e8cb4f90f4 Update JSHint conf. 2014-07-28 13:21:40 +02:00
Julien Fontanet
4e0b2e4f77 Update deps. 2014-07-28 12:42:55 +02:00
Julien Fontanet
a2bcadeb7c Remove now useless code & Update deps. 2014-07-26 23:51:23 +02:00
Julien Fontanet
84a4242e27 0.1.1 2014-07-26 23:48:53 +02:00
Julien Fontanet
6373d667d9 Work around XO-Server incomplete TLS 2014-07-26 23:42:04 +02:00
Julien Fontanet
7840d601e1 Use xo-lib. 2014-07-26 10:19:32 +02:00
Julien Fontanet
1893061c0d 0.1.0 2014-07-26 10:15:38 +02:00
Julien Fontanet
0289cc3ee5 Initial commit. 2014-07-26 10:15:29 +02:00
Julien Fontanet
a35c9d2d9e 0.2.3 2014-06-09 15:36:44 +02:00
Julien Fontanet
7f739a1371 Update deps. 2014-06-09 15:36:17 +02:00
Olivier Lambert
62df78f329 fix error in doc 2014-06-03 15:11:39 +02:00
Julien Fontanet
be4511d95b 0.2.2 2014-05-27 19:17:53 +01:00
Julien Fontanet
b17e6058d1 Minor fixes. 2014-05-27 19:17:47 +01:00
Julien Fontanet
99c28a184f 0.2.1 2014-05-27 18:55:33 +01:00
Julien Fontanet
7ed9adaf49 Allows connection to incorrect SSL certificates. 2014-05-27 18:55:25 +01:00
Julien Fontanet
c3ef051657 0.2.0 2014-05-26 14:26:55 +02:00
Julien Fontanet
ba149efa4a Merge branch 'dev' 2014-05-26 14:26:41 +02:00
Julien Fontanet
e30a7b3849 README update. 2014-05-26 14:22:08 +02:00
Julien Fontanet
97ad0483ec Minor change. 2014-05-26 14:01:55 +02:00
Julien Fontanet
3981c772a2 --register and --unregister. 2014-05-26 13:57:18 +02:00
Julien Fontanet
e762002560 Remove unused deps. 2014-05-26 13:42:36 +02:00
Julien Fontanet
061d8dc94f Use API introspection. 2014-05-26 13:33:56 +02:00
Julien Fontanet
84e6228f90 Various updates. 2014-05-24 17:09:10 +02:00
Julien Fontanet
301cc22985 Various updates. 2014-04-14 16:38:29 +02:00
Julien Fontanet
29398b9869 Various updates. 2014-04-03 10:19:31 +02:00
Julien Fontanet
8b65a75235 Fixes register in README. 2014-03-28 15:19:53 +01:00
Julien Fontanet
313f7e8173 Minor fixes and new command: whoami. 2014-03-25 17:18:02 +01:00
Julien Fontanet
d9e615e696 First working version. 2014-03-25 15:34:07 +01:00
Julien Fontanet
b526067eeb Initial commit. 2014-03-24 17:44:06 +01:00
180 changed files with 20568 additions and 114 deletions

View File

@@ -11,7 +11,7 @@ root = true
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespaces = true
trim_trailing_whitespace = true
# CoffeeScript
#
@@ -28,12 +28,12 @@ indent_style = space
# Package.json
#
# This indentation style is the one used by npm.
[/package.json]
[package.json]
indent_size = 2
indent_style = space
# Jade
[*.jade]
# Pug (Jade)
[*.{jade,pug}]
indent_size = 2
indent_style = space
@@ -41,7 +41,7 @@ indent_style = space
#
# Two spaces seems to be the standard most common style, at least in
# Node.js (http://nodeguide.com/style.html#tabs-vs-spaces).
[*.js]
[*.{js,jsx,ts,tsx}]
indent_size = 2
indent_style = space

15
.eslintrc.js Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
'extends': [
'standard',
],
'parser': 'babel-eslint',
'rules': {
'comma-dangle': ['error', 'always-multiline'],
'no-var': 'error',
'node/no-extraneous-import': 'error',
'node/no-extraneous-require': 'error',
'node/no-missing-import': 'error',
'node/no-missing-require': 'error',
'prefer-const': 'error',
},
}

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
/coverage/
/node_modules/
/lerna-debug.log
/lerna-debug.log.*
/packages/*/dist/
/packages/*/node_modules/
npm-debug.log
npm-debug.log.*
pnpm-debug.log
pnpm-debug.log.*
yarn-error.log
yarn-error.log.*

16
.travis.yml Normal file
View File

@@ -0,0 +1,16 @@
language: node_js
node_js:
- stable
- 8
- 6
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH="$HOME/.yarn/bin:$PATH"
cache:
yarn: true

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
Almost all dev for Xen Orchestra is happening in this repository.
Because transition is still underway, [xo-web](https://github.com/vatesfr/xo-web) and [xo-server](https://github.com/vatesfr/xo-server) are still developped in their own repositories.
For now, all issues are still to be reported in [xo-web's tracker](https://github.com/vatesfr/xo-web/issues).

52
package.json Normal file
View File

@@ -0,0 +1,52 @@
{
"devDependencies": {
"babel-eslint": "^8.0.1",
"eslint": "^4.12.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"exec-promise": "^0.7.0",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lint-staged": "^5.0.0",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5",
"sorted-object": "^2.0.1"
},
"engines": {
"yarn": "^1.2.1"
},
"jest": {
"collectCoverage": true,
"testEnvironment": "node",
"testPathIgnorePatterns": [
"/dist/",
"/xo-vmdk-to-vhd/"
],
"testRegex": "\\.spec\\.js$"
},
"lint-staged": {
"*.js": [
"lint-staged-stash",
"eslint --fix",
"jest --findRelatedTests",
"lint-staged-unstash"
]
},
"private": true,
"scripts": {
"dev-test": "jest --bail --watch",
"lint-staged-stash": "touch .lint-staged && git stash save --include-untracked --keep-index && true",
"lint-staged-unstash": "git stash pop && rm -f .lint-staged && true",
"posttest": "scripts/run-script test",
"precommit": "lint-staged",
"prepare": "scripts/run-script prepare",
"pretest": "eslint --ignore-path .gitignore .",
"test": "jest"
},
"workspaces": [
"packages/*"
]
}

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,51 @@
# vhd-cli [![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/vhd-cli):
```
> npm install --global vhd-cli
```
## Usage
```
> vhd-cli <VHD file>
```
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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,67 @@
{
"name": "vhd-cli",
"version": "0.0.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/vhd-cli",
"bugs": "https://github.com/vatesfr/xo-web/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": {
"vhd-cli": "dist/index.js"
},
"files": [
"dist/"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"@nraynaud/struct-fu": "^1.0.1",
"@nraynaud/xo-fs": "^0.0.5",
"babel-runtime": "^6.22.0",
"exec-promise": "^0.7.0"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"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.0.1",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [
"lodash",
"transform-runtime"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

19
packages/vhd-cli/src/index.js Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env node
import execPromise from 'exec-promise'
import { RemoteHandlerLocal } from '@nraynaud/xo-fs'
import { resolve } from 'path'
import Vhd from './vhd'
execPromise(async args => {
const vhd = new Vhd(
new RemoteHandlerLocal({ url: 'file:///' }),
resolve(args[0])
)
await vhd.readHeaderAndFooter()
console.log(vhd._header)
console.log(vhd._footer)
})

441
packages/vhd-cli/src/vhd.js Normal file
View File

@@ -0,0 +1,441 @@
import assert from 'assert'
import fu from '@nraynaud/struct-fu'
import { dirname } from 'path'
// ===================================================================
//
// Spec:
// https://www.microsoft.com/en-us/download/details.aspx?id=23850
//
// C implementation:
// https://github.com/rubiojr/vhd-util-convert
//
// ===================================================================
/* eslint-disable no-unused-vars */
const HARD_DISK_TYPE_DIFFERENCING = 4
const HARD_DISK_TYPE_DYNAMIC = 3
const HARD_DISK_TYPE_FIXED = 2
const PLATFORM_CODE_NONE = 0
export const SECTOR_SIZE = 512
/* eslint-enable no-unused vars */
// ===================================================================
const fuFooter = fu.struct([
fu.char('cookie', 8), // 0
fu.uint32('features'), // 8
fu.uint32('fileFormatVersion'), // 12
fu.struct('dataOffset', [
fu.uint32('high'), // 16
fu.uint32('low'), // 20
]),
fu.uint32('timestamp'), // 24
fu.char('creatorApplication', 4), // 28
fu.uint32('creatorVersion'), // 32
fu.uint32('creatorHostOs'), // 36
fu.struct('originalSize', [ // At the creation, current size of the hard disk.
fu.uint32('high'), // 40
fu.uint32('low'), // 44
]),
fu.struct('currentSize', [ // Current size of the virtual disk. At the creation: currentSize = originalSize.
fu.uint32('high'), // 48
fu.uint32('low'), // 52
]),
fu.struct('diskGeometry', [
fu.uint16('cylinders'), // 56
fu.uint8('heads'), // 58
fu.uint8('sectorsPerTrackCylinder'), // 59
]),
fu.uint32('diskType'), // 60 Disk type, must be equal to HARD_DISK_TYPE_DYNAMIC/HARD_DISK_TYPE_DIFFERENCING.
fu.uint32('checksum'), // 64
fu.uint8('uuid', 16), // 68
fu.char('saved'), // 84
fu.char('hidden'), // 85
fu.byte('reserved', 426), // 86
])
const FOOTER_SIZE = fuFooter.size
const fuHeader = fu.struct([
fu.char('cookie', 8),
fu.struct('dataOffset', [
fu.uint32('high'),
fu.uint32('low'),
]),
fu.struct('tableOffset', [ // Absolute byte offset of the Block Allocation Table.
fu.uint32('high'),
fu.uint32('low'),
]),
fu.uint32('headerVersion'),
fu.uint32('maxTableEntries'), // Max entries in the Block Allocation Table.
fu.uint32('blockSize'), // Block size (without bitmap) in bytes.
fu.uint32('checksum'),
fu.uint8('parentUuid', 16),
fu.uint32('parentTimestamp'),
fu.byte('reserved1', 4),
fu.char16be('parentUnicodeName', 512),
fu.struct('parentLocatorEntry', [
fu.uint32('platformCode'),
fu.uint32('platformDataSpace'),
fu.uint32('platformDataLength'),
fu.uint32('reserved'),
fu.struct('platformDataOffset', [ // Absolute byte offset of the locator data.
fu.uint32('high'),
fu.uint32('low'),
]),
], 8),
fu.byte('reserved2', 256),
])
const HEADER_SIZE = fuHeader.size
// ===================================================================
// Helpers
// ===================================================================
const SIZE_OF_32_BITS = Math.pow(2, 32)
const uint32ToUint64 = fu => fu.high * SIZE_OF_32_BITS + fu.low
// Returns a 32 bits integer corresponding to a Vhd version.
const getVhdVersion = (major, minor) => (major << 16) | (minor & 0x0000FFFF)
// bytes[] bit manipulation
const testBit = (map, bit) => map[bit >> 3] & 1 << (bit & 7)
const setBit = (map, bit) => {
map[bit >> 3] |= 1 << (bit & 7)
}
const unsetBit = (map, bit) => {
map[bit >> 3] &= ~(1 << (bit & 7))
}
const addOffsets = (...offsets) => offsets.reduce(
(a, b) => b == null
? a
: typeof b === 'object'
? { bytes: a.bytes + b.bytes, bits: a.bits + b.bits }
: { bytes: a.bytes + b, bits: a.bits },
{ bytes: 0, bits: 0 }
)
const pack = (field, value, buf, offset) => {
field.pack(
value,
buf,
addOffsets(field.offset, offset)
)
}
const unpack = (field, buf, offset) =>
field.unpack(
buf,
addOffsets(field.offset, offset)
)
// ===================================================================
const streamToNewBuffer = stream => new Promise((resolve, reject) => {
const chunks = []
let length = 0
const onData = chunk => {
chunks.push(chunk)
length += chunk.length
}
stream.on('data', onData)
const clean = () => {
stream.removeListener('data', onData)
stream.removeListener('end', onEnd)
stream.removeListener('error', onError)
}
const onEnd = () => {
resolve(Buffer.concat(chunks, length))
clean()
}
stream.on('end', onEnd)
const onError = error => {
reject(error)
clean()
}
stream.on('error', onError)
})
const streamToExistingBuffer = (
stream,
buffer,
offset = 0,
end = buffer.length
) => new Promise((resolve, reject) => {
assert(offset >= 0)
assert(end > offset)
assert(end <= buffer.length)
let i = offset
const onData = chunk => {
const prev = i
i += chunk.length
if (i > end) {
return onError(new Error('too much data'))
}
chunk.copy(buffer, prev)
}
stream.on('data', onData)
const clean = () => {
stream.removeListener('data', onData)
stream.removeListener('end', onEnd)
stream.removeListener('error', onError)
}
const onEnd = () => {
resolve(i - offset)
clean()
}
stream.on('end', onEnd)
const onError = error => {
reject(error)
clean()
}
stream.on('error', onError)
})
// ===================================================================
// Returns the checksum of a raw struct.
const computeChecksum = (struct, buf, offset = 0) => {
let sum = 0
// Do not use the stored checksum to compute the new checksum.
const checksumField = struct.fields.checksum
const checksumOffset = offset + checksumField.offset
for (let i = offset, n = checksumOffset; i < n; ++i) {
sum += buf[i]
}
for (let i = checksumOffset + checksumField.size, n = offset + struct.size; i < n; ++i) {
sum += buf[i]
}
return ~sum >>> 0
}
const verifyChecksum = (struct, buf, offset) =>
unpack(struct.fields.checksum, buf, offset) === computeChecksum(struct, buf, offset)
const getParentLocatorSize = parentLocatorEntry => {
const { platformDataSpace } = parentLocatorEntry
if (platformDataSpace < SECTOR_SIZE) {
return platformDataSpace * SECTOR_SIZE
}
return (platformDataSpace % SECTOR_SIZE === 0)
? platformDataSpace
: 0
}
// ===================================================================
// Euclidean division, returns the quotient and the remainder of a / b.
const div = (a, b) => [ Math.floor(a / b), a % b ]
export default class Vhd {
constructor (handler, path) {
this._handler = handler
this._path = path
this._blockAllocationTable = null
this._blockBitmapSize = null
this._footer = null
this._header = null
this._parent = null
this._sectorsPerBlock = null
}
// Read `length` bytes starting from `begin`.
//
// - if `buffer`: it is filled starting from `offset`, and the
// number of written bytes is returned;
// - otherwise: a new buffer is allocated and returned.
_read (begin, length, buf, offset) {
assert(begin >= 0)
assert(length > 0)
return this._handler.createReadStream(this._path, {
end: begin + length - 1,
start: begin,
}).then(buf
? stream => streamToExistingBuffer(stream, buf, offset, (offset || 0) + length)
: streamToNewBuffer
)
}
// - if `buffer`: it is filled with 0 starting from `offset`, and
// the number of written bytes is returned;
// - otherwise: a new buffer is allocated and returned.
_zeroes (length, buf, offset = 0) {
if (buf) {
assert(offset >= 0)
assert(length > 0)
const end = offset + length
assert(end <= buf.length)
buf.fill(0, offset, end)
return Promise.resolve(length)
}
return Promise.resolve(Buffer.alloc(length))
}
// Return the position of a block in the VHD or undefined if not found.
_getBlockAddress (block) {
assert(block >= 0)
assert(block < this._header.maxTableEntries)
const blockAddr = this._blockAllocationTable[block]
if (blockAddr !== 0xFFFFFFFF) {
return blockAddr * SECTOR_SIZE
}
}
// -----------------------------------------------------------------
async readHeaderAndFooter () {
const buf = await this._read(0, FOOTER_SIZE + HEADER_SIZE)
if (!verifyChecksum(fuFooter, buf)) {
throw new Error('footer checksum does not match')
}
if (!verifyChecksum(fuHeader, buf, FOOTER_SIZE)) {
throw new Error('header checksum does not match')
}
return this._initMetadata(
unpack(fuHeader, buf, FOOTER_SIZE),
unpack(fuFooter, buf)
)
}
async _initMetadata (header, footer) {
const sectorsPerBlock = header.blockSize / SECTOR_SIZE
assert(sectorsPerBlock % 1 === 0)
// 1 bit per sector, rounded up to full sectors
this._blockBitmapSize = Math.ceil(sectorsPerBlock / 8 / SECTOR_SIZE) * SECTOR_SIZE
assert(this._blockBitmapSize === SECTOR_SIZE)
this._footer = footer
this._header = header
this.size = uint32ToUint64(this._footer.currentSize)
if (footer.diskType === HARD_DISK_TYPE_DIFFERENCING) {
const parent = new Vhd(
this._handler,
`${dirname(this._path)}/${header.parentUnicodeName}`
)
await parent.readHeaderAndFooter()
await parent.readBlockAllocationTable()
this._parent = parent
}
}
// -----------------------------------------------------------------
async readBlockAllocationTable () {
const { maxTableEntries, tableOffset } = this._header
const fuTable = fu.uint32(maxTableEntries)
this._blockAllocationTable = unpack(
fuTable,
await this._read(uint32ToUint64(tableOffset), fuTable.size)
)
}
// -----------------------------------------------------------------
// read a single sector in a block
async _readBlockSector (block, sector, begin, length, buf, offset) {
assert(begin >= 0)
assert(length > 0)
assert(begin + length <= SECTOR_SIZE)
const blockAddr = this._getBlockAddress(block)
const blockBitmapSize = this._blockBitmapSize
const parent = this._parent
if (blockAddr && (
!parent ||
testBit(await this._read(blockAddr, blockBitmapSize), sector)
)) {
return this._read(
blockAddr + blockBitmapSize + sector * SECTOR_SIZE + begin,
length,
buf,
offset
)
}
return parent
? parent._readBlockSector(block, sector, begin, length, buf, offset)
: this._zeroes(length, buf, offset)
}
_readBlock (block, begin, length, buf, offset) {
assert(begin >= 0)
assert(length > 0)
const { blockSize } = this._header
assert(begin + length <= blockSize)
const blockAddr = this._getBlockAddress(block)
const parent = this._parent
if (!blockAddr) {
return parent
? parent._readBlock(block, begin, length, buf, offset)
: this._zeroes(length, buf, offset)
}
if (!parent) {
return this._read(blockAddr + this._blockBitmapSize + begin, length, buf, offset)
}
// FIXME: we should read as many sectors in a single pass as
// possible for maximum perf.
const [ sector, beginInSector ] = div(begin, SECTOR_SIZE)
return this._readBlockSector(
block,
sector,
beginInSector,
Math.min(length, SECTOR_SIZE - beginInSector),
buf,
offset
)
}
read (buf, begin, length = buf.length, offset) {
assert(Buffer.isBuffer(buf))
assert(begin >= 0)
const { size } = this
if (begin >= size) {
return Promise.resolve(0)
}
const { blockSize } = this._header
const [ block, beginInBlock ] = div(begin, blockSize)
return this._readBlock(
block,
beginInBlock,
Math.min(length, blockSize - beginInBlock, size - begin),
buf,
offset
)
}
}

1
packages/xen-api/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/plot.dat

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

@@ -1,11 +1,8 @@
language: node_js
node_js:
- '4'
- 'iojs-v3'
- 'iojs-v2'
- 'iojs-v1'
- '0.12'
- '0.10'
- stable
- 6
- 4
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/

128
packages/xen-api/README.md Normal file
View File

@@ -0,0 +1,128 @@
# xen-api [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> Connector to the Xen API
Tested with:
- XenServer 7.1
- XenServer 7
- XenServer 6.5
- XenServer 6.2
- XenServer 5.6
## Install
Installation of the [npm package](https://npmjs.org/package/xen-api):
```
> npm install --save xen-api
```
## Usage
### Library
```javascript
const { createClient } = require('xen-api')
const xapi = createClient({
url: 'https://xen1.company.net',
allowUnauthorized: false,
auth: {
user: 'root',
password: 'important secret password'
},
readOnly: false
})
```
Options:
- `url`: address of a host in the pool we are trying to connect to
- `allowUnauthorized`: whether to accept self-signed certificates
- `auth`: credentials used to sign in (can also be specified in the URL)
- `readOnly = false`: if true, no methods with side-effects can be called
```js
// Force connection.
xapi.connect().catch(error => {
console.error(error)
})
// Watch objects.
xapi.objects.on('add', objects => {
console.log('new objects:', objects)
})
```
> Note: all objects are frozen and cannot be altered!
Custom fields on objects (hidden ie. non enumerable):
- `$type`: the type of the object (`VM`, `task`, …);
- `$ref`: the (opaque) reference of the object;
- `$id`: the identifier of this object (its UUID if any, otherwise its reference);
- `$pool`: the pool object this object belongs to.
Furthermore, any field containing a reference (or references if an
array) can be resolved by prepending the field name with a `$`:
```javascript
console.log(xapi.pool.$master.$resident_VMs[0].name_label)
// vm1
```
### CLI
A CLI is provided to help exploration and discovery of the XAPI.
```
> xen-api https://xen1.company.net root
Password: ******
root@xen1.company.net> xapi.status
'connected'
root@xen1.company.net> xapi.pool.master
'OpaqueRef:ec7c5147-8aee-990f-c70b-0de916a8e993'
root@xen1.company.net> xapi.pool.$master.name_label
'xen1'
```
To ease searches, `find()` and `findAll()` functions are available:
```
root@xen1.company.net> findAll({ $type: 'vm' }).length
183
```
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/xen-api/issues)
you've encountered;
- fork and create a pull request.
## License
ISC © [Julien Fontanet](https://github.com/julien-f)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env node
process.env.DEBUG = '*'
const defer = require('golike-defer').default
const pump = require('pump')
const { fromCallback } = require('promise-toolbox')
const { createClient } = require('../')
const { createOutputStream, resolveRef } = require('./utils')
defer(async ($defer, args) => {
let raw = false
if (args[0] === '--raw') {
raw = true
args.shift()
}
if (args.length < 2) {
return console.log('Usage: export-vdi [--raw] <XS URL> <VDI identifier> [<VHD file>]')
}
const xapi = createClient({
allowUnauthorized: true,
url: args[0],
watchEvents: false
})
await xapi.connect()
$defer(() => xapi.disconnect())
// https://xapi-project.github.io/xen-api/snapshots.html#downloading-a-disk-or-snapshot
const exportStream = await xapi.getResource('/export_raw_vdi/', {
query: {
format: raw ? 'raw' : 'vhd',
vdi: await resolveRef(xapi, 'VDI', args[1])
}
})
console.warn('Export task:', exportStream.headers['task-id'])
await fromCallback(cb => pump(
exportStream,
createOutputStream(args[2]),
cb
))
})(process.argv.slice(2)).catch(
console.error.bind(console, 'error')
)

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env node
process.env.DEBUG = '*'
const defer = require('golike-defer').default
const pump = require('pump')
const { fromCallback } = require('promise-toolbox')
const { createClient } = require('../')
const { createOutputStream, resolveRef } = require('./utils')
defer(async ($defer, args) => {
if (args.length < 2) {
return console.log('Usage: export-vm <XS URL> <VM identifier> [<XVA file>]')
}
const xapi = createClient({
allowUnauthorized: true,
url: args[0],
watchEvents: false
})
await xapi.connect()
$defer(() => xapi.disconnect())
// https://xapi-project.github.io/xen-api/importexport.html
const exportStream = await xapi.getResource('/export/', {
query: {
ref: await resolveRef(xapi, 'VM', args[1]),
use_compression: 'true'
}
})
console.warn('Export task:', exportStream.headers['task-id'])
await fromCallback(cb => pump(
exportStream,
createOutputStream(args[2]),
cb
))
})(process.argv.slice(2)).catch(
console.error.bind(console, 'error')
)

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env node
process.env.DEBUG = '*'
const defer = require('golike-defer').default
const { createClient } = require('../')
const { createInputStream, resolveRef } = require('./utils')
defer(async ($defer, args) => {
let raw = false
if (args[0] === '--raw') {
raw = true
args.shift()
}
if (args.length < 2) {
return console.log('Usage: import-vdi [--raw] <XS URL> <VDI identifier> [<VHD file>]')
}
const xapi = createClient({
allowUnauthorized: true,
url: args[0],
watchEvents: false
})
await xapi.connect()
$defer(() => xapi.disconnect())
// https://xapi-project.github.io/xen-api/snapshots.html#uploading-a-disk-or-snapshot
await xapi.putResource(createInputStream(args[2]), '/import_raw_vdi/', {
query: {
format: raw ? 'raw' : 'vhd',
vdi: await resolveRef(xapi, 'VDI', args[1])
}
})
})(process.argv.slice(2)).catch(
console.error.bind(console, 'error')
)

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env node
process.env.DEBUG = '*'
const defer = require('golike-defer').default
const { createClient } = require('../')
const { createInputStream, resolveRef } = require('./utils')
defer(async ($defer, args) => {
if (args.length < 1) {
return console.log('Usage: import-vm <XS URL> [<XVA file>] [<SR identifier>]')
}
const xapi = createClient({
allowUnauthorized: true,
url: args[0],
watchEvents: false
})
await xapi.connect()
$defer(() => xapi.disconnect())
// https://xapi-project.github.io/xen-api/importexport.html
await xapi.putResource(createInputStream(args[1]), '/import/', {
query: args[2] && { sr_id: await resolveRef(xapi, 'SR', args[2]) }
})
})(process.argv.slice(2)).catch(
console.error.bind(console, 'error')
)

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env node
require('source-map-support').install()
const { forEach, size } = require('lodash')
const { createClient } = require('../')
// ===================================================================
if (process.argv.length < 3) {
return console.log('Usage: log-events <XS URL>')
}
// ===================================================================
// Creation
const xapi = createClient({
allowUnauthorized: true,
url: process.argv[2]
})
// ===================================================================
// Method call
xapi.connect().then(() => {
xapi.call('VM.get_all_records')
.then(function (vms) {
console.log('%s VMs fetched', size(vms))
})
.catch(function (error) {
console.error(error)
})
})
// ===================================================================
// Objects
const objects = xapi.objects
objects.on('add', objects => {
forEach(objects, object => {
console.log('+ %s: %s', object.$type, object.$id)
})
})
objects.on('update', objects => {
forEach(objects, object => {
console.log('± %s: %s', object.$type, object.$id)
})
})
objects.on('remove', objects => {
forEach(objects, (value, id) => {
console.log('- %s', id)
})
})

View File

@@ -0,0 +1,6 @@
{
"dependencies": {
"golike-defer": "^0.1.0",
"pump": "^1.0.2"
}
}

View File

@@ -0,0 +1,41 @@
const { createReadStream, createWriteStream, statSync } = require('fs')
const { PassThrough } = require('stream')
const { isOpaqueRef } = require('../') // eslint-disable-line node/no-missing-require
exports.createInputStream = path => {
if (path === undefined || path === '-') {
return process.stdin
}
const { size } = statSync(path)
const stream = createReadStream(path)
stream.length = size
return stream
}
exports.createOutputStream = path => {
if (path !== undefined && path !== '-') {
return createWriteStream(path)
}
// introduce a through stream because stdout is not a normal stream!
const stream = new PassThrough()
stream.pipe(process.stdout)
return stream
}
exports.resolveRef = (xapi, type, refOrUuidOrNameLabel) =>
isOpaqueRef(refOrUuidOrNameLabel)
? refOrUuidOrNameLabel
: xapi.call(`${type}.get_by_uuid`, refOrUuidOrNameLabel).catch(
() => xapi.call(`${type}.get_by_name_label`, refOrUuidOrNameLabel).then(
refs => {
if (refs.length === 1) {
return refs[0]
}
throw new Error(`no single match for ${type} with name label ${refOrUuidOrNameLabel}`)
}
)
)

View File

@@ -0,0 +1,4 @@
set yrange [ 0 : ]
set grid
plot for [i=2:4] "plot.dat" using 1:i with lines

View File

@@ -0,0 +1,89 @@
{
"name": "xen-api",
"version": "0.16.0",
"license": "ISC",
"description": "Connector to the Xen API",
"keywords": [
"xen",
"api",
"xen-api",
"xenapi",
"xapi"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xen-api",
"bugs": "https://github.com/vatesfr/xo-web/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": {
"xen-api": "dist/cli.js"
},
"files": [
"dist/"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"babel-polyfill": "^6.23.0",
"blocked": "^1.2.1",
"debug": "^3.1.0",
"event-to-promise": "^0.8.0",
"exec-promise": "^0.7.0",
"http-request-plus": "^0.5.0",
"iterable-backoff": "^0.0.0",
"json-rpc-protocol": "^0.11.2",
"kindof": "^2.0.0",
"lodash": "^4.17.4",
"make-error": "^1.3.0",
"minimist": "^1.2.0",
"ms": "^2.1.1",
"promise-toolbox": "^0.9.5",
"pw": "0.0.4",
"xmlrpc": "^1.3.2",
"xo-collection": "^0.4.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-function-bind": "^6.22.0",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.5",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"plot": "gnuplot -p memory-test.gnu",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [
"lodash",
"transform-decorators-legacy",
"transform-function-bind"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

106
packages/xen-api/src/cli.js Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env node
import 'babel-polyfill'
import blocked from 'blocked'
import createDebug from 'debug'
import eventToPromise from 'event-to-promise'
import execPromise from 'exec-promise'
import minimist from 'minimist'
import pw from 'pw'
import { asCallback, fromCallback } from 'promise-toolbox'
import { filter, find, isArray } from 'lodash'
import { start as createRepl } from 'repl'
import { createClient } from './'
// ===================================================================
function askPassword (prompt = 'Password: ') {
if (prompt) {
process.stdout.write(prompt)
}
return new Promise(resolve => {
pw(resolve)
})
}
// ===================================================================
const usage = 'Usage: xen-api <url> [<user> [<password>]]'
const main = async args => {
const opts = minimist(args, {
boolean: ['allow-unauthorized', 'help', 'read-only', 'verbose'],
alias: {
'allow-unauthorized': 'au',
debounce: 'd',
help: 'h',
'read-only': 'ro',
verbose: 'v',
},
})
if (opts.help) {
return usage
}
if (opts.verbose) {
// Does not work perfectly.
//
// https://github.com/visionmedia/debug/pull/156
createDebug.enable('xen-api,xen-api:*')
}
let auth
if (opts._.length > 1) {
const [ , user, password = await askPassword() ] = opts._
auth = { user, password }
}
{
const debug = createDebug('xen-api:perf')
blocked(ms => {
debug('blocked for %sms', ms | 0)
})
}
const xapi = createClient({
url: opts._[0],
allowUnauthorized: opts.au,
auth,
debounce: opts.debounce != null ? +opts.debounce : null,
readOnly: opts.ro,
})
await xapi.connect()
const repl = createRepl({
prompt: `${xapi._humanId}> `,
})
repl.context.xapi = xapi
repl.context.find = predicate => find(xapi.objects.all, predicate)
repl.context.findAll = predicate => filter(xapi.objects.all, predicate)
// Make the REPL waits for promise completion.
repl.eval = (evaluate => (cmd, context, filename, cb) => {
fromCallback(cb => {
evaluate.call(repl, cmd, context, filename, cb)
}).then(value =>
isArray(value) ? Promise.all(value) : value
)::asCallback(cb)
})(repl.eval)
await eventToPromise(repl, 'exit')
try {
await xapi.disconnect()
} catch (error) {}
}
export default main
if (!module.parent) {
execPromise(main)
}

View File

@@ -0,0 +1,999 @@
import Collection from 'xo-collection'
import createDebug from 'debug'
import kindOf from 'kindof'
import ms from 'ms'
import httpRequest from 'http-request-plus'
import { BaseError } from 'make-error'
import { EventEmitter } from 'events'
import { fibonacci } from 'iterable-backoff'
import {
filter,
forEach,
isArray,
isInteger,
isObject,
map,
mapValues,
noop,
omit,
reduce,
startsWith,
} from 'lodash'
import {
Cancel,
cancelable,
catchPlus as pCatch,
defer,
delay as pDelay,
fromEvents,
lastly,
} from 'promise-toolbox'
import autoTransport from './transports/auto'
const debug = createDebug('xen-api')
// ===================================================================
// http://www.gnu.org/software/libc/manual/html_node/Error-Codes.html
const NETWORK_ERRORS = {
// Connection has been closed outside of our control.
ECONNRESET: true,
// Connection has been aborted locally.
ECONNABORTED: true,
// Host is up but refuses connection (typically: no such service).
ECONNREFUSED: true,
// TODO: ??
EINVAL: true,
// Host is not reachable (does not respond).
EHOSTUNREACH: true,
// Connection configured timed out has been reach.
ETIMEDOUT: true,
}
const isNetworkError = ({code}) => NETWORK_ERRORS[code]
// -------------------------------------------------------------------
const XAPI_NETWORK_ERRORS = {
HOST_STILL_BOOTING: true,
HOST_HAS_NO_MANAGEMENT_IP: true,
}
const isXapiNetworkError = ({code}) => XAPI_NETWORK_ERRORS[code]
// -------------------------------------------------------------------
const areEventsLost = ({code}) => code === 'EVENTS_LOST'
const isHostSlave = ({code}) => code === 'HOST_IS_SLAVE'
const isMethodUnknown = ({code}) => code === 'MESSAGE_METHOD_UNKNOWN'
const isSessionInvalid = ({code}) => code === 'SESSION_INVALID'
// -------------------------------------------------------------------
class XapiError extends BaseError {
constructor (code, params) {
super(`${code}(${params.join(', ')})`)
this.code = code
this.params = params
// slots than can be assigned later
this.method = undefined
this.url = undefined
}
}
export const wrapError = error => {
let code, params
if (isArray(error)) { // < XenServer 7.3
[ code, ...params ] = error
} else {
code = error.message
params = error.data
}
return new XapiError(code, params)
}
// ===================================================================
const URL_RE = /^(?:(https?:)\/*)?(?:([^:]+):([^@]+)@)?([^/]+?)(?::([0-9]+))?\/?$/
const parseUrl = url => {
const matches = URL_RE.exec(url)
if (!matches) {
throw new Error('invalid URL: ' + url)
}
const [ , protocol = 'https:', username, password, hostname, port ] = matches
return { protocol, username, password, hostname, port }
}
// -------------------------------------------------------------------
const {
create: createObject,
defineProperties,
defineProperty,
freeze: freezeObject,
} = Object
// -------------------------------------------------------------------
const OPAQUE_REF_PREFIX = 'OpaqueRef:'
export const isOpaqueRef = value =>
typeof value === 'string' &&
startsWith(value, OPAQUE_REF_PREFIX)
// -------------------------------------------------------------------
const RE_READ_ONLY_METHOD = /^[^.]+\.get_/
const isReadOnlyCall = (method, args) => (
args.length === 1 &&
isOpaqueRef(args[0]) &&
RE_READ_ONLY_METHOD.test(method)
)
// Prepare values before passing them to the XenAPI:
//
// - cast integers to strings
const prepareParam = param => {
if (isInteger(param === 'number')) {
return String(param)
}
if (typeof param !== 'object' || param === null) {
return param
}
return (isArray(param) ? map : mapValues)(param, prepareParam)
}
// -------------------------------------------------------------------
const getKey = o => o.$id
// -------------------------------------------------------------------
const EMPTY_ARRAY = freezeObject([])
// -------------------------------------------------------------------
const getTaskResult = (task, onSuccess, onFailure) => {
const { status } = task
if (status === 'cancelled') {
return [ onFailure(new Cancel('task canceled')) ]
}
if (status === 'failure') {
return [ onFailure(wrapError(task.error_info)) ]
}
if (status === 'success') {
// the result might be:
// - empty string
// - an opaque reference
// - an XML-RPC value
return [ onSuccess(task.result) ]
}
}
// -------------------------------------------------------------------
const CONNECTED = 'connected'
const CONNECTING = 'connecting'
const DISCONNECTED = 'disconnected'
// -------------------------------------------------------------------
export class Xapi extends EventEmitter {
constructor (opts) {
super()
this._allowUnauthorized = opts.allowUnauthorized
this._auth = opts.auth
this._pool = null
this._readOnly = Boolean(opts.readOnly)
this._sessionId = null
const url = this._url = parseUrl(opts.url)
if (this._auth === undefined) {
const user = url.username
if (user !== undefined) {
this._auth = {
user,
password: url.password,
}
delete url.username
delete url.password
}
}
if (opts.watchEvents !== false) {
this._debounce = opts.debounce == null
? 200
: opts.debounce
this._eventWatchers = createObject(null)
this._fromToken = ''
// Memoize this function _addObject().
this._getPool = () => this._pool
const objects = this._objects = new Collection()
objects.getKey = getKey
this._objectsByRefs = createObject(null)
this._objectsByRefs['OpaqueRef:NULL'] = null
this._taskWatchers = Object.create(null)
this.on('connected', this._watchEvents)
this.on('disconnected', () => {
this._fromToken = ''
objects.clear()
})
}
}
get _url () {
return this.__url
}
set _url (url) {
this.__url = url
this._call = autoTransport({
allowUnauthorized: this._allowUnauthorized,
url,
})
}
get readOnly () {
return this._readOnly
}
set readOnly (ro) {
this._readOnly = Boolean(ro)
}
get sessionId () {
const id = this._sessionId
if (!id || id === CONNECTING) {
throw new Error('sessionId is only available when connected')
}
return id
}
get status () {
const id = this._sessionId
return id
? (
id === CONNECTING
? CONNECTING
: CONNECTED
)
: DISCONNECTED
}
get _humanId () {
return `${this._auth.user}@${this._url.hostname}`
}
// ensure we have received all events up to this call
//
// optionally returns the up to date object for the given ref
barrier (ref) {
const eventWatchers = this._eventWatchers
if (eventWatchers === undefined) {
return Promise.reject(new Error('Xapi#barrier() requires events watching'))
}
const key = `xo:barrier:${Math.random().toString(36).slice(2)}`
const poolRef = this._pool.$ref
const { promise, resolve } = defer()
eventWatchers[key] = resolve
return this._sessionCall(
'pool.add_to_other_config',
[ poolRef, key, '' ]
).then(() => promise.then(() => {
this._sessionCall('pool.remove_from_other_config', [ poolRef, key ]).catch(noop)
if (ref === undefined) {
return
}
// support legacy params (type, ref)
if (arguments.length === 2) {
ref = arguments[1]
}
return this.getObjectByRef(ref)
}))
}
connect () {
const {status} = this
if (status === CONNECTED) {
return Promise.reject(new Error('already connected'))
}
if (status === CONNECTING) {
return Promise.reject(new Error('already connecting'))
}
const auth = this._auth
if (auth === undefined) {
return Promise.reject(new Error('missing credentials'))
}
this._sessionId = CONNECTING
return this._transportCall('session.login_with_password', [
auth.user,
auth.password,
]).then(
sessionId => {
this._sessionId = sessionId
debug('%s: connected', this._humanId)
this.emit(CONNECTED)
},
error => {
this._sessionId = null
throw error
}
)
}
disconnect () {
return Promise.resolve().then(() => {
const { status } = this
if (status === DISCONNECTED) {
return Promise.reject(new Error('already disconnected'))
}
this._transportCall('session.logout', [ this._sessionId ]).catch(noop)
this._sessionId = null
debug('%s: disconnected', this._humanId)
this.emit(DISCONNECTED)
})
}
// High level calls.
call (method, ...args) {
return this._readOnly && !isReadOnlyCall(method, args)
? Promise.reject(new Error(`cannot call ${method}() in read only mode`))
: this._sessionCall(method, prepareParam(args))
}
@cancelable
callAsync ($cancelToken, method, ...args) {
return this._readOnly && !isReadOnlyCall(method, args)
? Promise.reject(new Error(`cannot call ${method}() in read only mode`))
: this._sessionCall(`Async.${method}`, ...args).then(taskRef => {
$cancelToken.promise.then(() => {
this._sessionCall('task.cancel', taskRef).catch(noop)
})
return this.watchTask(taskRef)::lastly(() => {
this._sessionCall('task.destroy', taskRef).catch(noop)
})
})
}
// create a task and automatically destroy it when settled
createTask (nameLabel, nameDescription = '') {
if (this._readOnly) {
return Promise.reject(new Error('cannot create task in read only mode'))
}
const promise = this._sessionCall('task.create', [
nameLabel,
nameDescription,
])
promise.then(taskRef => {
const destroy = () =>
this._sessionCall('task.destroy', taskRef).catch(noop)
this.watchTask(taskRef).then(destroy, destroy)
})
return promise
}
// Nice getter which returns the object for a given $id (internal to
// this lib), UUID (unique identifier that some objects have) or
// opaque reference (internal to XAPI).
getObject (idOrUuidOrRef, defaultValue) {
const object = typeof idOrUuidOrRef === 'string'
? (
// if there is an UUID, it is also the $id.
this._objects.all[idOrUuidOrRef] ||
this._objectsByRefs[idOrUuidOrRef]
)
: this._objects.all[idOrUuidOrRef.$id]
if (object) return object
if (arguments.length > 1) return defaultValue
throw new Error('there is not object can be matched to ' + idOrUuidOrRef)
}
// Returns the object for a given opaque reference (internal to
// XAPI).
getObjectByRef (ref, defaultValue) {
const object = this._objectsByRefs[ref]
if (object) return object
if (arguments.length > 1) return defaultValue
throw new Error('there is no object with the ref ' + ref)
}
// Returns the object for a given UUID (unique identifier that some
// objects have).
getObjectByUuid (uuid, defaultValue) {
// Objects ids are already UUIDs if they have one.
const object = this._objects.all[uuid]
if (object) return object
if (arguments.length > 1) return defaultValue
throw new Error('there is no object with the UUID ' + uuid)
}
getRecord (type, ref) {
return this._sessionCall(`${type}.get_record`, ref)
}
@cancelable
getResource ($cancelToken, pathname, {
host,
query,
task,
}) {
return this._autoTask(
task,
`Xapi#getResource ${pathname}`
).then(taskRef => {
query = { ...query, session_id: this.sessionId }
let taskResult
if (taskRef !== undefined) {
query.task_id = taskRef
taskResult = this.watchTask(taskRef)
if (typeof $cancelToken.addHandler === 'function') {
$cancelToken.addHandler(() => taskResult)
}
}
let promise = httpRequest(
$cancelToken,
this._url,
host && {
hostname: this.getObject(host).address,
},
{
pathname,
query,
rejectUnauthorized: !this._allowUnauthorized,
}
)
if (taskResult !== undefined) {
promise = promise.then(response => {
response.task = taskResult
return response
})
}
return promise
})
}
@cancelable
putResource ($cancelToken, body, pathname, {
host,
query,
task,
} = {}) {
if (this._readOnly) {
return Promise.reject(new Error(new Error('cannot put resource in read only mode')))
}
return this._autoTask(
task,
`Xapi#putResource ${pathname}`
).then(taskRef => {
query = { ...query, session_id: this.sessionId }
let taskResult
if (taskRef !== undefined) {
query.task_id = taskRef
taskResult = this.watchTask(taskRef)
if (typeof $cancelToken.addHandler === 'function') {
$cancelToken.addHandler(() => taskResult)
}
}
const headers = {}
// Xen API does not support chunk encoding.
const isStream = typeof body.pipe === 'function'
const { length } = body
if (isStream && length === undefined) {
// add a fake huge content length (1 PiB)
headers['content-length'] = '1125899906842624'
}
const doRequest = override => httpRequest.put(
$cancelToken,
this._url,
host && {
hostname: this.getObject(host).address,
},
{
body,
headers,
pathname,
query,
rejectUnauthorized: !this._allowUnauthorized,
},
override
)
const promise = isStream
// dummy request to probe for a redirection before consuming body
? doRequest({
body: '',
// omit task_id because this request will fail on purpose
query: 'task_id' in query
? omit(query, 'task_id')
: query,
maxRedirects: 0,
}).then(
response => {
response.req.abort()
return doRequest()
},
error => {
let response
if (error != null && (response = error.response) != null) {
response.req.abort()
const { headers: { location }, statusCode } = response
if (statusCode === 302 && location !== undefined) {
return doRequest(location)
}
}
throw error
}
)
// http-request-plus correctly handle redirects if body is not a stream
: doRequest()
return promise.then(response => {
const { req } = response
if (taskResult !== undefined) {
taskResult = taskResult.catch(error => {
error.url = response.url
throw error
})
}
if (req.finished) {
req.abort()
return taskResult
}
return fromEvents(req, ['close', 'finish']).then(() => {
req.abort()
return taskResult
})
})
})
}
watchTask (ref) {
const watchers = this._taskWatchers
if (watchers === undefined) {
throw new Error('Xapi#watchTask() requires events watching')
}
// allow task object to be passed
if (ref.$ref !== undefined) ref = ref.$ref
let watcher = watchers[ref]
if (watcher === undefined) {
// sync check if the task is already settled
const task = this.objects.all[ref]
if (task !== undefined) {
const result = getTaskResult(task, Promise.resolve, Promise.reject)
if (result) {
return result[0]
}
}
watcher = watchers[ref] = defer()
}
return watcher.promise
}
get pool () {
return this._pool
}
get objects () {
return this._objects
}
// return a promise which resolves to a task ref or undefined
_autoTask (task = this._taskWatchers !== undefined, name) {
if (task === false) {
return Promise.resolve()
}
if (task === true) {
return this.createTask(name)
}
// either a reference or a promise to a reference
return Promise.resolve(task)
}
// Medium level call: handle session errors.
_sessionCall (method, args) {
try {
if (startsWith(method, 'session.')) {
throw new Error('session.*() methods are disabled from this interface')
}
return this._transportCall(method, [this.sessionId].concat(args))
::pCatch(isSessionInvalid, () => {
// XAPI is sometimes reinitialized and sessions are lost.
// Try to login again.
debug('%s: the session has been reinitialized', this._humanId)
this._sessionId = null
return this.connect().then(() => this._sessionCall(method, args))
})
} catch (error) {
return Promise.reject(error)
}
}
_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 },
})
// Finally freezes the object.
freezeObject(object)
const objects = this._objects
// An object's UUID can change during its life.
const prev = objectsByRefs[ref]
let prevUuid
if (prev && (prevUuid = prev.uuid) && prevUuid !== object.uuid) {
objects.remove(prevUuid)
}
this._objects.set(object)
objectsByRefs[ref] = object
if (type === 'pool') {
this._pool = object
} else if (type === 'task') {
const taskWatchers = this._taskWatchers
const taskWatcher = taskWatchers[ref]
if (
taskWatcher !== undefined &&
getTaskResult(object, taskWatcher.resolve, taskWatcher.reject)
) {
delete taskWatchers[ref]
}
}
return object
}
_removeObject (ref) {
const byRefs = this._objectsByRefs
const object = byRefs[ref]
if (object !== undefined) {
this._objects.unset(object.$id)
delete byRefs[ref]
}
const taskWatchers = this._taskWatchers
const taskWatcher = taskWatchers[ref]
if (taskWatcher !== undefined) {
taskWatcher.reject(new Error('task has been detroyed before completion'))
delete taskWatchers[ref]
}
}
_processEvents (events) {
const eventWatchers = this._eventWatchers
forEach(events, event => {
let object
const { ref } = event
if (event.operation === 'del') {
this._removeObject(ref)
} else {
const type = event.class
object = this._addObject(type, ref, event.snapshot)
if (eventWatchers !== undefined && type === 'pool') {
forEach(object.other_config, (_, key) => {
const eventWatcher = eventWatchers[key]
if (eventWatcher !== undefined) {
delete eventWatchers[key]
eventWatcher(object)
}
})
}
}
})
}
_watchEvents () {
const loop = () => this.status === CONNECTED && this._sessionCall('event.from', [
['*'],
this._fromToken,
60 + 0.1, // Force float.
]).then(onSuccess, onFailure)
const onSuccess = ({token, events}) => {
this._fromToken = token
this._processEvents(events)
const debounce = this._debounce
return debounce != null
? pDelay(debounce).then(loop)
: loop()
}
const onFailure = error => {
if (areEventsLost(error)) {
this._fromToken = ''
this._objects.clear()
return loop()
}
throw error
}
return loop()::pCatch(
isMethodUnknown,
// If the server failed, it is probably due to an excessively
// large response.
// Falling back to legacy events watch should be enough.
error => error && error.res && error.res.statusCode === 500,
() => this._watchEventsLegacy()
)
}
// This method watches events using the legacy `event.next` XAPI
// methods.
//
// It also has to manually get all objects first.
_watchEventsLegacy () {
const getAllObjects = () => {
return this._sessionCall('system.listMethods', []).then(methods => {
// Uses introspection to determine the methods to use to get
// all objects.
const getAllRecordsMethods = filter(
methods,
::/\.get_all_records$/.test
)
return Promise.all(map(
getAllRecordsMethods,
method => this._sessionCall(method, []).then(
objects => {
const type = method.slice(0, method.indexOf('.')).toLowerCase()
forEach(objects, (object, ref) => {
this._addObject(type, ref, object)
})
},
error => {
if (error.code !== 'MESSAGE_REMOVED') {
throw error
}
}
)
))
})
}
const watchEvents = () => this._sessionCall('event.register', [ ['*'] ]).then(loop)
const loop = () => this.status === CONNECTED && this._sessionCall('event.next', []).then(onSuccess, onFailure)
const onSuccess = events => {
this._processEvents(events)
const debounce = this._debounce
return debounce == null
? loop()
: pDelay(debounce).then(loop)
}
const onFailure = error => {
if (areEventsLost(error)) {
return this._sessionCall('event.unregister', [ ['*'] ]).then(watchEvents)
}
throw error
}
return getAllObjects().then(watchEvents)
}
}
Xapi.prototype._transportCall = reduce([
function (method, args) {
return this._call(method, args).catch(error => {
if (!(error instanceof Error)) {
error = wrapError(error)
}
error.method = method
throw error
})
},
call => function () {
let iterator // lazily created
const loop = () => call.apply(this, arguments)
::pCatch(isNetworkError, isXapiNetworkError, error => {
if (iterator === undefined) {
iterator = fibonacci().clamp(undefined, 60).take(10).toMs()
}
const cursor = iterator.next()
if (!cursor.done) {
// TODO: ability to cancel the connection
// TODO: ability to force immediate reconnection
const delay = cursor.value
debug('%s: network error %s, next try in %s ms', this._humanId, error.code, delay)
return pDelay(delay).then(loop)
}
debug('%s: network error %s, aborting', this._humanId, error.code)
// mark as disconnected
this.disconnect()::pCatch(noop)
throw error
})
return loop()
},
call => function loop () {
return call.apply(this, arguments)
::pCatch(isHostSlave, ({params: [master]}) => {
debug('%s: host is slave, attempting to connect at %s', this._humanId, master)
const newUrl = {
...this._url,
hostname: master,
}
this.emit('redirect', newUrl)
this._url = newUrl
return loop.apply(this, arguments)
})
},
call => function (method) {
const startTime = Date.now()
return call.apply(this, arguments).then(
result => {
debug(
'%s: %s(...) [%s] ==> %s',
this._humanId,
method,
ms(Date.now() - startTime),
kindOf(result)
)
return result
},
error => {
debug(
'%s: %s(...) [%s] =!> %s',
this._humanId,
method,
ms(Date.now() - startTime),
error
)
throw error
}
)
},
], (call, decorator) => decorator(call))
// ===================================================================
// The default value is a factory function.
export const createClient = opts => new Xapi(opts)

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env node
import { delay as pDelay } from 'promise-toolbox'
import { createClient } from './'
const xapi = (() => {
const [ , , url, user, password ] = process.argv
return createClient({
auth: { user, password },
url,
watchEvents: false,
})
})()
xapi.connect()
// Get the pool record's ref.
.then(() => xapi.call('pool.get_all'))
// Injects lots of events.
.then(([ poolRef ]) => {
const loop = () => xapi.call('event.inject', 'pool', poolRef)
::pDelay(10) // A small delay is required to avoid overloading the Xen API.
.then(loop)
return loop()
})

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env node
import { createClient } from './'
let i = 0
setInterval(() => {
const usage = process.memoryUsage()
console.log(
'%s %s %s %s',
i++,
Math.round(usage.rss / 1e6),
Math.round(usage.heapTotal / 1e6),
Math.round(usage.heapUsed / 1e6)
)
}, 1e2)
const [ , , url, user, password ] = process.argv
createClient({
auth: { user, password },
readOnly: true,
url,
}).connect()

View File

@@ -0,0 +1,3 @@
import makeError from 'make-error'
export const UnsupportedTransport = makeError('UnsupportedTransport')

View File

@@ -0,0 +1,36 @@
import jsonRpc from './json-rpc'
import xmlRpc from './xml-rpc'
import xmlRpcJson from './xml-rpc-json'
import { UnsupportedTransport } from './_utils'
const factories = [ jsonRpc, xmlRpcJson, xmlRpc ]
const { length } = factories
export default opts => {
let i = 0
let call
function create () {
const current = factories[i++](opts)
if (i < length) {
const currentI = i
call = (method, args) => current(method, args).catch(
error => {
if (error instanceof UnsupportedTransport) {
if (currentI === i) { // not changed yet
create()
}
return call(method, args)
}
throw error
}
)
} else {
call = current
}
}
create()
return (method, args) => call(method, args)
}

View File

@@ -0,0 +1,38 @@
import httpRequestPlus from 'http-request-plus'
import { format, parse } from 'json-rpc-protocol'
import { UnsupportedTransport } from './_utils'
export default ({ allowUnauthorized, url }) => {
return (method, args) => httpRequestPlus.post(url, {
rejectUnauthorized: !allowUnauthorized,
body: format.request(0, method, args),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
path: '/jsonrpc',
}).readAll('utf8').then(
text => {
let response
try {
response = parse(text)
} catch (error) {
throw new UnsupportedTransport()
}
if (response.type === 'response') {
return response.result
}
throw response.error
},
error => {
if (error.response !== undefined) { // HTTP error
throw new UnsupportedTransport()
}
throw error
}
)
}

View File

@@ -0,0 +1,97 @@
import { createClient, createSecureClient } from 'xmlrpc'
import { promisify } from 'promise-toolbox'
import { UnsupportedTransport } from './_utils'
const logError = error => {
if (error.res) {
console.error(
'XML-RPC Error: %s (response status %s)',
error.message,
error.res.statusCode
)
console.error('%s', error.body)
}
throw error
}
const SPECIAL_CHARS = {
'\r': '\\r',
'\t': '\\t',
}
const SPECIAL_CHARS_RE = new RegExp(
Object.keys(SPECIAL_CHARS).join('|'),
'g'
)
const parseResult = result => {
const status = result.Status
// Return the plain result if it does not have a valid XAPI
// format.
if (status === undefined) {
return result
}
if (status !== 'Success') {
throw result.ErrorDescription
}
const value = result.Value
// XAPI returns an empty string (invalid JSON) for an empty
// result.
if (value === '') {
return ''
}
try {
return JSON.parse(value)
} catch (error) {
// XAPI JSON sometimes contains invalid characters.
if (!(error instanceof SyntaxError)) {
throw error
}
}
let replaced = false
const fixedValue = value.replace(SPECIAL_CHARS_RE, match => {
replaced = true
return SPECIAL_CHARS[match]
})
if (replaced) {
try {
return JSON.parse(fixedValue)
} catch (error) {
if (!(error instanceof SyntaxError)) {
throw error
}
}
}
throw new UnsupportedTransport()
}
export default ({
allowUnauthorized,
url: { hostname, path, port, protocol },
}) => {
const client = (
protocol === 'https:'
? createSecureClient
: createClient
)({
host: hostname,
path: '/json',
port,
rejectUnauthorized: !allowUnauthorized,
})
const call = promisify(client.methodCall, client)
return (method, args) => call(method, args).then(
parseResult,
logError
)
}

View File

@@ -0,0 +1,52 @@
import { createClient, createSecureClient } from 'xmlrpc'
import { promisify } from 'promise-toolbox'
const logError = error => {
if (error.res) {
console.error(
'XML-RPC Error: %s (response status %s)',
error.message,
error.res.statusCode
)
console.error('%s', error.body)
}
throw error
}
const parseResult = result => {
const status = result.Status
// Return the plain result if it does not have a valid XAPI
// format.
if (status === undefined) {
return result
}
if (status !== 'Success') {
throw result.ErrorDescription
}
return result.Value
}
export default ({
allowUnauthorized,
url: { hostname, path, port, protocol },
}) => {
const client = (
protocol === 'https:'
? createSecureClient
: createClient
)({
host: hostname,
port,
rejectUnauthorized: !allowUnauthorized,
})
const call = promisify(client.methodCall, client)
return (method, args) => call(method, args).then(
parseResult,
logError
)
}

View File

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

View File

@@ -0,0 +1,10 @@
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/test/
/tests/
*.spec.js
*.spec.js.map

View File

@@ -0,0 +1,74 @@
# xo-acl-resolver [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> [Xen-Orchestra](http://xen-orchestra.com/) internal: do ACLs resolution.
## Install
Installation of the [npm package](https://npmjs.org/package/xo-acl-resolver):
```
> npm install --save xo-acl-resolver
```
## Usage
```js
import check from 'xo-acl-resolver'
// This object contains a list of permissions returned from
// xo-server's acl.getCurrentPermissions.
const permissions = { /* ... */ }
// This function should returns synchronously an object from an id.
const getObject = id => { /* ... */ }
// For a single object:
if (check(permissions, getObject, objectId, permission)) {
console.log(`${permission} set for object ${objectId}`)
}
// For multiple objects/permissions:
if (check(permissions, getObject, [
[ object1Id, permission1 ],
[ object12d, permission2 ],
])) {
console.log('all permissions checked')
}
```
## Development
### Installing dependencies
```
> npm install
```
### Compilation
The sources files are watched and automatically recompiled on changes.
```
> npm run dev
```
### Tests
```
> npm run test-dev
```
## 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,35 @@
{
"name": "xo-acl-resolver",
"version": "0.2.3",
"license": "ISC",
"description": "Xen-Orchestra internal: do ACLs resolution",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-acl-resolver",
"bugs": "https://github.com/vatesfr/xo-web/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/"
],
"engines": {
"node": ">=0.12"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-preset-env": "^1.6.1"
},
"scripts": {
"build": "babel --source-maps --out-dir=dist/ src/",
"dev": "babel --watch --source-maps --out-dir=dist/ src/",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,131 @@
// These global variables are not a problem because the algorithm is
// synchronous.
let permissionsByObject
let getObject
// -------------------------------------------------------------------
const authorized = () => true // eslint-disable-line no-unused-vars
const forbiddden = () => false // eslint-disable-line no-unused-vars
const and = (...checkers) => (object, permission) => { // eslint-disable-line no-unused-vars
for (const checker of checkers) {
if (!checker(object, permission)) {
return false
}
}
return true
}
const or = (...checkers) => (object, permission) => { // eslint-disable-line no-unused-vars
for (const checker of checkers) {
if (checker(object, permission)) {
return true
}
}
return false
}
// -------------------------------------------------------------------
const checkMember = (memberName) => (object, permission) => {
const member = object[memberName]
return member !== object.id && checkAuthorization(member, permission)
}
const checkSelf = ({ id }, permission) => {
const permissionsForObject = permissionsByObject[id]
return (
permissionsForObject &&
permissionsForObject[permission]
)
}
// ===================================================================
const checkAuthorizationByTypes = {
host: or(checkSelf, checkMember('$pool')),
message: checkMember('$object'),
network: or(checkSelf, checkMember('$pool')),
SR: or(checkSelf, checkMember('$pool')),
task: checkMember('$host'),
VBD: checkMember('VDI'),
// Access to a VDI is granted if the user has access to the
// containing SR or to a linked VM.
VDI (vdi, permission) {
// Check authorization for the containing SR.
if (checkAuthorization(vdi.$SR, permission)) {
return true
}
// Check authorization for each of the connected VMs.
for (const vbdId of vdi.$VBDs) {
if (checkAuthorization(getObject(vbdId).VM, permission)) {
return true
}
}
return false
},
'VDI-snapshot': checkMember('$snapshot_of'),
VIF: or(checkMember('$network'), checkMember('$VM')),
VM: or(checkSelf, checkMember('$container')),
'VM-controller': checkMember('$container'),
'VM-snapshot': checkMember('$snapshot_of'),
'VM-template': or(checkSelf, checkMember('$pool')),
}
// Hoisting is important for this function.
function checkAuthorization (objectId, permission) {
const object = getObject(objectId)
if (!object) {
return false
}
const checker = checkAuthorizationByTypes[object.type] || checkSelf
return checker(object, permission)
}
// -------------------------------------------------------------------
export default (
permissionsByObject_,
getObject_,
permissions,
permission
) => {
// Assign global variables.
permissionsByObject = permissionsByObject_
getObject = getObject_
try {
if (permission) {
return checkAuthorization(permissions, permission)
} else {
for (const [objectId, permission] of permissions) {
if (!checkAuthorization(objectId, permission)) {
return false
}
}
}
return true
} finally {
// Free the global variables.
permissionsByObject = getObject = null
}
}

View File

@@ -0,0 +1,22 @@
const { NODE_ENV = 'development' } = process.env
module.exports = {
comments: false,
compact: true,
ignore: NODE_ENV === 'test' ? undefined : ['*.spec.js'],
// plugins: ['lodash']
presets: [
[
'env',
{
debug: true,
loose: true,
targets: {
node: process.env.NODE_ENV === 'production' ? '6' : 'current'
},
useBuiltIns: 'usage'
}
],
'flow'
]
}

View File

@@ -0,0 +1,12 @@
[ignore]
[include]
[libs]
[lints]
[options]
experimental.const_params=true
module.use_strict=true
unsafe.enable_getters_and_setters=true

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,8 @@
language: node_js
node_js:
- stable
- 6
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

156
packages/xo-cli/README.md Normal file
View File

@@ -0,0 +1,156 @@
# XO-CLI
[![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](http://travis-ci.org/vatesfr/xen-orchestra)
[![Dependency Status](https://david-dm.org/vatesfr/xo-cli/status.svg?theme=shields.io)](https://david-dm.org/vatesfr/xo-cli)
[![devDependency Status](https://david-dm.org/vatesfr/xo-cli/dev-status.svg?theme=shields.io)](https://david-dm.org/vatesfr/xo-cli#info=devDependencies)
> Basic CLI for Xen-Orchestra
## Install
#### [npm](https://npmjs.org/package/xo-cli)
```
npm install -g xo-cli
```
## Usage
```
> xo-cli --help
Usage:
xo-cli --register [--expiresIn duration] <XO-Server URL> <username> [<password>]
Registers the XO instance to use.
--expiresIn duration
Can be used to change the validity duration of the
authorization token (default: one month).
xo-cli --unregister
Remove stored credentials.
xo-cli --list-commands [--json] [<pattern>]...
Returns the list of available commands on the current XO instance.
The patterns can be used to filter on command names.
xo-cli --list-objects [--<property>]… [<property>=<value>]...
Returns a list of XO objects.
--<property>
Restricts displayed properties to those listed.
<property>=<value>
Restricted displayed objects to those matching the patterns.
xo-cli <command> [<name>=<value>]...
Executes a command on the current XO instance.
```
#### Register your XO instance
```
> xo-cli --register http://xo.my-company.net admin@admin.net admin
Successfully logged with admin@admin.net
```
Note: only a token will be saved in the configuration file.
#### List available objects
Prints all objects:
```
> xo-cli --list-objects
```
It is possible to filter on object properties, for instance to prints
all VM templates:
```
> xo-cli --list-objects type=VM-template
```
#### List available commands
```
> xo-cli --list-commands
```
Commands can be filtered using patterns:
```
> xo-cli --list-commands '{user,group}.*'
```
#### Execute a command
The same syntax is used for all commands: `xo-cli <command> <param
name>=<value>...`
E.g., adding a new server:
```
> xo-cli server.add host=my.server.net username=root password=secret-password
42
```
The return value is the identifier of this new server in XO.
Parameters (except `true` and `false` which are correctly parsed as
booleans) are assumed to be strings, for other types, you may use JSON
encoding by prefixing with `json:`:
```
> xo-cli foo.bar baz='json:[1, 2, 3]'
```
##### VM export
```
> xo-cli vm.export vm=a01667e0-8e29-49fc-a550-17be4226783c @=vm.xva
```
##### VM import
```
> xo-cli vm.import host=60a6939e-8b0a-4352-9954-5bde44bcdf7d @=vm.xva
```
## 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
# Commit changes
> yarn cz
```
## 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
XO-CLI is released under the [AGPL
v3](http://www.gnu.org/licenses/agpl-3.0-standalone.html).

View File

@@ -0,0 +1,68 @@
{
"name": "xo-cli",
"version": "0.10.1",
"license": "AGPL-3.0",
"description": "Basic CLI for Xen-Orchestra",
"keywords": [
"xo",
"xen-orchestra",
"xen",
"orchestra"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-cli",
"bugs": "https://github.com/vatesfr/xo-web/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": "Julien Fontanet <julien.fontanet@vates.fr>",
"preferGlobal": true,
"main": "dist/",
"bin": {
"xo-cli": "dist/index.js"
},
"files": [
"dist/"
],
"engines": {
"node": ">=6"
},
"dependencies": {
"babel-polyfill": "^7.0.0-beta.3",
"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",
"human-format": "^0.9.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",
"progress-stream": "^2.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-beta.3",
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^7.0.0-beta.3",
"babel-preset-flow": "^7.0.0-beta.3",
"cross-env": "^5.1.0",
"flow-bin": "^0.60.1",
"rimraf": "^2.6.2"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublishOnly": "npm run build",
"pretest": "flow status"
}
}

View File

@@ -0,0 +1,54 @@
'use strict'
// ===================================================================
const promisify = require('bluebird').promisify
const readFile = promisify(require('fs').readFile)
const writeFile = promisify(require('fs').writeFile)
const assign = require('lodash/assign')
const l33t = require('l33teral')
const mkdirp = promisify(require('mkdirp'))
const xdgBasedir = require('xdg-basedir')
// ===================================================================
const configPath = xdgBasedir.config + '/xo-cli'
const configFile = configPath + '/config.json'
// ===================================================================
const load = exports.load = function () {
return readFile(configFile).then(JSON.parse).catch(function () {
return {}
})
}
exports.get = function (path) {
return load().then(function (config) {
return l33t(config).tap(path)
})
}
const save = exports.save = function (config) {
return mkdirp(configPath).then(function () {
return writeFile(configFile, JSON.stringify(config))
})
}
exports.set = function (data) {
return load().then(function (config) {
return save(assign(config, data))
})
}
exports.unset = function (paths) {
return load().then(function (config) {
const l33tConfig = l33t(config)
;[].concat(paths).forEach(function (path) {
l33tConfig.purge(path, true)
})
return save(config)
})
}

410
packages/xo-cli/src/index.js Executable file
View File

@@ -0,0 +1,410 @@
#!/usr/bin/env node
'use strict'
const Bluebird = require('bluebird')
Bluebird.longStackTraces()
const createReadStream = require('fs').createReadStream
const createWriteStream = require('fs').createWriteStream
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 getKeys = require('lodash/keys')
const got = require('got')
const humanFormat = require('human-format')
const identity = require('lodash/identity')
const isArray = require('lodash/isArray')
const isObject = require('lodash/isObject')
const micromatch = require('micromatch')
const nicePipe = require('nice-pipe')
const pairs = require('lodash/toPairs')
const pick = require('lodash/pick')
const startsWith = require('lodash/startsWith')
const prettyMs = require('pretty-ms')
const progressStream = require('progress-stream')
const pw = require('pw')
// FIXME: re-enable the rule when https://github.com/mysticatea/eslint-plugin-node/issues/100 is fixed
// eslint-disable-next-line node/no-missing-require
const Xo = require('xo-lib').default
// -------------------------------------------------------------------
const config = require('./config')
// ===================================================================
async function connect () {
const { server, token } = await config.load()
if (server === undefined) {
throw new Error('no server to connect to!')
}
if (token === undefined) {
throw new Error('no token available')
}
const xo = new Xo({ url: server })
await xo.open()
await xo.signIn({ token })
return xo
}
const FLAG_RE = /^--([^=]+)(?:=([^]*))?$/
function extractFlags (args) {
const flags = {}
let i = 0
const n = args.length
let matches
while (i < n && (matches = args[i].match(FLAG_RE))) {
const value = matches[2]
flags[matches[1]] = value === undefined ? true : value
++i
}
args.splice(0, i)
return flags
}
const PARAM_RE = /^([^=]+)=([^]*)$/
function parseParameters (args) {
const params = {}
forEach(args, function (arg) {
let matches
if (!(matches = arg.match(PARAM_RE))) {
throw new Error('invalid arg: ' + arg)
}
const name = matches[1]
let value = matches[2]
if (startsWith(value, 'json:')) {
value = JSON.parse(value.slice(5))
}
if (name === '@') {
params['@'] = value
return
}
if (value === 'true') {
value = true
} else if (value === 'false') {
value = false
}
params[name] = value
})
return params
}
const humanFormatOpts = {
unit: 'B',
scale: 'binary',
}
function printProgress (progress) {
if (progress.length) {
console.warn('%s% of %s @ %s/s - ETA %s',
Math.round(progress.percentage),
humanFormat(progress.length, humanFormatOpts),
humanFormat(progress.speed, humanFormatOpts),
prettyMs(progress.eta * 1e3)
)
} else {
console.warn('%s @ %s/s',
humanFormat(progress.transferred, humanFormatOpts),
humanFormat(progress.speed, humanFormatOpts)
)
}
}
function wrap (val) {
return function wrappedValue () {
return val
}
}
// ===================================================================
const help = wrap((function (pkg) {
return require('strip-indent')(`
Usage:
$name --register [--expiresIn duration] <XO-Server URL> <username> [<password>]
Registers the XO instance to use.
--expiresIn duration
Can be used to change the validity duration of the
authorization token (default: one month).
$name --unregister
Remove stored credentials.
$name --list-commands [--json] [<pattern>]...
Returns the list of available commands on the current XO instance.
The patterns can be used to filter on command names.
$name --list-objects [--<property>]… [<property>=<value>]...
Returns a list of XO objects.
--<property>
Restricts displayed properties to those listed.
<property>=<value>
Restricted displayed objects to those matching the patterns.
$name <command> [<name>=<value>]...
Executes a command on the current XO instance.
$name v$version
`).replace(/<([^>]+)>|\$(\w+)/g, function (_, arg, key) {
if (arg) {
return '<' + chalk.yellow(arg) + '>'
}
if (key === 'name') {
return chalk.bold(pkg[key])
}
return pkg[key]
})
})(require('../package')))
// -------------------------------------------------------------------
function main (args) {
if (!args || !args.length || args[0] === '-h') {
return help()
}
const fnName = args[0].replace(/^--|-\w/g, function (match) {
if (match === '--') {
return ''
}
return match[1].toUpperCase()
})
if (fnName in exports) {
return exports[fnName](args.slice(1))
}
return exports.call(args)
}
exports = module.exports = main
// -------------------------------------------------------------------
exports.help = help
async function register (args) {
let expiresIn
if (args[0] === '--expiresIn') {
expiresIn = args[1]
args = args.slice(2)
}
const [
url,
email,
password = await new Promise(function (resolve) {
process.stdout.write('Password: ')
pw(resolve)
}),
] = args
const xo = new Xo({ url })
await xo.open()
await xo.signIn({ email, password })
console.log('Successfully logged with', xo.user.email)
await config.set({
server: url,
token: await xo.call('token.create', { expiresIn }),
})
}
exports.register = register
function unregister () {
return config.unset([
'server',
'token',
])
}
exports.unregister = unregister
async function listCommands (args) {
const xo = await connect()
let methods = await xo.call('system.getMethodsInfo')
let json = false
const patterns = []
forEach(args, function (arg) {
if (arg === '--json') {
json = true
} else {
patterns.push(arg)
}
})
if (patterns.length) {
methods = pick(methods, micromatch(Object.keys(methods), patterns))
}
if (json) {
return methods
}
methods = pairs(methods)
methods.sort(function (a, b) {
a = a[0]
b = b[0]
if (a < b) {
return -1
}
return +(a > b)
})
const str = []
forEach(methods, function (method) {
const name = method[0]
const info = method[1]
str.push(chalk.bold.blue(name))
forEach(info.params || [], function (info, name) {
str.push(' ')
if (info.optional) {
str.push('[')
}
const type = info.type
str.push(
name,
'=<',
type == null
? 'unknown type'
: isArray(type)
? type.join('|')
: type,
'>'
)
if (info.optional) {
str.push(']')
}
})
str.push('\n')
if (info.description) {
str.push(' ', info.description, '\n')
}
})
return str.join('')
}
exports.listCommands = listCommands
async function listObjects (args) {
const properties = getKeys(extractFlags(args))
const filterProperties = properties.length
? function (object) {
return pick(object, properties)
}
: identity
const filter = args.length ? parseParameters(args) : undefined
const xo = await connect()
const objects = await xo.call('xo.getAllObjects', { filter })
const stdout = process.stdout
stdout.write('[\n')
const keys = Object.keys(objects)
for (let i = 0, n = keys.length; i < n;) {
stdout.write(JSON.stringify(filterProperties(objects[keys[i]]), null, 2))
stdout.write(++i < n ? ',\n' : '\n')
}
stdout.write(']\n')
}
exports.listObjects = listObjects
async function call (args) {
if (!args.length) {
throw new Error('missing command name')
}
const method = args.shift()
const params = parseParameters(args)
const file = params['@']
delete params['@']
const xo = await connect()
// FIXME: do not use private properties.
const baseUrl = xo._url.replace(/^ws/, 'http')
const result = await xo.call(method, params)
let keys, key, url
if (
isObject(result) &&
(keys = getKeys(result)).length === 1
) {
key = keys[0]
if (key === '$getFrom') {
url = resolveUrl(baseUrl, result[key])
const output = createWriteStream(file)
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')
}
if (key === '$sendTo') {
url = resolveUrl(baseUrl, result[key])
const stats = await stat(file)
const length = stats.size
const input = nicePipe([
createReadStream(file),
progressStream({
length: length,
time: 1e3,
}, printProgress),
])
const response = await got.post(url, {
body: input,
headers: {
'content-length': length,
},
method: 'POST',
})
return response.body
}
}
return result
}
exports.call = call
// ===================================================================
if (!module.parent) {
require('exec-promise')(exports)
}

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,662 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

View File

@@ -0,0 +1,265 @@
# xo-collection [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> Generic in-memory collection with events
## Install
Installation of the [npm package](https://npmjs.org/package/xo-collection):
```
> npm install --save xo-collection
```
## Usage
```javascript
var Collection = require('xo-collection')
```
### Creation
```javascript
// Creates a new collection.
var col = new Collection()
```
### Manipulation
**Inserting a new item**
```javascript
col.add('foo', true)
```
- **Throws** `DuplicateItem` if the item is already in the collection.
**Updating an existing item**
```javascript
col.update('foo', false)
```
- **Throws** `NoSuchItem` if the item is not in the collection.
**Inserting or updating an item**
```javascript
col.set('bar', true)
```
**Notifying an external update**
> If an item is an object, it can be updated directly without using
> the `set`/`update` methods.
>
> To make sure the collection stays in sync and the correct events are
> sent, the `touch` method can be used to notify the change.
```javascript
var baz = {}
col.add('baz', baz)
baz.prop = true
col.touch('baz')
```
> Because this is a much used pattern, `touch` returns the item to
> allow its direct modification.
```javascript
col.touch('baz').prop = false
```
- **Throws** `NoSuchItem` if the item is not in the collection.
- **Throws** `IllegalTouch` if the item is not an object.
**Removing an existing item**
```javascript
col.remove('bar')
```
- **Throws** `NoSuchItem` if the item is not in the collection.
**Removing an item without error**
This is the symmetric method of `set()`: it removes the item if it
exists otherwise does nothing.
```javascript
col.unset('bar')
```
**Removing all items**
```javascript
col.clear()
```
### Query
**Checking the existence of an item**
```javascript
var hasBar = col.has('bar')
```
**Getting an existing item**
```javascript
var foo = col.get('foo')
// The second parameter can be used to specify a fallback in case the
// item does not exist.
var bar = col.get('bar', 6.28)
```
- **Throws** `NoSuchItem` if the item is not in the collection and no
fallback has been passed.
**Getting a read-only view of the collection**
> This property is useful for example to iterate over the collection
> or to make advanced queries with the help of utility libraries such
> as lodash.
```javascript
var _ = require('lodash')
// Prints all the items.
_.forEach(col.all, function (value, key) {
console.log('- %s: %j', key, value)
})
// Finds all the items which are objects and have a property
// `active` which equals `true`.
var results = _.where(col.all, { active: true })
```
**Getting the number of items**
```javascript
var size = col.size
```
### Events
> The events are emitted asynchronously (at the next turn/tick of the
> event loop) and are deduplicated which means, for instance, that an
> addition followed by an update will result only in a single
> addition.
**New items**
```javascript
col.on('add', (added) => {
forEach(added, (value, key) => {
console.log('+ %s: %j', key, value)
})
})
```
**Updated items**
```javascript
col.on('update', (updated) => {
forEach(updated, (value, key) => {
console.log('± %s: %j', key, value)
})
})
```
**Removed items**
```javascript
col.on('remove', (removed) => {
// For consistency, `removed` is also a map but contrary to `added`
// and `updated`, the values associated to the keys are not
// significant since the items have already be removed.
forEach(removed, (value, key) => {
console.log('- %s', key)
})
})
```
**End of update**
> Emitted when all the update process is finished and all the update
> events has been emitted.
```javascript
col.on('finish', () => {
console.log('the collection has been updated')
})
```
### Iteration
```javascript
for (const [key, value] of col) {
console.log('- %s: %j', key, value)
}
for (const key of col.keys()) {
console.log('- %s', key)
}
for (const value of col.values()) {
console.log('- %j', value)
}
```
### Views
```javascript
const View = require('xo-collection/view')
```
> A view is a read-only collection which contains only the items of a
> parent collection which satisfy a predicate.
>
> It is updated at most once per turn of the event loop and therefore
> can be briefly invalid.
```javascript
const myView = new View(parentCollection, function predicate (value, key) {
// This function should return a boolean indicating whether the
// current item should be in this view.
})
```
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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](http://vates.fr)

View File

@@ -0,0 +1 @@
module.exports = require('./dist/index') // eslint-disable-line node/no-missing-require

View File

@@ -0,0 +1,68 @@
{
"name": "xo-collection",
"version": "0.4.1",
"license": "ISC",
"description": "Generic in-memory collection with events",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-collection",
"bugs": "https://github.com/vatesfr/xo-web/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Fabrice Marsaud",
"email": "fabrice.marsaud@vates.fr"
},
"preferGlobal": false,
"main": "dist/collection",
"bin": {},
"files": [
"dist/",
"*.js"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"babel-runtime": "^6.18.0",
"kindof": "^2.0.0",
"lodash": "^4.17.2",
"make-error": "^1.0.2"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"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.0.1",
"event-to-promise": "^0.8.0",
"lint-staged": "^6.0.0",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"lodash",
"transform-runtime"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -0,0 +1,5 @@
export default function clearObject (object) {
for (const key in object) {
delete object[key]
}
}

View File

@@ -0,0 +1,363 @@
import kindOf from 'kindof'
import {BaseError} from 'make-error'
import {EventEmitter} from 'events'
import {forEach} from 'lodash'
import isEmpty from './is-empty'
import isObject from './is-object'
// ===================================================================
const {
create: createObject,
prototype: { hasOwnProperty },
} = Object
export const ACTION_ADD = 'add'
export const ACTION_UPDATE = 'update'
export const ACTION_REMOVE = 'remove'
// ===================================================================
export class BufferAlreadyFlushed extends BaseError {
constructor () {
super('buffer flush already requested')
}
}
export class DuplicateIndex extends BaseError {
constructor (name) {
super('there is already an index with the name ' + name)
}
}
export class DuplicateItem extends BaseError {
constructor (key) {
super('there is already a item with the key ' + key)
}
}
export class IllegalTouch extends BaseError {
constructor (value) {
super('only an object value can be touched (found a ' + kindOf(value) + ')')
}
}
export class InvalidKey extends BaseError {
constructor (key) {
super('invalid key of type ' + kindOf(key))
}
}
export class NoSuchIndex extends BaseError {
constructor (name) {
super('there is no index with the name ' + name)
}
}
export class NoSuchItem extends BaseError {
constructor (key) {
super('there is no item with the key ' + key)
}
}
// -------------------------------------------------------------------
export default class Collection extends EventEmitter {
constructor () {
super()
this._buffer = createObject(null)
this._buffering = 0
this._indexes = createObject(null)
this._indexedItems = createObject(null)
this._items = {} // createObject(null)
this._size = 0
}
// Overridable method used to compute the key of an item when
// unspecified.
//
// Default implementation returns the `id` property.
getKey (value) {
return value && value.id
}
// -----------------------------------------------------------------
// Properties
// -----------------------------------------------------------------
get all () {
return this._items
}
get indexes () {
return this._indexedItems
}
get size () {
return this._size
}
// -----------------------------------------------------------------
// Manipulation
// -----------------------------------------------------------------
add (keyOrObjectWithId, valueIfKey = undefined) {
const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
this._assertHasNot(key)
this._items[key] = value
this._size++
this._touch(ACTION_ADD, key)
}
clear () {
forEach(this._items, (_, key) => this._remove(key))
}
remove (keyOrObjectWithId) {
const [key] = this._resolveItem(keyOrObjectWithId)
this._assertHas(key)
this._remove(key)
}
set (keyOrObjectWithId, valueIfKey = undefined) {
const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
const action = this.has(key) ? ACTION_UPDATE : ACTION_ADD
this._items[key] = value
if (action === ACTION_ADD) {
this._size++
}
this._touch(action, key)
}
touch (keyOrObjectWithId) {
const [key] = this._resolveItem(keyOrObjectWithId)
this._assertHas(key)
const value = this.get(key)
if (!isObject(value)) {
throw new IllegalTouch(value)
}
this._touch(ACTION_UPDATE, key)
return this.get(key)
}
unset (keyOrObjectWithId) {
const [key] = this._resolveItem(keyOrObjectWithId)
if (this.has(key)) {
this._remove(key)
}
}
update (keyOrObjectWithId, valueIfKey = undefined) {
const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
this._assertHas(key)
this._items[key] = value
this._touch(ACTION_UPDATE, key)
}
// -----------------------------------------------------------------
// Query
// -----------------------------------------------------------------
get (key, defaultValue) {
if (this.has(key)) {
return this._items[key]
}
if (arguments.length > 1) {
return defaultValue
}
// Throws a NoSuchItem.
this._assertHas(key)
}
has (key) {
return hasOwnProperty.call(this._items, key)
}
// -----------------------------------------------------------------
// Indexes
// -----------------------------------------------------------------
createIndex (name, index) {
const {_indexes: indexes} = this
if (hasOwnProperty.call(indexes, name)) {
throw new DuplicateIndex(name)
}
indexes[name] = index
this._indexedItems[name] = index.items
index._attachCollection(this)
}
deleteIndex (name) {
const {_indexes: indexes} = this
if (!hasOwnProperty.call(indexes, name)) {
throw new NoSuchIndex(name)
}
const index = indexes[name]
delete indexes[name]
delete this._indexedItems[name]
index._detachCollection(this)
}
// -----------------------------------------------------------------
// Iteration
// -----------------------------------------------------------------
* [Symbol.iterator] () {
const {_items: items} = this
for (const key in items) {
yield [key, items[key]]
}
}
* keys () {
const {_items: items} = this
for (const key in items) {
yield key
}
}
* values () {
const {_items: items} = this
for (const key in items) {
yield items[key]
}
}
// -----------------------------------------------------------------
// Events buffering
// -----------------------------------------------------------------
bufferEvents () {
++this._buffering
let called = false
return () => {
if (called) {
throw new BufferAlreadyFlushed()
}
called = true
if (--this._buffering) {
return
}
const {_buffer: buffer} = this
// Due to deduplication there could be nothing in the buffer.
if (isEmpty(buffer)) {
return
}
const data = {
add: createObject(null),
remove: createObject(null),
update: createObject(null),
}
for (const key in this._buffer) {
data[buffer[key]][key] = this._items[key]
}
forEach(data, (items, action) => {
if (!isEmpty(items)) {
this.emit(action, items)
}
})
// Indicates the end of the update.
//
// This name has been chosen because it is used in Node writable
// streams when the data has been successfully committed.
this.emit('finish')
this._buffer = createObject(null)
}
}
// =================================================================
_assertHas (key) {
if (!this.has(key)) {
throw new NoSuchItem(key)
}
}
_assertHasNot (key) {
if (this.has(key)) {
throw new DuplicateItem(key)
}
}
_assertValidKey (key) {
if (!this._isValidKey(key)) {
throw new InvalidKey(key)
}
}
_isValidKey (key) {
return typeof key === 'number' || typeof key === 'string'
}
_remove (key) {
delete this._items[key]
this._size--
this._touch(ACTION_REMOVE, key)
}
_resolveItem (keyOrObjectWithId, valueIfKey = undefined) {
if (valueIfKey !== undefined) {
this._assertValidKey(keyOrObjectWithId)
return [keyOrObjectWithId, valueIfKey]
}
if (this._isValidKey(keyOrObjectWithId)) {
return [keyOrObjectWithId]
}
const key = this.getKey(keyOrObjectWithId)
this._assertValidKey(key)
return [key, keyOrObjectWithId]
}
_touch (action, key) {
if (this._buffering === 0) {
const flush = this.bufferEvents()
process.nextTick(flush)
}
if (action === ACTION_ADD) {
this._buffer[key] = this._buffer[key] ? ACTION_UPDATE : ACTION_ADD
} else if (action === ACTION_REMOVE) {
if (this._buffer[key] === ACTION_ADD) {
delete this._buffer[key]
} else {
this._buffer[key] = ACTION_REMOVE
}
} else { // update
if (!this._buffer[key]) {
this._buffer[key] = ACTION_UPDATE
}
}
}
}

View File

@@ -0,0 +1,340 @@
/* eslint-env jest */
import eventToPromise from 'event-to-promise'
import { forEach } from 'lodash'
import Collection, {DuplicateItem, NoSuchItem} from './collection'
// ===================================================================
function waitTicks (n = 2) {
const {nextTick} = process
return new Promise(function (resolve) {
(function waitNextTick () {
// The first tick is handled by Promise#then()
if (--n) {
nextTick(waitNextTick)
} else {
resolve()
}
})()
})
}
describe('Collection', function () {
let col
beforeEach(function () {
col = new Collection()
col.add('bar', 0)
return waitTicks()
})
it('is iterable', function () {
const iterator = col[Symbol.iterator]()
expect(iterator.next()).toEqual({done: false, value: ['bar', 0]})
expect(iterator.next()).toEqual({done: true, value: undefined})
})
describe('#keys()', function () {
it('returns an iterator over the keys', function () {
const iterator = col.keys()
expect(iterator.next()).toEqual({done: false, value: 'bar'})
expect(iterator.next()).toEqual({done: true, value: undefined})
})
})
describe('#values()', function () {
it('returns an iterator over the values', function () {
const iterator = col.values()
expect(iterator.next()).toEqual({done: false, value: 0})
expect(iterator.next()).toEqual({done: true, value: undefined})
})
})
describe('#add()', function () {
it('adds item to the collection', function () {
const spy = jest.fn()
col.on('add', spy)
col.add('foo', true)
expect(col.get('foo')).toBe(true)
// No sync events.
expect(spy).not.toHaveBeenCalled()
// Async event.
return eventToPromise(col, 'add').then(function (added) {
expect(Object.keys(added)).toEqual([ 'foo' ])
expect(added.foo).toBe(true)
})
})
it('throws an exception if the item already exists', function () {
expect(() => col.add('bar', true)).toThrowError(DuplicateItem)
})
it('accepts an object with an id property', function () {
const foo = { id: 'foo' }
col.add(foo)
expect(col.get(foo.id)).toBe(foo)
})
})
describe('#update()', function () {
it('updates an item of the collection', function () {
const spy = jest.fn()
col.on('update', spy)
col.update('bar', 1)
expect(col.get('bar')).toBe(1) // Will be forgotten by de-duplication
col.update('bar', 2)
expect(col.get('bar')).toBe(2)
// No sync events.
expect(spy).not.toHaveBeenCalled()
// Async event.
return eventToPromise(col, 'update').then(function (updated) {
expect(Object.keys(updated)).toEqual(['bar'])
expect(updated.bar).toBe(2)
})
})
it('throws an exception if the item does not exist', function () {
expect(() => col.update('baz', true)).toThrowError(NoSuchItem)
})
it('accepts an object with an id property', function () {
const bar = { id: 'bar' }
col.update(bar)
expect(col.get(bar.id)).toBe(bar)
})
})
describe('#remove()', function () {
it('removes an item of the collection', function () {
const spy = jest.fn()
col.on('remove', spy)
col.update('bar', 1)
expect(col.get('bar')).toBe(1) // Will be forgotten by de-duplication
col.remove('bar')
// No sync events.
expect(spy).not.toHaveBeenCalled()
// Async event.
return eventToPromise(col, 'remove').then(function (removed) {
expect(Object.keys(removed)).toEqual(['bar'])
expect(removed.bar).toBeUndefined()
})
})
it('throws an exception if the item does not exist', function () {
expect(() => col.remove('baz', true)).toThrowError(NoSuchItem)
})
it('accepts an object with an id property', function () {
const bar = { id: 'bar' }
col.remove(bar)
expect(col.has(bar.id)).toBe(false)
})
})
describe('#set()', function () {
it('adds item if collection has not key', function () {
const spy = jest.fn()
col.on('add', spy)
col.set('foo', true)
expect(col.get('foo')).toBe(true)
// No sync events.
expect(spy).not.toHaveBeenCalled()
// Async events.
return eventToPromise(col, 'add').then(function (added) {
expect(Object.keys(added)).toEqual(['foo'])
expect(added.foo).toBe(true)
})
})
it('updates item if collection has key', function () {
const spy = jest.fn()
col.on('udpate', spy)
col.set('bar', 1)
expect(col.get('bar')).toBe(1)
// No sync events.
expect(spy).not.toHaveBeenCalled()
// Async events.
return eventToPromise(col, 'update').then(function (updated) {
expect(Object.keys(updated)).toEqual(['bar'])
expect(updated.bar).toBe(1)
})
})
it('accepts an object with an id property', function () {
const foo = { id: 'foo' }
col.set(foo)
expect(col.get(foo.id)).toBe(foo)
})
})
describe('#unset()', function () {
it('removes an existing item', function () {
col.unset('bar')
expect(col.has('bar')).toBe(false)
return eventToPromise(col, 'remove').then(function (removed) {
expect(Object.keys(removed)).toEqual(['bar'])
expect(removed.bar).toBeUndefined()
})
})
it('does not throw if the item does not exists', function () {
col.unset('foo')
})
it('accepts an object with an id property', function () {
col.unset({id: 'bar'})
expect(col.has('bar')).toBe(false)
return eventToPromise(col, 'remove').then(function (removed) {
expect(Object.keys(removed)).toEqual(['bar'])
expect(removed.bar).toBeUndefined()
})
})
})
describe('touch()', function () {
it('can be used to signal an indirect update', function () {
const foo = { id: 'foo' }
col.add(foo)
return waitTicks().then(() => {
col.touch(foo)
return eventToPromise(col, 'update', (items) => {
expect(Object.keys(items)).toEqual(['foo'])
expect(items.foo).toBe(foo)
})
})
})
})
describe('clear()', function () {
it('removes all items from the collection', function () {
col.clear()
expect(col.size).toBe(0)
return eventToPromise(col, 'remove').then((items) => {
expect(Object.keys(items)).toEqual(['bar'])
expect(items.bar).toBeUndefined()
})
})
})
describe('deduplicates events', function () {
forEach({
'add & update → add': [
[
['add', 'foo', 0],
['update', 'foo', 1],
],
{
add: {
foo: 1,
},
},
],
'add & remove → ∅': [
[
['add', 'foo', 0],
['remove', 'foo'],
],
{},
],
'update & update → update': [
[
['update', 'bar', 1],
['update', 'bar', 2],
],
{
update: {
bar: 2,
},
},
],
'update & remove → remove': [
[
['update', 'bar', 1],
['remove', 'bar'],
],
{
remove: {
bar: undefined,
},
},
],
'remove & add → update': [
[
['remove', 'bar'],
['add', 'bar', 0],
],
{
update: {
bar: 0,
},
},
],
}, ([operations, results], label) => {
it(label, function () {
forEach(operations, ([method, ...args]) => {
col[method](...args)
})
const spies = Object.create(null)
forEach(['add', 'update', 'remove'], event => {
col.on(event, (spies[event] = jest.fn()))
})
return waitTicks().then(() => {
forEach(spies, (spy, event) => {
const items = results[event]
if (items) {
expect(spy.mock.calls).toEqual([ [ items ] ])
} else {
expect(spy).not.toHaveBeenCalled()
}
})
})
})
})
})
})

View File

@@ -0,0 +1,149 @@
import { bind, iteratee } from 'lodash'
import clearObject from './clear-object'
import isEmpty from './is-empty'
import NotImplemented from './not-implemented'
import {
ACTION_ADD,
ACTION_UPDATE,
ACTION_REMOVE,
} from './collection'
// ===================================================================
export default class Index {
constructor (computeHash) {
if (computeHash) {
this.computeHash = iteratee(computeHash)
}
this._itemsByHash = Object.create(null)
this._keysToHash = Object.create(null)
// Bound versions of listeners.
this._onAdd = bind(this._onAdd, this)
this._onUpdate = bind(this._onUpdate, this)
this._onRemove = bind(this._onRemove, this)
}
// This method is used to compute the hash under which an item must
// be saved.
computeHash (value, key) {
throw new NotImplemented('this method must be overridden')
}
// Remove empty items lists.
sweep () {
const {_itemsByHash: itemsByHash} = this
for (const hash in itemsByHash) {
if (isEmpty(itemsByHash[hash])) {
delete itemsByHash[hash]
}
}
}
// -----------------------------------------------------------------
get items () {
return this._itemsByHash
}
// -----------------------------------------------------------------
_attachCollection (collection) {
// Add existing entries.
//
// FIXME: I think there may be a race condition if the `add` event
// has not been emitted yet.
this._onAdd(collection.all)
collection.on(ACTION_ADD, this._onAdd)
collection.on(ACTION_UPDATE, this._onUpdate)
collection.on(ACTION_REMOVE, this._onRemove)
}
_detachCollection (collection) {
collection.removeListener(ACTION_ADD, this._onAdd)
collection.removeListener(ACTION_UPDATE, this._onUpdate)
collection.removeListener(ACTION_REMOVE, this._onRemove)
clearObject(this._itemsByHash)
clearObject(this._keysToHash)
}
// -----------------------------------------------------------------
_onAdd (items) {
const {
computeHash,
_itemsByHash: itemsByHash,
_keysToHash: keysToHash,
} = this
for (const key in items) {
const value = items[key]
const hash = computeHash(value, key)
if (hash != null) {
(
itemsByHash[hash] ||
// FIXME: We do not use objects without prototype for now
// because it breaks Angular in xo-web, change it back when
// this is fixed.
(itemsByHash[hash] = {})
)[key] = value
keysToHash[key] = hash
}
}
}
_onUpdate (items) {
const {
computeHash,
_itemsByHash: itemsByHash,
_keysToHash: keysToHash,
} = this
for (const key in items) {
const value = items[key]
const prev = keysToHash[key]
const hash = computeHash(value, key)
// Removes item from the previous hash's list if any.
if (prev != null) delete itemsByHash[prev][key]
// Inserts item into the new hash's list if any.
if (hash != null) {
(
itemsByHash[hash] ||
// FIXME: idem: change back to Object.create(null)
(itemsByHash[hash] = {})
)[key] = value
keysToHash[key] = hash
} else {
delete keysToHash[key]
}
}
}
_onRemove (items) {
const {
_itemsByHash: itemsByHash,
_keysToHash: keysToHash,
} = this
for (const key in items) {
const prev = keysToHash[key]
if (prev != null) {
delete itemsByHash[prev][key]
delete keysToHash[key]
}
}
}
}

View File

@@ -0,0 +1,180 @@
/* eslint-env jest */
import eventToPromise from 'event-to-promise'
import { forEach } from 'lodash'
import Collection from './collection'
import Index from './index'
// ===================================================================
const waitTicks = (n = 2) => {
const {nextTick} = process
return new Promise(resolve => {
(function waitNextTick () {
// The first tick is handled by Promise#then()
if (--n) {
nextTick(waitNextTick)
} else {
resolve()
}
})()
})
}
// ===================================================================
describe('Index', function () {
let col, byGroup
const item1 = {
id: '2ccb8a72-dc65-48e4-88fe-45ef541f2cba',
group: 'foo',
}
const item2 = {
id: '7d21dc51-4da8-4538-a2e9-dd6f4784eb76',
group: 'bar',
}
const item3 = {
id: '668c1274-4442-44a6-b99a-512188e0bb09',
group: 'foo',
}
const item4 = {
id: 'd90b7335-e540-4a44-ad22-c4baae9cd0a9',
}
beforeEach(function () {
col = new Collection()
forEach([item1, item2, item3, item4], item => {
col.add(item)
})
byGroup = new Index('group')
col.createIndex('byGroup', byGroup)
return waitTicks()
})
it('works with existing items', function () {
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3,
},
bar: {
[item2.id]: item2,
},
},
})
})
it('works with added items', function () {
const item5 = {
id: '823b56c4-4b96-4f3a-9533-5d08177167ac',
group: 'baz',
}
col.add(item5)
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3,
},
bar: {
[item2.id]: item2,
},
baz: {
[item5.id]: item5,
},
},
})
})
})
it('works with updated items', function () {
const item1bis = {
id: item1.id,
group: 'bar',
}
col.update(item1bis)
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item3.id]: item3,
},
bar: {
[item1.id]: item1bis,
[item2.id]: item2,
},
},
})
})
})
it('works with removed items', function () {
col.remove(item2)
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3,
},
bar: {},
},
})
})
})
it('correctly updates the value even the same object has the same hash', function () {
const item1bis = {
id: item1.id,
group: item1.group,
newProp: true,
}
col.update(item1bis)
return eventToPromise(col, 'finish').then(() => {
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item1.id]: item1bis,
[item3.id]: item3,
},
bar: {
[item2.id]: item2,
},
},
})
})
})
describe('#sweep()', function () {
it('removes empty items lists', function () {
col.remove(item2)
return waitTicks().then(() => {
byGroup.sweep()
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3,
},
},
})
})
})
})
})

View File

@@ -0,0 +1,7 @@
export default function isEmpty (object) {
/* eslint no-unused-vars: 0 */
for (const key in object) {
return false
}
return true
}

View File

@@ -0,0 +1,3 @@
export default function isObject (value) {
return (value !== null) && (typeof value === 'object')
}

View File

@@ -0,0 +1,7 @@
import {BaseError} from 'make-error'
export default class NotImplemented extends BaseError {
constructor (message) {
super(message || 'this method is not implemented')
}
}

View File

@@ -0,0 +1,124 @@
import { bind, iteratee } from 'lodash'
import clearObject from './clear-object'
import NotImplemented from './not-implemented'
import {
ACTION_ADD,
ACTION_UPDATE,
ACTION_REMOVE,
} from './collection'
// ===================================================================
export default class UniqueIndex {
constructor (computeHash) {
if (computeHash) {
this.computeHash = iteratee(computeHash)
}
this._itemByHash = Object.create(null)
this._keysToHash = Object.create(null)
// Bound versions of listeners.
this._onAdd = bind(this._onAdd, this)
this._onUpdate = bind(this._onUpdate, this)
this._onRemove = bind(this._onRemove, this)
}
// This method is used to compute the hash under which an item must
// be saved.
computeHash (value, key) {
throw new NotImplemented('this method must be overridden')
}
// -----------------------------------------------------------------
get items () {
return this._itemByHash
}
// -----------------------------------------------------------------
_attachCollection (collection) {
// Add existing entries.
//
// FIXME: I think there may be a race condition if the `add` event
// has not been emitted yet.
this._onAdd(collection.all)
collection.on(ACTION_ADD, this._onAdd)
collection.on(ACTION_UPDATE, this._onUpdate)
collection.on(ACTION_REMOVE, this._onRemove)
}
_detachCollection (collection) {
collection.removeListener(ACTION_ADD, this._onAdd)
collection.removeListener(ACTION_UPDATE, this._onUpdate)
collection.removeListener(ACTION_REMOVE, this._onRemove)
clearObject(this._itemByHash)
clearObject(this._keysToHash)
}
// -----------------------------------------------------------------
_onAdd (items) {
const {
computeHash,
_itemByHash: itemByHash,
_keysToHash: keysToHash,
} = this
for (const key in items) {
const value = items[key]
const hash = computeHash(value, key)
if (hash != null) {
itemByHash[hash] = value
keysToHash[key] = hash
}
}
}
_onUpdate (items) {
const {
computeHash,
_itemByHash: itemByHash,
_keysToHash: keysToHash,
} = this
for (const key in items) {
const value = items[key]
const prev = keysToHash[key]
const hash = computeHash(value, key)
// Removes item from the previous hash's list if any.
if (prev != null) delete itemByHash[prev]
// Inserts item into the new hash's list if any.
if (hash != null) {
itemByHash[hash] = value
keysToHash[key] = hash
} else {
delete keysToHash[key]
}
}
}
_onRemove (items) {
const {
_itemByHash: itemByHash,
_keysToHash: keysToHash,
} = this
for (const key in items) {
const prev = keysToHash[key]
if (prev != null) {
delete itemByHash[prev]
delete keysToHash[key]
}
}
}
}

View File

@@ -0,0 +1,131 @@
/* eslint-env jest */
import eventToPromise from 'event-to-promise'
import { forEach } from 'lodash'
import Collection from './collection'
import Index from './unique-index'
// ===================================================================
const waitTicks = (n = 2) => {
const {nextTick} = process
return new Promise(resolve => {
(function waitNextTick () {
// The first tick is handled by Promise#then()
if (--n) {
nextTick(waitNextTick)
} else {
resolve()
}
})()
})
}
// ===================================================================
describe('UniqueIndex', function () {
let col, byKey
const item1 = {
id: '2ccb8a72-dc65-48e4-88fe-45ef541f2cba',
key: '036dee1b-9a3b-4fb5-be8a-4f535b355581',
}
const item2 = {
id: '7d21dc51-4da8-4538-a2e9-dd6f4784eb76',
key: '103cd893-d2cc-4d37-96fd-c259ad04c0d4',
}
const item3 = {
id: '668c1274-4442-44a6-b99a-512188e0bb09',
}
beforeEach(function () {
col = new Collection()
forEach([item1, item2, item3], item => {
col.add(item)
})
byKey = new Index('key')
col.createIndex('byKey', byKey)
return waitTicks()
})
it('works with existing items', function () {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1,
[item2.key]: item2,
},
})
})
it('works with added items', function () {
const item4 = {
id: '823b56c4-4b96-4f3a-9533-5d08177167ac',
key: '1437af14-429a-40db-8a51-8a2f5ed03201',
}
col.add(item4)
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1,
[item2.key]: item2,
[item4.key]: item4,
},
})
})
})
it('works with updated items', function () {
const item1bis = {
id: item1.id,
key: 'e03d4a3a-0331-4aca-97a2-016bbd43a29b',
}
col.update(item1bis)
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byKey: {
[item1bis.key]: item1bis,
[item2.key]: item2,
},
})
})
})
it('works with removed items', function () {
col.remove(item2)
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1,
},
})
})
})
it('correctly updates the value even the same object has the same hash', function () {
const item1bis = {
id: item1.id,
key: item1.key,
newProp: true,
}
col.update(item1bis)
return eventToPromise(col, 'finish').then(() => {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1bis,
[item2.key]: item2,
},
})
})
})
})

View File

@@ -0,0 +1,56 @@
import { forEach } from 'lodash'
import Collection from './collection'
import View from './view'
// ===================================================================
// Create the collection.
const users = new Collection()
users.getKey = (user) => user.name
// Inserts some data.
users.add({
name: 'bob',
})
users.add({
name: 'clara',
active: true,
})
users.add({
name: 'ophelia',
})
users.add({
name: 'Steve',
active: true,
})
// -------------------------------------------------------------------
// Create the view.
const activeUsers = new View(users, 'active')
// Register some event listeners to see the changes.
activeUsers.on('add', users => {
forEach(users, (_, id) => {
console.log('+ active user:', id)
})
})
activeUsers.on('remove', users => {
forEach(users, (_, id) => {
console.log('- active user:', id)
})
})
// Make some changes in the future.
setTimeout(function () {
console.log('-----')
users.set({
name: 'ophelia',
active: true,
})
users.set({
name: 'Steve',
})
}, 10)

View File

@@ -0,0 +1,88 @@
import { bind, forEach, iteratee as createCallback } from 'lodash'
import Collection, {
ACTION_ADD,
ACTION_UPDATE,
ACTION_REMOVE,
} from './collection'
// ===================================================================
export default class View extends Collection {
constructor (collection, predicate) {
super()
this._collection = collection
this._predicate = createCallback(predicate)
// Handles initial items.
this._onAdd(this._collection.all)
// Bound versions of listeners.
this._onAdd = bind(this._onAdd, this)
this._onUpdate = bind(this._onUpdate, this)
this._onRemove = bind(this._onRemove, this)
// Register listeners.
this._collection.on(ACTION_ADD, this._onAdd)
this._collection.on(ACTION_UPDATE, this._onUpdate)
this._collection.on(ACTION_REMOVE, this._onRemove)
}
// This method is necessary to free the memory of the view if its
// life span is shorter than the collection.
destroy () {
this._collection.removeListener(ACTION_ADD, this._onAdd)
this._collection.removeListener(ACTION_UPDATE, this._onUpdate)
this._collection.removeListener(ACTION_REMOVE, this._onRemove)
}
add () {
throw new Error('a view is read only')
}
clear () {
throw new Error('a view is read only')
}
set () {
throw new Error('a view is read only')
}
update () {
throw new Error('a view is read only')
}
_onAdd (items) {
const {_predicate: predicate} = this
forEach(items, (value, key) => {
if (predicate(value, key, this)) {
// super.add() cannot be used because the item may already be
// in the view if it was already present at the creation of
// the view and its event not already emitted.
super.set(key, value)
}
})
}
_onUpdate (items) {
const {_predicate: predicate} = this
forEach(items, (value, key) => {
if (predicate(value, key, this)) {
super.set(key, value)
} else if (super.has(key)) {
super.remove(key)
}
})
}
_onRemove (items) {
forEach(items, (value, key) => {
if (super.has(key)) {
super.remove(key)
}
})
}
}

View File

@@ -0,0 +1 @@
module.exports = require('./dist/unique-index') // eslint-disable-line node/no-missing-require

View File

@@ -0,0 +1 @@
module.exports = require('./dist/view') // eslint-disable-line node/no-missing-require

View File

@@ -0,0 +1,10 @@
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/test/
/tests/
*.spec.js
*.spec.js.map

View File

@@ -0,0 +1,49 @@
# xo-common [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> Code shared between [XO](https://xen-orchestra.com) server and clients
## Install
Installation of the [npm package](https://npmjs.org/package/xo-common):
```
> npm install --save xo-common
```
## Usage
**TODO**
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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
AGPL3 © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1 @@
module.exports = require('./dist/api-errors') // eslint-disable-line node/no-missing-require

View File

@@ -0,0 +1,65 @@
{
"name": "xo-common",
"version": "0.1.1",
"license": "AGPL-3.0",
"description": "Code shared between [XO](https://xen-orchestra.com) server and clients",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-common",
"bugs": "https://github.com/vatesfr/xo-web/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@isonoe.net"
},
"preferGlobal": false,
"bin": {},
"files": [
"dist/",
"*.js"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"babel-runtime": "^6.18.0",
"lodash": "^4.16.6",
"make-error": "^1.2.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"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.0.1",
"rimraf": "^2.6.1"
},
"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 clean",
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"browsers": "> 1%",
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -0,0 +1,166 @@
import { BaseError } from 'make-error'
import { isArray, iteratee } from 'lodash'
class XoError extends BaseError {
constructor ({ code, message, data }) {
super(message)
this.code = code
this.data = data
}
toJsonRpcError () {
return {
message: this.message,
code: this.code,
data: this.data,
}
}
}
const create = (code, getProps) => {
const factory = (...args) => new XoError({ ...getProps(...args), code })
factory.is = (error, predicate) =>
error.code === code && iteratee(predicate)(error)
return factory
}
// =============================================================================
export const notImplemented = create(0, () => ({
message: 'not implemented',
}))
export const noSuchObject = create(1, (id, type) => ({
data: { id, type },
message: 'no such object',
}))
export const unauthorized = create(2, () => ({
message: 'not authenticated or not enough permissions',
}))
export const invalidCredentials = create(3, () => ({
message: 'invalid credentials',
}))
// Deprecated alreadyAuthenticated (4)
export const forbiddenOperation = create(5, (operation, reason) => ({
data: { operation, reason },
message: `forbidden operation: ${operation}`,
}))
// Deprecated GenericError (6)
export const noHostsAvailable = create(7, () => ({
message: 'no hosts available',
}))
export const authenticationFailed = create(8, () => ({
message: 'authentication failed',
}))
export const serverUnreachable = create(9, objectId => ({
data: {
objectId,
},
message: 'server unreachable',
}))
export const invalidParameters = create(10, (message, errors) => {
if (isArray(message)) {
errors = message
message = undefined
}
return {
data: { errors },
message: message || 'invalid parameters',
}
})
export const vmMissingPvDrivers = create(11, ({ vm }) => ({
data: {
objectId: vm,
},
message: 'missing PV drivers',
}))
export const vmIsTemplate = create(12, ({ vm }) => ({
data: {
objectId: vm,
},
message: 'VM is a template',
}))
// TODO: We should probably create a more generic error which gathers all incorrect state errors.
// e.g.:
// incorrectState {
// data: {
// objectId: 'af43e227-3deb-4822-a79b-968825de72eb',
// property: 'power_state',
// actual: 'Running',
// expected: 'Halted'
// },
// message: 'incorrect state'
// }
export const vmBadPowerState = create(13, ({ vm, expected, actual }) => ({
data: {
objectId: vm,
expected,
actual,
},
message: `VM state is ${actual} but should be ${expected}`,
}))
export const vmLacksFeature = create(14, ({ vm, feature }) => ({
data: {
objectId: vm,
feature,
},
message: `VM lacks feature ${feature || ''}`,
}))
export const notSupportedDuringUpgrade = create(15, () => ({
message: 'not supported during upgrade',
}))
export const objectAlreadyExists = create(16, ({ objectId, objectType }) => ({
data: {
objectId,
objectType,
},
message: `${objectType || 'object'} already exists`,
}))
export const vdiInUse = create(17, ({ vdi, operation }) => ({
data: {
objectId: vdi,
operation,
},
message: 'VDI in use',
}))
export const hostOffline = create(18, ({ host }) => ({
data: {
objectId: host,
},
message: 'host offline',
}))
export const operationBlocked = create(19, ({ objectId, code }) => ({
data: {
objectId,
code,
},
message: 'operation blocked',
}))
export const patchPrecheckFailed = create(20, ({ errorType, patch }) => ({
data: {
objectId: patch,
errorType,
},
message: `patch precheck failed: ${errorType}`,
}))

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__/

168
packages/xo-lib/README.md Normal file
View File

@@ -0,0 +1,168 @@
# xo-lib [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> Library to connect to XO-Server.
## Install
Installation of the [npm package](https://npmjs.org/package/xo-lib):
```
npm install --save xo-lib
```
Then require the package:
```javascript
import Xo from 'xo-lib'
```
## Usage
> If the URL is not provided and the current environment is a web
> browser, the location of the current page will be used.
```javascript
// Connect to XO.
const xo = new Xo({ url: 'https://xo.company.tld' })
// Let's start by opening the connection.
await xo.open()
// Must sign in before being able to call any methods (all calls will
// be buffered until signed in).
await xo.signIn({
email: 'admin@admin.net',
password: 'admin'
})
console('signed as', xo.user)
```
The credentials can also be passed directly to the constructor:
```javascript
const xo = Xo({
url: 'https://xo.company.tld',
credentials: {
email: 'admin@admin.net',
password: 'admin',
}
})
xo.open()
xo.on('authenticated', () => {
console.log(xo.user)
})
```
> If the URL is not provided and the current environment is a web
> browser, the location of the current page will be used.
### Connection
```javascript
await xo.open()
console.log('connected')
```
### Disconnection
```javascript
xo.close()
console.log('disconnected')
```
### Method call
```javascript
const token = await xo.call('token.create')
console.log('Token created', token)
```
### Status
The connection status is available through the status property which
is *open*, *connecting* or *closed*.
```javascript
console.log('%s to xo-server', xo.status)
```
### Current user
Information about the user account used to sign in is available
through the `user` property.
```javascript
console.log('Current user is', xo.user)
```
> This property is null when the status is not connected.
### Events
```javascript
xo.on('open', () => {
console.log('connected')
})
```
```javascript
xo.on('closed', () => {
console.log('disconnected')
})
```
```javascript
xo.on('notification', function (notif) {
console.log('notification:', notif.method, notif.params)
})
```
```javascript
xo.on('authenticated', () => {
console.log('authenticated as', xo.user)
})
xo.on('authenticationFailure', () => {
console.log('failed to authenticate')
})
```
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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](http://vates.fr)

View File

@@ -0,0 +1,45 @@
'use strict'
process.on('unhandledRejection', function (error) {
console.log(error)
})
const Xo = require('./').default // eslint-disable-line node/no-missing-require
const xo = new Xo({
url: 'localhost:9000',
})
xo.open().then(function () {
return xo.call('acl.get', {}).then(function (result) {
console.log('success:', result)
}).catch(function (error) {
console.log('failure:', error)
})
}).then(function () {
return xo.signIn({
email: 'admin@admin.net',
password: 'admin',
}).then(function () {
console.log('connected as ', xo.user)
}).catch(function (error) {
console.log('failure:', error)
})
}).then(function () {
return xo.signIn({
email: 'tom',
password: 'tom',
}).then(function () {
console.log('connected as', xo.user)
return xo.call('acl.get', {}).then(function (result) {
console.log('success:', result)
}).catch(function (error) {
console.log('failure:', error)
})
}).catch(function (error) {
console.log('failure', error)
})
}).then(function () {
return xo.close()
})

View File

@@ -0,0 +1,73 @@
{
"name": "xo-lib",
"version": "0.9.0",
"license": "ISC",
"description": "Library to connect to XO-Server",
"keywords": [
"xen",
"orchestra",
"xen-orchestra"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-lib",
"bugs": "https://github.com/vatesfr/xo-web/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/"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"jsonrpc-websocket-client": "^0.2.0",
"lodash": "^4.17.2",
"make-error": "^1.0.4"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.1",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublishOnly": "npm run build"
},
"babel": {
"env": {
"test": {
"ignore": null
}
},
"ignore": "*.spec.js",
"plugins": [
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"browsers": "> 2%",
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -0,0 +1,83 @@
import JsonRpcWebSocketClient, {
OPEN,
CLOSED,
} from 'jsonrpc-websocket-client'
import { BaseError } from 'make-error'
import { startsWith } from 'lodash'
// ===================================================================
const noop = () => {}
// ===================================================================
export class XoError extends BaseError {}
// -------------------------------------------------------------------
export default class Xo extends JsonRpcWebSocketClient {
constructor (opts) {
const url = opts != null ? opts.url : '.'
super(`${url === '/' ? '' : url}/api/`)
this._credentials = (opts != null ? opts.credentials : null)
this._user = null
this.on(OPEN, () => {
if (this._credentials) {
this._signIn(this._credentials).catch(noop)
}
})
this.on(CLOSED, () => {
this._user = null
})
}
get user () {
return this._user
}
call (method, args, i) {
if (startsWith(method, 'session.')) {
return Promise.reject(
new XoError('session.*() methods are disabled from this interface')
)
}
const promise = super.call(method, args)
promise.retry = (predicate) => promise.catch((error) => {
i = (i || 0) + 1
if (predicate(error, i)) {
return this.call(method, args, i)
}
})
return promise
}
refreshUser () {
return super.call('session.getUser').then(user => {
return (this._user = user)
})
}
signIn (credentials) {
// Register this credentials for future use.
this._credentials = credentials
return this._signIn(credentials)
}
_signIn (credentials) {
return super.call('session.signIn', credentials).then(
user => {
this._user = user
this.emit('authenticated')
},
error => {
this.emit('authenticationFailure', error)
throw error
}
)
}
}

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
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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,62 @@
{
"name": "xo-remote-parser",
"version": "0.3.0",
"license": "AGPL-3.0",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-remote-parser",
"bugs": "https://github.com/vatesfr/xo-web/issues",
"repository": {
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Fabrice Marsaud",
"email": "fabrice.marsaud@vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"bin": {},
"files": [
"dist/"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"lodash": "^4.13.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.1",
"deep-freeze": "^0.0.1",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"browsers": "> 5%",
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -0,0 +1,57 @@
import filter from 'lodash/filter'
import map from 'lodash/map'
import trim from 'lodash/trim'
import trimStart from 'lodash/trimStart'
const sanitizePath = (...paths) => filter(map(paths, s => s && filter(map(s.split('/'), trim)).join('/'))).join('/')
export const parse = string => {
const object = { }
const [type, rest] = string.split('://')
if (type === 'file') {
object.type = 'file'
object.path = `/${trimStart(rest, '/')}` // the leading slash has been forgotten on client side first implementation
} else if (type === 'nfs') {
object.type = 'nfs'
const [host, path] = rest.split(':')
object.host = host
object.path = `/${trimStart(path, '/')}` // takes care of a missing leading slash coming from previous version format
} else if (type === 'smb') {
object.type = 'smb'
const lastAtSign = rest.lastIndexOf('@')
const smb = rest.slice(lastAtSign + 1)
const auth = rest.slice(0, lastAtSign)
const firstColon = auth.indexOf(':')
const username = auth.slice(0, firstColon)
const password = auth.slice(firstColon + 1)
const [domain, sh] = smb.split('\\\\')
const [host, path] = sh.split('\0')
object.host = host
object.path = path
object.domain = domain
object.username = username
object.password = password
}
return object
}
export const format = ({type, host, path, username, password, domain}) => {
type === 'local' && (type = 'file')
let string = `${type}://`
if (type === 'nfs') {
string += `${host}:`
}
if (type === 'smb') {
string += `${username}:${password}@${domain}\\\\${host}`
}
path = sanitizePath(path)
if (type === 'smb') {
path = path.split('/')
path = '\0' + path.join('\\') // FIXME saving with the windows fashion \ was a bad idea :,(
} else {
path = `/${path}`
}
string += path
return string
}

View File

@@ -0,0 +1,89 @@
/* eslint-env jest */
import deepFreeze from 'deep-freeze'
import { parse, format } from './'
// ===================================================================
// Data used for both parse and format (i.e. correctly formatted).
const data = deepFreeze({
file: {
string: 'file:///var/lib/xoa/backup',
object: {
type: 'file',
path: '/var/lib/xoa/backup',
},
},
SMB: {
string: 'smb://Administrator:pas:sw@ord@toto\\\\192.168.100.225\\smb\0',
object: {
type: 'smb',
host: '192.168.100.225\\smb',
path: '',
domain: 'toto',
username: 'Administrator',
password: 'pas:sw@ord',
},
},
NFS: {
string: 'nfs://192.168.100.225:/media/nfs',
object: {
type: 'nfs',
host: '192.168.100.225',
path: '/media/nfs',
},
},
})
const parseData = deepFreeze({
...data,
'file with missing leading slash (#7)': {
string: 'file://var/lib/xoa/backup',
object: {
type: 'file',
path: '/var/lib/xoa/backup',
},
},
'nfs with missing leading slash': {
string: 'nfs://192.168.100.225:media/nfs',
object: {
type: 'nfs',
host: '192.168.100.225',
path: '/media/nfs',
},
},
})
const formatData = deepFreeze({
...data,
'file with local type': {
string: 'file:///var/lib/xoa/backup',
object: {
type: 'local',
path: '/var/lib/xoa/backup',
},
},
})
// -------------------------------------------------------------------
describe('format', () => {
for (const name in formatData) {
const datum = formatData[name]
it(name, () => {
expect(format(datum.object)).toBe(datum.string)
})
}
})
describe('parse', () => {
for (const name in parseData) {
const datum = parseData[name]
it(name, () => {
expect(parse(datum.string)).toEqual(datum.object)
})
}
})

View File

@@ -0,0 +1,10 @@
/examples/
example.js
example.js.map
*.example.js
*.example.js.map
/test/
/tests/
*.spec.js
*.spec.js.map

View File

@@ -0,0 +1,64 @@
# xo-server-auth-github [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> GitHub authentication plugin for XO-Server
This plugin allows GitHub users to authenticate to Xen-Orchestra.
The first time a user signs in, XO will create a new XO user with the
same identifier.
## Install
Installation of the [npm package](https://npmjs.org/package/xo-server-auth-github):
```
> npm install --global xo-server-auth-github
```
## Usage
> This plugin is based on [passport-github](https://github.com/jaredhanson/passport-github),
> see [its documentation](https://github.com/jaredhanson/passport-github#configure-strategy)
> for more information about the configuration.
Like all other xo-server plugins, it can be configured directly via
the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).
![Registering XO instance in GitHub](github.png)
## Development
### Installing dependencies
```
> npm install
```
### Compilation
The sources files are watched and automatically recompiled on changes.
```
> npm run dev
```
### Tests
```
> npm run test-dev
```
## 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
AGPL3 © [Vates SAS](http://vates.fr)

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -0,0 +1,60 @@
{
"name": "xo-server-auth-github",
"version": "0.2.1",
"license": "AGPL-3.0",
"description": "GitHub authentication plugin for XO-Server",
"keywords": [
"xo-server",
"xo-server",
"authentication",
"github"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-auth-github",
"bugs": "https://github.com/vatesfr/xo-web/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/"
],
"engines": {
"node": ">=4"
},
"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"
},
"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,44 @@
import {Strategy} from 'passport-github'
// ===================================================================
export const configurationSchema = {
type: 'object',
properties: {
clientID: {
type: 'string',
},
clientSecret: {
type: 'string',
},
},
required: ['clientID', 'clientSecret'],
}
// ===================================================================
class AuthGitHubXoPlugin {
constructor (xo) {
this._xo = xo
}
configure (conf) {
this._conf = conf
}
load () {
const {_xo: xo} = this
xo.registerPassportStrategy(new Strategy(this._conf, async (accessToken, refreshToken, profile, done) => {
try {
done(null, await xo.registerUser('github', profile.username))
} catch (error) {
done(error.message)
}
}))
}
}
// ===================================================================
export default ({xo}) => new AuthGitHubXoPlugin(xo)

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,9 @@
language: node_js
node_js:
- stable
- 6
- 4
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

View File

@@ -0,0 +1,77 @@
# xo-server-auth-google [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> Google authentication plugin for XO-Server
This plugin allows Google users to authenticate to Xen-Orchestra.
The first time a user signs in, XO will create a new XO user with the
same identifier.
## Install
Installation of the [npm package](https://npmjs.org/package/xo-server-auth-google):
```
> npm install --global xo-server-auth-google
```
## Usage
> This plugin is based on [passport-google](https://google.com/jaredhanson/passport-google),
> see [its documentation](https://google.com/jaredhanson/passport-google#configure-strategy)
> for more information about the configuration.
### Creating the Google project
[Create a new project](https://console.developers.google.com/project):
![](create-project.png)
![](create-project-2.png)
Enable the Google+ API:
![](enable-google+-api.png)
Add OAuth 2.0 credentials:
![](add-oauth2-credentials.png)
![](add-oauth2-credentials-2.png)
### Add the plugin to XO-Server config
Like all other xo-server plugins, it can be configured directly via
the web iterface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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
AGPL3 © [Vates SAS](http://vates.fr)

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,77 @@
{
"name": "xo-server-auth-google",
"version": "0.2.0",
"license": "AGPL-3.0",
"description": "Google authentication plugin for XO-Server",
"keywords": [
"authentication",
"google",
"orchestra",
"plugin",
"xen",
"xen-orchestra",
"xo-server"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-auth-google",
"bugs": "https://github.com/vatesfr/xo-web/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/"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"babel-runtime": "^6.23.0",
"passport-google-oauth20": "^1.0.0"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"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.1",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublishOnly": "npm run build"
},
"babel": {
"env": {
"test": {
"ignore": null
}
},
"ignore": "*.spec.js",
"plugins": [
"transform-runtime",
"lodash"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -0,0 +1,61 @@
import { Strategy } from 'passport-google-oauth20'
// ===================================================================
export const configurationSchema = {
type: 'object',
properties: {
callbackURL: {
type: 'string',
description: 'Must be exactly the same as specified on the Google developer console.',
},
clientID: {
type: 'string',
},
clientSecret: {
type: 'string',
},
scope: {
default: 'https://www.googleapis.com/auth/plus.login',
description: 'Note that changing this value will break existing users.',
enum: [ 'https://www.googleapis.com/auth/plus.login', 'email' ],
enumNames: [ 'Google+ name', 'Simple email address' ],
},
},
required: ['callbackURL', 'clientID', 'clientSecret'],
}
// ===================================================================
class AuthGoogleXoPlugin {
constructor ({ xo }) {
this._conf = null
this._xo = xo
}
configure (conf) {
this._conf = conf
}
load () {
const conf = this._conf
const xo = this._xo
xo.registerPassportStrategy(new Strategy(conf, async (accessToken, refreshToken, profile, done) => {
try {
done(null, await xo.registerUser(
'google',
conf.scope === 'email'
? profile.emails[0].value
: profile.displayName
))
} catch (error) {
done(error.message)
}
}))
}
}
// ===================================================================
export default opts => new AuthGoogleXoPlugin(opts)

View File

@@ -0,0 +1 @@
/ldap.cache.conf

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,82 @@
# xo-server-auth-ldap [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
> LDAP authentication plugin for XO-Server
This plugin allows LDAP users to authenticate to Xen-Orchestra.
The first time a user signs in, XO will create a new XO user with the
same identifier.
## Install
Installation of the [npm package](https://npmjs.org/package/xo-server-auth-ldap):
```
> npm install --global xo-server-auth-ldap
```
## Usage
Like all other xo-server plugins, it can be configured directly via
the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).
If you have issues, you can use the provided CLI to gather more
information:
```
> xo-server-auth-ldap
? uri ldap://ldap.company.net
? fill optional certificateAuthorities? No
? fill optional checkCertificate? No
? fill optional bind? No
? base ou=people,dc=company,dc=net
? fill optional filter? No
configuration saved in ./ldap.cache.conf
? Username john.smith
? Password *****
searching for entries...
0 entries found
could not authenticate john.smith
```
## Algorithm
1. If `bind` is defined, attempt to bind using this user.
2. Searches for the user in the directory starting from the `base`
with the defined `filter`.
3. If found, a bind is attempted using the distinguished name of this
user and the provided password.
## Development
```
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production (automatically called by npm install)
> npm run 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
AGPL3 © [Vates SAS](http://vates.fr)

View File

@@ -0,0 +1,77 @@
{
"name": "xo-server-auth-ldap",
"version": "0.6.4",
"license": "AGPL-3.0",
"description": "LDAP authentication plugin for XO-Server",
"keywords": [
"ldap",
"orchestra",
"plugin",
"xen",
"xen-orchestra",
"xo-server"
],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-auth-ldap",
"bugs": "https://github.com/vatesfr/xo-web/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": {
"xo-server-auth-ldap": "dist/test-cli.js"
},
"files": [
"dist/"
],
"engines": {
"node": ">=4"
},
"dependencies": {
"babel-runtime": "^6.22.0",
"event-to-promise": "^0.8.0",
"exec-promise": "^0.7.0",
"inquirer": "^4.0.1",
"ldapjs": "^1.0.1",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"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.0.1",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [
"lodash",
"transform-runtime"
],
"presets": [
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -0,0 +1,249 @@
/* eslint no-throw-literal: 0 */
import eventToPromise from 'event-to-promise'
import { bind, noop } from 'lodash'
import { createClient } from 'ldapjs'
import { escape } from 'ldapjs/lib/filters/escape'
import { promisify } from 'promise-toolbox'
import { readFile } from 'fs'
// ===================================================================
const VAR_RE = /\{\{([^}]+)\}\}/g
const evalFilter = (filter, vars) => filter.replace(VAR_RE, (_, name) => {
const value = vars[name]
if (value === undefined) {
throw new Error('invalid variable: ' + name)
}
return escape(value)
})
export const configurationSchema = {
type: 'object',
properties: {
uri: {
description: 'URI of the LDAP server.',
type: 'string',
},
certificateAuthorities: {
description: `
Paths to CA certificates to use when connecting to SSL-secured LDAP servers.
If not specified, it will use a default set of well-known CAs.
`.trim(),
type: 'array',
items: {
type: 'string',
},
},
checkCertificate: {
description: 'Enforce the validity of the server\'s certificates. You can disable it when connecting to servers that use a self-signed certificate.',
type: 'boolean',
default: true,
},
bind: {
description: 'Credentials to use before looking for the user record.',
type: 'object',
properties: {
dn: {
description: `
Full distinguished name of the user permitted to search the LDAP directory for the user to authenticate.
Example: uid=xoa-auth,ou=people,dc=company,dc=net
For Microsoft Active Directory, it can also be \`<user>@<domain>\`.
`.trim(),
type: 'string',
},
password: {
description: 'Password of the user permitted of search the LDAP directory.',
type: 'string',
},
},
required: ['dn', 'password'],
},
base: {
description: 'The base is the part of the description tree where the users are looked for.',
type: 'string',
},
filter: {
description: `
Filter used to find the user.
For Microsoft Active Directory, you can try one of the following filters:
- \`(cn={{name}})\`
- \`(sAMAccountName={{name}})\`
- \`(sAMAccountName={{name}}@<domain>)\` (replace \`<domain>\` by your own domain)
- \`(userPrincipalName={{name}})\`
For LDAP if you want to filter for a special group you can try
something like:
- \`(&(uid={{name}})(memberOf=<group DN>))\`
`.trim(),
type: 'string',
default: '(uid={{name}})',
},
},
required: ['uri', 'base'],
}
export const testSchema = {
type: 'object',
properties: {
username: {
description: 'LDAP username',
type: 'string',
},
password: {
description: 'LDAP password',
type: 'string',
},
},
required: ['username', 'password'],
}
// ===================================================================
class AuthLdap {
constructor (xo) {
this._xo = xo
this._authenticate = bind(this._authenticate, this)
}
async configure (conf) {
const clientOpts = this._clientOpts = {
url: conf.uri,
maxConnections: 5,
tlsOptions: {},
}
{
const {
bind,
checkCertificate = true,
certificateAuthorities,
} = conf
if (bind) {
clientOpts.bindDN = bind.dn
clientOpts.bindCredentials = bind.password
}
const {tlsOptions} = clientOpts
tlsOptions.rejectUnauthorized = checkCertificate
if (certificateAuthorities) {
tlsOptions.ca = await Promise.all(
certificateAuthorities.map(path => readFile(path))
)
}
}
const {
bind: credentials,
base: searchBase,
filter: searchFilter = '(uid={{name}})',
} = conf
this._credentials = credentials
this._searchBase = searchBase
this._searchFilter = searchFilter
}
load () {
this._xo.registerAuthenticationProvider(this._authenticate)
}
unload () {
this._xo.unregisterAuthenticationProvider(this._authenticate)
}
test ({ username, password }) {
return this._authenticate({
username,
password,
}).then(result => {
if (result === null) {
throw new Error('could not authenticate user')
}
})
}
async _authenticate ({ username, password }, logger = noop) {
if (username === undefined || password === undefined) {
logger('require `username` and `password` to authenticate!')
return null
}
const client = createClient(this._clientOpts)
try {
// Promisify some methods.
const bind = promisify(client.bind, client)
const search = promisify(client.search, client)
await eventToPromise(client, 'connect')
// Bind if necessary.
{
const {_credentials: credentials} = this
if (credentials) {
logger(`attempting to bind with as ${credentials.dn}...`)
await bind(credentials.dn, credentials.password)
logger(`successfully bound as ${credentials.dn}`)
}
}
// Search for the user.
const entries = []
{
logger('searching for entries...')
const response = await search(this._searchBase, {
scope: 'sub',
filter: evalFilter(this._searchFilter, {
name: username,
}),
})
response.on('searchEntry', entry => {
logger('.')
entries.push(entry.json)
})
const {status} = await eventToPromise(response, 'end')
if (status) {
throw new Error('unexpected search response status: ' + status)
}
logger(`${entries.length} entries found`)
}
// Try to find an entry which can be bind with the given password.
for (const entry of entries) {
try {
logger(`attempting to bind as ${entry.objectName}`)
await bind(entry.objectName, password)
logger(`successfully bound as ${entry.objectName} => ${username} authenticated`)
return { username }
} catch (error) {
logger(`failed to bind as ${entry.objectName}: ${error.message}`)
}
}
logger(`could not authenticate ${username}`)
return null
} finally {
client.unbind()
}
}
}
// ===================================================================
export default ({xo}) => new AuthLdap(xo)

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