Compare commits

..

54 Commits

Author SHA1 Message Date
Julien Fontanet
a4ab028ad6 feat(complex-matcher): 0.2.0 2018-01-25 16:57:29 +01:00
Julien Fontanet
9fc6013934 feat(xen-api): 0.16.3 2018-01-24 14:07:32 +01:00
Julien Fontanet
8a02d557f4 feat(xen-api): handle ghost tasks (#35)
See vatesfr/xo-web#2579
2018-01-24 14:06:46 +01:00
Julien Fontanet
bdddea2e29 feat(complex-matcher): support numbers (#32) 2018-01-17 12:06:44 +01:00
Julien Fontanet
be4ca0eede chore(complex-matcher): remove @babel/polyfill
Should be unnecessary.
2018-01-17 11:01:32 +01:00
Julien Fontanet
316d71b34d chore(xen-api): remove unused import 2018-01-11 11:22:16 +01:00
Julien Fontanet
b3bc9a2edd fix(package): update xen-api to 0.16.2 2018-01-09 17:48:04 +01:00
Julien Fontanet
90369acb63 fix(xen-api): remove undefined fields during preparation 2018-01-09 17:47:46 +01:00
Julien Fontanet
68cef1aa5d feat(xen-api): 0.16.1 2018-01-09 17:28:42 +01:00
Julien Fontanet
2f9ecb8fa8 fix(xen-api): fix integer param preparation 2018-01-09 17:28:13 +01:00
Julien Fontanet
6281374cf3 chore(package): update dependencies 2018-01-09 17:25:42 +01:00
Julien Fontanet
6f6e760a25 feat(normalize-packages): remove package-lock.json 2018-01-09 11:36:37 +01:00
Julien Fontanet
38fb549709 feat(import-packages): no commit hooks on move 2018-01-09 11:36:27 +01:00
Julien Fontanet
d4e46a0696 chore(package): update dependencies 2018-01-05 13:40:06 +01:00
Julien Fontanet
2d0e3b93cc chore(import-packages): simplify move_commit implementation 2017-12-31 17:43:05 +01:00
Julien Fontanet
668d572549 feat(import-packages): add commit mode and make it the default 2017-12-31 13:15:19 +01:00
Julien Fontanet
e023809c8c feat(import-packages): import all branches 2017-12-31 00:46:41 +01:00
Julien Fontanet
2174a9cec7 fix(precommit hook): pass if no tests found 2017-12-28 17:19:39 +01:00
Julien Fontanet
79ea50c829 feat(complex-matcher): 0.1.1 2017-12-27 16:39:57 +01:00
Julien Fontanet
8532d563de feat(complex-matcher): empty string is supported (#31) 2017-12-27 16:38:42 +01:00
Julien Fontanet
2e945e8349 chore(value-matcher): mark as public 2017-12-27 15:24:06 +01:00
Julien Fontanet
22e3037e85 feat(value-matcher): new package (#30) 2017-12-27 15:21:27 +01:00
Julien Fontanet
a505ded8a1 feat: run flow at root level 2017-12-27 10:49:31 +01:00
Julien Fontanet
bea7b90eb2 chore(xo-cli): update dependencies 2017-12-27 10:39:05 +01:00
Julien Fontanet
e1eb948c54 chore(package): update dependencies 2017-12-27 10:01:56 +01:00
Julien Fontanet
1b42523e2d feat(complex-matcher): new package (#28) 2017-12-22 13:08:24 +01:00
Julien Fontanet
ed7a40bc73 chore: update dependencies 2017-12-22 10:27:05 +01:00
Julien Fontanet
5b7a9fe969 fix(scripts/utils): ignore missing package.json 2017-12-18 14:10:13 +01:00
Julien Fontanet
97d54cab11 chore: update dependencies 2017-12-18 13:14:39 +01:00
Julien Fontanet
980a1cc205 chore(normalize-packages): remove ESLint config 2017-12-15 14:45:53 +01:00
Julien Fontanet
9517686e22 chore(normalize-packages): remove .travis.yml 2017-12-15 14:45:17 +01:00
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
104 changed files with 3268 additions and 2400 deletions

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

View File

@@ -7,6 +7,7 @@
[lints]
[options]
experimental.const_params=true
include_warnings=true
module.use_strict=true
unsafe.enable_getters_and_setters=true
[strict]

View File

@@ -1,6 +1,7 @@
language: node_js
node_js:
- stable
- 8
- 6
# Use containers.

View File

@@ -1,12 +1,21 @@
{
"devDependencies": {
"babel-eslint": "^8.0.1",
"babel-7-jest": "^21.3.2",
"babel-eslint": "^8.1.2",
"eslint": "^4.14.0",
"eslint-config-standard": "^11.0.0-beta.0",
"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",
"flow-bin": "^0.63.1",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lint-staged": "^5.0.0",
"jest": "^22.0.4",
"lint-staged": "^6.0.0",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5",
"standard": "^10.0.3"
"sorted-object": "^2.0.1"
},
"engines": {
"yarn": "^1.2.1"
@@ -18,31 +27,34 @@
"/dist/",
"/xo-vmdk-to-vhd/"
],
"testRegex": "\\.spec\\.js$"
"testRegex": "\\.spec\\.js$",
"transform": {
"complex-matcher/.+\\.jsx?$": "babel-7-jest",
"value-matcher/.+\\.jsx?$": "babel-7-jest",
"xo-cli/.+\\.jsx?$": "babel-7-jest",
"\\.jsx?$": "babel-jest"
}
},
"lint-staged": {
"*.js": [
"lint-staged-stash",
"standard --fix",
"jest --findRelatedTests",
"eslint --fix",
"jest --findRelatedTests --passWithNoTests",
"lint-staged-unstash"
]
},
"private": true,
"scripts": {
"build": "scripts/run-script --parallel build",
"clean": "scripts/run-script --parallel clean",
"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",
"prepublish": "scripts/run-script prepublish",
"pretest": "standard && jest",
"test": "scripts/run-script test"
},
"standard": {
"ignore": [
"packages/*/dist"
],
"parser": "babel-eslint"
"prepare": "scripts/run-script prepare",
"pretest": "eslint --ignore-path .gitignore .",
"test": "jest && flow status"
},
"workspaces": [
"packages/*"

View File

@@ -0,0 +1,28 @@
const dependencies = require('./package').dependencies || {}
const NODE_ENV = process.env.NODE_ENV || 'development'
const __PROD__ = NODE_ENV === 'production'
const __TEST__ = NODE_ENV === 'test'
module.exports = {
comments: !__PROD__,
ignore: __TEST__ ? undefined : [/\.spec\.js$/],
plugins: ['lodash'],
presets: [
[
'@babel/env',
{
debug: !__TEST__,
loose: true,
shippedProposals: true,
targets: __PROD__
? {
browsers: '>2%',
node: '4',
}
: { node: 'current' },
useBuiltIns: '@babel/polyfill' in dependencies && 'usage',
},
],
],
}

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,66 @@
# complex-matcher [![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/complex-matcher):
```
> npm install --save complex-matcher
```
## Usage
```js
import * as CM from 'complex-matcher'
const characters = [
{ name: 'Catwoman', costumeColor: 'black' },
{ name: 'Superman', costumeColor: 'blue', hasCape: true },
{ name: 'Wonder Woman', costumeColor: 'blue' },
]
const predicate = CM.parse('costumeColor:blue hasCape?').createPredicate()
characters.filter(predicate)
// [
// { name: 'Superman', costumeColor: 'blue', hasCape: true },
// ]
new CM.String('foo').createPredicate()
```
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/vatesfr/xo-web/issues)
you've encountered;
- fork and create a pull request.
## License
ISC © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,45 @@
{
"name": "complex-matcher",
"version": "0.2.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/complex-matcher",
"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": {
"lodash": "^4.17.4"
},
"devDependencies": {
"@babel/cli": "7.0.0-beta.37",
"@babel/core": "7.0.0-beta.37",
"@babel/preset-env": "7.0.0-beta.37",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^5.1.1",
"rimraf": "^2.6.2"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,12 @@
import { parse } from './'
import { ast, pattern } from './index.fixtures'
export default ({ benchmark }) => {
benchmark('parse', () => {
parse(pattern)
})
benchmark('toString', () => {
ast.toString()
})
}

View File

@@ -0,0 +1,15 @@
import * as CM from './'
export const pattern =
'foo !"\\\\ \\"" name:|(wonderwoman batman) hasCape? age:32'
export const ast = new CM.And([
new CM.String('foo'),
new CM.Not(new CM.String('\\ "')),
new CM.Property(
'name',
new CM.Or([new CM.String('wonderwoman'), new CM.String('batman')])
),
new CM.TruthyProperty('hasCape'),
new CM.Property('age', new CM.Number(32)),
])

View File

@@ -0,0 +1,470 @@
import { isPlainObject, some } from 'lodash'
// ===================================================================
// Invoke a function and returns it result.
// All parameters are forwarded.
//
// Why using `invoke()`?
// - avoid tedious IIFE syntax
// - avoid declaring variables in the common scope
// - monkey-patching
//
// ```js
// const sum = invoke(1, 2, (a, b) => a + b)
//
// eventEmitter.emit = invoke(eventEmitter.emit, emit => function (event) {
// if (event === 'foo') {
// throw new Error('event foo is disabled')
// }
//
// return emit.apply(this, arguments)
// })
// ```
function invoke (fn) {
const n = arguments.length - 1
if (!n) {
return fn()
}
fn = arguments[n]
const args = new Array(n)
for (let i = 0; i < n; ++i) {
args[i] = arguments[i]
}
return fn.apply(undefined, args)
}
// ===================================================================
const RAW_STRING_CHARS = invoke(() => {
const chars = { __proto__: null }
const add = (a, b = a) => {
let i = a.charCodeAt(0)
const j = b.charCodeAt(0)
while (i <= j) {
chars[String.fromCharCode(i++)] = true
}
}
add('$')
add('-')
add('.')
add('0', '9')
add('_')
add('A', 'Z')
add('a', 'z')
return chars
})
const isRawString = string => {
const { length } = string
for (let i = 0; i < length; ++i) {
if (!(string[i] in RAW_STRING_CHARS)) {
return false
}
}
return true
}
// -------------------------------------------------------------------
class Node {
createPredicate () {
return value => this.match(value)
}
}
export class Null extends Node {
match () {
return true
}
toString () {
return ''
}
}
const formatTerms = terms => terms.map(term => term.toString(true)).join(' ')
export class And extends Node {
constructor (children) {
super()
if (children.length === 1) {
return children[0]
}
this.children = children
}
match (value) {
return this.children.every(child => child.match(value))
}
toString (isNested) {
const terms = formatTerms(this.children)
return isNested ? `(${terms})` : terms
}
}
export class Or extends Node {
constructor (children) {
super()
if (children.length === 1) {
return children[0]
}
this.children = children
}
match (value) {
return this.children.some(child => child.match(value))
}
toString () {
return `|(${formatTerms(this.children)})`
}
}
export class Not extends Node {
constructor (child) {
super()
this.child = child
}
match (value) {
return !this.child.match(value)
}
toString () {
return '!' + this.child.toString(true)
}
}
export class NumberNode extends Node {
constructor (value) {
super()
this.value = value
}
match (value) {
return value === this.value
}
toString () {
return String(this.value)
}
}
export { NumberNode as Number }
export class Property extends Node {
constructor (name, child) {
super()
this.name = name
this.child = child
}
match (value) {
return value != null && this.child.match(value[this.name])
}
toString () {
return `${formatString(this.name)}:${this.child.toString(true)}`
}
}
const escapeChar = char => '\\' + char
const formatString = value =>
Number.isNaN(+value)
? isRawString(value) ? value : `"${value.replace(/\\|"/g, escapeChar)}"`
: `"${value}"`
export class StringNode extends Node {
constructor (value) {
super()
this.lcValue = value.toLowerCase()
this.value = value
// should not be enumerable for the tests
Object.defineProperty(this, 'match', {
value: this.match.bind(this),
})
}
match (value) {
if (typeof value === 'string') {
return value.toLowerCase().indexOf(this.lcValue) !== -1
}
if (Array.isArray(value) || isPlainObject(value)) {
return some(value, this.match)
}
return false
}
toString () {
return formatString(this.value)
}
}
export { StringNode as String }
export class TruthyProperty extends Node {
constructor (name) {
super()
this.name = name
}
match (value) {
return value != null && !!value[this.name]
}
toString () {
return formatString(this.name) + '?'
}
}
// -------------------------------------------------------------------
// terms = null || term+
// *null = /$/
// term = ws (and | or | not | property | truthyProperty | numberOrString) ws
// ws = ' '*
// *and = "(" terms ")"
// *or = "|" ws "(" terms ")"
// *not = "!" term
// *property = string ws ":" term
// *truthyProperty = string ws "?"
// numberOrString = string
// string = quotedString | rawString
// quotedString = "\"" ( /[^"\]/ | "\\\\" | "\\\"" )+
// rawString = /[a-z0-9-_.]+/i
export const parse = invoke(() => {
let i
let n
let input
// -----
const backtrace = parser => () => {
const pos = i
const node = parser()
if (node !== undefined) {
return node
}
i = pos
}
// -----
const parseTerms = Node => {
let term = parseTerm()
if (!term) {
return new Null()
}
const terms = [term]
while ((term = parseTerm())) {
terms.push(term)
}
return new Node(terms)
}
const parseTerm = () => {
parseWs()
const child =
parseAnd() ||
parseOr() ||
parseNot() ||
parseProperty() ||
parseTruthyProperty() ||
parseNumberOrString()
if (child) {
parseWs()
return child
}
}
const parseWs = () => {
while (input[i] === ' ') {
++i
}
return true
}
const parseAnd = backtrace(() => {
let and
if (input[i++] === '(' && (and = parseTerm(And)) && input[i++] === ')') {
return and
}
})
const parseOr = backtrace(() => {
let or
if (
input[i++] === '|' &&
parseWs() &&
input[i++] === '(' &&
(or = parseTerms(Or)) &&
input[i++] === ')'
) {
return or
}
})
const parseNot = backtrace(() => {
let child
if (input[i++] === '!' && (child = parseTerm())) {
return new Not(child)
}
})
const parseProperty = backtrace(() => {
let name, child
if (
(name = parseString()) &&
parseWs() &&
input[i++] === ':' &&
(child = parseTerm())
) {
return new Property(name, child)
}
})
const parseNumberOrString = () => {
let str = parseQuotedString()
if (str !== undefined) {
return new StringNode(str)
}
str = parseRawString()
if (str !== undefined) {
const asNum = +str
return Number.isNaN(asNum) ? new StringNode(str) : new NumberNode(asNum)
}
}
const parseString = () => {
let value
if (
(value = parseQuotedString()) !== undefined ||
(value = parseRawString()) !== undefined
) {
return value
}
}
const parseQuotedString = backtrace(() => {
if (input[i++] !== '"') {
return
}
const value = []
let char
while (i < n && (char = input[i++]) !== '"') {
if (char === '\\') {
char = input[i++]
}
value.push(char)
}
return value.join('')
})
const parseRawString = () => {
let value = ''
let c
while ((c = input[i]) && RAW_STRING_CHARS[c]) {
++i
value += c
}
if (value.length) {
return value
}
}
const parseTruthyProperty = backtrace(() => {
let name
if ((name = parseString()) && parseWs() && input[i++] === '?') {
return new TruthyProperty(name)
}
})
return input_ => {
i = 0
input = input_.split('')
n = input.length
try {
return parseTerms(And)
} finally {
input = undefined
}
}
})
// -------------------------------------------------------------------
const _getPropertyClauseStrings = ({ child }) => {
if (child instanceof Or) {
const strings = []
child.children.forEach(child => {
if (child instanceof StringNode) {
strings.push(child.value)
}
})
return strings
}
if (child instanceof StringNode) {
return [child.value]
}
return []
}
// Find possible values for property clauses in a and clause.
export const getPropertyClausesStrings = node => {
if (!node) {
return {}
}
if (node instanceof Property) {
return {
[node.name]: _getPropertyClauseStrings(node),
}
}
if (node instanceof And) {
const strings = {}
node.children.forEach(node => {
if (node instanceof Property) {
const { name } = node
const values = strings[name]
if (values) {
values.push.apply(values, _getPropertyClauseStrings(node))
} else {
strings[name] = _getPropertyClauseStrings(node)
}
}
})
return strings
}
return {}
}
// -------------------------------------------------------------------
export const setPropertyClause = (node, name, child) => {
const property = child && new Property(
name,
typeof child === 'string' ? new StringNode(child) : child
)
if (node === undefined) {
return property
}
const children = (node instanceof And ? node.children : [node]).filter(child =>
!(child instanceof Property && child.name === name)
)
if (property !== undefined) {
children.push(property)
}
return new And(children)
}

View File

@@ -0,0 +1,77 @@
/* eslint-env jest */
import {
getPropertyClausesStrings,
Null,
parse,
setPropertyClause,
} from './'
import { ast, pattern } from './index.fixtures'
it('getPropertyClausesStrings', () => {
const tmp = getPropertyClausesStrings(parse('foo bar:baz baz:|(foo bar)'))
expect(tmp).toEqual({
bar: ['baz'],
baz: ['foo', 'bar'],
})
})
describe('parse', () => {
it('analyses a string and returns a node/tree', () => {
expect(parse(pattern)).toEqual(ast)
})
it('supports an empty string', () => {
expect(parse('')).toEqual(new Null())
})
it('differentiate between numbers and numbers in strings', () => {
let node
node = parse('32')
expect(node.match(32)).toBe(true)
expect(node.match('32')).toBe(false)
expect(node.toString()).toBe('32')
node = parse('"32"')
expect(node.match(32)).toBe(false)
expect(node.match('32')).toBe(true)
expect(node.toString()).toBe('"32"')
})
})
describe('setPropertyClause', () => {
it('creates a node if none passed', () => {
expect(setPropertyClause(undefined, 'foo', 'bar').toString()).toBe('foo:bar')
})
it('adds a property clause if there was none', () => {
expect(
setPropertyClause(parse('baz'), 'foo', 'bar').toString()
).toBe('baz foo:bar')
})
it('replaces the property clause if there was one', () => {
expect(
setPropertyClause(parse('plip foo:baz plop'), 'foo', 'bar').toString()
).toBe('plip plop foo:bar')
expect(
setPropertyClause(parse('foo:|(baz plop)'), 'foo', 'bar').toString()
).toBe('foo:bar')
})
it('removes the property clause if no chid is passed', () => {
expect(
setPropertyClause(parse('foo bar:baz qux'), 'bar', undefined).toString()
).toBe('foo qux')
expect(
setPropertyClause(parse('foo bar:baz qux'), 'baz', undefined).toString()
).toBe('foo bar:baz qux')
})
})
it('toString', () => {
expect(pattern).toBe(ast.toString())
})

View File

@@ -0,0 +1,27 @@
const { NODE_ENV = 'development' } = process.env
const __PROD__ = NODE_ENV === 'production'
const __TEST__ = NODE_ENV === 'test'
module.exports = {
comments: !__PROD__,
compact: __PROD__,
ignore: __TEST__ ? undefined : [ /\.spec\.js$/ ],
presets: [
[
'@babel/env',
{
debug: !__TEST__,
loose: true,
shippedProposals: true,
targets: __PROD__
? {
browsers: '>2%',
node: '4',
}
: { node: 'current' },
useBuiltIns: 'usage',
},
],
'@babel/flow',
],
}

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,66 @@
# value-matcher [![Build Status](https://travis-ci.org/vatefr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatefr/xen-orchestra)
> ${pkg.description}
## Install
Installation of the [npm package](https://npmjs.org/package/value-matcher):
```
> npm install --save value-matcher
```
## Usage
```js
import { createPredicate } from 'value-matcher'
[
{ user: 'sam', age: 65, active: false },
{ user: 'barney', age: 36, active: true },
{ user: 'fred', age: 40, active: false },
].filter(createPredicate({
__or: [
{ user: 'sam' },
{ active: true },
],
}))
// [
// { user: 'sam', age: 65, active: false },
// { user: 'barney', age: 36, active: true },
// ]
```
## Development
```
# Install dependencies
> yarn
# Run the tests
> yarn test
# Continuously compile
> yarn dev
# Continuously run the tests
> yarn dev-test
# Build for production (automatically called by npm install)
> yarn build
```
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/vatesfr/xo-web/issues)
you've encountered;
- fork and create a pull request.
## License
ISC © [Vates SAS](https://vates.fr)

View File

@@ -0,0 +1,44 @@
{
"name": "value-matcher",
"version": "0.0.0",
"license": "ISC",
"description": "",
"keywords": [],
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/value-matcher",
"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/polyfill": "7.0.0-beta.37"
},
"devDependencies": {
"@babel/cli": "7.0.0-beta.37",
"@babel/core": "7.0.0-beta.37",
"@babel/preset-env": "7.0.0-beta.37",
"@babel/preset-flow": "7.0.0-beta.37",
"cross-env": "^5.1.3",
"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": "yarn run prebuild",
"prepublishOnly": "yarn run build"
}
}

View File

@@ -0,0 +1,63 @@
// @flow
// eslint-disable-next-line no-use-before-define
export type Pattern = OrPattern | NotPattern | ObjectPattern | ArrayPattern | ValuePattern
// one of the pattern must match
type OrPattern = {| __or: Array<Pattern> |}
// the pattern must not match
type NotPattern = {| __not: Pattern |}
// value is an object with properties matching the patterns
type ObjectPattern = { [string]: Pattern }
// value is an array and each patterns must match a different item
type ArrayPattern = Array<Pattern>
// value equals the pattern
type ValuePattern = bool | number | string
const match = (pattern: Pattern, value: any) => {
if (Array.isArray(pattern)) {
return Array.isArray(value) && pattern.every((subpattern, i) =>
// FIXME: subpatterns should match different subvalues
value.some(subvalue => match(subpattern, subvalue))
)
}
if (pattern !== null && typeof pattern === 'object') {
const keys = Object.keys(pattern)
const { length } = keys
if (length === 1) {
const [ key ] = keys
if (key === '__or') {
const orPattern: OrPattern = (pattern: any)
return orPattern.__or.some(subpattern => match(subpattern, value))
}
if (key === '__not') {
const notPattern: NotPattern = (pattern: any)
return !match(notPattern.__not, value)
}
}
if (value === null || typeof value !== 'object') {
return false
}
const objectPattern: ObjectPattern = (pattern: any)
for (let i = 0; i < length; ++i) {
const key = keys[i]
const subvalue = value[key]
if (subvalue === undefined || !match(objectPattern[key], subvalue)) {
return false
}
}
return true
}
return pattern === value
}
export const createPredicate = (pattern: Pattern) => (value: any) => match(pattern, value)

View File

@@ -37,18 +37,15 @@
"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",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"commitmsg": "npm test",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [

View File

@@ -30,7 +30,7 @@ const fuFooter = fu.struct([
fu.uint32('fileFormatVersion'), // 12
fu.struct('dataOffset', [
fu.uint32('high'), // 16
fu.uint32('low') // 20
fu.uint32('low'), // 20
]),
fu.uint32('timestamp'), // 24
fu.char('creatorApplication', 4), // 28
@@ -38,23 +38,23 @@ const fuFooter = fu.struct([
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.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.uint32('low'), // 52
]),
fu.struct('diskGeometry', [
fu.uint16('cylinders'), // 56
fu.uint8('heads'), // 58
fu.uint8('sectorsPerTrackCylinder') // 59
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
fu.byte('reserved', 426), // 86
])
const FOOTER_SIZE = fuFooter.size
@@ -62,11 +62,11 @@ const fuHeader = fu.struct([
fu.char('cookie', 8),
fu.struct('dataOffset', [
fu.uint32('high'),
fu.uint32('low')
fu.uint32('low'),
]),
fu.struct('tableOffset', [ // Absolute byte offset of the Block Allocation Table.
fu.uint32('high'),
fu.uint32('low')
fu.uint32('low'),
]),
fu.uint32('headerVersion'),
fu.uint32('maxTableEntries'), // Max entries in the Block Allocation Table.
@@ -83,10 +83,10 @@ const fuHeader = fu.struct([
fu.uint32('reserved'),
fu.struct('platformDataOffset', [ // Absolute byte offset of the locator data.
fu.uint32('high'),
fu.uint32('low')
])
fu.uint32('low'),
]),
], 8),
fu.byte('reserved2', 256)
fu.byte('reserved2', 256),
])
const HEADER_SIZE = fuHeader.size
@@ -265,7 +265,7 @@ export default class Vhd {
return this._handler.createReadStream(this._path, {
end: begin + length - 1,
start: begin
start: begin,
}).then(buf
? stream => streamToExistingBuffer(stream, buf, offset, (offset || 0) + length)
: streamToNewBuffer
@@ -324,7 +324,7 @@ export default class Vhd {
const sectorsPerBlock = header.blockSize / SECTOR_SIZE
assert(sectorsPerBlock % 1 === 0)
// 1 bit per sector, rounded up to full sectors
// 1 bit per sector, rounded up to full sectors
this._blockBitmapSize = Math.ceil(sectorsPerBlock / 8 / SECTOR_SIZE) * SECTOR_SIZE
assert(this._blockBitmapSize === SECTOR_SIZE)

View File

@@ -1,9 +0,0 @@
language: node_js
node_js:
- stable
- 6
- 4
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

View File

@@ -1,7 +1,7 @@
const { createReadStream, createWriteStream, statSync } = require('fs')
const { PassThrough } = require('stream')
const { isOpaqueRef } = require('../')
const { isOpaqueRef } = require('../') // eslint-disable-line node/no-missing-require
exports.createInputStream = path => {
if (path === undefined || path === '-') {

View File

@@ -1,6 +1,6 @@
{
"name": "xen-api",
"version": "0.15.1",
"version": "0.16.3",
"license": "ISC",
"description": "Connector to the Xen API",
"keywords": [
@@ -34,7 +34,7 @@
"dependencies": {
"babel-polyfill": "^6.23.0",
"blocked": "^1.2.1",
"debug": "^2.6.8",
"debug": "^3.1.0",
"event-to-promise": "^0.8.0",
"exec-promise": "^0.7.0",
"http-request-plus": "^0.5.0",
@@ -44,7 +44,7 @@
"lodash": "^4.17.4",
"make-error": "^1.3.0",
"minimist": "^1.2.0",
"ms": "^2.0.0",
"ms": "^2.1.1",
"promise-toolbox": "^0.9.5",
"pw": "0.0.4",
"xmlrpc": "^1.3.2",
@@ -54,26 +54,25 @@
"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-0": "^6.24.1",
"cross-env": "^5.0.5",
"dependency-check": "^2.9.1",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"commitmsg": "npm test",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"plot": "gnuplot -p memory-test.gnu",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [
"lodash",
"transform-decorators-legacy"
"transform-decorators-legacy",
"transform-function-bind"
],
"presets": [
[
@@ -84,7 +83,7 @@
}
}
],
"stage-0"
"stage-3"
]
}
}

View File

@@ -39,8 +39,8 @@ const main = async args => {
debounce: 'd',
help: 'h',
'read-only': 'ro',
verbose: 'v'
}
verbose: 'v',
},
})
if (opts.help) {
@@ -72,12 +72,12 @@ const main = async args => {
allowUnauthorized: opts.au,
auth,
debounce: opts.debounce != null ? +opts.debounce : null,
readOnly: opts.ro
readOnly: opts.ro,
})
await xapi.connect()
const repl = createRepl({
prompt: `${xapi._humanId}> `
prompt: `${xapi._humanId}> `,
})
repl.context.xapi = xapi

View File

@@ -6,7 +6,18 @@ import httpRequest from 'http-request-plus'
import { BaseError } from 'make-error'
import { EventEmitter } from 'events'
import { fibonacci } from 'iterable-backoff'
import { filter, forEach, isArray, isObject, map, noop, omit, reduce, startsWith } from 'lodash'
import {
filter,
forEach,
isArray,
isInteger,
isObject,
map,
noop,
omit,
reduce,
startsWith,
} from 'lodash'
import {
Cancel,
cancelable,
@@ -14,7 +25,7 @@ import {
defer,
delay as pDelay,
fromEvents,
lastly
lastly,
} from 'promise-toolbox'
import autoTransport from './transports/auto'
@@ -41,7 +52,7 @@ const NETWORK_ERRORS = {
EHOSTUNREACH: true,
// Connection configured timed out has been reach.
ETIMEDOUT: true
ETIMEDOUT: true,
}
const isNetworkError = ({code}) => NETWORK_ERRORS[code]
@@ -50,7 +61,7 @@ const isNetworkError = ({code}) => NETWORK_ERRORS[code]
const XAPI_NETWORK_ERRORS = {
HOST_STILL_BOOTING: true,
HOST_HAS_NO_MANAGEMENT_IP: true
HOST_HAS_NO_MANAGEMENT_IP: true,
}
const isXapiNetworkError = ({code}) => XAPI_NETWORK_ERRORS[code]
@@ -68,19 +79,28 @@ const isSessionInvalid = ({code}) => code === 'SESSION_INVALID'
// -------------------------------------------------------------------
class XapiError extends BaseError {
constructor ([code, ...params]) {
constructor (code, params) {
super(`${code}(${params.join(', ')})`)
this.code = code
this.params = params
// slot than can be assigned later
// slots than can be assigned later
this.method = undefined
this.url = undefined
}
}
export const wrapError = error => new XapiError(error)
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)
}
// ===================================================================
@@ -101,7 +121,7 @@ const {
create: createObject,
defineProperties,
defineProperty,
freeze: freezeObject
freeze: freezeObject,
} = Object
// -------------------------------------------------------------------
@@ -120,6 +140,31 @@ const isReadOnlyCall = (method, args) => (
RE_READ_ONLY_METHOD.test(method)
)
// Prepare values before passing them to the XenAPI:
//
// - cast integers to strings
const prepareParam = param => {
if (isInteger(param)) {
return String(param)
}
if (typeof param !== 'object' || param === null) {
return param
}
if (isArray(param)) {
return map(param, prepareParam)
}
const values = {}
forEach(param, (value, key) => {
if (value !== undefined) {
values[key] = prepareParam(value)
}
})
return values
}
// -------------------------------------------------------------------
const getKey = o => o.$id
@@ -171,7 +216,7 @@ export class Xapi extends EventEmitter {
if (user !== undefined) {
this._auth = {
user,
password: url.password
password: url.password,
}
delete url.username
delete url.password
@@ -190,6 +235,8 @@ export class Xapi extends EventEmitter {
// Memoize this function _addObject().
this._getPool = () => this._pool
this._nTasks = 0
const objects = this._objects = new Collection()
objects.getKey = getKey
@@ -214,7 +261,7 @@ export class Xapi extends EventEmitter {
this.__url = url
this._call = autoTransport({
allowUnauthorized: this._allowUnauthorized,
url
url,
})
}
@@ -306,7 +353,7 @@ export class Xapi extends EventEmitter {
return this._transportCall('session.login_with_password', [
auth.user,
auth.password
auth.password,
]).then(
sessionId => {
this._sessionId = sessionId
@@ -331,6 +378,8 @@ export class Xapi extends EventEmitter {
return Promise.reject(new Error('already disconnected'))
}
this._transportCall('session.logout', [ this._sessionId ]).catch(noop)
this._sessionId = null
debug('%s: disconnected', this._humanId)
@@ -343,27 +392,33 @@ export class Xapi extends EventEmitter {
call (method, ...args) {
return this._readOnly && !isReadOnlyCall(method, args)
? Promise.reject(new Error(`cannot call ${method}() in read only mode`))
: this._sessionCall(method, args)
: this._sessionCall(method, prepareParam(args))
}
@cancelable
callAsync ($cancelToken, method, ...args) {
return this.call(`Async.${method}`, ...args).then(taskRef => {
$cancelToken.promise.then(() => {
this._sessionCall('task.cancel', taskRef).catch(noop)
})
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)
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
nameDescription,
])
promise.then(taskRef => {
@@ -419,11 +474,15 @@ export class Xapi extends EventEmitter {
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
task,
}) {
return this._autoTask(
task,
@@ -444,12 +503,12 @@ export class Xapi extends EventEmitter {
$cancelToken,
this._url,
host && {
hostname: this.getObject(host).address
hostname: this.getObject(host).address,
},
{
pathname,
query,
rejectUnauthorized: !this._allowUnauthorized
rejectUnauthorized: !this._allowUnauthorized,
}
)
@@ -468,8 +527,12 @@ export class Xapi extends EventEmitter {
putResource ($cancelToken, body, pathname, {
host,
query,
task
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}`
@@ -500,14 +563,14 @@ export class Xapi extends EventEmitter {
$cancelToken,
this._url,
host && {
hostname: this.getObject(host).address
hostname: this.getObject(host).address,
},
{
body,
headers,
pathname,
query,
rejectUnauthorized: !this._allowUnauthorized
rejectUnauthorized: !this._allowUnauthorized,
},
override
)
@@ -523,7 +586,7 @@ export class Xapi extends EventEmitter {
? omit(query, 'task_id')
: query,
maxRedirects: 0
maxRedirects: 0,
}).then(
response => {
response.req.abort()
@@ -645,7 +708,7 @@ export class Xapi extends EventEmitter {
id: true,
pool: true,
ref: true,
type: true
type: true,
}
const getKey = (key, obj) => reservedKeys[key] && obj === object
? `$$${key}`
@@ -659,7 +722,7 @@ export class Xapi extends EventEmitter {
// 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
value: EMPTY_ARRAY,
})
// Minor memory optimization, use the same empty array for
@@ -668,7 +731,7 @@ export class Xapi extends EventEmitter {
} else if (isOpaqueRef(value[0])) {
// This is an array of refs.
defineProperty(object, getKey(key, object), {
get: () => freezeObject(map(value, (ref) => objectsByRefs[ref]))
get: () => freezeObject(map(value, (ref) => objectsByRefs[ref])),
})
freezeObject(value)
@@ -679,7 +742,7 @@ export class Xapi extends EventEmitter {
freezeObject(value)
} else if (isOpaqueRef(value)) {
defineProperty(object, getKey(key, object), {
get: () => objectsByRefs[value]
get: () => objectsByRefs[value],
})
}
})
@@ -689,7 +752,7 @@ export class Xapi extends EventEmitter {
$id: { value: object.uuid || ref },
$pool: { get: this._getPool },
$ref: { value: ref },
$type: { value: type }
$type: { value: type },
})
// Finally freezes the object.
@@ -709,9 +772,24 @@ export class Xapi extends EventEmitter {
if (type === 'pool') {
this._pool = object
const eventWatchers = this._eventWatchers
if (eventWatchers !== undefined) {
forEach(object.other_config, (_, key) => {
const eventWatcher = eventWatchers[key]
if (eventWatcher !== undefined) {
delete eventWatchers[key]
eventWatcher(object)
}
})
}
} else if (type === 'task') {
if (prev === undefined) {
++this._nTasks
}
const taskWatchers = this._taskWatchers
let taskWatcher = taskWatchers[ref]
const taskWatcher = taskWatchers[ref]
if (
taskWatcher !== undefined &&
getTaskResult(object, taskWatcher.resolve, taskWatcher.reject)
@@ -719,16 +797,18 @@ export class Xapi extends EventEmitter {
delete taskWatchers[ref]
}
}
return object
}
_removeObject (ref) {
_removeObject (type, ref) {
const byRefs = this._objectsByRefs
const object = byRefs[ref]
if (object !== undefined) {
this._objects.unset(object.$id)
delete byRefs[ref]
if (type === 'task') {
--this._nTasks
}
}
const taskWatchers = this._taskWatchers
@@ -740,26 +820,12 @@ export class Xapi extends EventEmitter {
}
_processEvents (events) {
const eventWatchers = this._eventWatchers
forEach(events, event => {
let object
const { ref } = event
const { class: type, ref } = event
if (event.operation === 'del') {
this._removeObject(ref)
this._removeObject(type, 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)
}
})
}
this._addObject(type, ref, event.snapshot)
}
})
}
@@ -768,13 +834,27 @@ export class Xapi extends EventEmitter {
const loop = () => this.status === CONNECTED && this._sessionCall('event.from', [
['*'],
this._fromToken,
60 + 0.1 // Force float.
60 + 0.1, // Force float.
]).then(onSuccess, onFailure)
const onSuccess = ({token, events}) => {
const onSuccess = ({ events, token, valid_ref_counts: { task } }) => {
this._fromToken = token
this._processEvents(events)
if (task !== this._nTasks) {
forEach(this.objects.all, object => {
if (object.$type === 'task') {
this._removeObject('task', object.$ref)
}
})
this._sessionCall('task.get_all_records').then(tasks => {
forEach(tasks, (task, ref) => {
this._addObject('task', ref, task)
})
}).catch(noop)
}
const debounce = this._debounce
return debounce != null
? pDelay(debounce).then(loop)
@@ -864,7 +944,7 @@ export class Xapi extends EventEmitter {
Xapi.prototype._transportCall = reduce([
function (method, args) {
return this._call(method, args).catch(error => {
if (isArray(error)) {
if (!(error instanceof Error)) {
error = wrapError(error)
}
@@ -906,7 +986,7 @@ Xapi.prototype._transportCall = reduce([
const newUrl = {
...this._url,
hostname: master
hostname: master,
}
this.emit('redirect', newUrl)
this._url = newUrl
@@ -938,7 +1018,7 @@ Xapi.prototype._transportCall = reduce([
throw error
}
)
}
},
], (call, decorator) => decorator(call))
// ===================================================================

View File

@@ -10,7 +10,7 @@ const xapi = (() => {
return createClient({
auth: { user, password },
url,
watchEvents: false
watchEvents: false,
})
})()

View File

@@ -18,5 +18,5 @@ const [ , , url, user, password ] = process.argv
createClient({
auth: { user, password },
readOnly: true,
url
url,
}).connect()

View File

@@ -9,9 +9,9 @@ export default ({ allowUnauthorized, url }) => {
body: format.request(0, method, args),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
'Content-Type': 'application/json',
},
path: '/jsonrpc'
path: '/jsonrpc',
}).readAll('utf8').then(
text => {
let response

View File

@@ -18,7 +18,7 @@ const logError = error => {
const SPECIAL_CHARS = {
'\r': '\\r',
'\t': '\\t'
'\t': '\\t',
}
const SPECIAL_CHARS_RE = new RegExp(
Object.keys(SPECIAL_CHARS).join('|'),
@@ -76,7 +76,7 @@ const parseResult = result => {
export default ({
allowUnauthorized,
url: { hostname, path, port, protocol }
url: { hostname, path, port, protocol },
}) => {
const client = (
protocol === 'https:'
@@ -86,7 +86,7 @@ export default ({
host: hostname,
path: '/json',
port,
rejectUnauthorized: !allowUnauthorized
rejectUnauthorized: !allowUnauthorized,
})
const call = promisify(client.methodCall, client)

View File

@@ -32,7 +32,7 @@ const parseResult = result => {
export default ({
allowUnauthorized,
url: { hostname, path, port, protocol }
url: { hostname, path, port, protocol },
}) => {
const client = (
protocol === 'https:'
@@ -41,7 +41,7 @@ export default ({
)({
host: hostname,
port,
rejectUnauthorized: !allowUnauthorized
rejectUnauthorized: !allowUnauthorized,
})
const call = promisify(client.methodCall, client)

View File

@@ -2,7 +2,12 @@
"comments": false,
"compact": true,
"presets": [
"stage-0",
"es2015"
[
"env", {
"targets": {
"node": 4
}
}
]
]
}

View File

@@ -25,15 +25,11 @@
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"dependency-check": "^2.9.1"
"babel-preset-env": "^1.6.1"
},
"scripts": {
"build": "babel --source-maps --out-dir=dist/ src/",
"depcheck": "dependency-check ./package.json",
"dev": "babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
}
}

View File

@@ -85,7 +85,7 @@ const checkAuthorizationByTypes = {
'VM-snapshot': checkMember('$snapshot_of'),
'VM-template': or(checkSelf, checkMember('$pool'))
'VM-template': or(checkSelf, checkMember('$pool')),
}
// Hoisting is important for this function.

View File

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

View File

@@ -1,8 +0,0 @@
language: node_js
node_js:
- stable
- 6
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

View File

@@ -134,9 +134,6 @@ encoding by prefixing with `json:`:
# Build for production (automatically called by npm install)
> yarn build
# Commit changes
> yarn cz
```
## Contributions

View File

@@ -1,6 +1,6 @@
{
"name": "xo-cli",
"version": "0.10.0",
"version": "0.10.1",
"license": "AGPL-3.0",
"description": "Basic CLI for Xen-Orchestra",
"keywords": [
@@ -28,14 +28,14 @@
"node": ">=6"
},
"dependencies": {
"babel-polyfill": "^7.0.0-beta.3",
"@babel/polyfill": "7.0.0-beta.37",
"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": "^7.1.0",
"human-format": "^0.9.0",
"got": "^8.0.1",
"human-format": "^0.9.2",
"l33teral": "^3.0.3",
"lodash": "^4.17.4",
"micromatch": "^3.1.3",
@@ -49,28 +49,20 @@
"xo-lib": "^0.9.0"
},
"devDependencies": {
"babel-cli": "^7.0.0-beta.3",
"@babel/cli": "7.0.0-beta.37",
"@babel/core": "7.0.0-beta.37",
"@babel/preset-env": "7.0.0-beta.37",
"@babel/preset-flow": "7.0.0-beta.37",
"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",
"dependency-check": "^2.9.1",
"flow-bin": "^0.59.0",
"cross-env": "^5.1.3",
"rimraf": "^2.6.2"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"commitmsg": "npm test",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublish": "npm run build",
"prepublishOnly": "npm run build",
"pretest": "flow status"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}

View File

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

View File

@@ -27,6 +27,9 @@ 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
// -------------------------------------------------------------------
@@ -103,7 +106,7 @@ function parseParameters (args) {
const humanFormatOpts = {
unit: 'B',
scale: 'binary'
scale: 'binary',
}
function printProgress (progress) {
@@ -214,7 +217,7 @@ async function register (args) {
password = await new Promise(function (resolve) {
process.stdout.write('Password: ')
pw(resolve)
})
}),
] = args
const xo = new Xo({ url })
@@ -224,7 +227,7 @@ async function register (args) {
await config.set({
server: url,
token: await xo.call('token.create', { expiresIn })
token: await xo.call('token.create', { expiresIn }),
})
}
exports.register = register
@@ -232,7 +235,7 @@ exports.register = register
function unregister () {
return config.unset([
'server',
'token'
'token',
])
}
exports.unregister = unregister
@@ -367,7 +370,7 @@ async function call (args) {
}
}),
progress,
output
output,
]), 'finish')
}
@@ -381,16 +384,16 @@ async function call (args) {
createReadStream(file),
progressStream({
length: length,
time: 1e3
}, printProgress)
time: 1e3,
}, printProgress),
])
const response = await got.post(url, {
body: input,
headers: {
'content-length': length
'content-length': length,
},
method: 'POST'
method: 'POST',
})
return response.body
}

View File

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

View File

@@ -36,18 +36,17 @@
"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",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"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/",
"posttest": "dependency-check ./package.json --entry dist/collection.js index.js unique-index.js view.js",
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [

View File

@@ -10,7 +10,7 @@ import isObject from './is-object'
const {
create: createObject,
prototype: { hasOwnProperty }
prototype: { hasOwnProperty },
} = Object
export const ACTION_ADD = 'add'
@@ -269,7 +269,7 @@ export default class Collection extends EventEmitter {
const data = {
add: createObject(null),
remove: createObject(null),
update: createObject(null)
update: createObject(null),
}
for (const key in this._buffer) {

View File

@@ -3,7 +3,7 @@
import eventToPromise from 'event-to-promise'
import { forEach } from 'lodash'
import Collection, {DuplicateItem, NoSuchItem} from '..'
import Collection, {DuplicateItem, NoSuchItem} from './collection'
// ===================================================================
@@ -261,58 +261,58 @@ describe('Collection', function () {
'add & update → add': [
[
['add', 'foo', 0],
['update', 'foo', 1]
['update', 'foo', 1],
],
{
add: {
foo: 1
}
}
foo: 1,
},
},
],
'add & remove → ∅': [
[
['add', 'foo', 0],
['remove', 'foo']
['remove', 'foo'],
],
{}
{},
],
'update & update → update': [
[
['update', 'bar', 1],
['update', 'bar', 2]
['update', 'bar', 2],
],
{
update: {
bar: 2
}
}
bar: 2,
},
},
],
'update & remove → remove': [
[
['update', 'bar', 1],
['remove', 'bar']
['remove', 'bar'],
],
{
remove: {
bar: undefined
}
}
bar: undefined,
},
},
],
'remove & add → update': [
[
['remove', 'bar'],
['add', 'bar', 0]
['add', 'bar', 0],
],
{
update: {
bar: 0
}
}
]
bar: 0,
},
},
],
}, ([operations, results], label) => {
it(label, function () {
forEach(operations, ([method, ...args]) => {

View File

@@ -6,7 +6,7 @@ import NotImplemented from './not-implemented'
import {
ACTION_ADD,
ACTION_UPDATE,
ACTION_REMOVE
ACTION_REMOVE,
} from './collection'
// ===================================================================
@@ -77,7 +77,7 @@ export default class Index {
const {
computeHash,
_itemsByHash: itemsByHash,
_keysToHash: keysToHash
_keysToHash: keysToHash,
} = this
for (const key in items) {
@@ -104,7 +104,7 @@ export default class Index {
const {
computeHash,
_itemsByHash: itemsByHash,
_keysToHash: keysToHash
_keysToHash: keysToHash,
} = this
for (const key in items) {
@@ -135,7 +135,7 @@ export default class Index {
_onRemove (items) {
const {
_itemsByHash: itemsByHash,
_keysToHash: keysToHash
_keysToHash: keysToHash,
} = this
for (const key in items) {

View File

@@ -3,8 +3,8 @@
import eventToPromise from 'event-to-promise'
import { forEach } from 'lodash'
import Collection from '..'
import Index from '../index'
import Collection from './collection'
import Index from './index'
// ===================================================================
@@ -29,18 +29,18 @@ describe('Index', function () {
let col, byGroup
const item1 = {
id: '2ccb8a72-dc65-48e4-88fe-45ef541f2cba',
group: 'foo'
group: 'foo',
}
const item2 = {
id: '7d21dc51-4da8-4538-a2e9-dd6f4784eb76',
group: 'bar'
group: 'bar',
}
const item3 = {
id: '668c1274-4442-44a6-b99a-512188e0bb09',
group: 'foo'
group: 'foo',
}
const item4 = {
id: 'd90b7335-e540-4a44-ad22-c4baae9cd0a9'
id: 'd90b7335-e540-4a44-ad22-c4baae9cd0a9',
}
beforeEach(function () {
@@ -61,19 +61,19 @@ describe('Index', function () {
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3
[item3.id]: item3,
},
bar: {
[item2.id]: item2
}
}
[item2.id]: item2,
},
},
})
})
it('works with added items', function () {
const item5 = {
id: '823b56c4-4b96-4f3a-9533-5d08177167ac',
group: 'baz'
group: 'baz',
}
col.add(item5)
@@ -83,15 +83,15 @@ describe('Index', function () {
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3
[item3.id]: item3,
},
bar: {
[item2.id]: item2
[item2.id]: item2,
},
baz: {
[item5.id]: item5
}
}
[item5.id]: item5,
},
},
})
})
})
@@ -99,7 +99,7 @@ describe('Index', function () {
it('works with updated items', function () {
const item1bis = {
id: item1.id,
group: 'bar'
group: 'bar',
}
col.update(item1bis)
@@ -108,13 +108,13 @@ describe('Index', function () {
expect(col.indexes).toEqual({
byGroup: {
foo: {
[item3.id]: item3
[item3.id]: item3,
},
bar: {
[item1.id]: item1bis,
[item2.id]: item2
}
}
[item2.id]: item2,
},
},
})
})
})
@@ -127,10 +127,10 @@ describe('Index', function () {
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3
[item3.id]: item3,
},
bar: {}
}
bar: {},
},
})
})
})
@@ -139,7 +139,7 @@ describe('Index', function () {
const item1bis = {
id: item1.id,
group: item1.group,
newProp: true
newProp: true,
}
col.update(item1bis)
@@ -149,12 +149,12 @@ describe('Index', function () {
byGroup: {
foo: {
[item1.id]: item1bis,
[item3.id]: item3
[item3.id]: item3,
},
bar: {
[item2.id]: item2
}
}
[item2.id]: item2,
},
},
})
})
})
@@ -170,9 +170,9 @@ describe('Index', function () {
byGroup: {
foo: {
[item1.id]: item1,
[item3.id]: item3
}
}
[item3.id]: item3,
},
},
})
})
})

