Merge branch 'master' into nr-fix-S3-upload
This commit is contained in:
@@ -13,7 +13,7 @@ module.exports = {
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: ['cli.js', '*-cli.js', '**/*cli*/**/*.js'],
|
||||
files: ['cli.{,c,m}js', '*-cli.{,c,m}js', '**/*cli*/**/*.{,c,m}js'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
},
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -19,9 +19,9 @@
|
||||
/packages/xen-api/plot.dat
|
||||
|
||||
/packages/xo-server/.xo-server.*
|
||||
/packages/xo-server/src/api/index.js
|
||||
/packages/xo-server/src/xapi/mixins/index.js
|
||||
/packages/xo-server/src/xo-mixins/index.js
|
||||
/packages/xo-server/src/api/index.mjs
|
||||
/packages/xo-server/src/xapi/mixins/index.mjs
|
||||
/packages/xo-server/src/xo-mixins/index.mjs
|
||||
|
||||
/packages/xo-server-auth-ldap/ldap.cache.conf
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const { asyncMap, asyncMapSettled } = require('@xen-orchestra/async-map')
|
||||
const Disposable = require('promise-toolbox/Disposable.js')
|
||||
const ignoreErrors = require('promise-toolbox/ignoreErrors.js')
|
||||
const limitConcurrency = require('limit-concurrency-decorator').default
|
||||
const { compileTemplate } = require('@xen-orchestra/template')
|
||||
const { limitConcurrency } = require('limit-concurrency-decorator')
|
||||
|
||||
const { extractIdsFromSimplePattern } = require('./_extractIdsFromSimplePattern.js')
|
||||
const { PoolMetadataBackup } = require('./_PoolMetadataBackup.js')
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const assert = require('assert')
|
||||
const limitConcurrency = require('limit-concurrency-decorator').default
|
||||
const { asyncMap } = require('@xen-orchestra/async-map')
|
||||
const { default: Vhd, mergeVhd } = require('vhd-lib')
|
||||
const { dirname, resolve } = require('path')
|
||||
const { DISK_TYPE_DIFFERENCING } = require('vhd-lib/dist/_constants.js')
|
||||
const { isMetadataFile, isVhdFile, isXvaFile, isXvaSumFile } = require('./_backupType.js')
|
||||
const { limitConcurrency } = require('limit-concurrency-decorator')
|
||||
|
||||
// chain is an array of VHDs from child to parent
|
||||
//
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"end-of-stream": "^1.4.4",
|
||||
"fs-extra": "^9.0.0",
|
||||
"golike-defer": "^0.5.1",
|
||||
"limit-concurrency-decorator": "^0.4.0",
|
||||
"limit-concurrency-decorator": "^0.5.0",
|
||||
"lodash": "^4.17.20",
|
||||
"node-zone": "^0.4.0",
|
||||
"parse-pairs": "^1.1.0",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))
|
||||
@@ -1 +0,0 @@
|
||||
../../scripts/babel-eslintrc.js
|
||||
@@ -11,7 +11,7 @@
|
||||
// process.env.http_proxy
|
||||
// ])
|
||||
// ```
|
||||
export default function defined() {
|
||||
function defined() {
|
||||
let args = arguments
|
||||
let n = args.length
|
||||
if (n === 1) {
|
||||
@@ -29,6 +29,7 @@ export default function defined() {
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = exports = defined
|
||||
|
||||
// Usage:
|
||||
//
|
||||
@@ -39,7 +40,7 @@ export default function defined() {
|
||||
// const getFriendName = _ => _.friends[0].name
|
||||
// const friendName = get(getFriendName, props.user)
|
||||
// ```
|
||||
export const get = (accessor, arg) => {
|
||||
function get(accessor, arg) {
|
||||
try {
|
||||
return accessor(arg)
|
||||
} catch (error) {
|
||||
@@ -49,6 +50,7 @@ export const get = (accessor, arg) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.get = get
|
||||
|
||||
// Usage:
|
||||
//
|
||||
@@ -58,4 +60,6 @@ export const get = (accessor, arg) => {
|
||||
// _ => new ProxyAgent(_)
|
||||
// )
|
||||
// ```
|
||||
export const ifDef = (value, thenFn) => (value !== undefined ? thenFn(value) : value)
|
||||
exports.ifDef = function ifDef(value, thenFn) {
|
||||
return value !== undefined ? thenFn(value) : value
|
||||
}
|
||||
@@ -16,27 +16,13 @@
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"browserslist": [
|
||||
">2%"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"rimraf": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"postversion": "npm publish"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))
|
||||
@@ -1 +0,0 @@
|
||||
../../scripts/babel-eslintrc.js
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function emitAsync(event) {
|
||||
module.exports = function emitAsync(event) {
|
||||
let opts
|
||||
let i = 1
|
||||
|
||||
@@ -16,27 +16,13 @@
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"browserslist": [
|
||||
">2%"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"rimraf": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"postversion": "npm publish"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"execa": "^5.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"get-stream": "^6.0.0",
|
||||
"limit-concurrency-decorator": "^0.4.0",
|
||||
"limit-concurrency-decorator": "^0.5.0",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.19.2",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import asyncMapSettled from '@xen-orchestra/async-map/legacy'
|
||||
import getStream from 'get-stream'
|
||||
import limit from 'limit-concurrency-decorator'
|
||||
import path, { basename } from 'path'
|
||||
import synchronized from 'decorator-synchronized'
|
||||
import { coalesceCalls } from '@vates/coalesce-calls'
|
||||
import { fromCallback, fromEvent, ignoreErrors, timeout } from 'promise-toolbox'
|
||||
import { limitConcurrency } from 'limit-concurrency-decorator'
|
||||
import { parse } from 'xo-remote-parser'
|
||||
import { pipeline } from 'stream'
|
||||
import { randomBytes } from 'crypto'
|
||||
@@ -74,7 +74,7 @@ export default class RemoteHandlerAbstract {
|
||||
}
|
||||
;({ highWaterMark: this._highWaterMark, timeout: this._timeout = DEFAULT_TIMEOUT } = options)
|
||||
|
||||
const sharedLimit = limit(options.maxParallelOperations ?? DEFAULT_MAX_PARALLEL_OPERATIONS)
|
||||
const sharedLimit = limitConcurrency(options.maxParallelOperations ?? DEFAULT_MAX_PARALLEL_OPERATIONS)
|
||||
this.closeFile = sharedLimit(this.closeFile)
|
||||
this.getInfo = sharedLimit(this.getInfo)
|
||||
this.getSize = sharedLimit(this.getSize)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))
|
||||
@@ -1 +0,0 @@
|
||||
../../scripts/babel-eslintrc.js
|
||||
@@ -1 +1,107 @@
|
||||
module.exports = require('./dist/configure')
|
||||
const createConsoleTransport = require('./transports/console')
|
||||
const { LEVELS, resolve } = require('./levels')
|
||||
const { compileGlobPattern } = require('./utils')
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const createTransport = config => {
|
||||
if (typeof config === 'function') {
|
||||
return config
|
||||
}
|
||||
|
||||
if (Array.isArray(config)) {
|
||||
const transports = config.map(createTransport)
|
||||
const { length } = transports
|
||||
return function () {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
transports[i].apply(this, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let { filter } = config
|
||||
let transport = createTransport(config.transport)
|
||||
const level = resolve(config.level)
|
||||
|
||||
if (filter !== undefined) {
|
||||
if (typeof filter === 'string') {
|
||||
const re = compileGlobPattern(filter)
|
||||
filter = log => re.test(log.namespace)
|
||||
}
|
||||
|
||||
const orig = transport
|
||||
transport = function (log) {
|
||||
if ((level !== undefined && log.level >= level) || filter(log)) {
|
||||
return orig.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
} else if (level !== undefined) {
|
||||
const orig = transport
|
||||
transport = function (log) {
|
||||
if (log.level >= level) {
|
||||
return orig.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transport
|
||||
}
|
||||
|
||||
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
|
||||
|
||||
const { env } = process
|
||||
global[symbol] = createTransport({
|
||||
// display warnings or above, and all that are enabled via DEBUG or
|
||||
// NODE_DEBUG env
|
||||
filter: [env.DEBUG, env.NODE_DEBUG].filter(Boolean).join(','),
|
||||
level: resolve(env.LOG_LEVEL, LEVELS.INFO),
|
||||
|
||||
transport: createConsoleTransport(),
|
||||
})
|
||||
|
||||
const configure = config => {
|
||||
global[symbol] = createTransport(config)
|
||||
}
|
||||
exports.configure = configure
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const catchGlobalErrors = logger => {
|
||||
// patch process
|
||||
const onUncaughtException = error => {
|
||||
logger.error('uncaught exception', { error })
|
||||
}
|
||||
const onUnhandledRejection = error => {
|
||||
logger.warn('possibly unhandled rejection', { error })
|
||||
}
|
||||
const onWarning = error => {
|
||||
logger.warn('Node warning', { error })
|
||||
}
|
||||
process.on('uncaughtException', onUncaughtException)
|
||||
process.on('unhandledRejection', onUnhandledRejection)
|
||||
process.on('warning', onWarning)
|
||||
|
||||
// patch EventEmitter
|
||||
const EventEmitter = require('events')
|
||||
const { prototype } = EventEmitter
|
||||
const { emit } = prototype
|
||||
function patchedEmit(event, error) {
|
||||
if (event === 'error' && this.listenerCount(event) === 0) {
|
||||
logger.error('unhandled error event', { error })
|
||||
return false
|
||||
}
|
||||
return emit.apply(this, arguments)
|
||||
}
|
||||
prototype.emit = patchedEmit
|
||||
|
||||
return () => {
|
||||
process.removeListener('uncaughtException', onUncaughtException)
|
||||
process.removeListener('unhandledRejection', onUnhandledRejection)
|
||||
process.removeListener('warning', onWarning)
|
||||
|
||||
if (prototype.emit === patchedEmit) {
|
||||
prototype.emit = emit
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.catchGlobalErrors = catchGlobalErrors
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import createTransport from './transports/console'
|
||||
import LEVELS, { resolve } from './levels'
|
||||
const createTransport = require('./transports/console')
|
||||
const { LEVELS, resolve } = require('./levels')
|
||||
|
||||
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
|
||||
if (!(symbol in global)) {
|
||||
@@ -68,5 +68,7 @@ prototype.wrap = function (message, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
export const createLogger = namespace => new Logger(namespace)
|
||||
export { createLogger as default }
|
||||
const createLogger = namespace => new Logger(namespace)
|
||||
|
||||
module.exports = exports = createLogger
|
||||
exports.createLogger = createLogger
|
||||
@@ -1,5 +1,5 @@
|
||||
const LEVELS = Object.create(null)
|
||||
export { LEVELS as default }
|
||||
exports.LEVELS = LEVELS
|
||||
|
||||
// https://github.com/trentm/node-bunyan#levels
|
||||
LEVELS.FATAL = 60 // service/app is going down
|
||||
@@ -8,7 +8,8 @@ LEVELS.WARN = 40 // something went wrong but it's not fatal
|
||||
LEVELS.INFO = 30 // detail on unusual but normal operation
|
||||
LEVELS.DEBUG = 20
|
||||
|
||||
export const NAMES = Object.create(null)
|
||||
const NAMES = Object.create(null)
|
||||
exports.NAMES = NAMES
|
||||
for (const name in LEVELS) {
|
||||
NAMES[LEVELS[name]] = name
|
||||
}
|
||||
@@ -16,7 +17,7 @@ for (const name in LEVELS) {
|
||||
// resolves to the number representation of a level
|
||||
//
|
||||
// returns `defaultLevel` if invalid
|
||||
export const resolve = (level, defaultLevel) => {
|
||||
const resolve = (level, defaultLevel) => {
|
||||
const type = typeof level
|
||||
if (type === 'number') {
|
||||
if (level in NAMES) {
|
||||
@@ -30,6 +31,7 @@ export const resolve = (level, defaultLevel) => {
|
||||
}
|
||||
return defaultLevel
|
||||
}
|
||||
exports.resolve = resolve
|
||||
|
||||
Object.freeze(LEVELS)
|
||||
Object.freeze(NAMES)
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import { forEach, isInteger } from 'lodash'
|
||||
const { forEach, isInteger } = require('lodash')
|
||||
|
||||
import LEVELS, { NAMES, resolve } from './levels'
|
||||
const { LEVELS, NAMES, resolve } = require('./levels')
|
||||
|
||||
describe('LEVELS', () => {
|
||||
it('maps level names to their integer values', () => {
|
||||
@@ -16,7 +16,6 @@
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"browserslist": [
|
||||
">2%"
|
||||
],
|
||||
@@ -27,21 +26,7 @@
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.19.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^7.0.2",
|
||||
"rimraf": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"postversion": "npm publish"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import createConsoleTransport from './transports/console'
|
||||
import LEVELS, { resolve } from './levels'
|
||||
import { compileGlobPattern } from './utils'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const createTransport = config => {
|
||||
if (typeof config === 'function') {
|
||||
return config
|
||||
}
|
||||
|
||||
if (Array.isArray(config)) {
|
||||
const transports = config.map(createTransport)
|
||||
const { length } = transports
|
||||
return function () {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
transports[i].apply(this, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let { filter } = config
|
||||
let transport = createTransport(config.transport)
|
||||
const level = resolve(config.level)
|
||||
|
||||
if (filter !== undefined) {
|
||||
if (typeof filter === 'string') {
|
||||
const re = compileGlobPattern(filter)
|
||||
filter = log => re.test(log.namespace)
|
||||
}
|
||||
|
||||
const orig = transport
|
||||
transport = function (log) {
|
||||
if ((level !== undefined && log.level >= level) || filter(log)) {
|
||||
return orig.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
} else if (level !== undefined) {
|
||||
const orig = transport
|
||||
transport = function (log) {
|
||||
if (log.level >= level) {
|
||||
return orig.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transport
|
||||
}
|
||||
|
||||
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
|
||||
|
||||
const { env } = process
|
||||
global[symbol] = createTransport({
|
||||
// display warnings or above, and all that are enabled via DEBUG or
|
||||
// NODE_DEBUG env
|
||||
filter: [env.DEBUG, env.NODE_DEBUG].filter(Boolean).join(','),
|
||||
level: resolve(env.LOG_LEVEL, LEVELS.INFO),
|
||||
|
||||
transport: createConsoleTransport(),
|
||||
})
|
||||
|
||||
export const configure = config => {
|
||||
global[symbol] = createTransport(config)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const catchGlobalErrors = logger => {
|
||||
// patch process
|
||||
const onUncaughtException = error => {
|
||||
logger.error('uncaught exception', { error })
|
||||
}
|
||||
const onUnhandledRejection = error => {
|
||||
logger.warn('possibly unhandled rejection', { error })
|
||||
}
|
||||
const onWarning = error => {
|
||||
logger.warn('Node warning', { error })
|
||||
}
|
||||
process.on('uncaughtException', onUncaughtException)
|
||||
process.on('unhandledRejection', onUnhandledRejection)
|
||||
process.on('warning', onWarning)
|
||||
|
||||
// patch EventEmitter
|
||||
const EventEmitter = require('events')
|
||||
const { prototype } = EventEmitter
|
||||
const { emit } = prototype
|
||||
function patchedEmit(event, error) {
|
||||
if (event === 'error' && this.listenerCount(event) === 0) {
|
||||
logger.error('unhandled error event', { error })
|
||||
return false
|
||||
}
|
||||
return emit.apply(this, arguments)
|
||||
}
|
||||
prototype.emit = patchedEmit
|
||||
|
||||
return () => {
|
||||
process.removeListener('uncaughtException', onUncaughtException)
|
||||
process.removeListener('unhandledRejection', onUnhandledRejection)
|
||||
process.removeListener('warning', onWarning)
|
||||
|
||||
if (prototype.emit === patchedEmit) {
|
||||
prototype.emit = emit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import LEVELS, { NAMES } from '../levels'
|
||||
|
||||
const { DEBUG, ERROR, FATAL, INFO, WARN } = LEVELS
|
||||
|
||||
let formatLevel, formatNamespace
|
||||
if (process.stdout !== undefined && process.stdout.isTTY && process.stderr !== undefined && process.stderr.isTTY) {
|
||||
const ansi = (style, str) => `\x1b[${style}m${str}\x1b[0m`
|
||||
|
||||
const LEVEL_STYLES = {
|
||||
[DEBUG]: '2',
|
||||
[ERROR]: '1;31',
|
||||
[FATAL]: '1;31',
|
||||
[INFO]: '1',
|
||||
[WARN]: '1;33',
|
||||
}
|
||||
formatLevel = level => {
|
||||
const style = LEVEL_STYLES[level]
|
||||
const name = NAMES[level]
|
||||
return style === undefined ? name : ansi(style, name)
|
||||
}
|
||||
|
||||
const NAMESPACE_COLORS = [
|
||||
196,
|
||||
202,
|
||||
208,
|
||||
214,
|
||||
220,
|
||||
226,
|
||||
190,
|
||||
154,
|
||||
118,
|
||||
82,
|
||||
46,
|
||||
47,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
45,
|
||||
39,
|
||||
33,
|
||||
27,
|
||||
21,
|
||||
57,
|
||||
93,
|
||||
129,
|
||||
165,
|
||||
201,
|
||||
200,
|
||||
199,
|
||||
198,
|
||||
197,
|
||||
]
|
||||
formatNamespace = namespace => {
|
||||
// https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
|
||||
let hash = 0
|
||||
for (let i = 0, n = namespace.length; i < n; ++i) {
|
||||
hash = ((hash << 5) - hash + namespace.charCodeAt(i)) | 0
|
||||
}
|
||||
return ansi(`1;38;5;${NAMESPACE_COLORS[Math.abs(hash) % NAMESPACE_COLORS.length]}`, namespace)
|
||||
}
|
||||
} else {
|
||||
formatLevel = str => NAMES[str]
|
||||
formatNamespace = str => str
|
||||
}
|
||||
|
||||
const consoleTransport = ({ data, level, namespace, message, time }) => {
|
||||
const fn =
|
||||
/* eslint-disable no-console */
|
||||
level < INFO ? console.log : level < WARN ? console.info : level < ERROR ? console.warn : console.error
|
||||
/* eslint-enable no-console */
|
||||
|
||||
const args = [time.toISOString(), formatNamespace(namespace), formatLevel(level), message]
|
||||
if (data != null) {
|
||||
args.push(data)
|
||||
}
|
||||
fn.apply(console, args)
|
||||
}
|
||||
export default () => consoleTransport
|
||||
@@ -1,64 +0,0 @@
|
||||
import fromCallback from 'promise-toolbox/fromCallback'
|
||||
import prettyFormat from 'pretty-format' // eslint-disable-line node/no-extraneous-import
|
||||
import { createTransport } from 'nodemailer' // eslint-disable-line node/no-extraneous-import
|
||||
|
||||
import { evalTemplate, required } from '../utils'
|
||||
import { NAMES } from '../levels'
|
||||
|
||||
export default ({
|
||||
// transport options (https://nodemailer.com/smtp/)
|
||||
auth,
|
||||
authMethod,
|
||||
host,
|
||||
ignoreTLS,
|
||||
port,
|
||||
proxy,
|
||||
requireTLS,
|
||||
secure,
|
||||
service,
|
||||
tls,
|
||||
|
||||
// message options (https://nodemailer.com/message/)
|
||||
bcc,
|
||||
cc,
|
||||
from = required('from'),
|
||||
to = required('to'),
|
||||
subject = '[{{level}} - {{namespace}}] {{time}} {{message}}',
|
||||
}) => {
|
||||
const transporter = createTransport(
|
||||
{
|
||||
auth,
|
||||
authMethod,
|
||||
host,
|
||||
ignoreTLS,
|
||||
port,
|
||||
proxy,
|
||||
requireTLS,
|
||||
secure,
|
||||
service,
|
||||
tls,
|
||||
|
||||
disableFileAccess: true,
|
||||
disableUrlAccess: true,
|
||||
},
|
||||
{
|
||||
bcc,
|
||||
cc,
|
||||
from,
|
||||
to,
|
||||
}
|
||||
)
|
||||
|
||||
return log =>
|
||||
fromCallback(cb =>
|
||||
transporter.sendMail(
|
||||
{
|
||||
subject: evalTemplate(subject, key =>
|
||||
key === 'level' ? NAMES[log.level] : key === 'time' ? log.time.toISOString() : log[key]
|
||||
),
|
||||
text: prettyFormat(log.data),
|
||||
},
|
||||
cb
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default () => {
|
||||
const memoryLogger = log => {
|
||||
logs.push(log)
|
||||
}
|
||||
const logs = (memoryLogger.logs = [])
|
||||
return memoryLogger
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import fromCallback from 'promise-toolbox/fromCallback'
|
||||
import splitHost from 'split-host'
|
||||
import { createClient, Facility, Severity, Transport } from 'syslog-client'
|
||||
|
||||
import LEVELS from '../levels'
|
||||
|
||||
// https://github.com/paulgrove/node-syslog-client#syslogseverity
|
||||
const LEVEL_TO_SEVERITY = {
|
||||
[LEVELS.FATAL]: Severity.Critical,
|
||||
[LEVELS.ERROR]: Severity.Error,
|
||||
[LEVELS.WARN]: Severity.Warning,
|
||||
[LEVELS.INFO]: Severity.Informational,
|
||||
[LEVELS.DEBUG]: Severity.Debug,
|
||||
}
|
||||
|
||||
const facility = Facility.User
|
||||
|
||||
export default target => {
|
||||
const opts = {}
|
||||
if (target !== undefined) {
|
||||
if (target.startsWith('tcp://')) {
|
||||
target = target.slice(6)
|
||||
opts.transport = Transport.Tcp
|
||||
} else if (target.startsWith('udp://')) {
|
||||
target = target.slice(6)
|
||||
opts.transport = Transport.Udp
|
||||
}
|
||||
|
||||
;({ host: target, port: opts.port } = splitHost(target))
|
||||
}
|
||||
|
||||
const client = createClient(target, opts)
|
||||
|
||||
return log =>
|
||||
fromCallback(cb =>
|
||||
client.log(log.message, {
|
||||
facility,
|
||||
severity: LEVEL_TO_SEVERITY[log.level],
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -1 +1,82 @@
|
||||
module.exports = require('../dist/transports/console.js')
|
||||
const { LEVELS, NAMES } = require('../levels')
|
||||
|
||||
const { DEBUG, ERROR, FATAL, INFO, WARN } = LEVELS
|
||||
|
||||
let formatLevel, formatNamespace
|
||||
if (process.stdout !== undefined && process.stdout.isTTY && process.stderr !== undefined && process.stderr.isTTY) {
|
||||
const ansi = (style, str) => `\x1b[${style}m${str}\x1b[0m`
|
||||
|
||||
const LEVEL_STYLES = {
|
||||
[DEBUG]: '2',
|
||||
[ERROR]: '1;31',
|
||||
[FATAL]: '1;31',
|
||||
[INFO]: '1',
|
||||
[WARN]: '1;33',
|
||||
}
|
||||
formatLevel = level => {
|
||||
const style = LEVEL_STYLES[level]
|
||||
const name = NAMES[level]
|
||||
return style === undefined ? name : ansi(style, name)
|
||||
}
|
||||
|
||||
const NAMESPACE_COLORS = [
|
||||
196,
|
||||
202,
|
||||
208,
|
||||
214,
|
||||
220,
|
||||
226,
|
||||
190,
|
||||
154,
|
||||
118,
|
||||
82,
|
||||
46,
|
||||
47,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
45,
|
||||
39,
|
||||
33,
|
||||
27,
|
||||
21,
|
||||
57,
|
||||
93,
|
||||
129,
|
||||
165,
|
||||
201,
|
||||
200,
|
||||
199,
|
||||
198,
|
||||
197,
|
||||
]
|
||||
formatNamespace = namespace => {
|
||||
// https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
|
||||
let hash = 0
|
||||
for (let i = 0, n = namespace.length; i < n; ++i) {
|
||||
hash = ((hash << 5) - hash + namespace.charCodeAt(i)) | 0
|
||||
}
|
||||
return ansi(`1;38;5;${NAMESPACE_COLORS[Math.abs(hash) % NAMESPACE_COLORS.length]}`, namespace)
|
||||
}
|
||||
} else {
|
||||
formatLevel = str => NAMES[str]
|
||||
formatNamespace = str => str
|
||||
}
|
||||
|
||||
const consoleTransport = ({ data, level, namespace, message, time }) => {
|
||||
const fn =
|
||||
/* eslint-disable no-console */
|
||||
level < INFO ? console.log : level < WARN ? console.info : level < ERROR ? console.warn : console.error
|
||||
/* eslint-enable no-console */
|
||||
|
||||
const args = [time.toISOString(), formatNamespace(namespace), formatLevel(level), message]
|
||||
if (data != null) {
|
||||
args.push(data)
|
||||
}
|
||||
fn.apply(console, args)
|
||||
}
|
||||
|
||||
const createTransport = () => consoleTransport
|
||||
|
||||
module.exports = exports = createTransport
|
||||
|
||||
@@ -1 +1,66 @@
|
||||
module.exports = require('../dist/transports/email.js')
|
||||
const fromCallback = require('promise-toolbox/fromCallback')
|
||||
const nodemailer = require('nodemailer') // eslint-disable-line node/no-extraneous-import
|
||||
const prettyFormat = require('pretty-format') // eslint-disable-line node/no-extraneous-import
|
||||
|
||||
const { evalTemplate, required } = require('../utils')
|
||||
const { NAMES } = require('../levels')
|
||||
|
||||
function createTransport({
|
||||
// transport options (https://nodemailer.com/smtp/)
|
||||
auth,
|
||||
authMethod,
|
||||
host,
|
||||
ignoreTLS,
|
||||
port,
|
||||
proxy,
|
||||
requireTLS,
|
||||
secure,
|
||||
service,
|
||||
tls,
|
||||
|
||||
// message options (https://nodemailer.com/message/)
|
||||
bcc,
|
||||
cc,
|
||||
from = required('from'),
|
||||
to = required('to'),
|
||||
subject = '[{{level}} - {{namespace}}] {{time}} {{message}}',
|
||||
}) {
|
||||
const transporter = nodemailer.createTransport(
|
||||
{
|
||||
auth,
|
||||
authMethod,
|
||||
host,
|
||||
ignoreTLS,
|
||||
port,
|
||||
proxy,
|
||||
requireTLS,
|
||||
secure,
|
||||
service,
|
||||
tls,
|
||||
|
||||
disableFileAccess: true,
|
||||
disableUrlAccess: true,
|
||||
},
|
||||
{
|
||||
bcc,
|
||||
cc,
|
||||
from,
|
||||
to,
|
||||
}
|
||||
)
|
||||
|
||||
return log =>
|
||||
fromCallback(cb =>
|
||||
transporter.sendMail(
|
||||
{
|
||||
subject: evalTemplate(subject, key =>
|
||||
key === 'level' ? NAMES[log.level] : key === 'time' ? log.time.toISOString() : log[key]
|
||||
),
|
||||
text: prettyFormat(log.data),
|
||||
},
|
||||
cb
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = exports = createTransport
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
module.exports = require('../dist/transports/memory.js')
|
||||
function createTransport() {
|
||||
const memoryLogger = log => {
|
||||
logs.push(log)
|
||||
}
|
||||
const logs = (memoryLogger.logs = [])
|
||||
return memoryLogger
|
||||
}
|
||||
|
||||
module.exports = exports = createTransport
|
||||
|
||||
@@ -1 +1,43 @@
|
||||
module.exports = require('../dist/transports/syslog.js')
|
||||
const fromCallback = require('promise-toolbox/fromCallback')
|
||||
const splitHost = require('split-host')
|
||||
const { createClient, Facility, Severity, Transport } = require('syslog-client')
|
||||
|
||||
const LEVELS = require('../levels')
|
||||
|
||||
// https://github.com/paulgrove/node-syslog-client#syslogseverity
|
||||
const LEVEL_TO_SEVERITY = {
|
||||
[LEVELS.FATAL]: Severity.Critical,
|
||||
[LEVELS.ERROR]: Severity.Error,
|
||||
[LEVELS.WARN]: Severity.Warning,
|
||||
[LEVELS.INFO]: Severity.Informational,
|
||||
[LEVELS.DEBUG]: Severity.Debug,
|
||||
}
|
||||
|
||||
const facility = Facility.User
|
||||
|
||||
function createTransport(target) {
|
||||
const opts = {}
|
||||
if (target !== undefined) {
|
||||
if (target.startsWith('tcp://')) {
|
||||
target = target.slice(6)
|
||||
opts.transport = Transport.Tcp
|
||||
} else if (target.startsWith('udp://')) {
|
||||
target = target.slice(6)
|
||||
opts.transport = Transport.Udp
|
||||
}
|
||||
|
||||
;({ host: target, port: opts.port } = splitHost(target))
|
||||
}
|
||||
|
||||
const client = createClient(target, opts)
|
||||
|
||||
return log =>
|
||||
fromCallback(cb =>
|
||||
client.log(log.message, {
|
||||
facility,
|
||||
severity: LEVEL_TO_SEVERITY[log.level],
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = exports = createTransport
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import escapeRegExp from 'lodash/escapeRegExp'
|
||||
const escapeRegExp = require('lodash/escapeRegExp')
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const TPL_RE = /\{\{(.+?)\}\}/g
|
||||
export const evalTemplate = (tpl, data) => {
|
||||
const evalTemplate = (tpl, data) => {
|
||||
const getData = typeof data === 'function' ? (_, key) => data(key) : (_, key) => data[key]
|
||||
|
||||
return tpl.replace(TPL_RE, getData)
|
||||
}
|
||||
exports.evalTemplate = evalTemplate
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const compileGlobPatternFragment = pattern => pattern.split('*').map(escapeRegExp).join('.*')
|
||||
|
||||
export const compileGlobPattern = pattern => {
|
||||
const compileGlobPattern = pattern => {
|
||||
const no = []
|
||||
const yes = []
|
||||
pattern.split(/[\s,]+/).forEach(pattern => {
|
||||
@@ -40,19 +41,22 @@ export const compileGlobPattern = pattern => {
|
||||
|
||||
return new RegExp(raw.join(''))
|
||||
}
|
||||
exports.compileGlobPattern = compileGlobPattern
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const required = name => {
|
||||
const required = name => {
|
||||
throw new Error(`missing required arg ${name}`)
|
||||
}
|
||||
exports.required = required
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const serializeError = error => ({
|
||||
const serializeError = error => ({
|
||||
...error, // Copy enumerable properties.
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
name: error.name,
|
||||
stack: error.stack,
|
||||
})
|
||||
exports.serializeError = serializeError
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import { compileGlobPattern } from './utils'
|
||||
const { compileGlobPattern } = require('./utils')
|
||||
|
||||
describe('compileGlobPattern()', () => {
|
||||
it('works', () => {
|
||||
@@ -1,5 +1,5 @@
|
||||
const assert = require('assert')
|
||||
const emitAsync = require('@xen-orchestra/emit-async').default
|
||||
const emitAsync = require('@xen-orchestra/emit-async')
|
||||
const EventEmitter = require('events')
|
||||
const { createLogger } = require('@xen-orchestra/log')
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"golike-defer": "^0.5.1",
|
||||
"http-server-plus": "^0.11.0",
|
||||
"json-rpc-protocol": "^0.13.1",
|
||||
"jsonrpc-websocket-client": "^0.5.0",
|
||||
"jsonrpc-websocket-client": "^0.6.0",
|
||||
"koa": "^2.5.1",
|
||||
"koa-compress": "^5.0.1",
|
||||
"koa-helmet": "^5.1.0",
|
||||
@@ -73,8 +73,7 @@
|
||||
"@vates/toggle-scripts": "^1.0.0",
|
||||
"babel-plugin-transform-dev": "^2.0.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"index-modules": "^0.4.0",
|
||||
"rimraf": "^3.0.0"
|
||||
"index-modules": "^0.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"_build": "index-modules --index-file index.mjs src/app/mixins && babel --delete-dir-on-start --keep-file-extension --source-maps --out-dir=dist/ src/",
|
||||
|
||||
@@ -2,18 +2,18 @@ import Disposable from 'promise-toolbox/Disposable.js'
|
||||
import fromCallback from 'promise-toolbox/fromCallback.js'
|
||||
import fromEvent from 'promise-toolbox/fromEvent.js'
|
||||
import fse from 'fs-extra'
|
||||
import JsonRpcWebsocketClient from 'jsonrpc-websocket-client'
|
||||
import parsePairs from 'parse-pairs'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import { deduped } from '@vates/disposable/deduped.js'
|
||||
import { execFile, spawn } from 'child_process'
|
||||
import { JsonRpcWebSocketClient } from 'jsonrpc-websocket-client'
|
||||
|
||||
const TUNNEL_SERVICE = 'xoa-support-tunnel.service'
|
||||
|
||||
const { debug, warn } = createLogger('xo:proxy:appliance')
|
||||
|
||||
const getUpdater = deduped(async function () {
|
||||
const updater = new JsonRpcWebsocketClient('ws://localhost:9001')
|
||||
const updater = new JsonRpcWebSocketClient('ws://localhost:9001')
|
||||
await updater.open()
|
||||
return new Disposable(() => updater.close(), updater)
|
||||
})
|
||||
@@ -153,6 +153,10 @@ export default class Appliance {
|
||||
|
||||
// A proxy can be bound to a unique license
|
||||
getSelfLicense() {
|
||||
return Disposable.use(getUpdater(), _ => _.call('getSelfLicenses').then(licenses => licenses[0]))
|
||||
return Disposable.use(getUpdater(), async updater => {
|
||||
const licenses = await updater.call('getSelfLicenses')
|
||||
const now = Date.now()
|
||||
return licenses.find(({ expires }) => expires === undefined || expires > now)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ export default class Backups {
|
||||
async function () {
|
||||
if (!__DEV__) {
|
||||
const license = await app.appliance.getSelfLicense()
|
||||
if (license === undefined || license.expires < Date.now()) {
|
||||
throw new Error('the proxy license is not valid')
|
||||
if (license === undefined) {
|
||||
throw new Error('no valid proxy license')
|
||||
}
|
||||
}
|
||||
return run.apply(this, arguments)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
> Users must be able to say: “Nice enhancement, I'm eager to test it”
|
||||
|
||||
- [Metadata Backup] Add a warning on restoring a metadata backup (PR [#5769](https://github.com/vatesfr/xen-orchestra/pull/5769))
|
||||
- [SAML] Compatible with users created with other authentication providers (PR [#5781](https://github.com/vatesfr/xen-orchestra/pull/5781))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -32,6 +33,12 @@
|
||||
>
|
||||
> In case of conflict, the highest (lowest in previous list) `$version` wins.
|
||||
|
||||
- @xen-orchestra/emit-async minor
|
||||
- @xen-orchestra/defined patch
|
||||
- xo-collection minor
|
||||
- @xen-orchestra/log patch
|
||||
- xen-api minor
|
||||
- xo-server-auth-saml minor
|
||||
- xo-server-backup-reports patch
|
||||
- xo-web minor
|
||||
- xo-server patch
|
||||
|
||||
@@ -113,7 +113,7 @@ Then restart Xen Orchestra if it was running.
|
||||
```
|
||||
yarn global add forever
|
||||
# Run the below as the user owning XO
|
||||
forever start bin/xo-server
|
||||
forever start dist/cli.mjs
|
||||
```
|
||||
|
||||
- Or you can use [forever-service](https://github.com/zapty/forever-service) to install XO as a system service, so it starts automatically at boot. Run the following as root:
|
||||
|
||||
@@ -190,7 +190,7 @@ Then restart Xen Orchestra if it was running.
|
||||
```
|
||||
yarn global add forever
|
||||
# Run the below as the user owning XO
|
||||
forever start bin/xo-server
|
||||
forever start dist/cli.mjs
|
||||
```
|
||||
|
||||
- Or you can use [forever-service](https://github.com/zapty/forever-service) to install XO as a system service, so it starts automatically at boot. Run the following as root:
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"async-iterator-to-stream": "^1.0.2",
|
||||
"core-js": "^3.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"limit-concurrency-decorator": "^0.4.0",
|
||||
"limit-concurrency-decorator": "^0.5.0",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.19.2",
|
||||
"struct-fu": "^1.2.0",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// TODO: remove once completely merged in vhd.js
|
||||
|
||||
import assert from 'assert'
|
||||
import concurrency from 'limit-concurrency-decorator'
|
||||
import noop from './_noop'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import { limitConcurrency } from 'limit-concurrency-decorator'
|
||||
|
||||
import Vhd from './vhd'
|
||||
import { basename, dirname } from 'path'
|
||||
@@ -14,7 +14,7 @@ const { warn } = createLogger('vhd-lib:merge')
|
||||
// Merge vhd child into vhd parent.
|
||||
//
|
||||
// TODO: rename the VHD file during the merge
|
||||
export default concurrency(2)(async function merge(
|
||||
export default limitConcurrency(2)(async function merge(
|
||||
parentHandler,
|
||||
parentPath,
|
||||
childHandler,
|
||||
|
||||
@@ -42,6 +42,7 @@ const usage = 'Usage: xen-api <url> [<user> [<password>]]'
|
||||
|
||||
async function main(createClient) {
|
||||
const opts = minimist(process.argv.slice(2), {
|
||||
string: ['session-id'],
|
||||
boolean: ['allow-unauthorized', 'help', 'read-only', 'verbose'],
|
||||
|
||||
alias: {
|
||||
@@ -68,6 +69,8 @@ async function main(createClient) {
|
||||
if (opts._.length > 1) {
|
||||
const [, user, password = await askPassword()] = opts._
|
||||
auth = { user, password }
|
||||
} else if (opts['session-id'] !== undefined) {
|
||||
auth = { sessionId: opts['session-id'] }
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import assert from 'assert'
|
||||
import Collection from 'xo-collection'
|
||||
import dns from 'dns'
|
||||
import kindOf from 'kindof'
|
||||
import ms from 'ms'
|
||||
import httpRequest from 'http-request-plus'
|
||||
import { Collection } from 'xo-collection'
|
||||
import { EventEmitter } from 'events'
|
||||
import { map, noop, omit } from 'lodash'
|
||||
import { cancelable, defer, fromCallback, fromEvents, ignoreErrors, pDelay, pRetry, pTimeout } from 'promise-toolbox'
|
||||
@@ -700,25 +700,34 @@ export class Xapi extends EventEmitter {
|
||||
|
||||
_sessionCallRetryOptions = {
|
||||
tries: 2,
|
||||
when: error => this._status !== DISCONNECTED && error?.code === 'SESSION_INVALID',
|
||||
when: error =>
|
||||
this._status !== DISCONNECTED && error?.code === 'SESSION_INVALID' && this._auth.password !== undefined,
|
||||
onRetry: () => this._sessionOpen(),
|
||||
}
|
||||
_sessionCall(method, args, timeout) {
|
||||
async _sessionCall(method, args, timeout) {
|
||||
if (method.startsWith('session.')) {
|
||||
return Promise.reject(new Error('session.*() methods are disabled from this interface'))
|
||||
}
|
||||
|
||||
return pRetry(() => {
|
||||
const sessionId = this._sessionId
|
||||
assert.notStrictEqual(sessionId, undefined)
|
||||
try {
|
||||
return await pRetry(() => {
|
||||
const sessionId = this._sessionId
|
||||
assert.notStrictEqual(sessionId, undefined)
|
||||
|
||||
const newArgs = [sessionId]
|
||||
if (args !== undefined) {
|
||||
newArgs.push.apply(newArgs, args)
|
||||
const newArgs = [sessionId]
|
||||
if (args !== undefined) {
|
||||
newArgs.push.apply(newArgs, args)
|
||||
}
|
||||
|
||||
return this._call(method, newArgs, timeout)
|
||||
}, this._sessionCallRetryOptions)
|
||||
} catch (error) {
|
||||
if (error?.code === 'SESSION_INVALID') {
|
||||
await ignoreErrors.call(this.disconnect())
|
||||
}
|
||||
|
||||
return this._call(method, newArgs, timeout)
|
||||
}, this._sessionCallRetryOptions)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: (probably rare) race condition leading to unnecessary login when:
|
||||
@@ -728,29 +737,40 @@ export class Xapi extends EventEmitter {
|
||||
// 3. the session is renewed
|
||||
// 4. the second call fails with SESSION_INVALID which leads to a new
|
||||
// unnecessary renewal
|
||||
_sessionOpenRetryOptions = {
|
||||
tries: 2,
|
||||
when: { code: 'HOST_IS_SLAVE' },
|
||||
onRetry: error => {
|
||||
this._setUrl({ ...this._url, hostname: error.params[0] })
|
||||
},
|
||||
}
|
||||
_sessionOpen = coalesceCalls(this._sessionOpen)
|
||||
async _sessionOpen() {
|
||||
const { user, password } = this._auth
|
||||
const params = [user, password]
|
||||
this._sessionId = await pRetry(
|
||||
() => this._interruptOnDisconnect(this._call('session.login_with_password', params)),
|
||||
{
|
||||
tries: 2,
|
||||
when: { code: 'HOST_IS_SLAVE' },
|
||||
onRetry: error => {
|
||||
this._setUrl({ ...this._url, hostname: error.params[0] })
|
||||
},
|
||||
}
|
||||
)
|
||||
const { user, password, sessionId } = this._auth
|
||||
|
||||
this._sessionId = sessionId
|
||||
|
||||
if (sessionId === undefined) {
|
||||
const params = [user, password]
|
||||
this._sessionId = await pRetry(
|
||||
() => this._interruptOnDisconnect(this._call('session.login_with_password', params)),
|
||||
this._sessionOpenRetryOptions
|
||||
)
|
||||
}
|
||||
|
||||
const oldPoolRef = this._pool?.$ref
|
||||
|
||||
// Similar to `(await this.getAllRecords('pool'))[0]` but prevents a
|
||||
// deadlock in case of error due to a pRetry calling _sessionOpen again
|
||||
const pools = await this._call('pool.get_all_records', [this._sessionId])
|
||||
const pools = await pRetry(
|
||||
() => this._call('pool.get_all_records', [this._sessionId]),
|
||||
this._sessionOpenRetryOptions
|
||||
)
|
||||
const poolRef = Object.keys(pools)[0]
|
||||
this._pool = this._wrapRecord('pool', poolRef, pools[poolRef])
|
||||
|
||||
this.emit('sessionId', this._sessionId)
|
||||
|
||||
// if the pool ref has changed, it means that the XAPI has been restarted or
|
||||
// it's not the same XAPI, we need to refetch the available types and reset
|
||||
// the event loop in that case
|
||||
@@ -781,7 +801,7 @@ export class Xapi extends EventEmitter {
|
||||
}
|
||||
|
||||
_setUrl(url) {
|
||||
this._humanId = `${this._auth.user}@${url.hostname}`
|
||||
this._humanId = `${this._auth.user ?? 'unknown'}@${url.hostname}`
|
||||
this._transport = autoTransport({
|
||||
secureOptions: {
|
||||
minVersion: 'TLSv1',
|
||||
|
||||
@@ -17,7 +17,7 @@ Installation of the [npm package](https://npmjs.org/package/xo-collection):
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
var Collection = require('xo-collection')
|
||||
var { Collection } = require('xo-collection')
|
||||
```
|
||||
|
||||
### Creation
|
||||
@@ -218,7 +218,7 @@ for (const value of col.values()) {
|
||||
### Views
|
||||
|
||||
```javascript
|
||||
const View = require('xo-collection/view')
|
||||
const { View } = require('xo-collection/view')
|
||||
```
|
||||
|
||||
> A view is a read-only collection which contains only the items of a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
```javascript
|
||||
var Collection = require('xo-collection')
|
||||
var { Collection } = require('xo-collection')
|
||||
```
|
||||
|
||||
### Creation
|
||||
@@ -200,7 +200,7 @@ for (const value of col.values()) {
|
||||
### Views
|
||||
|
||||
```javascript
|
||||
const View = require('xo-collection/view')
|
||||
const { View } = require('xo-collection/view')
|
||||
```
|
||||
|
||||
> A view is a read-only collection which contains only the items of a
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"promise-toolbox": "^0.19.2",
|
||||
"rimraf": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -74,7 +74,7 @@ const isValidKey = key => typeof key === 'number' || typeof key === 'string'
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export default class Collection extends EventEmitter {
|
||||
export class Collection extends EventEmitter {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import fromEvent from 'promise-toolbox/fromEvent'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection, { DuplicateItem, NoSuchItem } from './collection'
|
||||
import { Collection, DuplicateItem, NoSuchItem } from './collection'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('Collection', function () {
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
|
||||
// Async event.
|
||||
return eventToPromise(col, 'add').then(function (added) {
|
||||
return fromEvent(col, 'add').then(function (added) {
|
||||
expect(Object.keys(added)).toEqual(['foo'])
|
||||
expect(added.foo).toBe(true)
|
||||
})
|
||||
@@ -102,7 +102,7 @@ describe('Collection', function () {
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
|
||||
// Async event.
|
||||
return eventToPromise(col, 'update').then(function (updated) {
|
||||
return fromEvent(col, 'update').then(function (updated) {
|
||||
expect(Object.keys(updated)).toEqual(['bar'])
|
||||
expect(updated.bar).toBe(2)
|
||||
})
|
||||
@@ -134,7 +134,7 @@ describe('Collection', function () {
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
|
||||
// Async event.
|
||||
return eventToPromise(col, 'remove').then(function (removed) {
|
||||
return fromEvent(col, 'remove').then(function (removed) {
|
||||
expect(Object.keys(removed)).toEqual(['bar'])
|
||||
expect(removed.bar).toBeUndefined()
|
||||
})
|
||||
@@ -166,7 +166,7 @@ describe('Collection', function () {
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
|
||||
// Async events.
|
||||
return eventToPromise(col, 'add').then(function (added) {
|
||||
return fromEvent(col, 'add').then(function (added) {
|
||||
expect(Object.keys(added)).toEqual(['foo'])
|
||||
expect(added.foo).toBe(true)
|
||||
})
|
||||
@@ -184,7 +184,7 @@ describe('Collection', function () {
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
|
||||
// Async events.
|
||||
return eventToPromise(col, 'update').then(function (updated) {
|
||||
return fromEvent(col, 'update').then(function (updated) {
|
||||
expect(Object.keys(updated)).toEqual(['bar'])
|
||||
expect(updated.bar).toBe(1)
|
||||
})
|
||||
@@ -205,7 +205,7 @@ describe('Collection', function () {
|
||||
|
||||
expect(col.has('bar')).toBe(false)
|
||||
|
||||
return eventToPromise(col, 'remove').then(function (removed) {
|
||||
return fromEvent(col, 'remove').then(function (removed) {
|
||||
expect(Object.keys(removed)).toEqual(['bar'])
|
||||
expect(removed.bar).toBeUndefined()
|
||||
})
|
||||
@@ -220,7 +220,7 @@ describe('Collection', function () {
|
||||
|
||||
expect(col.has('bar')).toBe(false)
|
||||
|
||||
return eventToPromise(col, 'remove').then(function (removed) {
|
||||
return fromEvent(col, 'remove').then(function (removed) {
|
||||
expect(Object.keys(removed)).toEqual(['bar'])
|
||||
expect(removed.bar).toBeUndefined()
|
||||
})
|
||||
@@ -235,7 +235,7 @@ describe('Collection', function () {
|
||||
return waitTicks().then(() => {
|
||||
col.touch(foo)
|
||||
|
||||
return eventToPromise(col, 'update', items => {
|
||||
return fromEvent(col, 'update', items => {
|
||||
expect(Object.keys(items)).toEqual(['foo'])
|
||||
expect(items.foo).toBe(foo)
|
||||
})
|
||||
@@ -249,7 +249,7 @@ describe('Collection', function () {
|
||||
|
||||
expect(col.size).toBe(0)
|
||||
|
||||
return eventToPromise(col, 'remove').then(items => {
|
||||
return fromEvent(col, 'remove').then(items => {
|
||||
expect(Object.keys(items)).toEqual(['bar'])
|
||||
expect(items.bar).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ACTION_ADD, ACTION_UPDATE, ACTION_REMOVE } from './collection'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class Index {
|
||||
export class Index {
|
||||
constructor(computeHash) {
|
||||
if (computeHash) {
|
||||
this.computeHash = iteratee(computeHash)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import fromEvent from 'promise-toolbox/fromEvent'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection from './collection'
|
||||
import Index from './index'
|
||||
import { Collection } from './collection'
|
||||
import { Index } from './index'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('Index', function () {
|
||||
|
||||
col.update(item1bis)
|
||||
|
||||
return eventToPromise(col, 'finish').then(() => {
|
||||
return fromEvent(col, 'finish').then(() => {
|
||||
expect(col.indexes).toEqual({
|
||||
byGroup: {
|
||||
foo: {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ACTION_ADD, ACTION_UPDATE, ACTION_REMOVE } from './collection'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class UniqueIndex {
|
||||
export class UniqueIndex {
|
||||
constructor(computeHash) {
|
||||
if (computeHash) {
|
||||
this.computeHash = iteratee(computeHash)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import fromEvent from 'promise-toolbox/fromEvent'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection from './collection'
|
||||
import Index from './unique-index'
|
||||
import { Collection } from './collection'
|
||||
import { UniqueIndex } from './unique-index'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('UniqueIndex', function () {
|
||||
col.add(item)
|
||||
})
|
||||
|
||||
byKey = new Index('key')
|
||||
byKey = new UniqueIndex('key')
|
||||
|
||||
col.createIndex('byKey', byKey)
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('UniqueIndex', function () {
|
||||
|
||||
col.update(item1bis)
|
||||
|
||||
return eventToPromise(col, 'finish').then(() => {
|
||||
return fromEvent(col, 'finish').then(() => {
|
||||
expect(col.indexes).toEqual({
|
||||
byKey: {
|
||||
[item1.key]: item1bis,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable no-console */
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection from './collection'
|
||||
import View from './view'
|
||||
import { Collection } from './collection'
|
||||
import { View } from './view'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import Collection, { ACTION_ADD, ACTION_UPDATE, ACTION_REMOVE } from './collecti
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class View extends Collection {
|
||||
export class View extends Collection {
|
||||
constructor(collection, predicate) {
|
||||
super()
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonrpc-websocket-client": "^0.5.0",
|
||||
"jsonrpc-websocket-client": "^0.6.0",
|
||||
"lodash": "^4.17.2",
|
||||
"make-error": "^1.0.4"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import JsonRpcWebSocketClient, { OPEN, CLOSED } from 'jsonrpc-websocket-client'
|
||||
import trimEnd from 'lodash/trimEnd'
|
||||
import { BaseError } from 'make-error'
|
||||
import { JsonRpcWebSocketClient, OPEN, CLOSED } from 'jsonrpc-websocket-client'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class AuthSamlXoPlugin {
|
||||
}
|
||||
|
||||
try {
|
||||
done(null, await xo.registerUser('saml', name))
|
||||
done(null, await xo.registerUser2('saml', { user: { id: name, name } }))
|
||||
} catch (error) {
|
||||
done(error.message)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env jest */
|
||||
import Xo from 'xo-lib'
|
||||
import XoCollection from 'xo-collection'
|
||||
import { Collection as XoCollection } from 'xo-collection'
|
||||
import { decorateWith } from '@vates/decorate-with'
|
||||
import { defaultsDeep, find, forOwn, iteratee, pick } from 'lodash'
|
||||
import { defer } from 'golike-defer'
|
||||
|
||||
5
packages/xo-server/.babelrc.cjs
Normal file
5
packages/xo-server/.babelrc.cjs
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'), {
|
||||
'@babel/preset-env': {
|
||||
modules: false,
|
||||
},
|
||||
})
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
global.Promise = require('bluebird')
|
||||
|
||||
process.on('unhandledRejection', function (reason) {
|
||||
console.warn('[Warn] Possibly unhandled rejection:', (reason && reason.stack) || reason)
|
||||
})
|
||||
|
||||
require('exec-promise')(require('../dist/vhd-test').default)
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
// Better stack traces if possible.
|
||||
try {
|
||||
require('source-map-support').install({
|
||||
handleUncaughtExceptions: false,
|
||||
})
|
||||
} catch (_) {}
|
||||
|
||||
require('exec-promise')(require('../dist/logs-cli').default)
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('exec-promise')(require('../dist/recover-account-cli').default)
|
||||
@@ -1,11 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
// Enable xo logs by default.
|
||||
if (process.env.DEBUG === undefined) {
|
||||
process.env.DEBUG = 'app-conf,xo:*,-xo:api'
|
||||
}
|
||||
|
||||
// Import the real main module.
|
||||
module.exports = require('./dist').default
|
||||
@@ -18,11 +18,13 @@
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"directories": {
|
||||
"bin": "bin"
|
||||
"bin": {
|
||||
"xo-server": "dist/cli.mjs",
|
||||
"xo-server-logs": "dist/logs-cli.mjs",
|
||||
"xo-server-recover-account": "dist/recover-account-cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
"node": ">=14.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.1",
|
||||
@@ -58,7 +60,7 @@
|
||||
"cookie": "^0.4.0",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"d3-time-format": "^3.0.0",
|
||||
"decorator-synchronized": "^0.5.0",
|
||||
"decorator-synchronized": "^0.6.0",
|
||||
"deptree": "^1.0.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"execa": "^5.0.0",
|
||||
@@ -83,7 +85,7 @@
|
||||
"json5": "^2.0.1",
|
||||
"kindof": "^2.0.0",
|
||||
"level-party": "^5.0.0",
|
||||
"limit-concurrency-decorator": "^0.4.0",
|
||||
"limit-concurrency-decorator": "^0.5.0",
|
||||
"lodash": "^4.17.4",
|
||||
"make-error": "^1",
|
||||
"memorystore": "^1.6.2",
|
||||
@@ -142,20 +144,16 @@
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-dev": "^2.0.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"index-modules": "^0.3.0",
|
||||
"rimraf": "^3.0.0"
|
||||
"index-modules": "^0.4.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": "index-modules src/api src/xapi/mixins src/xo-mixins && yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"_build": "index-modules --index-file index.mjs src/api src/xapi/mixins src/xo-mixins && babel --delete-dir-on-start --keep-file-extension --source-maps --out-dir=dist/ src/",
|
||||
"build": "cross-env NODE_ENV=production yarn run _build",
|
||||
"dev": "cross-env NODE_ENV=development yarn run _build --watch",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "node bin/xo-server"
|
||||
"start": "node dist/cli.mjs"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
|
||||
@@ -12,7 +12,7 @@ function* values(object) {
|
||||
*
|
||||
* @param {(Array|Object)} collection
|
||||
*/
|
||||
module.exports = asyncIteratorToStream(function* (collection) {
|
||||
export default asyncIteratorToStream(function* (collection) {
|
||||
for (const value of Array.isArray(collection) ? collection : values(collection)) {
|
||||
yield JSON.stringify(value)
|
||||
yield '\n'
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import ensureArray from './_ensureArray.js'
|
||||
import ensureArray from './_ensureArray.mjs'
|
||||
|
||||
describe('ensureArray()', function () {
|
||||
it('wrap the value in an array', function () {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MultiKeyMap } from '@vates/multi-key-map'
|
||||
|
||||
import ensureArray from './_ensureArray.js'
|
||||
import ensureArray from './_ensureArray.mjs'
|
||||
|
||||
function removeCacheEntry(cache, keys) {
|
||||
cache.delete(keys)
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import { debounceWithKey, REMOVE_CACHE_ENTRY } from './_pDebounceWithKey.js'
|
||||
import { debounceWithKey, REMOVE_CACHE_ENTRY } from './_pDebounceWithKey.mjs'
|
||||
|
||||
describe('REMOVE_CACHE_ENTRY', () => {
|
||||
it('clears the cache', async () => {
|
||||
@@ -2,9 +2,9 @@ import { basename } from 'path'
|
||||
import { fromCallback } from 'promise-toolbox'
|
||||
import { pipeline } from 'readable-stream'
|
||||
|
||||
import createNdJsonStream from '../_createNdJsonStream.js'
|
||||
import { REMOVE_CACHE_ENTRY } from '../_pDebounceWithKey.js'
|
||||
import { safeDateFormat } from '../utils.js'
|
||||
import createNdJsonStream from '../_createNdJsonStream.mjs'
|
||||
import { REMOVE_CACHE_ENTRY } from '../_pDebounceWithKey.mjs'
|
||||
import { safeDateFormat } from '../utils.mjs'
|
||||
|
||||
export function createJob({ schedules, ...job }) {
|
||||
job.userId = this.user.id
|
||||
@@ -8,7 +8,7 @@ import { noSuchObject } from 'xo-common/api-errors.js'
|
||||
import { peekFooterFromVhdStream } from 'vhd-lib'
|
||||
import { vmdkToVhd } from 'xo-vmdk-to-vhd'
|
||||
|
||||
import { VDI_FORMAT_VHD } from '../xapi/index.js'
|
||||
import { VDI_FORMAT_VHD } from '../xapi/index.mjs'
|
||||
|
||||
const log = createLogger('xo:disk')
|
||||
|
||||
@@ -288,7 +288,7 @@ isHostServerTimeConsistent.params = {
|
||||
}
|
||||
|
||||
isHostServerTimeConsistent.resolve = {
|
||||
host: ['host', 'host', 'administrate'],
|
||||
host: ['host', 'host', 'view'],
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -1,4 +1,4 @@
|
||||
import xapiObjectToXo from '../xapi-object-to-xo.js'
|
||||
import xapiObjectToXo from '../xapi-object-to-xo.mjs'
|
||||
|
||||
export function getBondModes() {
|
||||
return ['balance-slb', 'active-backup', 'lacp']
|
||||
@@ -1,8 +1,9 @@
|
||||
// TODO: too low level, move into host.
|
||||
|
||||
import { filter, find } from 'lodash'
|
||||
import filter from 'lodash/filter.js'
|
||||
import find from 'lodash/find.js'
|
||||
|
||||
import { IPV4_CONFIG_MODES, IPV6_CONFIG_MODES } from '../xapi/index.js'
|
||||
import { IPV4_CONFIG_MODES, IPV6_CONFIG_MODES } from '../xapi/index.mjs'
|
||||
|
||||
export function getIpv4ConfigurationModes() {
|
||||
return IPV4_CONFIG_MODES
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user