View File

@@ -5,7 +5,7 @@ import NotImplemented from './not-implemented'
import {
ACTION_ADD,
ACTION_UPDATE,
ACTION_REMOVE
ACTION_REMOVE,
} from './collection'
// ===================================================================
@@ -66,7 +66,7 @@ export default class UniqueIndex {
const {
computeHash,
_itemByHash: itemByHash,
_keysToHash: keysToHash
_keysToHash: keysToHash,
} = this
for (const key in items) {
@@ -85,7 +85,7 @@ export default class UniqueIndex {
const {
computeHash,
_itemByHash: itemByHash,
_keysToHash: keysToHash
_keysToHash: keysToHash,
} = this
for (const key in items) {
@@ -110,7 +110,7 @@ export default class UniqueIndex {
_onRemove (items) {
const {
_itemByHash: itemByHash,
_keysToHash: keysToHash
_keysToHash: keysToHash,
} = this
for (const key in items) {

View File

@@ -3,8 +3,8 @@
import eventToPromise from 'event-to-promise'
import { forEach } from 'lodash'
import Collection from '..'
import Index from '../unique-index'
import Collection from './collection'
import Index from './unique-index'
// ===================================================================
@@ -29,14 +29,14 @@ describe('UniqueIndex', function () {
let col, byKey
const item1 = {
id: '2ccb8a72-dc65-48e4-88fe-45ef541f2cba',
key: '036dee1b-9a3b-4fb5-be8a-4f535b355581'
key: '036dee1b-9a3b-4fb5-be8a-4f535b355581',
}
const item2 = {
id: '7d21dc51-4da8-4538-a2e9-dd6f4784eb76',
key: '103cd893-d2cc-4d37-96fd-c259ad04c0d4'
key: '103cd893-d2cc-4d37-96fd-c259ad04c0d4',
}
const item3 = {
id: '668c1274-4442-44a6-b99a-512188e0bb09'
id: '668c1274-4442-44a6-b99a-512188e0bb09',
}
beforeEach(function () {
@@ -56,15 +56,15 @@ describe('UniqueIndex', function () {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1,
[item2.key]: item2
}
[item2.key]: item2,
},
})
})
it('works with added items', function () {
const item4 = {
id: '823b56c4-4b96-4f3a-9533-5d08177167ac',
key: '1437af14-429a-40db-8a51-8a2f5ed03201'
key: '1437af14-429a-40db-8a51-8a2f5ed03201',
}
col.add(item4)
@@ -74,8 +74,8 @@ describe('UniqueIndex', function () {
byKey: {
[item1.key]: item1,
[item2.key]: item2,
[item4.key]: item4
}
[item4.key]: item4,
},
})
})
})
@@ -83,7 +83,7 @@ describe('UniqueIndex', function () {
it('works with updated items', function () {
const item1bis = {
id: item1.id,
key: 'e03d4a3a-0331-4aca-97a2-016bbd43a29b'
key: 'e03d4a3a-0331-4aca-97a2-016bbd43a29b',
}
col.update(item1bis)
@@ -92,8 +92,8 @@ describe('UniqueIndex', function () {
expect(col.indexes).toEqual({
byKey: {
[item1bis.key]: item1bis,
[item2.key]: item2
}
[item2.key]: item2,
},
})
})
})
@@ -104,8 +104,8 @@ describe('UniqueIndex', function () {
return waitTicks().then(() => {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1
}
[item1.key]: item1,
},
})
})
})
@@ -114,7 +114,7 @@ describe('UniqueIndex', function () {
const item1bis = {
id: item1.id,
key: item1.key,
newProp: true
newProp: true,
}
col.update(item1bis)
@@ -123,8 +123,8 @@ describe('UniqueIndex', function () {
expect(col.indexes).toEqual({
byKey: {
[item1.key]: item1bis,
[item2.key]: item2
}
[item2.key]: item2,
},
})
})
})

View File

@@ -1,7 +1,7 @@
import { forEach } from 'lodash'
import Collection from '..'
import View from '../view'
import Collection from './collection'
import View from './view'
// ===================================================================
@@ -11,18 +11,18 @@ users.getKey = (user) => user.name
// Inserts some data.
users.add({
name: 'bob'
name: 'bob',
})
users.add({
name: 'clara',
active: true
active: true,
})
users.add({
name: 'ophelia'
name: 'ophelia',
})
users.add({
name: 'Steve',
active: true
active: true,
})
// -------------------------------------------------------------------
@@ -48,9 +48,9 @@ setTimeout(function () {
users.set({
name: 'ophelia',
active: true
active: true,
})
users.set({
name: 'Steve'
name: 'Steve',
})
}, 10)

View File

@@ -3,7 +3,7 @@ import { bind, forEach, iteratee as createCallback } from 'lodash'
import Collection, {
ACTION_ADD,
ACTION_UPDATE,
ACTION_REMOVE
ACTION_REMOVE,
} from './collection'
// ===================================================================

View File

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

View File

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

View File

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

View File

@@ -33,20 +33,17 @@
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-0": "^6.24.1",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json --entry api-errors.js",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
@@ -62,7 +59,7 @@
}
}
],
"stage-0"
"stage-3"
]
}
}

View File

@@ -12,7 +12,7 @@ class XoError extends BaseError {
return {
message: this.message,
code: this.code,
data: this.data
data: this.data,
}
}
}
@@ -28,44 +28,44 @@ const create = (code, getProps) => {
// =============================================================================
export const notImplemented = create(0, () => ({
message: 'not implemented'
message: 'not implemented',
}))
export const noSuchObject = create(1, (id, type) => ({
data: { id, type },
message: 'no such object'
message: 'no such object',
}))
export const unauthorized = create(2, () => ({
message: 'not authenticated or not enough permissions'
message: 'not authenticated or not enough permissions',
}))
export const invalidCredentials = create(3, () => ({
message: 'invalid credentials'
message: 'invalid credentials',
}))
// Deprecated alreadyAuthenticated (4)
export const forbiddenOperation = create(5, (operation, reason) => ({
data: { operation, reason },
message: `forbidden operation: ${operation}`
message: `forbidden operation: ${operation}`,
}))
// Deprecated GenericError (6)
export const noHostsAvailable = create(7, () => ({
message: 'no hosts available'
message: 'no hosts available',
}))
export const authenticationFailed = create(8, () => ({
message: 'authentication failed'
message: 'authentication failed',
}))
export const serverUnreachable = create(9, objectId => ({
data: {
objectId
objectId,
},
message: 'server unreachable'
message: 'server unreachable',
}))
export const invalidParameters = create(10, (message, errors) => {
@@ -76,22 +76,22 @@ export const invalidParameters = create(10, (message, errors) => {
return {
data: { errors },
message: message || 'invalid parameters'
message: message || 'invalid parameters',
}
})
export const vmMissingPvDrivers = create(11, ({ vm }) => ({
data: {
objectId: vm
objectId: vm,
},
message: 'missing PV drivers'
message: 'missing PV drivers',
}))
export const vmIsTemplate = create(12, ({ vm }) => ({
data: {
objectId: vm
objectId: vm,
},
message: 'VM is a template'
message: 'VM is a template',
}))
// TODO: We should probably create a more generic error which gathers all incorrect state errors.
@@ -109,58 +109,58 @@ export const vmBadPowerState = create(13, ({ vm, expected, actual }) => ({
data: {
objectId: vm,
expected,
actual
actual,
},
message: `VM state is ${actual} but should be ${expected}`
message: `VM state is ${actual} but should be ${expected}`,
}))
export const vmLacksFeature = create(14, ({ vm, feature }) => ({
data: {
objectId: vm,
feature
feature,
},
message: `VM lacks feature ${feature || ''}`
message: `VM lacks feature ${feature || ''}`,
}))
export const notSupportedDuringUpgrade = create(15, () => ({
message: 'not supported during upgrade'
message: 'not supported during upgrade',
}))
export const objectAlreadyExists = create(16, ({ objectId, objectType }) => ({
data: {
objectId,
objectType
objectType,
},
message: `${objectType || 'object'} already exists`
message: `${objectType || 'object'} already exists`,
}))
export const vdiInUse = create(17, ({ vdi, operation }) => ({
data: {
objectId: vdi,
operation
operation,
},
message: 'VDI in use'
message: 'VDI in use',
}))
export const hostOffline = create(18, ({ host }) => ({
data: {
objectId: host
objectId: host,
},
message: 'host offline'
message: 'host offline',
}))
export const operationBlocked = create(19, ({ objectId, code }) => ({
data: {
objectId,
code
code,
},
message: 'operation blocked'
message: 'operation blocked',
}))
export const patchPrecheckFailed = create(20, ({ errorType, patch }) => ({
data: {
objectId: patch,
errorType
errorType,
},
message: `patch precheck failed: ${errorType}`
message: `patch precheck failed: ${errorType}`,
}))

View File

@@ -4,10 +4,10 @@ process.on('unhandledRejection', function (error) {
console.log(error)
})
var Xo = require('./').default
const Xo = require('./').default // eslint-disable-line node/no-missing-require
var xo = new Xo({
url: 'localhost:9000'
const xo = new Xo({
url: 'localhost:9000',
})
xo.open().then(function () {
@@ -19,7 +19,7 @@ xo.open().then(function () {
}).then(function () {
return xo.signIn({
email: 'admin@admin.net',
password: 'admin'
password: 'admin',
}).then(function () {
console.log('connected as ', xo.user)
}).catch(function (error) {
@@ -28,7 +28,7 @@ xo.open().then(function () {
}).then(function () {
return xo.signIn({
email: 'tom',
password: 'tom'
password: 'tom',
}).then(function () {
console.log('connected as', xo.user)

View File

@@ -28,7 +28,7 @@
"node": ">=4"
},
"dependencies": {
"jsonrpc-websocket-client": "^0.2.0",
"jsonrpc-websocket-client": "^0.3.0",
"lodash": "^4.17.2",
"make-error": "^1.0.4"
},
@@ -37,18 +37,15 @@
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"commitmsg": "npm test",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"env": {

View File

@@ -1,6 +1,6 @@
import JsonRpcWebSocketClient, {
OPEN,
CLOSED
CLOSED,
} from 'jsonrpc-websocket-client'
import { BaseError } from 'make-error'
import { startsWith } from 'lodash'

View File

@@ -31,18 +31,16 @@
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.1",
"cross-env": "^5.1.3",
"deep-freeze": "^0.0.1",
"dependency-check": "^2.9.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/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [

View File

@@ -12,8 +12,8 @@ const data = deepFreeze({
string: 'file:///var/lib/xoa/backup',
object: {
type: 'file',
path: '/var/lib/xoa/backup'
}
path: '/var/lib/xoa/backup',
},
},
SMB: {
string: 'smb://Administrator:pas:sw@ord@toto\\\\192.168.100.225\\smb\0',
@@ -23,17 +23,17 @@ const data = deepFreeze({
path: '',
domain: 'toto',
username: 'Administrator',
password: 'pas:sw@ord'
}
password: 'pas:sw@ord',
},
},
NFS: {
string: 'nfs://192.168.100.225:/media/nfs',
object: {
type: 'nfs',
host: '192.168.100.225',
path: '/media/nfs'
}
}
path: '/media/nfs',
},
},
})
const parseData = deepFreeze({
@@ -43,17 +43,17 @@ const parseData = deepFreeze({
string: 'file://var/lib/xoa/backup',
object: {
type: 'file',
path: '/var/lib/xoa/backup'
}
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'
}
}
path: '/media/nfs',
},
},
})
const formatData = deepFreeze({
@@ -63,9 +63,9 @@ const formatData = deepFreeze({
string: 'file:///var/lib/xoa/backup',
object: {
type: 'local',
path: '/var/lib/xoa/backup'
}
}
path: '/var/lib/xoa/backup',
},
},
})
// -------------------------------------------------------------------

View File

@@ -35,24 +35,26 @@
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"dependency-check": "^2.9.1"
"babel-preset-env": "^1.6.1"
},
"scripts": {
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"depcheck": "dependency-check ./package.json",
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"transform-runtime"
],
"presets": [
"latest",
"stage-0"
[
"env",
{
"targets": {
"node": 4
}
}
]
]
}
}

View File

@@ -6,13 +6,13 @@ export const configurationSchema = {
type: 'object',
properties: {
clientID: {
type: 'string'
type: 'string',
},
clientSecret: {
type: 'string'
}
type: 'string',
},
},
required: ['clientID', 'clientSecret']
required: ['clientID', 'clientSecret'],
}
// ===================================================================

View File

@@ -1,9 +0,0 @@
language: node_js
node_js:
- stable
- 6
- 4
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

View File

@@ -41,18 +41,15 @@
"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",
"dependency-check": "^2.8.0",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"commitmsg": "npm test",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"env": {

View File

@@ -7,22 +7,22 @@ export const configurationSchema = {
properties: {
callbackURL: {
type: 'string',
description: 'Must be exactly the same as specified on the Google developer console.'
description: 'Must be exactly the same as specified on the Google developer console.',
},
clientID: {
type: 'string'
type: 'string',
},
clientSecret: {
type: 'string'
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' ]
}
enumNames: [ 'Google+ name', 'Simple email address' ],
},
},
required: ['callbackURL', 'clientID', 'clientSecret']
required: ['callbackURL', 'clientID', 'clientSecret'],
}
// ===================================================================

View File

@@ -1,6 +1,6 @@
{
"name": "xo-server-auth-ldap",
"version": "0.6.3",
"version": "0.6.4",
"license": "AGPL-3.0",
"description": "LDAP authentication plugin for XO-Server",
"keywords": [
@@ -36,7 +36,7 @@
"babel-runtime": "^6.22.0",
"event-to-promise": "^0.8.0",
"exec-promise": "^0.7.0",
"inquirer": "^3.0.1",
"inquirer": "^5.0.0",
"ldapjs": "^1.0.1",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5"
@@ -47,18 +47,15 @@
"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",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"commitmsg": "npm test",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "npm run prebuild",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [

View File

@@ -25,7 +25,7 @@ export const configurationSchema = {
properties: {
uri: {
description: 'URI of the LDAP server.',
type: 'string'
type: 'string',
},
certificateAuthorities: {
description: `
@@ -35,13 +35,13 @@ If not specified, it will use a default set of well-known CAs.
`.trim(),
type: 'array',
items: {
type: 'string'
}
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
default: true,
},
bind: {
description: 'Credentials to use before looking for the user record.',
@@ -55,18 +55,18 @@ Example: uid=xoa-auth,ou=people,dc=company,dc=net
For Microsoft Active Directory, it can also be \`<user>@<domain>\`.
`.trim(),
type: 'string'
type: 'string',
},
password: {
description: 'Password of the user permitted of search the LDAP directory.',
type: 'string'
}
type: 'string',
},
},
required: ['dn', 'password']
required: ['dn', 'password'],
},
base: {
description: 'The base is the part of the description tree where the users are looked for.',
type: 'string'
type: 'string',
},
filter: {
description: `
@@ -85,10 +85,10 @@ something like:
- \`(&(uid={{name}})(memberOf=<group DN>))\`
`.trim(),
type: 'string',
default: '(uid={{name}})'
}
default: '(uid={{name}})',
},
},
required: ['uri', 'base']
required: ['uri', 'base'],
}
export const testSchema = {
@@ -96,14 +96,14 @@ export const testSchema = {
properties: {
username: {
description: 'LDAP username',
type: 'string'
type: 'string',
},
password: {
description: 'LDAP password',
type: 'string'
}
type: 'string',
},
},
required: ['username', 'password']
required: ['username', 'password'],
}
// ===================================================================
@@ -119,14 +119,14 @@ class AuthLdap {
const clientOpts = this._clientOpts = {
url: conf.uri,
maxConnections: 5,
tlsOptions: {}
tlsOptions: {},
}
{
const {
bind,
checkCertificate = true,
certificateAuthorities
certificateAuthorities,
} = conf
if (bind) {
@@ -147,7 +147,7 @@ class AuthLdap {
const {
bind: credentials,
base: searchBase,
filter: searchFilter = '(uid={{name}})'
filter: searchFilter = '(uid={{name}})',
} = conf
this._credentials = credentials
@@ -166,7 +166,7 @@ class AuthLdap {
test ({ username, password }) {
return this._authenticate({
username,
password
password,
}).then(result => {
if (result === null) {
throw new Error('could not authenticate user')
@@ -207,8 +207,8 @@ class AuthLdap {
const response = await search(this._searchBase, {
scope: 'sub',
filter: evalFilter(this._searchFilter, {
name: username
})
name: username,
}),
})
response.on('searchEntry', entry => {

View File

@@ -9,46 +9,46 @@ const EMPTY_OBJECT = Object.freeze({ __proto__: null })
const _extractValue = ({ value }) => value
export const confirm = (message, {
default: defaultValue = null
default: defaultValue = null,
} = EMPTY_OBJECT) => prompt({
default: defaultValue,
message,
name: 'value',
type: 'confirm'
type: 'confirm',
}).then(_extractValue)
export const input = (message, {
default: defaultValue = null,
filter = undefined,
validate = undefined
validate = undefined,
} = EMPTY_OBJECT) => prompt({
default: defaultValue,
message,
name: 'value',
type: 'input',
validate
validate,
}).then(_extractValue)
export const list = (message, choices, {
default: defaultValue = null
default: defaultValue = null,
} = EMPTY_OBJECT) => prompt({
default: defaultValue,
choices,
message,
name: 'value',
type: 'list'
type: 'list',
}).then(_extractValue)
export const password = (message, {
default: defaultValue = null,
filter = undefined,
validate = undefined
validate = undefined,
} = EMPTY_OBJECT) => prompt({
default: defaultValue,
message,
name: 'value',
type: 'password',
validate
validate,
}).then(_extractValue)
// ===================================================================
@@ -86,7 +86,7 @@ const promptByType = {
while (
i < n && // eslint-disable-line no-unmodified-loop-condition
await confirm('additional item?', {
default: false
default: false,
})
) {
await promptItem()
@@ -96,23 +96,23 @@ const promptByType = {
},
boolean: (schema, defaultValue, path) => confirm(path, {
default: defaultValue != null ? defaultValue : schema.default
default: defaultValue != null ? defaultValue : schema.default,
}),
enum: (schema, defaultValue, path) => list(path, schema.enum, {
defaultValue: defaultValue || schema.defaultValue
defaultValue: defaultValue || schema.defaultValue,
}),
integer: (schema, defaultValue, path) => input(path, {
default: defaultValue || schema.default,
filter: input => +input,
validate: input => isInteger(+input)
validate: input => isInteger(+input),
}),
number: (schema, defaultValue, path) => input(path, {
default: defaultValue || schema.default,
filter: input => +input,
validate: input => isFinite(+input)
validate: input => isFinite(+input),
}),
object: async (schema, defaultValue, path) => {
@@ -131,7 +131,7 @@ const promptByType = {
if (
required[name] ||
await confirm(`fill optional ${subpath}?`, {
default: Boolean(defaultValue && name in defaultValue)
default: Boolean(defaultValue && name in defaultValue),
})
) {
value[name] = await promptGeneric(
@@ -148,8 +148,8 @@ const promptByType = {
},
string: (schema, defaultValue, path) => input(path, {
default: defaultValue || schema.default
})
default: defaultValue || schema.default,
}),
}
export default function promptGeneric (schema, defaultValue, path) {

View File

@@ -7,10 +7,10 @@ import { readFile, writeFile } from 'fs'
import promptSchema, {
input,
password
password,
} from './prompt-schema'
import createPlugin, {
configurationSchema
configurationSchema,
} from './'
// ===================================================================
@@ -42,8 +42,8 @@ execPromise(async args => {
await plugin._authenticate({
username: await input('Username', {
validate: input => !!input.length
validate: input => !!input.length,
}),
password: await password('Password')
password: await password('Password'),
}, bind(console.log, console))
})

View File

@@ -1,6 +1,6 @@
{
"name": "xo-server-auth-saml",
"version": "0.4.1",
"version": "0.5.0",
"license": "AGPL-3.0",
"description": "SAML authentication plugin for XO-Server",
"keywords": [
@@ -33,34 +33,38 @@
},
"dependencies": {
"babel-runtime": "^6.11.6",
"passport-saml": "^0.15.0"
"passport-saml": "^0.32.1"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"transform-runtime"
],
"presets": [
"latest",
"stage-0"
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -6,19 +6,19 @@ export const configurationSchema = {
type: 'object',
properties: {
cert: {
type: 'string'
type: 'string',
},
entryPoint: {
type: 'string'
type: 'string',
},
issuer: {
type: 'string'
type: 'string',
},
usernameField: {
type: 'string'
}
type: 'string',
},
},
required: ['cert', 'entryPoint', 'issuer']
required: ['cert', 'entryPoint', 'issuer'],
}
// ===================================================================

View File

@@ -35,28 +35,24 @@
"node": ">=4"
},
"dependencies": {
"human-format": "^0.9.0",
"human-format": "^0.9.2",
"lodash": "^4.13.1",
"moment-timezone": "^0.5.13"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-eslint": "^8.0.1",
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [

View File

@@ -14,9 +14,9 @@ export const configurationSchema = {
description: 'an array of recipients (mails)',
items: {
type: 'string'
type: 'string',
},
minItems: 1
minItems: 1,
},
toXmpp: {
type: 'array',
@@ -24,11 +24,11 @@ export const configurationSchema = {
description: 'an array of recipients (xmpp)',
items: {
type: 'string'
type: 'string',
},
minItems: 1
}
}
minItems: 1,
},
},
}
// ===================================================================
@@ -50,13 +50,13 @@ const formatMethod = method =>
const formatSize = bytes =>
humanFormat(bytes, {
scale: 'binary',
unit: 'B'
unit: 'B',
})
const formatSpeed = (bytes, milliseconds) =>
humanFormat(bytes * 1e3 / milliseconds, {
scale: 'binary',
unit: 'B/s'
unit: 'B/s',
})
const logError = e => {
@@ -115,7 +115,7 @@ class BackupReportsXoPlugin {
const reportOnFailure =
reportWhen === 'fail' || // xo-web < 5
reportWhen === 'failure' // xo-web >= 5
reportWhen === 'failure' // xo-web >= 5
let globalMergeSize = 0
let globalTransferSize = 0
@@ -143,7 +143,7 @@ class BackupReportsXoPlugin {
`- **UUID**: ${vm !== undefined ? vm.uuid : id}`,
`- **Start time**: ${formatDate(start)}`,
`- **End time**: ${formatDate(end)}`,
`- **Duration**: ${formatDuration(duration)}`
`- **Duration**: ${formatDuration(duration)}`,
]
const { error } = call
@@ -205,7 +205,7 @@ class BackupReportsXoPlugin {
`- **Start time**: ${formatDate(start)}`,
`- **End time**: ${formatDate(end)}`,
`- **Duration**: ${formatDuration(duration)}`,
`- **Successes**: ${nSuccesses} / ${nCalls}`
`- **Successes**: ${nSuccesses} / ${nCalls}`,
]
if (globalTransferSize !== 0) {
markdown.push(
@@ -256,21 +256,21 @@ class BackupReportsXoPlugin {
} Backup report for ${tag} ${
globalSuccess ? ICON_SUCCESS : ICON_FAILURE
}`,
markdown
markdown,
}),
xo.sendToXmppClient !== undefined && xo.sendToXmppClient({
to: this._xmppReceivers,
message: markdown
message: markdown,
}),
xo.sendSlackMessage !== undefined && xo.sendSlackMessage({
message: markdown
message: markdown,
}),
xo.sendPassiveCheck !== undefined && xo.sendPassiveCheck({
status: globalSuccess ? 0 : 2,
message: globalSuccess
? `[Xen Orchestra] [Success] Backup report for ${tag}`
: `[Xen Orchestra] [Failure] Backup report for ${tag} - VMs : ${nagiosText.join(' ')}`
})
: `[Xen Orchestra] [Failure] Backup report for ${tag} - VMs : ${nagiosText.join(' ')}`,
}),
])
}
}

View File

@@ -33,27 +33,24 @@
"dependencies": {
"babel-runtime": "^6.20.0",
"event-to-promise": "^0.8.0",
"jsonrpc-websocket-client": "^0.2.0",
"superagent": "^3.4.4"
"jsonrpc-websocket-client": "^0.3.0",
"superagent": "^3.8.2"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.16.0",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.5.4"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [

View File

@@ -13,7 +13,7 @@ class XoServerCloud {
constructor ({ xo }) {
this._xo = xo
// Defined in configure().
// Defined in configure().
this._conf = null
this._key = null
}
@@ -31,16 +31,16 @@ class XoServerCloud {
registerResource.description = 'Register a resource via cloud plugin'
registerResource.params = {
namespace: {
type: 'string'
}
type: 'string',
},
}
registerResource.permission = 'admin'
this._unsetApiMethods = this._xo.addApiMethods({
cloud: {
getResourceCatalog,
registerResource
}
registerResource,
},
})
this._unsetRequestResource = this._xo.defineProperty('requestResource', this._requestResource, this)
@@ -127,7 +127,7 @@ class XoServerCloud {
const downloadToken = await this._updater.call('getResourceDownloadToken', {
token: namespaceCatalog._token,
id,
version
version,
})
if (!downloadToken) {

View File

@@ -39,16 +39,12 @@
"babel-cli": "^6.24.1",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"dependency-check": "^2.9.1"
"babel-preset-stage-3": "^6.24.1"
},
"scripts": {
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"depcheck": "dependency-check ./package.json",
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
@@ -56,8 +52,15 @@
"lodash"
],
"presets": [
"es2015",
"stage-0"
[
"env",
{
"targets": {
"node": 4
}
}
],
"stage-3"
]
}
}

View File

@@ -21,11 +21,11 @@ export default class DensityPlan extends Plan {
const {
hosts,
toOptimize
toOptimize,
} = results
let {
averages: hostsAverages
averages: hostsAverages,
} = results
const pools = await this._getPlanPools()
@@ -34,11 +34,11 @@ export default class DensityPlan extends Plan {
for (const hostToOptimize of toOptimize) {
const {
id: hostId,
$poolId: poolId
$poolId: poolId,
} = hostToOptimize
const {
master: masterId
master: masterId,
} = pools[poolId]
// Avoid master optimization.
@@ -60,7 +60,7 @@ export default class DensityPlan extends Plan {
for (const dest of hosts) {
const {
id: destId,
$poolId: destPoolId
$poolId: destPoolId,
} = dest
// Destination host != Host to optimize!
@@ -87,9 +87,9 @@ export default class DensityPlan extends Plan {
[ poolMaster ],
poolHosts,
masters,
otherHosts
otherHosts,
],
hostsAverages: clone(hostsAverages)
hostsAverages: clone(hostsAverages),
})
if (simulResults) {
@@ -127,7 +127,7 @@ export default class DensityPlan extends Plan {
const simulResults = {
hostsAverages,
moves: []
moves: [],
}
// Try to find a destination for each VM.
@@ -140,7 +140,7 @@ export default class DensityPlan extends Plan {
vm,
destinations: subDestinations,
hostsAverages,
vmsAverages
vmsAverages,
})
// Destination found.
@@ -164,8 +164,8 @@ export default class DensityPlan extends Plan {
_testMigration ({ vm, destinations, hostsAverages, vmsAverages }) {
const {
_thresholds: {
critical: criticalThreshold
}
critical: criticalThreshold,
},
} = this
// Sort the destinations by available memory. (- -> +)
@@ -192,7 +192,7 @@ export default class DensityPlan extends Plan {
// Available movement.
return {
vm,
destination
destination,
}
}
}
@@ -206,7 +206,7 @@ export default class DensityPlan extends Plan {
mapToArray(moves, move => {
const {
vm,
destination
destination,
} = move
const xapiDest = this.xo.getXapi(destination)
debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${vm.$container}).`)

View File

@@ -7,11 +7,11 @@ import DensityPlan from './density-plan'
import PerformancePlan from './performance-plan'
import {
DEFAULT_CRITICAL_THRESHOLD_CPU,
DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE
DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE,
} from './plan'
import {
EXECUTION_DELAY,
debug
debug,
} from './utils'
// ===================================================================
@@ -37,12 +37,12 @@ export const configurationSchema = {
properties: {
name: {
type: 'string',
title: 'Name'
title: 'Name',
},
mode: {
enum: [ 'Performance mode', 'Density mode' ],
title: 'Mode'
title: 'Mode',
},
pools: {
@@ -51,8 +51,8 @@ export const configurationSchema = {
items: {
type: 'string',
$type: 'Pool'
}
$type: 'Pool',
},
},
thresholds: {
@@ -63,14 +63,14 @@ export const configurationSchema = {
cpu: {
type: 'integer',
title: 'CPU (%)',
default: DEFAULT_CRITICAL_THRESHOLD_CPU
default: DEFAULT_CRITICAL_THRESHOLD_CPU,
},
memoryFree: {
type: 'integer',
title: 'RAM, Free memory (MB)',
default: DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE
}
}
default: DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE,
},
},
},
excludedHosts: {
@@ -80,19 +80,19 @@ export const configurationSchema = {
items: {
type: 'string',
$type: 'Host'
}
}
$type: 'Host',
},
},
},
required: [ 'name', 'mode', 'pools' ]
required: [ 'name', 'mode', 'pools' ],
},
minItems: 1
}
minItems: 1,
},
},
additionalProperties: false
additionalProperties: false,
}
// ===================================================================
@@ -102,7 +102,7 @@ export const configurationSchema = {
const makeJob = (cronPattern, fn) => {
const job = {
running: false,
emitter: new EventEmitter()
emitter: new EventEmitter(),
}
job.cron = new CronJob(cronPattern, async () => {
@@ -133,7 +133,7 @@ const makeJob = (cronPattern, fn) => {
class LoadBalancerPlugin {
constructor (xo) {
this.xo = xo
this._job = makeJob(`*/${EXECUTION_DELAY} * * * *`, ::this._executePlans)
this._job = makeJob(`*/${EXECUTION_DELAY} * * * *`, this._executePlans.bind(this))
}
async configure ({ plans }) {

View File

@@ -54,7 +54,7 @@ export default class PerformancePlan extends Plan {
const {
averages,
toOptimize
toOptimize,
} = results
let { hosts } = results
@@ -75,7 +75,7 @@ export default class PerformancePlan extends Plan {
await this._optimize({
exceededHost,
hosts,
hostsAverages: averages
hostsAverages: averages,
})
}
}

View File

@@ -2,7 +2,7 @@ import { filter, includes, map as mapToArray } from 'lodash'
import {
EXECUTION_DELAY,
debug
debug,
} from './utils'
const MINUTES_OF_HISTORICAL_DATA = 30
@@ -58,7 +58,7 @@ function computeRessourcesAverage (objects, objectsStats, nPoints) {
),
nCpus: stats.cpus.length,
memoryFree: computeAverage(stats.memoryFree, nPoints),
memory: computeAverage(stats.memory, nPoints)
memory: computeAverage(stats.memory, nPoints),
}
}
@@ -91,7 +91,7 @@ function setRealCpuAverageOfVms (vms, vmsAverages, nCpus) {
export default class Plan {
constructor (xo, name, poolIds, {
excludedHosts,
thresholds
thresholds,
} = {}) {
this.xo = xo
this._name = name
@@ -99,11 +99,11 @@ export default class Plan {
this._excludedHosts = excludedHosts
this._thresholds = {
cpu: {
critical: numberOrDefault(thresholds && thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU)
critical: numberOrDefault(thresholds && thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU),
},
memoryFree: {
critical: numberOrDefault(thresholds && thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024
}
critical: numberOrDefault(thresholds && thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024,
},
}
for (const key in this._thresholds) {
@@ -157,7 +157,7 @@ export default class Plan {
return {
toOptimize,
averages: avgWithRatio,
hosts
hosts,
}
}
@@ -213,7 +213,7 @@ export default class Plan {
hostsStats[host.id] = {
nPoints: hostStats.stats.cpus[0].length,
stats: hostStats.stats,
averages: {}
averages: {},
}
})
))
@@ -229,7 +229,7 @@ export default class Plan {
vmsStats[vm.id] = {
nPoints: vmStats.stats.cpus[0].length,
stats: vmStats.stats,
averages: {}
averages: {},
}
})
))

View File

@@ -9,10 +9,10 @@ exports.configurationSchema = {
type: 'object',
properties: {
foo: {
type: 'string'
}
type: 'string',
},
},
required: ['foo']
required: ['foo'],
}
// This (optional) schema is used to test the configuration
@@ -21,10 +21,10 @@ exports.testSchema = {
type: 'object',
properties: {
test: {
type: 'string'
}
type: 'string',
},
},
required: ['test']
required: ['test'],
}
// The default export is just a factory function which will create an
@@ -73,6 +73,6 @@ exports.default = function (opts) {
test: function (data) {
console.log('the configuration is about to be tested')
// TODO: test the configuration, i.e, use the main feature of the plugin and throws any errors.
}
},
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "xo-server-transport-email",
"version": "0.4.2",
"version": "0.5.0",
"license": "AGPL-3.0",
"description": "xo-server plugin to send emails",
"keywords": [
@@ -31,7 +31,7 @@
"node": ">=4"
},
"dependencies": {
"nodemailer": "3.1.8",
"nodemailer": "4.4.1",
"nodemailer-markdown": "^1.0.1",
"promise-toolbox": "^0.9.5"
},
@@ -40,17 +40,15 @@
"babel-plugin-lodash": "^3.3.2",
"babel-preset-env": "^1.5.2",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"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/",
"posttest": "dependency-check ./package.json",
"prebuild": "rimraf dist/",
"predev": "yarn run prebuild",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [

View File

@@ -33,16 +33,16 @@ export const configurationSchema = {
properties: {
name: {
type: 'string',
description: 'human readable name of the sender'
description: 'human readable name of the sender',
},
address: {
type: 'string',
description: 'email address of the sender'
}
description: 'email address of the sender',
},
},
additionalProperties: false,
required: ['address']
required: ['address'],
},
transport: {
@@ -51,11 +51,11 @@ export const configurationSchema = {
properties: {
host: {
type: 'string',
description: 'hostname or IP address of the SMTP server'
description: 'hostname or IP address of the SMTP server',
},
port: {
type: 'integer',
description: 'port of the SMTP server (defaults to 25 or 465 for TLS)'
description: 'port of the SMTP server (defaults to 25 or 465 for TLS)',
},
secure: {
default: false,
@@ -64,24 +64,24 @@ export const configurationSchema = {
'auto (uses STARTTLS if available)',
'force (requires STARTTLS or fail)',
'disabled (never use STARTTLS)',
'TLS'
'TLS',
],
description: 'whether the connection should use TLS'
description: 'whether the connection should use TLS',
},
ignoreUnauthorized: {
type: 'boolean',
description: 'ignore certificates error (e.g. self-signed certificate)'
description: 'ignore certificates error (e.g. self-signed certificate)',
},
// FIXME: xo-web does not support edition of too nested
user: {
type: 'string',
description: 'name to use to authenticate'
description: 'name to use to authenticate',
},
password: {
type: 'string',
description: 'password to use to authenticate'
}
description: 'password to use to authenticate',
},
// properties.
// auth: {
// type: 'object',
@@ -103,12 +103,12 @@ export const configurationSchema = {
},
additionalProperties: false,
required: ['host']
}
required: ['host'],
},
},
additionalProperties: false,
required: ['from', 'transport']
required: ['from', 'transport'],
}
export const testSchema = {
@@ -117,12 +117,12 @@ export const testSchema = {
properties: {
to: {
type: 'string',
description: 'recipient of the test mail'
}
description: 'recipient of the test mail',
},
},
additionalProperties: false,
required: ['to']
required: ['to'],
}
// ===================================================================
@@ -144,7 +144,7 @@ class TransportEmailPlugin {
secure,
user,
...transportConf
}
},
}) {
if (ignoreUnauthorized != null) {
(
@@ -190,8 +190,8 @@ The transport-email plugin for Xen Orchestra server seems to be working fine, ni
`,
attachments: [ {
filename: 'example.txt',
content: 'Attachments are working too, great!\n'
} ]
content: 'Attachments are working too, great!\n',
} ],
})
}
@@ -200,7 +200,7 @@ The transport-email plugin for Xen Orchestra server seems to be working fine, ni
to, cc, bcc,
subject,
markdown,
attachments
attachments,
}) {
return this._send(removeUndefined({
from,
@@ -209,7 +209,7 @@ The transport-email plugin for Xen Orchestra server seems to be working fine, ni
bcc,
subject,
markdown,
attachments
attachments,
})).catch(logAndRethrow)
}
}

View File

@@ -39,19 +39,16 @@
"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",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [

View File

@@ -10,27 +10,27 @@ export const configurationSchema = {
properties: {
server: {
type: 'string',
description: 'The nagios server adress'
description: 'The nagios server adress',
},
port: {
type: 'integer',
description: 'The NSCA port'
description: 'The NSCA port',
},
key: {
type: 'string',
description: 'The encryption key'
description: 'The encryption key',
},
host: {
type: 'string',
description: 'The host name in Nagios'
description: 'The host name in Nagios',
},
service: {
type: 'string',
description: 'The service description in Nagios'
}
description: 'The service description in Nagios',
},
},
additionalProperties: false,
required: ['server', 'port', 'key', 'host', 'service']
required: ['server', 'port', 'key', 'host', 'service'],
}
// ===================================================================
@@ -45,7 +45,7 @@ function nscaPacketBuilder ({
message,
service,
status,
timestamp
timestamp,
}) {
// Building NSCA packet
const SIZE = 720
@@ -92,7 +92,7 @@ class XoServerNagios {
this._set = bind(xo.defineProperty, xo)
this._unset = null
// Defined in configure().
// Defined in configure().
this._conf = null
this._key = null
}
@@ -113,13 +113,13 @@ class XoServerNagios {
test () {
return this._sendPassiveCheck({
message: 'The server-nagios plugin for Xen Orchestra server seems to be working fine, nicely done :)',
status: OK
status: OK,
})
}
_sendPassiveCheck ({
message,
status
status,
}) {
return new Promise((resolve, reject) => {
if (/\r|\n/.test(message)) {
@@ -140,7 +140,7 @@ class XoServerNagios {
iv,
message,
status,
timestamp
timestamp,
})
// 1) Using xor between the NSCA packet and the initialization vector

View File

@@ -1,9 +0,0 @@
language: node_js
node_js:
- stable
- 6
- 4
# Use containers.
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

View File

@@ -38,20 +38,17 @@
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-preset-env": "^1.0.0",
"babel-preset-stage-0": "^6.16.0",
"cross-env": "^5.1.1",
"dependency-check": "^2.6.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.1.3",
"rimraf": "^2.5.4"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "npm run depcheck",
"prebuild": "npm run clean",
"predev": "npm run clean",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"presets": [
@@ -63,7 +60,7 @@
}
}
],
"stage-0"
"stage-3"
]
},
"config": {

View File

@@ -16,34 +16,34 @@ export const configurationSchema = {
properties: {
webhookUri: {
type: 'string',
description: 'The Mattermost or Slack webhook URL.'
description: 'The Mattermost or Slack webhook URL.',
},
channel: {
type: 'string',
description: 'Channel, private group, or IM channel to send message to.'
description: 'Channel, private group, or IM channel to send message to.',
},
username: {
type: 'string',
description: 'Bot username.'
description: 'Bot username.',
},
icon_emoji: {
type: 'string',
description: 'The bot icon. It can be a slack emoji or a URL image.'
}
description: 'The bot icon. It can be a slack emoji or a URL image.',
},
},
additionalProperties: false,
required: ['webhookUri', 'channel']
required: ['webhookUri', 'channel'],
}
// ===================================================================
class XoServerTransportSlack {
constructor ({ xo }) {
this._sendSlack = ::this._sendSlack
this._set = ::xo.defineProperty
this._sendSlack = this._sendSlack.bind(this)
this._set = xo.defineProperty.bind(xo)
this._unset = null
// Defined in configure().
// Defined in configure().
this._conf = null
this._send = null
}
@@ -70,12 +70,12 @@ class XoServerTransportSlack {
return this._sendSlack({
message: `Hi there,
The transport-slack plugin for Xen Orchestra server seems to be working fine, nicely done :)`
The transport-slack plugin for Xen Orchestra server seems to be working fine, nicely done :)`,
})
}
_sendSlack ({
message
message,
}) {
// TODO: handle errors
return this._send({ ...this._conf, text: message }).catch(logAndRethrow)

View File

@@ -39,29 +39,31 @@
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"cross-env": "^5.0.1",
"dependency-check": "^2.9.1",
"babel-preset-env": "^1.6.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "yarn run depcheck",
"prebuild": "yarn run clean",
"predev": "yarn run clean",
"prepublish": "yarn run build"
"prepublishOnly": "yarn run build"
},
"babel": {
"plugins": [
"transform-runtime"
],
"presets": [
"latest",
"stage-0"
[
"env",
{
"targets": {
"node": 4
}
}
]
]
}
}

View File

@@ -9,34 +9,34 @@ export const configurationSchema = {
properties: {
host: {
type: 'string',
description: 'host where the XMPP server is located'
description: 'host where the XMPP server is located',
},
port: {
type: 'integer',
description: 'port of the XMPP server (default to 5222)',
default: 5222
default: 5222,
},
jid: {
type: 'string',
title: 'user',
description: 'Xmpp address to use to authenticate'
description: 'Xmpp address to use to authenticate',
},
password: {
type: 'string',
description: 'password to use to authenticate'
}
description: 'password to use to authenticate',
},
},
additionalProperties: false,
required: ['jid', 'password']
required: ['jid', 'password'],
}
// ===================================================================
class TransportXmppPlugin {
constructor ({ xo }) {
this._sendToXmppClient = ::this._sendToXmppClient
this._set = ::xo.defineProperty
this._sendToXmppClient = this._sendToXmppClient.bind(this)
this._set = xo.defineProperty.bind(xo)
this._unset = null
// Defined in configure().
@@ -73,7 +73,7 @@ class TransportXmppPlugin {
this._client.send(
new XmppClient.Stanza('message', {
to: receiver,
type: 'chat'
type: 'chat',
}).c('body').t(message)
)
}

View File

@@ -37,7 +37,7 @@
"babel-runtime": "^6.23.0",
"cron": "^1.2.1",
"handlebars": "^4.0.6",
"html-minifier": "^3.4.0",
"html-minifier": "^3.5.8",
"lodash": "^4.17.4",
"promise-toolbox": "^0.9.5"
},
@@ -47,19 +47,16 @@
"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",
"dependency-check": "^2.9.1",
"cross-env": "^5.1.3",
"rimraf": "^2.6.1"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"posttest": "npm run depcheck",
"prebuild": "npm run clean",
"predev": "npm run clean",
"prepublish": "npm run build"
"prepublishOnly": "npm run build"
},
"babel": {
"plugins": [

View File

@@ -12,14 +12,14 @@ import {
orderBy,
round,
values,
zipObject
zipObject,
} from 'lodash'
import {
promisify
promisify,
} from 'promise-toolbox'
import {
readFile,
writeFile
writeFile,
} from 'fs'
// ===================================================================
@@ -30,10 +30,10 @@ const pWriteFile = promisify(writeFile)
const currDate = new Date().toISOString().slice(0, 10)
const compareOperators = {
'>': (l, r) => l > r
'>': (l, r) => l > r,
}
const mathOperators = {
'+': (l, r) => l + r
'+': (l, r) => l + r,
}
const gibPower = Math.pow(2, 30)
@@ -50,7 +50,7 @@ pReadFile(`${__dirname}/../report.html.tpl`, 'utf8')
removeAttributeQuotes: true,
removeComments: true,
removeOptionalTags: true,
removeRedundantAttributes: true
removeRedundantAttributes: true,
}))
})
@@ -69,18 +69,18 @@ export const configurationSchema = {
emails: {
type: 'array',
items: {
type: 'string'
}
type: 'string',
},
},
periodicity: {
type: 'string',
enum: ['monthly', 'weekly'],
description: 'If you choose weekly you will receive the report every sunday and if you choose monthly you will receive it every first day of the month.'
}
description: 'If you choose weekly you will receive the report every sunday and if you choose monthly you will receive it every first day of the month.',
},
},
additionalProperties: false,
required: [ 'emails', 'periodicity' ]
required: [ 'emails', 'periodicity' ],
}
// ===================================================================
@@ -137,7 +137,7 @@ function computeMeans (objects, options) {
options,
map(
options,
opt => round(computeMean(map(objects, opt)), 2)
opt => round(computeMean(map(objects, opt)), 2)
)
)
}
@@ -156,7 +156,7 @@ function getTop (objects, options) {
obj => ({
uuid: obj.uuid,
name: obj.name,
value: round(obj[opt], 2)
value: round(obj[opt], 2),
})
)
)
@@ -176,7 +176,7 @@ function conputePercentage (curr, prev, options) {
function getDiff (oldElements, newElements) {
return {
added: differenceBy(oldElements, newElements, 'uuid'),
removed: differenceBy(newElements, oldElements, 'uuid')
removed: differenceBy(newElements, oldElements, 'uuid'),
}
}
@@ -184,7 +184,7 @@ function getDiff (oldElements, newElements) {
function getVmsStats ({
runningVms,
xo
xo,
}) {
return Promise.all(map(runningVms, async vm => {
const vmStats = await xo.getXapiVmStats(vm, 'days')
@@ -196,14 +196,14 @@ function getVmsStats ({
diskRead: computeDoubleMean(values(vmStats.stats.xvds.r)) / mibPower,
diskWrite: computeDoubleMean(values(vmStats.stats.xvds.w)) / mibPower,
netReception: computeDoubleMean(vmStats.stats.vifs.rx) / kibPower,
netTransmission: computeDoubleMean(vmStats.stats.vifs.tx) / kibPower
netTransmission: computeDoubleMean(vmStats.stats.vifs.tx) / kibPower,
}
}))
}
function getHostsStats ({
runningHosts,
xo
xo,
}) {
return Promise.all(map(runningHosts, async host => {
const hostStats = await xo.getXapiHostStats(host, 'days')
@@ -214,7 +214,7 @@ function getHostsStats ({
ram: computeMean(hostStats.stats.memoryUsed) / gibPower,
load: computeMean(hostStats.stats.load),
netReception: computeDoubleMean(hostStats.stats.pifs.rx) / kibPower,
netTransmission: computeDoubleMean(hostStats.stats.pifs.tx) / kibPower
netTransmission: computeDoubleMean(hostStats.stats.pifs.tx) / kibPower,
}
}))
}
@@ -222,76 +222,76 @@ function getHostsStats ({
function computeGlobalVmsStats ({
haltedVms,
vmsStats,
xo
xo,
}) {
const allVms = concat(
map(vmsStats, vm => ({
uuid: vm.uuid,
name: vm.name
name: vm.name,
})),
map(haltedVms, vm => ({
uuid: vm.uuid,
name: vm.name_label
name: vm.name_label,
}))
)
return assign(computeMeans(vmsStats, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission']), {
number: allVms.length,
allVms
allVms,
})
}
function computeGlobalHostsStats ({
haltedHosts,
hostsStats,
xo
xo,
}) {
const allHosts = concat(
map(hostsStats, host => ({
uuid: host.uuid,
name: host.name
name: host.name,
})),
map(haltedHosts, host => ({
uuid: host.uuid,
name: host.name_label
name: host.name_label,
}))
)
return assign(computeMeans(hostsStats, ['cpu', 'ram', 'load', 'netReception', 'netTransmission']), {
number: allHosts.length,
allHosts
allHosts,
})
}
function getTopVms ({
vmsStats,
xo
xo,
}) {
return getTop(vmsStats, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission'])
}
function getTopHosts ({
hostsStats,
xo
xo,
}) {
return getTop(hostsStats, ['cpu', 'ram', 'load', 'netReception', 'netTransmission'])
}
function getMostAllocatedSpaces ({
disks,
xo
xo,
}) {
return map(
orderBy(disks, ['size'], ['desc']).slice(0, 3), disk => ({
uuid: disk.uuid,
name: disk.name_label,
size: round(disk.size / gibPower, 2)
size: round(disk.size / gibPower, 2),
}))
}
async function getHostsMissingPatches ({
runningHosts,
xo
xo,
}) {
const hostsMissingPatches = await Promise.all(map(runningHosts, async host => {
const hostsPatches = await xo.getXapi(host).listMissingPoolPatchesOnHost(host._xapiId)
@@ -299,7 +299,7 @@ async function getHostsMissingPatches ({
return {
uuid: host.uuid,
name: host.name_label,
patches: map(hostsPatches, 'name')
patches: map(hostsPatches, 'name'),
}
}
}))
@@ -312,7 +312,7 @@ function getAllUsersEmail (users) {
async function storeStats ({
data,
storedStatsPath
storedStatsPath,
}) {
await pWriteFile(storedStatsPath, JSON.stringify(data))
}
@@ -332,12 +332,12 @@ async function computeEvolution ({
const vmsEvolution = {
number: newStatsVms.number - oldStatsVms.number,
...conputePercentage(newStatsVms, oldStatsVms, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission'])
...conputePercentage(newStatsVms, oldStatsVms, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission']),
}
const hostsEvolution = {
number: newStatsHosts.number - oldStatsHosts.number,
...conputePercentage(newStatsHosts, oldStatsHosts, ['cpu', 'ram', 'load', 'netReception', 'netTransmission'])
...conputePercentage(newStatsHosts, oldStatsHosts, ['cpu', 'ram', 'load', 'netReception', 'netTransmission']),
}
const vmsRessourcesEvolution = getDiff(oldStatsVms.allVms, newStatsVms.allVms)
@@ -351,7 +351,7 @@ async function computeEvolution ({
prevDate,
vmsRessourcesEvolution,
hostsRessourcesEvolution,
usersEvolution
usersEvolution,
}
} catch (err) {
if (err.code !== 'ENOENT') throw err
@@ -360,7 +360,7 @@ async function computeEvolution ({
async function dataBuilder ({
xo,
storedStatsPath
storedStatsPath,
}) {
const xoObjects = values(xo.getObjects())
const runningVms = filter(xoObjects, {type: 'VM', power_state: 'Running'})
@@ -373,7 +373,7 @@ async function dataBuilder ({
getVmsStats({xo, runningVms}),
getHostsStats({xo, runningHosts}),
getMostAllocatedSpaces({xo, disks}),
getHostsMissingPatches({xo, runningHosts})
getHostsMissingPatches({xo, runningHosts}),
])
const [globalVmsStats, globalHostsStats, topVms, topHosts, usersEmail] = await Promise.all([
@@ -381,13 +381,13 @@ async function dataBuilder ({
computeGlobalHostsStats({xo, hostsStats, haltedHosts}),
getTopVms({xo, vmsStats}),
getTopHosts({xo, hostsStats}),
getAllUsersEmail(users)
getAllUsersEmail(users),
])
const evolution = await computeEvolution({
storedStatsPath,
hosts: globalHostsStats,
usersEmail,
vms: globalVmsStats
vms: globalVmsStats,
})
const data = {
@@ -395,7 +395,7 @@ async function dataBuilder ({
vms: globalVmsStats,
hosts: globalHostsStats,
vmsEvolution: evolution && evolution.vmsEvolution,
hostsEvolution: evolution && evolution.hostsEvolution
hostsEvolution: evolution && evolution.hostsEvolution,
},
topVms,
topHosts,
@@ -409,8 +409,8 @@ async function dataBuilder ({
imgXo,
currDate,
prevDate: evolution && evolution.prevDate,
page: '{{page}}'
}
page: '{{page}}',
},
}
return data
@@ -432,7 +432,7 @@ class UsageReportPlugin {
this._job = new CronJob({
cronTime: configuration.periodicity === 'monthly' ? '00 06 1 * *' : '00 06 * * 0',
onTick: () => this._sendReport(),
start: false
start: false,
})
}
@@ -454,7 +454,7 @@ class UsageReportPlugin {
async _sendReport () {
const data = await dataBuilder({
xo: this._xo,
storedStatsPath: this._storedStatsPath
storedStatsPath: this._storedStatsPath,
})
await Promise.all([
@@ -469,13 +469,13 @@ class UsageReportPlugin {
best regards.`,
attachments: [{
filename: `xoReport_${currDate}.html`,
content: template(data)
}]
content: template(data),
}],
}),
storeStats({
data,
storedStatsPath: this._storedStatsPath
})
storedStatsPath: this._storedStatsPath,
}),
])
}
}

View File

@@ -1,33 +0,0 @@
language: node_js
sudo: required
dist: trusty
node_js:
- stable
- 6
- 4
env:
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- uuid-dev
- build-essential
- qemu-utils
install:
- sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ trusty multiverse"
- sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ trusty-updates multiverse"
- sudo apt-get update
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install virtualbox
- npm install
- wget https://github.com/rubiojr/vhd-util-convert/archive/master.tar.gz -O /tmp/vhd-util-convert.tar.gz
- tar -xvf /tmp/vhd-util-convert.tar.gz
- (cd vhd-util-convert-master && make)
- df
- pwd
- echo "$TRAVIS_BUILD_DIR"
script:
- npm test
- find $TRAVIS_BUILD_DIR -name '*.vhd' -print0 | xargs -n 1 -0 ./vhd-util-convert-master/vhd-util scan -v

View File

@@ -33,9 +33,7 @@
"babel-cli": "^6.18.0",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-env": "^1.0.0",
"babel-preset-stage-0": "^6.16.0",
"cross-env": "^5.1.1",
"dependency-check": "^2.6.0",
"cross-env": "^5.1.3",
"mocha": "^4.0.1",
"must": "^0.13.2",
"rimraf": "^2.5.4"
@@ -43,13 +41,11 @@
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"depcheck": "dependency-check ./package.json",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"dev-test": "mocha --watch --reporter=min \"dist/**/*.spec.js\"",
"posttest": "npm run depcheck",
"prebuild": "npm run clean",
"predev": "npm run clean",
"prepublish": "npm run build",
"prepublishOnly": "npm run build",
"test-disabled": "mocha \"dist/**/*.spec.js\""
},
"babel": {
@@ -64,8 +60,7 @@
"node": 4
}
}
],
"stage-0"
]
]
},
"config": {

View File

@@ -124,7 +124,7 @@ export class VHDFile {
}
export function computeGeometryForSize (size) {
let totalSectors = Math.ceil(size / 512)
const totalSectors = Math.ceil(size / 512)
let sectorsPerTrack
let heads
let cylinderTimesHeads
@@ -154,13 +154,13 @@ export function computeGeometryForSize (size) {
cylinderTimesHeads = totalSectors / sectorsPerTrack
}
}
let cylinders = Math.floor(cylinderTimesHeads / heads)
let actualSize = cylinders * heads * sectorsPerTrack * sectorSize
const cylinders = Math.floor(cylinderTimesHeads / heads)
const actualSize = cylinders * heads * sectorsPerTrack * sectorSize
return {cylinders, heads, sectorsPerTrack, actualSize}
}
export function createFooter (size, timestamp, geometry, diskType, dataOffsetLow = 0xFFFFFFFF, dataOffsetHigh = 0xFFFFFFFF) {
let footer = Buffer.alloc(512)
const footer = Buffer.alloc(512)
Buffer.from(footerCookie, 'ascii').copy(footer)
footer.writeUInt32BE(2, 8)
footer.writeUInt32BE(0x00010000, 12)
@@ -180,13 +180,13 @@ export function createFooter (size, timestamp, geometry, diskType, dataOffsetLow
footer.writeUInt8(geometry['heads'], 58)
footer.writeUInt8(geometry['sectorsPerTrack'], 59)
footer.writeUInt32BE(diskType, 60)
let checksum = computeChecksum(footer)
const checksum = computeChecksum(footer)
footer.writeUInt32BE(checksum, 64)
return footer
}
export function createDynamicDiskHeader (tableEntries, blockSize) {
let header = Buffer.alloc(1024)
const header = Buffer.alloc(1024)
Buffer.from(headerCookie, 'ascii').copy(header)
// hard code no next data
header.writeUInt32BE(0xFFFFFFFF, 8)
@@ -197,7 +197,7 @@ export function createDynamicDiskHeader (tableEntries, blockSize) {
header.writeUInt32BE(0x00010000, 24)
header.writeUInt32BE(tableEntries, 28)
header.writeUInt32BE(blockSize, 32)
let checksum = computeChecksum(header)
const checksum = computeChecksum(header)
header.writeUInt32BE(checksum, 36)
return header
}
@@ -213,7 +213,7 @@ export class ReadableRawVHDStream extends stream.Readable {
constructor (size, vmdkParser) {
super()
this.size = size
var geometry = computeGeometryForSize(size)
const geometry = computeGeometryForSize(size)
this.footer = createFooter(size, Math.floor(Date.now() / 1000), geometry, fixedHardDiskType)
this.position = 0
this.vmdkParser = vmdkParser

View File

@@ -12,7 +12,7 @@ import {
createDynamicDiskHeader,
createFooter,
ReadableRawVHDStream,
VHDFile
VHDFile,
} from './vhd-write'
describe('VHD writing', () => {
@@ -37,10 +37,10 @@ describe('VHD writing', () => {
it('ReadableRawVHDStream does not crash', () => {
const data = [{
lbaBytes: 100,
grain: Buffer.from('azerzaerazeraze', 'ascii')
grain: Buffer.from('azerzaerazeraze', 'ascii'),
}, {
lbaBytes: 700,
grain: Buffer.from('gdfslkdfguer', 'ascii')
grain: Buffer.from('gdfslkdfguer', 'ascii'),
}]
let index = 0
const mockParser = {
@@ -52,7 +52,7 @@ describe('VHD writing', () => {
} else {
return null
}
}
},
}
const stream = new ReadableRawVHDStream(100000, mockParser)
const pipe = stream.pipe(createWriteStream('outputStream'))
@@ -65,10 +65,10 @@ describe('VHD writing', () => {
it('ReadableRawVHDStream detects when blocks are out of order', () => {
const data = [{
lbaBytes: 700,
grain: Buffer.from('azerzaerazeraze', 'ascii')
grain: Buffer.from('azerzaerazeraze', 'ascii'),
}, {
lbaBytes: 100,
grain: Buffer.from('gdfslkdfguer', 'ascii')
grain: Buffer.from('gdfslkdfguer', 'ascii'),
}]
let index = 0
const mockParser = {
@@ -80,7 +80,7 @@ describe('VHD writing', () => {
} else {
return null
}
}
},
}
return expect(new Promise((resolve, reject) => {
const stream = new ReadableRawVHDStream(100000, mockParser)

View File

@@ -7,7 +7,7 @@ import {VirtualBuffer} from './virtual-buffer'
describe('Virtual Buffer', function () {
it('can read a file correctly', async () => {
let rawFileName = 'random-data'
const rawFileName = 'random-data'
await exec('base64 /dev/urandom | head -c 104448 > ' + rawFileName)
const buffer = new VirtualBuffer(createReadStream(rawFileName))
const part1 = await buffer.readChunk(10)

View File

@@ -37,8 +37,8 @@ function parseDescriptor (descriptorSlice) {
const lines = descriptorText.split(/\r?\n/).filter((line) => {
return line.trim().length > 0 && line[0] !== '#'
})
for (let line of lines) {
let defLine = line.split('=')
for (const line of lines) {
const defLine = line.split('=')
// the wonky quote test is to avoid having an equal sign in the name of an extent
if (defLine.length === 2 && defLine[0].indexOf('"') === -1) {
descriptorDict[defLine[0]] = defLine[1].replace(/['"]+/g, '')
@@ -49,7 +49,7 @@ function parseDescriptor (descriptorSlice) {
sizeSectors: items[1],
type: items[2],
name: items[3],
offset: items.length > 4 ? items[4] : 0
offset: items.length > 4 ? items[4] : 0,
})
}
}
@@ -63,7 +63,7 @@ function parseFlags (flagBuffer) {
useSecondaryGrain: !!(number & (1 << 1)),
useZeroedGrainTable: !!(number & (1 << 2)),
compressedGrains: !!(number & (1 << 16)),
hasMarkers: !!(number & (1 << 17))
hasMarkers: !!(number & (1 << 17)),
}
}
@@ -98,7 +98,7 @@ function parseHeader (buffer) {
grainDirectoryOffsetSectors,
rGrainDirectoryOffsetSectors,
l1EntrySectors,
numGTEsPerGT
numGTEsPerGT,
}
}
async function readGrain (offsetSectors, buffer, compressed) {
@@ -115,7 +115,7 @@ async function readGrain (offsetSectors, buffer, compressed) {
size,
buffer: grainBuffer,
grain: grainContent,
grainSize: grainContent.byteLength
grainSize: grainContent.byteLength,
}
}

View File

@@ -7,8 +7,8 @@ import {VMDKDirectParser} from './vmdk-read'
describe('VMDK reading', () => {
it('VMDKDirectParser reads OK', async () => {
let rawFileName = 'random-data'
let fileName = 'random-data.vmdk'
const rawFileName = 'random-data'
const fileName = 'random-data.vmdk'
await exec('base64 /dev/urandom | head -c 104448 > ' + rawFileName)
await exec('rm -f ' + fileName + '&& VBoxManage convertfromraw --format VMDK --variant Stream ' + rawFileName + ' ' + fileName)
const parser = new VMDKDirectParser(createReadStream(fileName))

View File

@@ -9,11 +9,11 @@ import {VHDFile, convertFromVMDK, computeGeometryForSize} from './vhd-write'
describe('VMDK to VHD conversion', () => {
it('can convert a random data file with readRawContent()', async () => {
let inputRawFileName = 'random-data.raw'
let vmdkFileName = 'random-data.vmdk'
let vhdFileName = 'from-vmdk-readRawContent.vhd'
let reconvertedRawFilemane = 'from-vhd.raw'
let dataSize = 5222400
const inputRawFileName = 'random-data.raw'
const vmdkFileName = 'random-data.vmdk'
const vhdFileName = 'from-vmdk-readRawContent.vhd'
const reconvertedRawFilemane = 'from-vhd.raw'
const dataSize = 5222400
await exec('rm -f ' + [inputRawFileName, vmdkFileName, vhdFileName, reconvertedRawFilemane].join(' '))
await exec('base64 /dev/urandom | head -c ' + dataSize + ' > ' + inputRawFileName)
await exec('VBoxManage convertfromraw --format VMDK --variant Stream ' + inputRawFileName + ' ' + vmdkFileName)
@@ -33,12 +33,12 @@ describe('VMDK to VHD conversion', () => {
})
it('can convert a random data file with VMDKDirectParser', async () => {
let inputRawFileName = 'random-data.raw'
let vmdkFileName = 'random-data.vmdk'
let vhdFileName = 'from-vmdk-VMDKDirectParser.vhd'
let reconvertedRawFilemane = 'from-vhd.raw'
let reconvertedByVBoxRawFilemane = 'from-vhd-by-vbox.raw'
let dataSize = computeGeometryForSize(8 * 1024 * 1024).actualSize
const inputRawFileName = 'random-data.raw'
const vmdkFileName = 'random-data.vmdk'
const vhdFileName = 'from-vmdk-VMDKDirectParser.vhd'
const reconvertedRawFilemane = 'from-vhd.raw'
const reconvertedByVBoxRawFilemane = 'from-vhd-by-vbox.raw'
const dataSize = computeGeometryForSize(8 * 1024 * 1024).actualSize
await exec('rm -f ' + [inputRawFileName, vmdkFileName, vhdFileName, reconvertedRawFilemane, reconvertedByVBoxRawFilemane].join(' '))
await exec('base64 /dev/urandom | head -c ' + dataSize + ' > ' + inputRawFileName)
await exec('VBoxManage convertfromraw --format VMDK --variant Stream ' + inputRawFileName + ' ' + vmdkFileName)

View File

@@ -1,27 +1,83 @@
#!/bin/sh
# Import repositories into this mono-repo.
#
# For each repository:
#
# 1. rewrite history, moving all files into packages/<repository>
# 2. rename all tags: <tag> → <repository>-<tag>
# 3. merge with master
for pkg
set -eu
usage () {
echo "$0 [--commit | --rewrite] <repository url>..."
}
move=commit
while [ $# -ne 0 ]
do
[ -d "packages/$pkg" ] || { {
[ -f ".git/refs/remotes/$pkg/master" ] || \
git remote add "$pkg" "https://github.com/vatesfr/$pkg"
} && {
git fetch --no-tags "$pkg" master refs/tags/*:refs/tags/"$pkg"-* && \
git filter-branch -f --index-filter '
git ls-files -s | \
sed "s%\t\"*%&packages/'"$pkg"'/%" | \
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info && \
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' --tag-name-filter 'cat' "$pkg/master" && \
git merge --allow-unrelated-histories "$pkg/master"
git remote rm "$pkg"
} }
case "$1" in
-h|--help)
usage
exit
;;
--commit)
move=commit
;;
--rewrite)
move=rewrite
;;
-*)
echo "Invalid argument: $1" >&2
usage >&2
exit 1
;;
*)
break
;;
esac
shift
done
# Create a commit in branch <package>/master moving all files in <path>.
move_commit () {
local head
head=$(git symbolic-ref HEAD)
git symbolic-ref HEAD "refs/heads/$1/master"
rm -f "$(git rev-parse --git-path index)"
git read-tree --prefix="$2/" HEAD
git commit -nm "feat($1): move all files to $2"
git symbolic-ref HEAD "$head"
git read-tree HEAD
}
# Rewrite all branches <package>/* moving all files in <path>.
move_rewrite () {
git filter-branch -f --index-filter '
git ls-files -s | \
sed "s%\t\"*%&'"$2"'/%" | \
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info && \
if [ -f "$GIT_INDEX_FILE.new" ]; then \
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; \
fi
' --tag-name-filter 'cat' -- --branches="$1/*"
}
for url
do
pkg=$(basename "$url" .git)
if [ -d "packages/$pkg" ]
then
continue
fi
# import all branches and tags as <repository>/*
git remote add "$pkg" "$url"
git fetch --no-tags "$pkg" \
"refs/heads/*:refs/heads/$pkg/*" \
"refs/tags/*:refs/tags/$pkg/*"
git remote rm "$pkg"
move_"$move" "$pkg" "packages/$pkg"
# merge and delete master branch
git merge --allow-unrelated-histories "$pkg/master"
git branch -d "$pkg/master"
done

View File

@@ -1,7 +1,22 @@
#!/usr/bin/env node
const isEmpty = require('lodash/isEmpty')
const sortedObject = require('sorted-object')
const { getPackages, writeFile, unlink } = require('./utils')
const deleteProperties = (object, property, properties) => {
const nestedObject = object[property]
if (nestedObject === undefined) {
return
}
properties.forEach(property => {
delete nestedObject[property]
})
if (isEmpty(object[property])) {
delete object[property]
}
}
require('exec-promise')(() =>
getPackages(true).map(({ dir, name, package: pkg }) => {
pkg.name = name
@@ -9,24 +24,44 @@ require('exec-promise')(() =>
pkg.bugs = `https://github.com/vatesfr/xo-web/issues`
pkg.repository = {
type: 'git',
url: 'https://github.com/vatesfr/xen-orchestra.git'
url: 'https://github.com/vatesfr/xen-orchestra.git',
}
delete pkg.jest
delete pkg.standard
const { devDependencies, scripts } = pkg
if (devDependencies !== undefined) {
delete devDependencies.commitizen
delete devDependencies.ghooks
delete devDependencies.husky
delete devDependencies.jest
delete devDependencies.standard
delete devDependencies['babel-eslint']
delete devDependencies['cz-conventional-changelog']
}
deleteProperties(pkg, 'config', [ 'commitizen' ])
deleteProperties(pkg, 'devDependencies', [
'babel-7-jest',
'babel-eslint',
'commitizen',
'cz-conventional-changelog',
'dependency-check',
'eslint',
'eslint-config-standard',
'eslint-plugin-import',
'eslint-plugin-node',
'eslint-plugin-promise',
'eslint-plugin-standard',
'flow-bin',
'ghooks',
'husky',
'jest',
'standard',
])
deleteProperties(pkg, 'scripts', [ 'commitmsg', 'cz' ])
const { scripts } = pkg
if (scripts !== undefined) {
delete scripts.cz
const prepublish = scripts.prepublish
if (
prepublish !== undefined &&
!('prepublishOnly' in scripts)
) {
delete scripts.prepublish
scripts.prepublishOnly = prepublish
pkg.scripts = sortedObject(scripts)
}
}
return Promise.all([
@@ -35,9 +70,13 @@ require('exec-promise')(() =>
JSON.stringify(pkg, null, 2) + '\n'
),
unlink(`${dir}/.editorconfig`),
unlink(`${dir}/.eslintrc.js`),
unlink(`${dir}/.flowconfig`),
unlink(`${dir}/.gitignore`),
unlink(`${dir}/.jshintrc`),
unlink(`${dir}/yarn.lock`)
unlink(`${dir}/.travis.yml`),
unlink(`${dir}/package-lock.json`),
unlink(`${dir}/yarn.lock`),
])
})
)

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