Compare commits

..

1 Commits

Author SHA1 Message Date
Mohamedox
a1c19c92a9 fix(xo-web/ips): bad range formatting
Fixes #3170
2019-05-28 15:17:38 +02:00
68 changed files with 609 additions and 2792 deletions

View File

@@ -35,9 +35,6 @@ module.exports = {
},
},
rules: {
// disabled because XAPI objects are using camel case
camelcase: ['off'],
'no-console': ['error', { allow: ['warn', 'error'] }],
'no-var': 'error',
'node/no-extraneous-import': 'error',

View File

@@ -16,7 +16,7 @@
},
"dependencies": {
"golike-defer": "^0.4.1",
"xen-api": "^0.26.0"
"xen-api": "^0.25.1"
},
"scripts": {
"postversion": "npm publish"

View File

@@ -1,60 +1,25 @@
# ChangeLog
## **next**
## **next** (2019-05-14)
### Enhancements
- [VM/Advanced] Ability to use UEFI instead of BIOS [#4264](https://github.com/vatesfr/xen-orchestra/issues/4264) (PR [#4268](https://github.com/vatesfr/xen-orchestra/pull/4268))
### Bug fixes
- [XOA] Don't require editing the _email_ field in case of re-registration (PR [#4259](https://github.com/vatesfr/xen-orchestra/pull/4259))
### Released packages
- xen-api v0.25.2
- xo-server v5.43.0
- xo-web v5.43.0
## **5.35.0** (2019-05-29)
![Channel: latest](https://badgen.net/badge/channel/latest/yellow)
### Enhancements
- [VM/general] Display 'Started... ago' instead of 'Halted... ago' for paused state [#3750](https://github.com/vatesfr/xen-orchestra/issues/3750) (PR [#4170](https://github.com/vatesfr/xen-orchestra/pull/4170))
- [Metadata backup] Ability to define when the backup report will be sent (PR [#4149](https://github.com/vatesfr/xen-orchestra/pull/4149))
- [XOA/Update] Ability to select release channel [#4200](https://github.com/vatesfr/xen-orchestra/issues/4200) (PR [#4202](https://github.com/vatesfr/xen-orchestra/pull/4202))
- [User] Forget connection tokens on password change or on demand [#4214](https://github.com/vatesfr/xen-orchestra/issues/4214) (PR [#4224](https://github.com/vatesfr/xen-orchestra/pull/4224))
- [Settings/Logs] LICENCE_RESTRICTION errors: suggest XCP-ng as an Open Source alternative [#3876](https://github.com/vatesfr/xen-orchestra/issues/3876) (PR [#4238](https://github.com/vatesfr/xen-orchestra/pull/4238))
- [VM/Migrate] Display VDI size on migrate modal [#2534](https://github.com/vatesfr/xen-orchestra/issues/2534) (PR [#4250](https://github.com/vatesfr/xen-orchestra/pull/4250))
- [Host] Display hyperthreading status on advanced tab [#4262](https://github.com/vatesfr/xen-orchestra/issues/4262) (PR [#4263](https://github.com/vatesfr/xen-orchestra/pull/4263))
### Bug fixes
- [Pool/Patches] Fix "an error has occurred" in "Applied patches" [#4192](https://github.com/vatesfr/xen-orchestra/issues/4192) (PR [#4193](https://github.com/vatesfr/xen-orchestra/pull/4193))
- [Backup NG] Fix report sent even though "Never" is selected [#4092](https://github.com/vatesfr/xen-orchestra/issues/4092) (PR [#4178](https://github.com/vatesfr/xen-orchestra/pull/4178))
- [Remotes] Fix issues after a config import (PR [#4197](https://github.com/vatesfr/xen-orchestra/pull/4197))
- [Charts] Fixed the chart lines sometimes changing order/color (PR [#4221](https://github.com/vatesfr/xen-orchestra/pull/4221))
- Prevent non-admin users to access admin pages with URL (PR [#4220](https://github.com/vatesfr/xen-orchestra/pull/4220))
- [Upgrade] Fix alert before upgrade while running backup jobs [#4164](https://github.com/vatesfr/xen-orchestra/issues/4164) (PR [#4235](https://github.com/vatesfr/xen-orchestra/pull/4235))
- [Import] Fix import OVA files (PR [#4232](https://github.com/vatesfr/xen-orchestra/pull/4232))
- [VM/network] Fix duplicate IPv4 (PR [#4239](https://github.com/vatesfr/xen-orchestra/pull/4239))
- [Remotes] Fix disconnected remotes which may appear to work
- [Host] Fix incorrect hypervisor name [#4246](https://github.com/vatesfr/xen-orchestra/issues/4246) (PR [#4248](https://github.com/vatesfr/xen-orchestra/pull/4248))
### Released packages
- xo-server-backup-reports v0.16.1
- @xen-orchestra/fs v0.9.0
- vhd-lib v0.7.0
- xo-server v5.42.1
- xo-web v5.42.1
- xo-server v5.41.0
- xo-web v5.41.0
## **5.34.0** (2019-04-30)
![Channel: stable](https://badgen.net/badge/channel/stable/green)
### Highlights
- [Self/New VM] Add network config box to custom cloud-init [#3872](https://github.com/vatesfr/xen-orchestra/issues/3872) (PR [#4150](https://github.com/vatesfr/xen-orchestra/pull/4150))

View File

@@ -2,31 +2,24 @@
### Enhancements
- [Backup-ng/restore] Display size for full VM backup [#4009](https://github.com/vatesfr/xen-orchestra/issues/4009) (PR [#4245](https://github.com/vatesfr/xen-orchestra/pull/4245))
- [Sr/new] Ability to select NFS version when creating NFS storage [#3951](https://github.com/vatesfr/xen-orchestra/issues/3951) (PR [#4277](https://github.com/vatesfr/xen-orchestra/pull/4277))
- [auth-saml] Improve compatibility with Microsoft Azure Active Directory (PR [#4294](https://github.com/vatesfr/xen-orchestra/pull/4294))
- [Host] Display warning when "Citrix Hypervisor" license has restrictions [#4251](https://github.com/vatesfr/xen-orchestra/issues/4164) (PR [#4235](https://github.com/vatesfr/xen-orchestra/pull/4279))
- [VM/Backup] Create backup bulk action [#2573](https://github.com/vatesfr/xen-orchestra/issues/2573) (PR [#4257](https://github.com/vatesfr/xen-orchestra/pull/4257))
- [Sr/new] Ability to select NFS version when creating NFS storage [#3951](https://github.com/vatesfr/xen-orchestra/issues/#3951) (PR [#4277](https://github.com/vatesfr/xen-orchestra/pull/4277))
- [SR/new] Create ZFS storage [#4260](https://github.com/vatesfr/xen-orchestra/issues/4260) (PR [#4266](https://github.com/vatesfr/xen-orchestra/pull/4266))
- [Host] Display warning when host's time differs too much from XOA's time [#4113](https://github.com/vatesfr/xen-orchestra/issues/4113) (PR [#4173](https://github.com/vatesfr/xen-orchestra/pull/4173))
- [Host/storages, SR/hosts] Display PBD details [#4264](https://github.com/vatesfr/xen-orchestra/issues/4161) (PR [#4268](https://github.com/vatesfr/xen-orchestra/pull/4284))
- [VM/network] Display and set bandwidth rate-limit of a VIF [#4215](https://github.com/vatesfr/xen-orchestra/issues/4215) (PR [#4293](https://github.com/vatesfr/xen-orchestra/pull/4293))
- [SDN Controller] New plugin which enables creating pool-wide private networks [xcp-ng/xcp#175](https://github.com/xcp-ng/xcp/issues/175) (PR [#4269](https://github.com/vatesfr/xen-orchestra/pull/4269))
- [VM/general] Display 'Started... ago' instead of 'Halted... ago' for paused state [#3750](https://github.com/vatesfr/xen-orchestra/issues/3750) (PR [#4170](https://github.com/vatesfr/xen-orchestra/pull/4170))
- [Metadata backup] Ability to define when the backup report will be sent (PR [#4149](https://github.com/vatesfr/xen-orchestra/pull/4149))
- [XOA/Update] Ability to select release channel [#4200](https://github.com/vatesfr/xen-orchestra/issues/4200) (PR [#4202](https://github.com/vatesfr/xen-orchestra/pull/4202))
- [User] Forget connection tokens on password change or on demand [#4214](https://github.com/vatesfr/xen-orchestra/issues/4214) (PR [#4224](https://github.com/vatesfr/xen-orchestra/pull/4224))
- [Settings/Logs] LICENCE_RESTRICTION errors: suggest XCP-ng as an Open Source alternative [#3876](https://github.com/vatesfr/xen-orchestra/issues/3876) (PR [#4238](https://github.com/vatesfr/xen-orchestra/pull/4238))
- [VM/Migrate] Display VDI size on migrate modal [#2534](https://github.com/vatesfr/xen-orchestra/issues/2534) (PR [#4250](https://github.com/vatesfr/xen-orchestra/pull/4250))
### Bug fixes
- [Metadata backup] Missing XAPIs should trigger a failure job [#4281](https://github.com/vatesfr/xen-orchestra/issues/4281) (PR [#4283](https://github.com/vatesfr/xen-orchestra/pull/4283))
- [Host/advanced] Fix host CPU hyperthreading detection [#4262](https://github.com/vatesfr/xen-orchestra/issues/4262) (PR [#4285](https://github.com/vatesfr/xen-orchestra/pull/4285))
- [iSCSI] Fix fibre channel paths display [#4291](https://github.com/vatesfr/xen-orchestra/issues/4291) (PR [#4303](https://github.com/vatesfr/xen-orchestra/pull/4303))
- [New VM] Fix tooltips not displayed on disabled elements in some browsers (e.g. Google Chrome) [#4304](https://github.com/vatesfr/xen-orchestra/issues/4304) (PR [#4309](https://github.com/vatesfr/xen-orchestra/pull/4309))
- [Charts] Fixed the chart lines sometimes changing order/color (PR [#4221](https://github.com/vatesfr/xen-orchestra/pull/4221))
- Prevent non-admin users to access admin pages with URL
- [Upgrade] Fix alert before upgrade while running backup jobs (PR [#4235](https://github.com/vatesfr/xen-orchestra/pull/4235))
- [Import] Fix import OVA files (PR [#4232](https://github.com/vatesfr/xen-orchestra/pull/4232))
- [VM/network] Fix duplicate IPv4 (PR [#4239](https://github.com/vatesfr/xen-orchestra/pull/4239))
- [Remotes] Fix disconnected remotes which may appear to work
- [Host] Fix incorrect hypervisor name [#4246](https://github.com/vatesfr/xen-orchestra/issues/4246) (PR [#4248](https://github.com/vatesfr/xen-orchestra/pull/4248))
### Released packages
- xo-server-auth-ldap v0.6.5
- xen-api v0.26.0
- xo-server-sdn-controller v0.1
- xo-server-auth-saml v0.6.0
- xo-server-backup-reports v0.16.2
- xo-server v5.44.0
- xo-web v5.44.0
- xo-server v5.42.0
- xo-web v5.42.0

View File

@@ -17,7 +17,7 @@
"eslint-plugin-react": "^7.6.1",
"eslint-plugin-standard": "^4.0.0",
"exec-promise": "^0.7.0",
"flow-bin": "^0.100.0",
"flow-bin": "^0.98.0",
"globby": "^9.0.0",
"husky": "^2.2.0",
"jest": "^24.1.0",

View File

@@ -40,7 +40,7 @@
"cross-env": "^5.1.3",
"execa": "^1.0.0",
"fs-promise": "^2.0.0",
"get-stream": "^5.1.0",
"get-stream": "^4.0.0",
"index-modules": "^0.3.0",
"readable-stream": "^3.0.6",
"rimraf": "^2.6.2",

View File

@@ -41,7 +41,7 @@
"human-format": "^0.10.0",
"lodash": "^4.17.4",
"pw": "^0.0.4",
"xen-api": "^0.26.0"
"xen-api": "^0.25.1"
},
"devDependencies": {
"@babel/cli": "^7.1.5",

View File

@@ -82,7 +82,7 @@ console.log(xapi.pool.$master.$resident_VMs[0].name_label)
A CLI is provided to help exploration and discovery of the XAPI.
```
> xen-api xen1.company.net root
> xen-api https://xen1.company.net root
Password: ******
root@xen1.company.net> xapi.status
'connected'
@@ -92,14 +92,6 @@ root@xen1.company.net> xapi.pool.$master.name_label
'xen1'
```
You can optionally prefix the address by a protocol: `https://` (default) or `http://`.
In case of error due to invalid or self-signed certificates you can use the `--allow-unauthorized` flag (or `--au`):
```
> xen-api --au xen1.company.net root
```
To ease searches, `find()` and `findAll()` functions are available:
```

View File

@@ -1,6 +1,6 @@
{
"name": "xen-api",
"version": "0.26.0",
"version": "0.25.1",
"license": "ISC",
"description": "Connector to the Xen API",
"keywords": [

View File

@@ -168,6 +168,22 @@ export class Xapi extends EventEmitter {
try {
await this._sessionOpen()
// Uses introspection to list available types.
const types = (this._types = (await this._interruptOnDisconnect(
this._call('system.listMethods')
))
.filter(isGetAllRecordsMethod)
.map(method => method.slice(0, method.indexOf('.'))))
this._lcToTypes = { __proto__: null }
types.forEach(type => {
const lcType = type.toLowerCase()
if (lcType !== type) {
this._lcToTypes[lcType] = type
}
})
this._pool = (await this.getAllRecords('pool'))[0]
debug('%s: connected', this._humanId)
this._status = CONNECTED
this._resolveConnected()
@@ -723,28 +739,6 @@ export class Xapi extends EventEmitter {
},
}
)
const oldPoolRef = this._pool?.$ref
this._pool = (await this.getAllRecords('pool'))[0]
// 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
if (this._pool.$ref !== oldPoolRef) {
// Uses introspection to list available types.
const types = (this._types = (await this._interruptOnDisconnect(
this._call('system.listMethods')
))
.filter(isGetAllRecordsMethod)
.map(method => method.slice(0, method.indexOf('.'))))
this._lcToTypes = { __proto__: null }
types.forEach(type => {
const lcType = type.toLowerCase()
if (lcType !== type) {
this._lcToTypes[lcType] = type
}
})
}
}
_setUrl(url) {
@@ -942,12 +936,9 @@ export class Xapi extends EventEmitter {
let result
try {
// don't use _sessionCall because a session failure should break the
// loop and trigger a complete refetch
result = await this._call(
result = await this._sessionCall(
'event.from',
[
this._sessionId,
types,
fromToken,
EVENT_TIMEOUT + 0.1, // must be float for XML-RPC transport
@@ -955,8 +946,7 @@ export class Xapi extends EventEmitter {
EVENT_TIMEOUT * 1e3 * 1.1
)
} catch (error) {
const code = error?.code
if (code === 'EVENTS_LOST' || code === 'SESSION_INVALID') {
if (error?.code === 'EVENTS_LOST') {
// eslint-disable-next-line no-labels
continue mainLoop
}
@@ -1069,14 +1059,9 @@ export class Xapi extends EventEmitter {
}
}
props[`add_${field}`] = function(value) {
props[`add_to_${field}`] = function(...values) {
return xapi
.call(`${type}.add_${field}`, this.$ref, value)
.then(noop)
}
props[`remove_${field}`] = function(value) {
return xapi
.call(`${type}.remove_${field}`, this.$ref, value)
.call(`${type}.add_${field}`, this.$ref, values)
.then(noop)
}
} else if (value !== null && typeof value === 'object') {

View File

@@ -1,6 +1,6 @@
{
"name": "xo-server-auth-ldap",
"version": "0.6.5",
"version": "0.6.4",
"license": "AGPL-3.0",
"description": "LDAP authentication plugin for XO-Server",
"keywords": [

View File

@@ -234,7 +234,6 @@ class AuthLdap {
entry.objectName
} => ${username} authenticated`
)
logger(JSON.stringify(entry, null, 2))
return { username }
} catch (error) {
logger(`failed to bind as ${entry.objectName}: ${error.message}`)

View File

@@ -1,6 +1,6 @@
{
"name": "xo-server-auth-saml",
"version": "0.6.0",
"version": "0.5.3",
"license": "AGPL-3.0",
"description": "SAML authentication plugin for XO-Server",
"keywords": [
@@ -33,7 +33,7 @@
"node": ">=6"
},
"dependencies": {
"passport-saml": "^1.1.0"
"passport-saml": "^1.0.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0",

View File

@@ -24,10 +24,7 @@ export const configurationSchema = {
},
usernameField: {
title: 'Username field',
description: `Field to use as the XO username
You should try \`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\` if you are using Microsoft Azure Active Directory.
`,
description: 'Field to use as the XO username',
type: 'string',
},
},

View File

@@ -142,14 +142,12 @@ const getErrorMarkdown = task => {
const MARKDOWN_BY_TYPE = {
pool(task, { formatDate }) {
const { id, pool = {}, poolMaster = {} } = task.data
const { pool, poolMaster = {} } = task.data
const name = pool.name_label || poolMaster.name_label || UNKNOWN_ITEM
return {
body: [
pool.uuid !== undefined
? `- **UUID**: ${pool.uuid}`
: `- **ID**: ${id}`,
`- **UUID**: ${pool.uuid}`,
...getTemporalDataMarkdown(task.end, task.start, formatDate),
getErrorMarkdown(task),
],

View File

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

View File

@@ -1,43 +0,0 @@
# xo-server-sdn-controller [![Build Status](https://travis-ci.org/vatesfr/xen-orchestra.png?branch=master)](https://travis-ci.org/vatesfr/xen-orchestra)
XO Server plugin that allows the creation of pool-wide private networks.
## Install
For installing XO and the plugins from the sources, please take a look at [the documentation](https://xen-orchestra.com/docs/from_the_sources.html).
## Usage
### Network creation
In the network creation view, select a `pool` and `Private network`.
Create the network.
Choice is offer between `GRE` and `VxLAN`, if `VxLAN` is chosen, then the port 4789 must be open for UDP traffic.
The following line needs to be added, if not already present, in `/etc/sysconfig/iptables` of all the hosts where `VxLAN` is wanted:
`-A xapi-INPUT -p udp -m conntrack --ctstate NEW -m udp --dport 4789 -j ACCEPT`
### Configuration
Like all other xo-server plugins, it can be configured directly via
the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).
The plugin's configuration contains:
- `cert-dir`: A path where to find the certificates to create SSL connections with the hosts.
If none is provided, the plugin will create its own self-signed certificates.
- `override-certs:` Whether or not to uninstall an already existing SDN controller CA certificate in order to replace it by the plugin's one.
## Contributions
Contributions are *very* welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
you've encountered;
- fork and create a pull request.
## License
AGPL3 © [Vates SAS](http://vates.fr)

View File

@@ -1,35 +0,0 @@
{
"name": "xo-server-sdn-controller",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-sdn-controller",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "packages/xo-server-sdn-controller",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"main": "./dist",
"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"
},
"version": "0.1.0",
"engines": {
"node": ">=6"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.4",
"@babel/preset-env": "^7.4.4",
"cross-env": "^5.2.0"
},
"dependencies": {
"@xen-orchestra/log": "^0.1.4",
"lodash": "^4.17.11",
"node-openssl-cert": "^0.0.81",
"promise-toolbox": "^0.13.0"
},
"private": true
}

View File

@@ -1,830 +0,0 @@
import assert from 'assert'
import createLogger from '@xen-orchestra/log'
import NodeOpenssl from 'node-openssl-cert'
import { access, constants, readFile, writeFile } from 'fs'
import { EventEmitter } from 'events'
import { filter, find, forOwn, map } from 'lodash'
import { fromCallback, fromEvent } from 'promise-toolbox'
import { join } from 'path'
import { OvsdbClient } from './ovsdb-client'
// =============================================================================
const log = createLogger('xo:xo-server:sdn-controller')
const PROTOCOL = 'pssl'
const CA_CERT = 'ca-cert.pem'
const CLIENT_KEY = 'client-key.pem'
const CLIENT_CERT = 'client-cert.pem'
const SDN_CONTROLLER_CERT = 'sdn-controller-ca.pem'
const NB_DAYS = 9999
// =============================================================================
export const configurationSchema = {
type: 'object',
properties: {
'cert-dir': {
description: `Full path to a directory where to find: \`client-cert.pem\`,
\`client-key.pem\` and \`ca-cert.pem\` to create ssl connections with hosts.
If none is provided, the plugin will create its own self-signed certificates.`,
type: 'string',
},
'override-certs': {
description: `Replace already existing SDN controller CA certificate`,
type: 'boolean',
default: false,
},
},
}
// =============================================================================
async function fileWrite(path, data) {
await fromCallback(writeFile, path, data)
log.debug(`${path} successfully written`)
}
async function fileRead(path) {
const result = await fromCallback(readFile, path)
return result
}
async function fileExists(path) {
try {
await fromCallback(access, path, constants.F_OK)
} catch (error) {
if (error.code === 'ENOENT') {
return false
}
throw error
}
return true
}
// =============================================================================
class SDNController extends EventEmitter {
constructor({ xo, getDataDir }) {
super()
this._xo = xo
this._getDataDir = getDataDir
this._clientKey = null
this._clientCert = null
this._caCert = null
this._poolNetworks = []
this._ovsdbClients = []
this._newHosts = []
this._networks = new Map()
this._starCenters = new Map()
this._cleaners = []
this._objectsAdded = this._objectsAdded.bind(this)
this._objectsUpdated = this._objectsUpdated.bind(this)
this._overrideCerts = false
this._unsetApiMethod = null
}
// ---------------------------------------------------------------------------
async configure(configuration) {
this._overrideCerts = configuration['override-certs']
let certDirectory = configuration['cert-dir']
if (certDirectory == null) {
log.debug(`No cert-dir provided, using default self-signed certificates`)
certDirectory = await this._getDataDir()
if (!(await fileExists(join(certDirectory, CA_CERT)))) {
// If one certificate doesn't exist, none should
assert(
!(await fileExists(join(certDirectory, CLIENT_KEY))),
`${CLIENT_KEY} should not exist`
)
assert(
!(await fileExists(join(certDirectory, CLIENT_CERT))),
`${CLIENT_CERT} should not exist`
)
log.debug(`No default self-signed certificates exists, creating them`)
await this._generateCertificatesAndKey(certDirectory)
}
}
// TODO: verify certificates and create new certificates if needed
;[this._clientKey, this._clientCert, this._caCert] = await Promise.all([
fileRead(join(certDirectory, CLIENT_KEY)),
fileRead(join(certDirectory, CLIENT_CERT)),
fileRead(join(certDirectory, CA_CERT)),
])
this._ovsdbClients.forEach(client => {
client.updateCertificates(this._clientKey, this._clientCert, this._caCert)
})
const updatedPools = []
for (let i = 0; i < this._poolNetworks.length; ++i) {
const poolNetwork = this._poolNetworks[i]
if (updatedPools.includes(poolNetwork.pool)) {
continue
}
const xapi = this._xo.getXapi(poolNetwork.pool)
await this._installCaCertificateIfNeeded(xapi)
updatedPools.push(poolNetwork.pool)
}
}
async load() {
const createPrivateNetwork = this._createPrivateNetwork.bind(this)
createPrivateNetwork.description =
'Creates a pool-wide private network on a selected pool'
createPrivateNetwork.params = {
poolId: { type: 'string' },
networkName: { type: 'string' },
networkDescription: { type: 'string' },
encapsulation: { type: 'string' },
}
createPrivateNetwork.resolve = {
xoPool: ['poolId', 'pool', ''],
}
this._unsetApiMethod = this._xo.addApiMethod(
'plugin.SDNController.createPrivateNetwork',
createPrivateNetwork
)
// FIXME: we should monitor when xapis are added/removed
forOwn(this._xo.getAllXapis(), async xapi => {
await xapi.objectsFetched
if (this._setControllerNeeded(xapi) === false) {
this._cleaners.push(await this._manageXapi(xapi))
const hosts = filter(xapi.objects.all, { $type: 'host' })
await Promise.all(
map(hosts, async host => {
this._createOvsdbClient(host)
})
)
// Add already existing pool-wide private networks
const networks = filter(xapi.objects.all, { $type: 'network' })
forOwn(networks, async network => {
if (network.other_config.private_pool_wide === 'true') {
log.debug(
`Adding network: '${network.name_label}' for pool: '${
network.$pool.name_label
}' to managed networks`
)
const center = await this._electNewCenter(network, true)
this._poolNetworks.push({
pool: network.$pool.$ref,
network: network.$ref,
starCenter: center ? center.$ref : null,
})
this._networks.set(network.$id, network.$ref)
if (center != null) {
this._starCenters.set(center.$id, center.$ref)
}
}
})
}
})
}
async unload() {
this._ovsdbClients = []
this._poolNetworks = []
this._newHosts = []
this._networks.clear()
this._starCenters.clear()
this._cleaners.forEach(cleaner => cleaner())
this._cleaners = []
this._unsetApiMethod()
}
// ===========================================================================
async _createPrivateNetwork({
xoPool,
networkName,
networkDescription,
encapsulation,
}) {
const pool = this._xo.getXapiObject(xoPool)
await this._setPoolControllerIfNeeded(pool)
// Create the private network
const privateNetworkRef = await pool.$xapi.call('network.create', {
name_label: networkName,
name_description: networkDescription,
MTU: 0,
other_config: {
automatic: 'false',
private_pool_wide: 'true',
encapsulation: encapsulation,
},
})
const privateNetwork = await pool.$xapi._getOrWaitObject(privateNetworkRef)
log.info(
`Private network '${
privateNetwork.name_label
}' has been created for pool '${pool.name_label}'`
)
// For each pool's host, create a tunnel to the private network
const hosts = filter(pool.$xapi.objects.all, { $type: 'host' })
await Promise.all(
map(hosts, async host => {
await this._createTunnel(host, privateNetwork)
this._createOvsdbClient(host)
})
)
const center = await this._electNewCenter(privateNetwork, false)
this._poolNetworks.push({
pool: pool.$ref,
network: privateNetwork.$ref,
starCenter: center ? center.$ref : null,
encapsulation: encapsulation,
})
this._networks.set(privateNetwork.$id, privateNetwork.$ref)
if (center != null) {
this._starCenters.set(center.$id, center.$ref)
}
}
// ---------------------------------------------------------------------------
async _manageXapi(xapi) {
const { objects } = xapi
const objectsRemovedXapi = this._objectsRemoved.bind(this, xapi)
objects.on('add', this._objectsAdded)
objects.on('update', this._objectsUpdated)
objects.on('remove', objectsRemovedXapi)
await this._installCaCertificateIfNeeded(xapi)
return () => {
objects.removeListener('add', this._objectsAdded)
objects.removeListener('update', this._objectsUpdated)
objects.removeListener('remove', objectsRemovedXapi)
}
}
async _objectsAdded(objects) {
await Promise.all(
map(objects, async object => {
const { $type } = object
if ($type === 'host') {
log.debug(
`New host: '${object.name_label}' in pool: '${
object.$pool.name_label
}'`
)
if (find(this._newHosts, { $ref: object.$ref }) == null) {
this._newHosts.push(object)
}
this._createOvsdbClient(object)
}
})
)
}
async _objectsUpdated(objects) {
await Promise.all(
map(objects, async (object, id) => {
const { $type } = object
if ($type === 'PIF') {
await this._pifUpdated(object)
} else if ($type === 'host') {
await this._hostUpdated(object)
}
})
)
}
async _objectsRemoved(xapi, objects) {
await Promise.all(
map(objects, async (object, id) => {
const client = find(this._ovsdbClients, { id: id })
if (client != null) {
this._ovsdbClients.splice(this._ovsdbClients.indexOf(client), 1)
}
// If a Star center host is removed: re-elect a new center where needed
const starCenterRef = this._starCenters.get(id)
if (starCenterRef != null) {
this._starCenters.delete(id)
const poolNetworks = filter(this._poolNetworks, {
starCenter: starCenterRef,
})
for (let i = 0; i < poolNetworks.length; ++i) {
const poolNetwork = poolNetworks[i]
const network = await xapi._getOrWaitObject(poolNetwork.network)
const newCenter = await this._electNewCenter(network, true)
poolNetwork.starCenter = newCenter ? newCenter.$ref : null
if (newCenter != null) {
this._starCenters.set(newCenter.$id, newCenter.$ref)
}
}
return
}
// If a network is removed, clean this._poolNetworks from it
const networkRef = this._networks.get(id)
if (networkRef != null) {
this._networks.delete(id)
const poolNetwork = find(this._poolNetworks, {
network: networkRef,
})
if (poolNetwork != null) {
this._poolNetworks.splice(
this._poolNetworks.indexOf(poolNetwork),
1
)
}
}
})
)
}
async _pifUpdated(pif) {
// Only if PIF is in a private network
const poolNetwork = find(this._poolNetworks, { network: pif.network })
if (poolNetwork == null) {
return
}
if (!pif.currently_attached) {
if (poolNetwork.starCenter !== pif.host) {
return
}
log.debug(
`PIF: '${pif.device}' of network: '${
pif.$network.name_label
}' star-center host: '${
pif.$host.name_label
}' has been unplugged, electing a new host`
)
const newCenter = await this._electNewCenter(pif.$network, true)
poolNetwork.starCenter = newCenter ? newCenter.$ref : null
this._starCenters.delete(pif.$host.$id)
if (newCenter != null) {
this._starCenters.set(newCenter.$id, newCenter.$ref)
}
} else {
if (poolNetwork.starCenter == null) {
const host = pif.$host
log.debug(
`First available host: '${
host.name_label
}' becomes star center of network: '${pif.$network.name_label}'`
)
poolNetwork.starCenter = pif.host
this._starCenters.set(host.$id, host.$ref)
}
log.debug(
`PIF: '${pif.device}' of network: '${pif.$network.name_label}' host: '${
pif.$host.name_label
}' has been plugged`
)
const starCenter = await pif.$xapi._getOrWaitObject(
poolNetwork.starCenter
)
await this._addHostToNetwork(pif.$host, pif.$network, starCenter)
}
}
async _hostUpdated(host) {
const xapi = host.$xapi
if (host.enabled) {
if (host.PIFs.length === 0) {
return
}
const tunnels = filter(xapi.objects.all, { $type: 'tunnel' })
const newHost = find(this._newHosts, { $ref: host.$ref })
if (newHost != null) {
this._newHosts.splice(this._newHosts.indexOf(newHost), 1)
try {
await xapi.call('pool.certificate_sync')
} catch (error) {
log.error(
`Couldn't sync SDN controller ca certificate in pool: '${
host.$pool.name_label
}' because: ${error}`
)
}
}
for (let i = 0; i < tunnels.length; ++i) {
const tunnel = tunnels[i]
const accessPIF = await xapi._getOrWaitObject(tunnel.access_PIF)
if (accessPIF.host !== host.$ref) {
continue
}
const poolNetwork = find(this._poolNetworks, {
network: accessPIF.network,
})
if (poolNetwork == null) {
continue
}
if (accessPIF.currently_attached) {
continue
}
log.debug(
`Pluging PIF: '${accessPIF.device}' for host: '${
host.name_label
}' on network: '${accessPIF.$network.name_label}'`
)
try {
await xapi.call('PIF.plug', accessPIF.$ref)
} catch (error) {
log.error(
`XAPI error while pluging PIF: '${accessPIF.device}' on host: '${
host.name_label
}' for network: '${accessPIF.$network.name_label}'`
)
}
const starCenter = await host.$xapi._getOrWaitObject(
poolNetwork.starCenter
)
await this._addHostToNetwork(host, accessPIF.$network, starCenter)
}
} else {
const poolNetworks = filter(this._poolNetworks, { starCenter: host.$ref })
for (let i = 0; i < poolNetworks.length; ++i) {
const poolNetwork = poolNetworks[i]
const network = await host.$xapi._getOrWaitObject(poolNetwork.network)
log.debug(
`Star center host: '${host.name_label}' of network: '${
network.name_label
}' in pool: '${
host.$pool.name_label
}' is no longer reachable, electing a new host`
)
const newCenter = await this._electNewCenter(network, true)
poolNetwork.starCenter = newCenter ? newCenter.$ref : null
this._starCenters.delete(host.$id)
if (newCenter != null) {
this._starCenters.set(newCenter.$id, newCenter.$ref)
}
}
}
}
// ---------------------------------------------------------------------------
async _setPoolControllerIfNeeded(pool) {
if (!this._setControllerNeeded(pool.$xapi)) {
// Nothing to do
return
}
const controller = find(pool.$xapi.objects.all, { $type: 'SDN_controller' })
if (controller != null) {
await pool.$xapi.call('SDN_controller.forget', controller.$ref)
log.debug(`Remove old SDN controller from pool: '${pool.name_label}'`)
}
await pool.$xapi.call('SDN_controller.introduce', PROTOCOL)
log.debug(`Set SDN controller of pool: '${pool.name_label}'`)
this._cleaners.push(await this._manageXapi(pool.$xapi))
}
_setControllerNeeded(xapi) {
const controller = find(xapi.objects.all, { $type: 'SDN_controller' })
return !(
controller != null &&
controller.protocol === PROTOCOL &&
controller.address === '' &&
controller.port === 0
)
}
// ---------------------------------------------------------------------------
async _installCaCertificateIfNeeded(xapi) {
let needInstall = false
try {
const result = await xapi.call('pool.certificate_list')
if (!result.includes(SDN_CONTROLLER_CERT)) {
needInstall = true
} else if (this._overrideCerts) {
await xapi.call('pool.certificate_uninstall', SDN_CONTROLLER_CERT)
log.debug(
`Old SDN Controller CA certificate uninstalled on pool: '${
xapi.pool.name_label
}'`
)
needInstall = true
}
} catch (error) {
log.error(
`Couldn't retrieve certificate list of pool: '${xapi.pool.name_label}'`
)
}
if (!needInstall) {
return
}
try {
await xapi.call(
'pool.certificate_install',
SDN_CONTROLLER_CERT,
this._caCert.toString()
)
await xapi.call('pool.certificate_sync')
log.debug(
`SDN controller CA certificate install in pool: '${
xapi.pool.name_label
}'`
)
} catch (error) {
log.error(
`Couldn't install SDN controller CA certificate in pool: '${
xapi.pool.name_label
}' because: ${error}`
)
}
}
// ---------------------------------------------------------------------------
async _electNewCenter(network, resetNeeded) {
const pool = network.$pool
let newCenter = null
const hosts = filter(pool.$xapi.objects.all, { $type: 'host' })
await Promise.all(
map(hosts, async host => {
if (resetNeeded) {
// Clean old ports and interfaces
const hostClient = find(this._ovsdbClients, { host: host.$ref })
if (hostClient != null) {
try {
await hostClient.resetForNetwork(network.uuid, network.name_label)
} catch (error) {
log.error(
`Couldn't reset network: '${network.name_label}' for host: '${
host.name_label
}' in pool: '${network.$pool.name_label}' because: ${error}`
)
return
}
}
}
if (newCenter != null) {
return
}
const pif = find(host.$PIFs, { network: network.$ref })
if (pif != null && pif.currently_attached && host.enabled) {
newCenter = host
}
})
)
if (newCenter == null) {
log.error(
`Unable to elect a new star-center host to network: '${
network.name_label
}' for pool: '${
network.$pool.name_label
}' because there's no available host`
)
return null
}
// Recreate star topology
await Promise.all(
await map(hosts, async host => {
await this._addHostToNetwork(host, network, newCenter)
})
)
log.info(
`New star center host elected: '${newCenter.name_label}' in network: '${
network.name_label
}'`
)
return newCenter
}
async _createTunnel(host, network) {
const pif = find(host.$PIFs, { physical: true })
if (pif == null) {
log.error(
`No PIF found to create tunnel on host: '${
host.name_label
}' for network: '${network.name_label}'`
)
return
}
await host.$xapi.call('tunnel.create', pif.$ref, network.$ref)
log.debug(
`Tunnel added on host '${host.name_label}' for network '${
network.name_label
}'`
)
}
async _addHostToNetwork(host, network, starCenter) {
if (host.$ref === starCenter.$ref) {
// Nothing to do
return
}
const hostClient = find(this._ovsdbClients, {
host: host.$ref,
})
if (hostClient == null) {
log.error(`No OVSDB client found for host: '${host.name_label}'`)
return
}
const starCenterClient = find(this._ovsdbClients, {
host: starCenter.$ref,
})
if (starCenterClient == null) {
log.error(
`No OVSDB client found for star-center host: '${starCenter.name_label}'`
)
return
}
const encapsulation =
network.other_config.encapsulation != null
? network.other_config.encapsulation
: 'gre'
try {
await hostClient.addInterfaceAndPort(
network.uuid,
network.name_label,
starCenterClient.address,
encapsulation
)
await starCenterClient.addInterfaceAndPort(
network.uuid,
network.name_label,
hostClient.address,
encapsulation
)
} catch (error) {
log.error(
`Couldn't add host: '${host.name_label}' to network: '${
network.name_label
}' in pool: '${host.$pool.name_label}' because: ${error}`
)
}
}
// ---------------------------------------------------------------------------
_createOvsdbClient(host) {
const foundClient = find(this._ovsdbClients, { host: host.$ref })
if (foundClient != null) {
return foundClient
}
const client = new OvsdbClient(
host,
this._clientKey,
this._clientCert,
this._caCert
)
this._ovsdbClients.push(client)
return client
}
// ---------------------------------------------------------------------------
async _generateCertificatesAndKey(dataDir) {
const openssl = new NodeOpenssl()
const rsakeyoptions = {
rsa_keygen_bits: 4096,
format: 'PKCS8',
}
const subject = {
countryName: 'XX',
localityName: 'Default City',
organizationName: 'Default Company LTD',
}
const csroptions = {
hash: 'sha256',
startdate: new Date('1984-02-04 00:00:00'),
enddate: new Date('2143-06-04 04:16:23'),
subject: subject,
}
const cacsroptions = {
hash: 'sha256',
days: NB_DAYS,
subject: subject,
}
openssl.generateRSAPrivateKey(rsakeyoptions, (err, cakey, cmd) => {
if (err) {
log.error(`Error while generating CA private key: ${err}`)
return
}
openssl.generateCSR(cacsroptions, cakey, null, (err, csr, cmd) => {
if (err) {
log.error(`Error while generating CA certificate: ${err}`)
return
}
openssl.selfSignCSR(
csr,
cacsroptions,
cakey,
null,
async (err, cacrt, cmd) => {
if (err) {
log.error(`Error while signing CA certificate: ${err}`)
return
}
await fileWrite(join(dataDir, CA_CERT), cacrt)
openssl.generateRSAPrivateKey(
rsakeyoptions,
async (err, key, cmd) => {
if (err) {
log.error(`Error while generating private key: ${err}`)
return
}
await fileWrite(join(dataDir, CLIENT_KEY), key)
openssl.generateCSR(csroptions, key, null, (err, csr, cmd) => {
if (err) {
log.error(`Error while generating certificate: ${err}`)
return
}
openssl.CASignCSR(
csr,
cacsroptions,
false,
cacrt,
cakey,
null,
async (err, crt, cmd) => {
if (err) {
log.error(`Error while signing certificate: ${err}`)
return
}
await fileWrite(join(dataDir, CLIENT_CERT), crt)
this.emit('certWritten')
}
)
})
}
)
}
)
})
})
await fromEvent(this, 'certWritten', {})
log.debug('All certificates have been successfully written')
}
}
export default opts => new SDNController(opts)

View File

@@ -1,511 +0,0 @@
import assert from 'assert'
import createLogger from '@xen-orchestra/log'
import forOwn from 'lodash/forOwn'
import fromEvent from 'promise-toolbox/fromEvent'
import { connect } from 'tls'
const log = createLogger('xo:xo-server:sdn-controller:ovsdb-client')
const OVSDB_PORT = 6640
// =============================================================================
export class OvsdbClient {
constructor(host, clientKey, clientCert, caCert) {
this._host = host
this._numberOfPortAndInterface = 0
this._requestID = 0
this.updateCertificates(clientKey, clientCert, caCert)
log.debug(`[${this._host.name_label}] New OVSDB client`)
}
// ---------------------------------------------------------------------------
get address() {
return this._host.address
}
get host() {
return this._host.$ref
}
get id() {
return this._host.$id
}
updateCertificates(clientKey, clientCert, caCert) {
this._clientKey = clientKey
this._clientCert = clientCert
this._caCert = caCert
log.debug(`[${this._host.name_label}] Certificates have been updated`)
}
// ---------------------------------------------------------------------------
async addInterfaceAndPort(
networkUuid,
networkName,
remoteAddress,
encapsulation
) {
const socket = await this._connect()
const index = this._numberOfPortAndInterface
++this._numberOfPortAndInterface
const [bridgeUuid, bridgeName] = await this._getBridgeUuidForNetwork(
networkUuid,
networkName,
socket
)
if (bridgeUuid == null) {
socket.destroy()
return
}
const alreadyExist = await this._interfaceAndPortAlreadyExist(
bridgeUuid,
bridgeName,
remoteAddress,
socket
)
if (alreadyExist) {
socket.destroy()
return
}
const interfaceName = 'tunnel_iface' + index
const portName = 'tunnel_port' + index
// Add interface and port to the bridge
const options = ['map', [['remote_ip', remoteAddress]]]
const addInterfaceOperation = {
op: 'insert',
table: 'Interface',
row: {
type: encapsulation,
options: options,
name: interfaceName,
other_config: ['map', [['private_pool_wide', 'true']]],
},
'uuid-name': 'new_iface',
}
const addPortOperation = {
op: 'insert',
table: 'Port',
row: {
name: portName,
interfaces: ['set', [['named-uuid', 'new_iface']]],
other_config: ['map', [['private_pool_wide', 'true']]],
},
'uuid-name': 'new_port',
}
const mutateBridgeOperation = {
op: 'mutate',
table: 'Bridge',
where: [['_uuid', '==', ['uuid', bridgeUuid]]],
mutations: [['ports', 'insert', ['set', [['named-uuid', 'new_port']]]]],
}
const params = [
'Open_vSwitch',
addInterfaceOperation,
addPortOperation,
mutateBridgeOperation,
]
const jsonObjects = await this._sendOvsdbTransaction(params, socket)
if (jsonObjects == null) {
socket.destroy()
return
}
let error
let details
let i = 0
let opResult
do {
opResult = jsonObjects[0].result[i]
if (opResult != null && opResult.error != null) {
error = opResult.error
details = opResult.details
}
++i
} while (opResult && !error)
if (error != null) {
log.error(
`[${
this._host.name_label
}] Error while adding port: '${portName}' and interface: '${interfaceName}' to bridge: '${bridgeName}' on network: '${networkName}' because: ${error}: ${details}`
)
socket.destroy()
return
}
log.debug(
`[${
this._host.name_label
}] Port: '${portName}' and interface: '${interfaceName}' added to bridge: '${bridgeName}' on network: '${networkName}'`
)
socket.destroy()
}
async resetForNetwork(networkUuid, networkName) {
const socket = await this._connect()
const [bridgeUuid, bridgeName] = await this._getBridgeUuidForNetwork(
networkUuid,
networkName,
socket
)
if (bridgeUuid == null) {
socket.destroy()
return
}
// Delete old ports created by a SDN controller
const ports = await this._getBridgePorts(bridgeUuid, bridgeName, socket)
if (ports == null) {
socket.destroy()
return
}
const portsToDelete = []
for (let i = 0; i < ports.length; ++i) {
const portUuid = ports[i][1]
const where = [['_uuid', '==', ['uuid', portUuid]]]
const selectResult = await this._select(
'Port',
['name', 'other_config'],
where,
socket
)
if (selectResult == null) {
continue
}
forOwn(selectResult.other_config[1], config => {
if (config[0] === 'private_pool_wide' && config[1] === 'true') {
log.debug(
`[${this._host.name_label}] Adding port: '${
selectResult.name
}' to delete list from bridge: '${bridgeName}'`
)
portsToDelete.push(['uuid', portUuid])
}
})
}
if (portsToDelete.length === 0) {
// Nothing to do
socket.destroy()
return
}
const mutateBridgeOperation = {
op: 'mutate',
table: 'Bridge',
where: [['_uuid', '==', ['uuid', bridgeUuid]]],
mutations: [['ports', 'delete', ['set', portsToDelete]]],
}
const params = ['Open_vSwitch', mutateBridgeOperation]
const jsonObjects = await this._sendOvsdbTransaction(params, socket)
if (jsonObjects == null) {
socket.destroy()
return
}
if (jsonObjects[0].error != null) {
log.error(
`[${
this._host.name_label
}] Couldn't delete ports from bridge: '${bridgeName}' because: ${
jsonObjects.error
}`
)
socket.destroy()
return
}
log.debug(
`[${this._host.name_label}] Deleted ${
jsonObjects[0].result[0].count
} ports from bridge: '${bridgeName}'`
)
socket.destroy()
}
// ===========================================================================
_parseJson(chunk) {
let data = chunk.toString()
let buffer = ''
let depth = 0
let pos = 0
const objects = []
for (let i = pos; i < data.length; ++i) {
const c = data.charAt(i)
if (c === '{') {
depth++
} else if (c === '}') {
depth--
if (depth === 0) {
const object = JSON.parse(buffer + data.substr(0, i + 1))
objects.push(object)
buffer = ''
data = data.substr(i + 1)
pos = 0
i = -1
}
}
}
buffer += data
return objects
}
// ---------------------------------------------------------------------------
async _getBridgeUuidForNetwork(networkUuid, networkName, socket) {
const where = [
[
'external_ids',
'includes',
['map', [['xs-network-uuids', networkUuid]]],
],
]
const selectResult = await this._select(
'Bridge',
['_uuid', 'name'],
where,
socket
)
if (selectResult == null) {
return [null, null]
}
const bridgeUuid = selectResult._uuid[1]
const bridgeName = selectResult.name
log.debug(
`[${
this._host.name_label
}] Found bridge: '${bridgeName}' for network: '${networkName}'`
)
return [bridgeUuid, bridgeName]
}
async _interfaceAndPortAlreadyExist(
bridgeUuid,
bridgeName,
remoteAddress,
socket
) {
const ports = await this._getBridgePorts(bridgeUuid, bridgeName, socket)
if (ports == null) {
return
}
for (let i = 0; i < ports.length; ++i) {
const portUuid = ports[i][1]
const interfaces = await this._getPortInterfaces(portUuid, socket)
if (interfaces == null) {
continue
}
let j
for (j = 0; j < interfaces.length; ++j) {
const interfaceUuid = interfaces[j][1]
const hasRemote = await this._interfaceHasRemote(
interfaceUuid,
remoteAddress,
socket
)
if (hasRemote === true) {
return true
}
}
}
return false
}
async _getBridgePorts(bridgeUuid, bridgeName, socket) {
const where = [['_uuid', '==', ['uuid', bridgeUuid]]]
const selectResult = await this._select('Bridge', ['ports'], where, socket)
if (selectResult == null) {
return null
}
return selectResult.ports[0] === 'set'
? selectResult.ports[1]
: [selectResult.ports]
}
async _getPortInterfaces(portUuid, socket) {
const where = [['_uuid', '==', ['uuid', portUuid]]]
const selectResult = await this._select(
'Port',
['name', 'interfaces'],
where,
socket
)
if (selectResult == null) {
return null
}
return selectResult.interfaces[0] === 'set'
? selectResult.interfaces[1]
: [selectResult.interfaces]
}
async _interfaceHasRemote(interfaceUuid, remoteAddress, socket) {
const where = [['_uuid', '==', ['uuid', interfaceUuid]]]
const selectResult = await this._select(
'Interface',
['name', 'options'],
where,
socket
)
if (selectResult == null) {
return false
}
for (let i = 0; i < selectResult.options[1].length; ++i) {
const option = selectResult.options[1][i]
if (option[0] === 'remote_ip' && option[1] === remoteAddress) {
return true
}
}
return false
}
// ---------------------------------------------------------------------------
async _select(table, columns, where, socket) {
const selectOperation = {
op: 'select',
table: table,
columns: columns,
where: where,
}
const params = ['Open_vSwitch', selectOperation]
const jsonObjects = await this._sendOvsdbTransaction(params, socket)
if (jsonObjects == null) {
return
}
const jsonResult = jsonObjects[0].result[0]
if (jsonResult.error != null) {
log.error(
`[${
this._host.name_label
}] Couldn't retrieve: '${columns}' in: '${table}' because: ${
jsonResult.error
}: ${jsonResult.details}`
)
return null
}
if (jsonResult.rows.length === 0) {
log.error(
`[${
this._host.name_label
}] No '${columns}' found in: '${table}' where: '${where}'`
)
return null
}
// For now all select operations should return only 1 row
assert(
jsonResult.rows.length === 1,
`[${
this._host.name_label
}] There should exactly 1 row when searching: '${columns}' in: '${table}' where: '${where}'`
)
return jsonResult.rows[0]
}
async _sendOvsdbTransaction(params, socket) {
const stream = socket
const requestId = this._requestID
++this._requestID
const req = {
id: requestId,
method: 'transact',
params: params,
}
try {
stream.write(JSON.stringify(req))
} catch (error) {
log.error(
`[${this._host.name_label}] Error while writing into stream: ${error}`
)
return null
}
let result
let jsonObjects
let resultRequestId
do {
try {
result = await fromEvent(stream, 'data', {})
} catch (error) {
log.error(
`[${
this._host.name_label
}] Error while waiting for stream data: ${error}`
)
return null
}
jsonObjects = this._parseJson(result)
resultRequestId = jsonObjects[0].id
} while (resultRequestId !== requestId)
return jsonObjects
}
// ---------------------------------------------------------------------------
async _connect() {
const options = {
ca: this._caCert,
key: this._clientKey,
cert: this._clientCert,
host: this._host.address,
port: OVSDB_PORT,
rejectUnauthorized: false,
requestCert: false,
}
const socket = connect(options)
try {
await fromEvent(socket, 'secureConnect', {})
} catch (error) {
log.error(
`[${this._host.name_label}] TLS connection failed because: ${error}: ${
error.code
}`
)
throw error
}
log.debug(`[${this._host.name_label}] TLS connection successful`)
socket.on('error', error => {
log.error(
`[${
this._host.name_label
}] OVSDB client socket error: ${error} with code: ${error.code}`
)
})
return socket
}
}

View File

@@ -49,11 +49,6 @@ maxTokenValidity = '0.5 year'
# Delay for which backups listing on a remote is cached
listingDebounce = '1 min'
# Duration for which we can wait for the backup size before returning
#
# It should be short to avoid blocking the display of the available backups.
vmBackupSizeTimeout = '2 seconds'
# Helmet handles HTTP security via headers
#
# https://helmetjs.github.io/docs/

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "xo-server",
"version": "5.43.0",
"version": "5.42.0",
"license": "AGPL-3.0",
"description": "Server part of Xen-Orchestra",
"keywords": [
@@ -123,7 +123,7 @@
"value-matcher": "^0.2.0",
"vhd-lib": "^0.7.0",
"ws": "^6.0.0",
"xen-api": "^0.26.0",
"xen-api": "^0.25.1",
"xml2js": "^0.4.19",
"xo-acl-resolver": "^0.4.1",
"xo-collection": "^0.4.1",

View File

@@ -183,7 +183,6 @@ getLogs.params = {
after: { type: ['number', 'string'], optional: true },
before: { type: ['number', 'string'], optional: true },
limit: { type: 'number', optional: true },
'*': { type: 'any' },
}
// -----------------------------------------------------------------------------

View File

@@ -4,19 +4,23 @@ import { format, JsonRpcError } from 'json-rpc-peer'
export async function set({
host,
multipathing,
// TODO: use camel case.
name_label: nameLabel,
name_description: nameDescription,
}) {
host = this.getXapiObject(host)
const xapi = this.getXapi(host)
const hostId = host._xapiId
await Promise.all([
nameDescription !== undefined && host.set_name_description(nameDescription),
nameLabel !== undefined && host.set_name_label(nameLabel),
multipathing !== undefined &&
host.$xapi.setHostMultipathing(host.$id, multipathing),
])
if (multipathing !== undefined) {
await xapi.setHostMultipathing(hostId, multipathing)
}
return xapi.setHostProperties(hostId, {
nameLabel,
nameDescription,
})
}
set.description = 'changes the properties of an host'
@@ -211,25 +215,6 @@ emergencyShutdownHost.resolve = {
// -------------------------------------------------------------------
export async function isHostServerTimeConsistent({ host }) {
try {
await this.getXapi(host).assertConsistentHostServerTime(host._xapiRef)
return true
} catch (e) {
return false
}
}
isHostServerTimeConsistent.params = {
host: { type: 'string' },
}
isHostServerTimeConsistent.resolve = {
host: ['host', 'host', 'administrate'],
}
// -------------------------------------------------------------------
export function stats({ host, granularity }) {
return this.getXapiHostStats(host._xapiId, granularity)
}
@@ -284,19 +269,3 @@ installSupplementalPack.params = {
installSupplementalPack.resolve = {
host: ['host', 'host', 'admin'],
}
// -------------------------------------------------------------------
export function isHyperThreadingEnabled({ host }) {
return this.getXapi(host).isHyperThreadingEnabled(host._xapiId)
}
isHyperThreadingEnabled.description = 'get hyper-threading information'
isHyperThreadingEnabled.params = {
id: { type: 'string' },
}
isHyperThreadingEnabled.resolve = {
host: ['id', 'host', 'administrate'],
}

View File

@@ -85,26 +85,18 @@ createBonded.description =
// ===================================================================
export async function set({
network,
automatic,
defaultIsLocked,
name_description: nameDescription,
name_label: nameLabel,
network,
}) {
network = this.getXapiObject(network)
await Promise.all([
automatic !== undefined &&
network.update_other_config('automatic', automatic ? 'true' : null),
defaultIsLocked !== undefined &&
network.set_default_locking_mode(
defaultIsLocked ? 'disabled' : 'unlocked'
),
nameDescription !== undefined &&
network.set_name_description(nameDescription),
nameLabel !== undefined && network.set_name_label(nameLabel),
])
await this.getXapi(network).setNetworkProperties(network._xapiId, {
automatic,
defaultIsLocked,
nameDescription,
nameLabel,
})
}
set.params = {

View File

@@ -1,7 +1,5 @@
// TODO: too low level, move into host.
import { filter, find } from 'lodash'
import { IPV4_CONFIG_MODES, IPV6_CONFIG_MODES } from '../xapi'
export function getIpv4ConfigurationModes() {
@@ -17,17 +15,7 @@ export function getIpv6ConfigurationModes() {
async function delete_({ pif }) {
// TODO: check if PIF is attached before
const xapi = this.getXapi(pif)
const tunnels = filter(xapi.objects.all, { $type: 'tunnel' })
const tunnel = find(tunnels, { access_PIF: pif._xapiRef })
if (tunnel != null) {
await xapi.callAsync('PIF.unplug', pif._xapiRef)
await xapi.callAsync('tunnel.destroy', tunnel.$ref)
return
}
await xapi.callAsync('PIF.destroy', pif._xapiRef)
await this.getXapi(pif).callAsync('PIF.destroy', pif._xapiRef)
}
export { delete_ as delete }

View File

@@ -5,15 +5,14 @@ import { format, JsonRPcError } from 'json-rpc-peer'
export async function set({
pool,
// TODO: use camel case.
name_description: nameDescription,
name_label: nameLabel,
}) {
pool = this.getXapiObject(pool)
await Promise.all([
nameDescription !== undefined && pool.set_name_description(nameDescription),
nameLabel !== undefined && pool.set_name_label(nameLabel),
])
await this.getXapi(pool).setPoolProperties({
nameDescription,
nameLabel,
})
}
set.params = {

View File

@@ -10,15 +10,14 @@ import { forEach, parseXml } from '../utils'
export async function set({
sr,
// TODO: use camel case.
name_description: nameDescription,
name_label: nameLabel,
}) {
sr = this.getXapiObject(sr)
await Promise.all([
nameDescription !== undefined && sr.set_name_description(nameDescription),
nameLabel !== undefined && sr.set_name_label(nameLabel),
])
await this.getXapi(sr).setSrProperties(sr._xapiId, {
nameDescription,
nameLabel,
})
}
set.params = {
@@ -180,35 +179,6 @@ createIso.resolve = {
host: ['host', 'host', 'administrate'],
}
// -------------------------------------------------------------------
export async function createFile({
host,
nameLabel,
nameDescription,
location,
}) {
const xapi = this.getXapi(host)
return xapi.createSr({
hostRef: host._xapiRef,
name_label: nameLabel,
name_description: nameDescription,
type: 'file',
device_config: { location },
})
}
createFile.params = {
host: { type: 'string' },
nameLabel: { type: 'string' },
nameDescription: { type: 'string' },
location: { type: 'string' },
}
createFile.resolve = {
host: ['host', 'host', 'administrate'],
}
// -------------------------------------------------------------------
// NFS SR
@@ -391,58 +361,6 @@ createExt.resolve = {
host: ['host', 'host', 'administrate'],
}
// -------------------------------------------------------------------
// This function helps to detect all ZFS pools
// Return a dict of pools with their parameters { <poolname>: {<paramdict>}}
// example output (the parameter mountpoint is of interest):
// {"tank":
// {
// "setuid": "on", "relatime": "off", "referenced": "24K", "written": "24K", "zoned": "off", "primarycache": "all",
// "logbias": "latency", "creation": "Mon May 27 17:24 2019", "sync": "standard", "snapdev": "hidden",
// "dedup": "off", "sharenfs": "off", "usedbyrefreservation": "0B", "sharesmb": "off", "createtxg": "1",
// "canmount": "on", "mountpoint": "/tank", "casesensitivity": "sensitive", "utf8only": "off", "xattr": "on",
// "dnodesize": "legacy", "mlslabel": "none", "objsetid": "54", "defcontext": "none", "rootcontext": "none",
// "mounted": "yes", "compression": "off", "overlay": "off", "logicalused": "47K", "usedbysnapshots": "0B",
// "filesystem_count": "none", "copies": "1", "snapshot_limit": "none", "aclinherit": "restricted",
// "compressratio": "1.00x", "readonly": "off", "version": "5", "normalization": "none", "filesystem_limit": "none",
// "type": "filesystem", "secondarycache": "all", "refreservation": "none", "available": "17.4G", "used": "129K",
// "exec": "on", "refquota": "none", "refcompressratio": "1.00x", "quota": "none", "keylocation": "none",
// "snapshot_count": "none", "fscontext": "none", "vscan": "off", "reservation": "none", "atime": "on",
// "recordsize": "128K", "usedbychildren": "105K", "usedbydataset": "24K", "guid": "656061077639704004",
// "pbkdf2iters": "0", "checksum": "on", "special_small_blocks": "0", "redundant_metadata": "all",
// "volmode": "default", "devices": "on", "keyformat": "none", "logicalreferenced": "12K", "acltype": "off",
// "nbmand": "off", "context": "none", "encryption": "off", "snapdir": "hidden"}}
export async function probeZfs({ host }) {
const xapi = this.getXapi(host)
try {
const result = await xapi.call(
'host.call_plugin',
host._xapiRef,
'zfs.py',
'list_zfs_pools',
{}
)
return JSON.parse(result)
} catch (error) {
if (
error.code === 'XENAPI_MISSING_PLUGIN' ||
error.code === 'UNKNOWN_XENAPI_PLUGIN_FUNCTION'
) {
return {}
} else {
throw error
}
}
}
probeZfs.params = {
host: { type: 'string' },
}
probeZfs.resolve = {
host: ['host', 'host', 'administrate'],
}
// -------------------------------------------------------------------
// This function helps to detect all NFS shares (exports) on a NFS server
// Return a table of exports with their paths and ACLs

View File

@@ -1,5 +1,5 @@
export async function add({ tag, object }) {
await this.getXapiObject(object).add_tags(tag)
await this.getXapi(object).addTag(object._xapiId, tag)
}
add.description = 'add a new tag to an object'
@@ -16,7 +16,7 @@ add.params = {
// -------------------------------------------------------------------
export async function remove({ tag, object }) {
await this.getXapiObject(object).remove_tags(tag)
await this.getXapi(object).removeTag(object._xapiId, tag)
}
remove.description = 'remove an existing tag from an object'

View File

@@ -320,11 +320,6 @@ create.params = {
},
},
},
hvmBootFirmware: { type: 'string', optional: true },
// other params are passed to `editVm`
'*': { type: 'any' },
}
create.resolve = {
@@ -565,8 +560,6 @@ set.params = {
// Identifier of the VM to update.
id: { type: 'string' },
auto_poweron: { type: 'boolean', optional: true },
name_label: { type: 'string', optional: true },
name_description: { type: 'string', optional: true },
@@ -628,9 +621,6 @@ set.params = {
// set the VM network interface controller
nicType: { type: ['string', 'null'], optional: true },
// set the VM boot firmware mode
hvmBootFirmware: { type: ['string', 'null'], optional: true },
}
set.resolve = {
@@ -1370,7 +1360,9 @@ createInterface.resolve = {
// -------------------------------------------------------------------
export async function attachPci({ vm, pciId }) {
await this.getXapiObject(vm).update_other_config('pci', pciId)
const xapi = this.getXapi(vm)
await xapi.call('VM.add_to_other_config', vm._xapiRef, 'pci', pciId)
}
attachPci.params = {
@@ -1385,7 +1377,9 @@ attachPci.resolve = {
// -------------------------------------------------------------------
export async function detachPci({ vm }) {
await this.getXapiObject(vm).update_other_config('pci', null)
const xapi = this.getXapi(vm)
await xapi.call('VM.remove_from_other_config', vm._xapiRef, 'pci')
}
detachPci.params = {
@@ -1422,7 +1416,7 @@ export async function setBootOrder({ vm, order }) {
throw invalidParameters('You can only set the boot order on a HVM guest')
}
await this.getXapiObject(vm).update_HVM_boot_params('order', order)
await this.getXapiObject(vm).set_HVM_boot_params({ order })
}
setBootOrder.params = {

View File

@@ -55,7 +55,6 @@ getAllObjects.description = 'Returns all XO objects'
getAllObjects.params = {
filter: { type: 'object', optional: true },
limit: { type: 'number', optional: true },
ndjson: { type: 'boolean', optional: true },
}
// -------------------------------------------------------------------

View File

@@ -887,10 +887,10 @@ async function createVDIOnLVMWithoutSizeLimit(xapi, lvmSr, diskSize) {
await xapi.callAsync('SR.scan', xapi.getObject(lvmSr).$ref)
const vdi = find(xapi.getObject(lvmSr).$VDIs, vdi => vdi.uuid === uuid)
if (vdi != null) {
await Promise.all([
vdi.set_name_description('Created by XO'),
vdi.set_name_label('xosan_data'),
])
await xapi.setSrProperties(vdi.$ref, {
nameLabel: 'xosan_data',
nameDescription: 'Created by XO',
})
return vdi
}
}
@@ -1124,7 +1124,7 @@ async function _prepareGlusterVm(
}
}
}
await newVM.add_tags('XOSAN')
await xapi.addTag(newVM.$id, 'XOSAN')
await xapi.editVm(newVM, {
name_label: `XOSAN - ${lvmSr.name_label} - ${
host.name_label

View File

@@ -22,8 +22,8 @@ import { forbiddenOperation } from 'xo-common/api-errors'
import { Xapi as XapiBase, NULL_REF } from 'xen-api'
import {
every,
filter,
find,
filter,
flatMap,
flatten,
groupBy,
@@ -247,6 +247,69 @@ export default class Xapi extends XapiBase {
)::ignoreErrors()
}
async setHostProperties(id, { nameLabel, nameDescription }) {
const host = this.getObject(id)
await Promise.all([
nameDescription !== undefined &&
host.set_name_description(nameDescription),
nameLabel !== undefined && host.set_name_label(nameLabel),
])
}
async setPoolProperties({ autoPoweron, nameLabel, nameDescription }) {
const { pool } = this
await Promise.all([
nameDescription !== undefined &&
pool.set_name_description(nameDescription),
nameLabel !== undefined && pool.set_name_label(nameLabel),
autoPoweron != null &&
pool.update_other_config('autoPoweron', autoPoweron ? 'true' : null),
])
}
async setSrProperties(id, { nameLabel, nameDescription }) {
const sr = this.getObject(id)
await Promise.all([
nameDescription !== undefined && sr.set_name_description(nameDescription),
nameLabel !== undefined && sr.set_name_label(nameLabel),
])
}
async setNetworkProperties(
id,
{ automatic, defaultIsLocked, nameDescription, nameLabel }
) {
let defaultLockingMode
if (defaultIsLocked != null) {
defaultLockingMode = defaultIsLocked ? 'disabled' : 'unlocked'
}
const network = this.getObject(id)
await Promise.all([
defaultLockingMode !== undefined &&
network.set_default_locking_mode(defaultLockingMode),
nameDescription !== undefined &&
network.set_name_description(nameDescription),
nameLabel !== undefined && network.set_name_label(nameLabel),
automatic !== undefined &&
network.update_other_config('automatic', automatic ? 'true' : null),
])
}
// =================================================================
async addTag(id, tag) {
const { $ref: ref, $type: type } = this.getObject(id)
await this.call(`${type}.add_tags`, ref, tag)
}
async removeTag(id, tag) {
const { $ref: ref, $type: type } = this.getObject(id)
await this.call(`${type}.remove_tags`, ref, tag)
}
// =================================================================
setDefaultSr(srId) {
@@ -956,21 +1019,17 @@ export default class Xapi extends XapiBase {
await this._createVmRecord({
...delta.vm,
affinity: null,
blocked_operations: {
...delta.vm.blocked_operations,
start: 'Importing…',
},
ha_always_run: false,
is_a_template: false,
name_label: `[Importing…] ${name_label}`,
other_config: {
...delta.vm.other_config,
[TAG_COPY_SRC]: delta.vm.uuid,
},
})
)
$defer.onFailure(() => this._deleteVm(vm))
await Promise.all([
vm.set_name_label(`[Importing…] ${name_label}`),
vm.update_blocked_operations('start', 'Importing…'),
vm.update_other_config(TAG_COPY_SRC, delta.vm.uuid),
])
// 2. Delete all VBDs which may have been created by the import.
await asyncMap(vm.$VBDs, vbd => this._deleteVbd(vbd))::ignoreErrors()
@@ -1087,7 +1146,6 @@ export default class Xapi extends XapiBase {
}
await Promise.all([
delta.vm.ha_always_run && vm.set_ha_always_run(true),
vm.set_name_label(name_label),
// FIXME: move
vm.update_blocked_operations(
@@ -1318,7 +1376,11 @@ export default class Xapi extends XapiBase {
$defer.onFailure(() => this._deleteVm(vm))
// Disable start and change the VM name label during import.
await Promise.all([
vm.update_blocked_operations('start', 'OVA import in progress...'),
this.addForbiddenOperationToVm(
vm.$id,
'start',
'OVA import in progress...'
),
vm.set_name_label(`[Importing...] ${nameLabel}`),
])
@@ -1379,7 +1441,7 @@ export default class Xapi extends XapiBase {
// Enable start and restore the VM name label after import.
await Promise.all([
vm.update_blocked_operations('start', null),
this.removeForbiddenOperationFromVm(vm.$id, 'start'),
vm.set_name_label(nameLabel),
])
return vm
@@ -1609,6 +1671,24 @@ export default class Xapi extends XapiBase {
}
}
// vm_operations: http://xapi-project.github.io/xen-api/classes/vm.html
async addForbiddenOperationToVm(vmId, operation, reason) {
await this.call(
'VM.add_to_blocked_operations',
this.getObject(vmId).$ref,
operation,
`[XO] ${reason}`
)
}
async removeForbiddenOperationFromVm(vmId, operation) {
await this.call(
'VM.remove_from_blocked_operations',
this.getObject(vmId).$ref,
operation
)
}
// =================================================================
async createVbd({
@@ -2136,16 +2216,6 @@ export default class Xapi extends XapiBase {
mapToArray(bonds, bond => this.call('Bond.destroy', bond))
)
const tunnels = filter(this.objects.all, { $type: 'tunnel' })
await Promise.all(
map(pifs, async pif => {
const tunnel = find(tunnels, { access_PIF: pif.$ref })
if (tunnel != null) {
await this.callAsync('tunnel.destroy', tunnel.$ref)
}
})
)
await this.callAsync('network.destroy', network.$ref)
}
@@ -2346,7 +2416,7 @@ export default class Xapi extends XapiBase {
)
}
async assertConsistentHostServerTime(hostRef) {
async _assertConsistentHostServerTime(hostRef) {
const delta =
parseDateTime(await this.call('host.get_servertime', hostRef)).getTime() -
Date.now()
@@ -2358,27 +2428,4 @@ export default class Xapi extends XapiBase {
)
}
}
async isHyperThreadingEnabled(hostId) {
try {
return (
(await this.call(
'host.call_plugin',
this.getObject(hostId).$ref,
'hyperthreading.py',
'get_hyperthreading',
{}
)) !== 'false'
)
} catch (error) {
if (
error.code === 'XENAPI_MISSING_PLUGIN' ||
error.code === 'UNKNOWN_XENAPI_PLUGIN_FUNCTION'
) {
return null
} else {
throw error
}
}
}
}

View File

@@ -74,6 +74,7 @@ declare export class Xapi {
): Promise<void>;
_snapshotVm(cancelToken: mixed, vm: Vm, nameLabel?: string): Promise<Vm>;
addTag(object: Id, tag: string): Promise<void>;
barrier(): Promise<void>;
barrier(ref: string): Promise<XapiObject>;
deleteVm(vm: Id): Promise<void>;

View File

@@ -84,32 +84,4 @@ export default {
})
return unhealthyVdis
},
async createSr({
hostRef,
content_type = 'user', // recommended by Citrix
device_config = {},
name_description = '',
name_label,
shared = false,
physical_size = 0,
sm_config = {},
type,
}) {
const srRef = await this.call(
'SR.create',
hostRef,
device_config,
physical_size,
name_label,
name_description,
type,
content_type,
shared,
sm_config
)
return (await this.barrier(srRef)).uuid
},
}

View File

@@ -107,12 +107,15 @@ export default {
if (isHvm) {
if (!isEmpty(vdis) || installMethod === 'network') {
const { order } = vm.HVM_boot_params
const { HVM_boot_params: bootParams } = vm
let order = bootParams.order
if (order) {
order = 'n' + order.replace('n', '')
} else {
order = 'ncd'
}
vm.update_HVM_boot_params(
'order',
order ? 'n' + order.replace('n', '') : 'ncd'
)
vm.set_HVM_boot_params({ ...bootParams, order })
}
} else {
// PV
@@ -265,8 +268,11 @@ export default {
autoPoweron: {
set(value, vm) {
return Promise.all([
vm.update_other_config('auto_poweron', value ? 'true' : null),
value && vm.$pool.update_other_config('auto_poweron', 'true'),
vm.update_other_config('autoPoweron', value ? 'true' : null),
value &&
this.setPoolProperties({
autoPoweron: true,
}),
])
},
},
@@ -449,10 +455,6 @@ export default {
get: vm => +vm.start_delay,
set: (startDelay, vm) => vm.set_start_delay(startDelay),
},
hvmBootFirmware: {
set: (firmware, vm) => vm.update_HVM_boot_params('firmware', firmware),
},
}),
async editVm(id, props, checkLimits) {

View File

@@ -60,9 +60,8 @@ function checkParams(method, params) {
const result = schemaInspector.validate(
{
properties: schema,
strict: true,
type: 'object',
properties: schema,
},
params
)
@@ -262,15 +261,11 @@ export default class Api {
//
// The goal here is to standardize the calls by always providing
// an id parameter when possible to simplify calls to the API.
if (params?.id === undefined) {
if (params != null && params.id === undefined) {
const namespace = name.slice(0, name.indexOf('.'))
const spec = method.params
if (spec !== undefined && 'id' in spec && !(namespace in spec)) {
const id = params[namespace]
if (typeof id === 'string') {
delete params[namespace]
params.id = id
}
const id = params[namespace]
if (typeof id === 'string') {
params.id = id
}
}

View File

@@ -1,8 +1,10 @@
import ms from 'ms'
import { forEach, isEmpty, iteratee, sortedIndexBy } from 'lodash'
import { noSuchObject } from 'xo-common/api-errors'
const isSkippedError = error =>
error.message === 'no disks found' ||
noSuchObject.is(error) ||
error.message === 'no VMs match this pattern' ||
error.message === 'unhealthy VDI chain'

View File

@@ -29,7 +29,6 @@ import {
ignoreErrors,
pFinally,
pFromEvent,
timeout,
} from 'promise-toolbox'
import Vhd, {
chainVhd,
@@ -42,7 +41,6 @@ import { type CallJob, type Executor, type Job } from '../jobs'
import { type Schedule } from '../scheduling'
import createSizeStream from '../../size-stream'
import parseDuration from '../../_parseDuration'
import {
type DeltaVmExport,
type DeltaVmImport,
@@ -288,7 +286,7 @@ const importers: $Dict<
xapi.importVm(xva, { srId: sr.$id })
)
await Promise.all([
vm.add_tags('restored from backup'),
xapi.addTag(vm.$id, 'restored from backup'),
xapi.editVm(vm.$id, {
name_label: `${metadata.vm.name_label} (${safeDateFormat(
metadata.timestamp
@@ -452,7 +450,7 @@ const disableVmHighAvailability = async (xapi: Xapi, vm: Vm) => {
return Promise.all([
vm.set_ha_restart_priority(''),
vm.add_tags('HA disabled'),
xapi.addTag(vm.$ref, 'HA disabled'),
])
}
@@ -509,17 +507,9 @@ const disableVmHighAvailability = async (xapi: Xapi, vm: Vm) => {
// │ │ ├─ task.start(message: 'transfer')
// │ │ │ ├─ task.warning(message: string)
// │ │ │ └─ task.end(result: { size: number })
// │ │ │
// │ │ │ // in case of full backup, DR and CR
// │ │ ├─ task.start(message: 'clean')
// │ │ │ ├─ task.warning(message: string)
// │ │ │ └─ task.end
// │ │ │
// │ │ │ // in case of delta backup
// │ │ ├─ task.start(message: 'merge')
// │ │ │ ├─ task.warning(message: string)
// │ │ │ └─ task.end(result: { size: number })
// │ │ │
// │ │ └─ task.end
// │ └─ task.end
// └─ job.end
@@ -546,11 +536,10 @@ export default class BackupNg {
return this._runningRestores
}
constructor(app: any, { backup }) {
constructor(app: any) {
this._app = app
this._logger = undefined
this._runningRestores = new Set()
this._backupOptions = backup
app.on('start', async () => {
this._logger = await app.getLogger('restore')
@@ -1202,20 +1191,11 @@ export default class BackupNg {
)
): any)
const deleteOldBackups = () =>
wrapTask(
{
logger,
message: 'clean',
parentId: taskId,
},
this._deleteFullVmBackups(handler, oldBackups)
)
const deleteFirst = getSetting(settings, 'deleteFirst', [
remoteId,
])
if (deleteFirst) {
await deleteOldBackups()
await this._deleteFullVmBackups(handler, oldBackups)
}
await wrapTask(
@@ -1231,7 +1211,7 @@ export default class BackupNg {
await handler.outputFile(metadataFilename, jsonMetadata)
if (!deleteFirst) {
await deleteOldBackups()
await this._deleteFullVmBackups(handler, oldBackups)
}
}
)
@@ -1262,18 +1242,9 @@ export default class BackupNg {
listReplicatedVms(xapi, scheduleId, srId, vmUuid)
)
const deleteOldBackups = () =>
wrapTask(
{
logger,
message: 'clean',
parentId: taskId,
},
this._deleteVms(xapi, oldVms)
)
const deleteFirst = getSetting(settings, 'deleteFirst', [srId])
if (deleteFirst) {
await deleteOldBackups()
await this._deleteVms(xapi, oldVms)
}
const vm = await xapi.barrier(
@@ -1295,7 +1266,7 @@ export default class BackupNg {
)
await Promise.all([
vm.add_tags('Disaster Recovery'),
xapi.addTag(vm.$ref, 'Disaster Recovery'),
disableVmHighAvailability(xapi, vm),
vm.update_blocked_operations(
'start',
@@ -1305,7 +1276,7 @@ export default class BackupNg {
])
if (!deleteFirst) {
await deleteOldBackups()
await this._deleteVms(xapi, oldVms)
}
}
)
@@ -1631,19 +1602,9 @@ export default class BackupNg {
listReplicatedVms(xapi, scheduleId, srId, vmUuid)
)
const deleteOldBackups = () =>
wrapTask(
{
logger,
message: 'clean',
parentId: taskId,
},
this._deleteVms(xapi, oldVms)
)
const deleteFirst = getSetting(settings, 'deleteFirst', [srId])
if (deleteFirst) {
await deleteOldBackups()
await this._deleteVms(xapi, oldVms)
}
const { vm } = await wrapTask(
@@ -1663,7 +1624,7 @@ export default class BackupNg {
)
await Promise.all([
vm.add_tags('Continuous Replication'),
xapi.addTag(vm.$ref, 'Continuous Replication'),
disableVmHighAvailability(xapi, vm),
vm.update_blocked_operations(
'start',
@@ -1673,7 +1634,7 @@ export default class BackupNg {
])
if (!deleteFirst) {
await deleteOldBackups()
await this._deleteVms(xapi, oldVms)
}
}
)
@@ -1800,16 +1761,6 @@ export default class BackupNg {
const path = `${dir}/${file}`
try {
const metadata = JSON.parse(String(await handler.readFile(path)))
if (metadata.mode === 'full') {
metadata.size = await timeout
.call(
handler.getSize(resolveRelativeFromFile(path, metadata.xva)),
parseDuration(this._backupOptions.vmBackupSizeTimeout)
)
.catch(err => {
log.warn(`_listVmBackups, getSize`, { err })
})
}
if (predicate === undefined || predicate(metadata)) {
Object.defineProperty(metadata, '_filename', {
value: path,

View File

@@ -372,7 +372,7 @@ export default class {
const { datetime } = parseVmBackupPath(file)
await Promise.all([
vm.add_tags('restored from backup'),
xapi.addTag(vm.$id, 'restored from backup'),
xapi.editVm(vm.$id, {
name_label: `${vm.name_label} (${shortDate(datetime * 1e3)})`,
}),
@@ -972,13 +972,12 @@ export default class {
nameLabel: copyName,
})
await Promise.all([
data.vm.add_tags('Disaster Recovery'),
data.vm.update_blocked_operations(
'start',
'Start operation for this vm is blocked, clone it if you want to use it.'
),
])
data.vm.update_blocked_operations(
'start',
'Start operation for this vm is blocked, clone it if you want to use it.'
)
await targetXapi.addTag(data.vm.$id, 'Disaster Recovery')
if (!deleteOldBackupsFirst) {
await this._removeVms(targetXapi, vmsToRemove)

View File

@@ -43,20 +43,6 @@ type MetadataBackupJob = {
xoMetadata?: boolean,
}
const logInstantFailureTask = (logger, { data, error, message, parentId }) => {
const taskId = logger.notice(message, {
data,
event: 'task.start',
parentId,
})
logger.error(message, {
event: 'task.end',
result: serializeError(error),
status: 'failure',
taskId,
})
}
const createSafeReaddir = (handler, methodName) => (path, options) =>
handler.list(path, options).catch(error => {
if (error?.code !== 'ENOENT') {
@@ -111,7 +97,7 @@ const deleteOldBackups = (handler, dir, retention, handleError) =>
// Task logs emitted in a metadata backup execution:
//
// job.start(data: { reportWhen: ReportWhen })
// ├─ task.start(data: { type: 'pool', id: string, pool?: <Pool />, poolMaster?: <Host /> })
// ├─ task.start(data: { type: 'pool', id: string, pool: <Pool />, poolMaster: <Host /> })
// │ ├─ task.start(data: { type: 'remote', id: string })
// │ │ └─ task.end
// │ └─ task.end
@@ -541,15 +527,16 @@ export default class metadataBackup {
try {
xapi = this._app.getXapi(id)
} catch (error) {
logInstantFailureTask(logger, {
data: {
type: 'pool',
id,
},
error,
message: `unable to get the xapi associated to the pool (${id})`,
parentId: runJobId,
})
logger.warning(
`unable to get the xapi associated to the pool (${id})`,
{
event: 'task.warning',
taskId: runJobId,
data: {
error,
},
}
)
}
if (xapi !== undefined) {
promises.push(

View File

@@ -40,7 +40,7 @@
"event-to-promise": "^0.8.0",
"execa": "^1.0.0",
"fs-extra": "^8.0.1",
"get-stream": "^5.1.0",
"get-stream": "^4.0.0",
"index-modules": "^0.3.0",
"rimraf": "^2.6.2"
},

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "xo-web",
"version": "5.43.0",
"version": "5.42.0",
"license": "AGPL-3.0",
"description": "Web interface client for Xen-Orchestra",
"keywords": [

View File

@@ -1,30 +0,0 @@
import _ from 'intl'
import decorate from 'apply-decorators'
import Icon from 'icon'
import PropTypes from 'prop-types'
import React from 'react'
import Tooltip from 'tooltip'
import { injectState, provideState } from 'reaclette'
import { isHostTimeConsistentWithXoaTime } from 'xo'
const InconsistentHostTimeWarning = decorate([
provideState({
computed: {
isHostTimeConsistentWithXoaTime: (_, { hostId }) =>
isHostTimeConsistentWithXoaTime(hostId),
},
}),
injectState,
({ state: { isHostTimeConsistentWithXoaTime = true } }) =>
isHostTimeConsistentWithXoaTime ? null : (
<Tooltip content={_('warningHostTimeTooltip')}>
<Icon color='text-danger' icon='alarm' />
</Tooltip>
),
])
InconsistentHostTimeWarning.propTypes = {
hostId: PropTypes.string.isRequired,
}
export { InconsistentHostTimeWarning as default }

View File

@@ -47,8 +47,6 @@ const messages = {
chooseBackup: 'Choose a backup',
clickToShowError: 'Click to show error',
backupJobs: 'Backup jobs',
iscsiSessions:
'({ nSessions, number }) iSCSI session{nSessions, plural, one {} other {s}}',
// ----- Modals -----
alertOk: 'OK',
@@ -220,8 +218,6 @@ const messages = {
homeResourceSet: 'Resource set: {resourceSet}',
highAvailability: 'High Availability',
srSharedType: 'Shared {type}',
warningHostTimeTooltip:
'Host time and XOA time are not consistent with each other',
// ----- Home snapshots -----
snapshotVmsName: 'Name',
@@ -575,12 +571,9 @@ const messages = {
newSrPasswordPlaceHolder: 'Password',
newSrLvmDevicePlaceHolder: 'Device, e.g /dev/sda…',
newSrLocalPathPlaceHolder: '/path/to/directory',
newSrNfsDefaultVersion: 'Default NFS version',
newSrUseNfs4: 'Use NFSv4',
newSrNfsOptions: 'Comma delimited NFS options',
newSrNfs: 'NFS version',
noSharedZfsAvailable: 'No shared ZFS available',
reattachNewSrTooltip: 'Reattach SR',
srLocation: 'Storage location',
// ------ New Network -----
createNewNetworkNoPermission:
@@ -682,7 +675,6 @@ const messages = {
cloneVmLabel: 'Clone',
fastCloneVmLabel: 'Fast clone',
vmConsoleLabel: 'Console',
backupLabel: 'Backup',
// ----- SR advanced tab -----
@@ -798,9 +790,6 @@ const messages = {
memoryHostState:
'RAM: {memoryUsed} used on {memoryTotal} ({memoryFree} free)',
hardwareHostSettingsLabel: 'Hardware',
hyperThreading: 'Hyper-threading (SMT)',
hyperThreadingNotAvailable:
'HT detection is only available on XCP-ng 7.6 and higher',
hostAddress: 'Address',
hostStatus: 'Status',
hostBuildNumber: 'Build number',
@@ -808,7 +797,7 @@ const messages = {
hostNoIscsiSr: 'Not connected to an iSCSI SR',
hostMultipathingSrs: 'Click to see concerned SRs',
hostMultipathingPaths:
'{nActives, number} of {nPaths, number} path{nPaths, plural, one {} other {s}}',
'{nActives, number} of {nPaths, number} path{nPaths, plural, one {} other {s}} ({ nSessions, number } iSCSI session{nSessions, plural, one {} other {s}})',
hostMultipathingRequiredState:
'This action will not be fulfilled if a VM is in a running state. Please ensure that all VMs are evacuated or stopped before performing this action!',
hostMultipathingWarning:
@@ -864,7 +853,6 @@ const messages = {
// ----- Host storage tabs -----
addSrDeviceButton: 'Add a storage',
srType: 'Type',
pbdDetails: 'PBD details',
pbdStatus: 'Status',
pbdStatusConnected: 'Connected',
pbdStatusDisconnected: 'Disconnected',
@@ -1037,7 +1025,6 @@ const messages = {
vifMacLabel: 'MAC address',
vifMtuLabel: 'MTU',
vifNetworkLabel: 'Network',
vifRateLimitLabel: 'Rate limit (kB/s)',
vifStatusLabel: 'Status',
vifStatusConnected: 'Connected',
vifStatusDisconnected: 'Disconnected',
@@ -1143,10 +1130,6 @@ const messages = {
addAclsErrorMessage: 'User(s)/group(s) and role are required.',
removeAcl: 'Delete',
moreAcls: '{nAcls, number} more…',
vmBootFirmware: 'Boot firmware',
vmDefaultBootFirmwareLabel: 'default (bios)',
vmBootFirmwareWarningMessage:
"You're about to change your boot firmware. This is still experimental in CH/XCP-ng 8.0. Are you sure you want to continue?",
// ----- VM placeholders -----
@@ -1720,13 +1703,11 @@ const messages = {
newNetworkBondMode: 'Bond mode',
newNetworkInfo: 'Info',
newNetworkType: 'Type',
newNetworkEncapsulation: 'Encapsulation',
deleteNetwork: 'Delete network',
deleteNetworkConfirm: 'Are you sure you want to delete this network?',
networkInUse: 'This network is currently in use',
pillBonded: 'Bonded',
bondedNetwork: 'Bonded network',
privateNetwork: 'Private network',
// ----- Add host -----
addHostSelectHost: 'Host',

View File

@@ -1,7 +1,9 @@
import forEachRight from 'lodash/forEachRight'
import forEach from 'lodash/forEach'
import forEachRight from 'lodash/forEachRight'
import head from 'lodash/head'
import isArray from 'lodash/isArray'
import isIp from 'is-ip'
import last from 'lodash/last'
import some from 'lodash/some'
export { isIp }
@@ -82,6 +84,9 @@ export const formatIps = ips => {
if (ips.length === 0) {
return []
}
if (ips.length === 1) {
return ips
}
const sortedIps = ips.sort((ip1, ip2) => {
const splitIp1 = ip1.split('.')
const splitIp2 = ip2.split('.')
@@ -99,24 +104,8 @@ export const formatIps = ips => {
(splitIp1[0] - splitIp2[0]) * 256 * 256 * 256
)
})
const range = { first: '', last: '' }
const formattedIps = []
let index = 0
forEach(sortedIps, ip => {
if (ip !== getNextIpV4(range.last)) {
if (range.first) {
formattedIps[index] =
range.first === range.last ? range.first : { ...range }
index++
}
range.first = range.last = ip
} else {
range.last = ip
}
})
formattedIps[index] = range.first === range.last ? range.first : range
return formattedIps
return [{ first: head(sortedIps), last: last(sortedIps) }]
}
export const parseIpPattern = pattern => {

View File

@@ -504,9 +504,6 @@ const xoItemToRender = {
{backup.mode}
</span>{' '}
<span className='tag tag-warning'>{backup.remote.name}</span>{' '}
{backup.size !== undefined && (
<span className='tag tag-info'>{formatSize(backup.size)}</span>
)}{' '}
<FormattedDate
value={new Date(backup.timestamp)}
month='long'

View File

@@ -1,53 +0,0 @@
import _ from 'intl'
import PropTypes from 'prop-types'
import React from 'react'
import { confirm } from 'modal'
import { injectState, provideState } from 'reaclette'
import { noop } from 'utils'
// https://docs.citrix.com/en-us/citrix-hypervisor/whats-new/experimental.html
// XAPI values should be lowercased
const VM_BOOT_FIRMWARES = ['bios', 'uefi']
const withState = provideState({
effects: {
handleBootFirmwareChange(
__,
{
target: { value },
}
) {
if (value !== '') {
// TODO: Confirm should be removed once the feature is stabilized
confirm({
title: _('vmBootFirmware'),
body: _('vmBootFirmwareWarningMessage'),
}).then(() => this.props.onChange(value), noop)
} else {
this.props.onChange(value)
}
},
},
})
const SelectBootFirmware = ({ effects, value }) => (
<select
className='form-control'
onChange={effects.handleBootFirmwareChange}
value={value}
>
<option value=''>{_('vmDefaultBootFirmwareLabel')}</option>
{VM_BOOT_FIRMWARES.map(val => (
<option key={val} value={val}>
{val}
</option>
))}
</select>
)
SelectBootFirmware.propTypes = {
onChange: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
}
export default withState(injectState(SelectBootFirmware))

View File

@@ -53,7 +53,3 @@ export const setXoaConfiguration = createAction(
'XOA_CONFIGURATION',
configuration => configuration
)
export const setHomeVmIdsSelection = createAction(
'SET_HOME_VM_IDS_SELECTION',
homeVmIdsSelection => homeVmIdsSelection
)

View File

@@ -86,12 +86,6 @@ export default {
}
),
// These IDs are used temporarily to be preselected in backup-ng/new/vms
homeVmIdsSelection: combineActionHandlers([], {
[actions.setHomeVmIdsSelection]: (_, homeVmIdsSelection) =>
homeVmIdsSelection,
}),
objects: combineActionHandlers(
{
all: {}, // Mutable for performance!

View File

@@ -58,12 +58,6 @@ export class TooltipViewer extends Component {
// ===================================================================
// Wrap disabled HTML element before wrapping it with Tooltip
// <Tooltip>
// <div>
// <MyComponent disabled />
// </div>
// </Tooltip>
export default class Tooltip extends Component {
static propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),

View File

@@ -646,14 +646,10 @@ export const createCompare = criterias => (...items) => {
// ===================================================================
export const hasLicenseRestrictions = host => {
const licenseType = host.license_params.sku_type
return (
host.productBrand !== 'XCP-ng' &&
versionSatisfies(host.version, '>=7.3.0') &&
(licenseType === 'free' || licenseType === 'express')
)
}
export const hasLicenseRestrictions = host =>
host.productBrand !== 'XCP-ng' &&
versionSatisfies(host.version, '>=7.3.0') &&
host.license_params.sku_type === 'free'
// ===================================================================

View File

@@ -777,14 +777,6 @@ export const emergencyShutdownHosts = hosts => {
}).then(() => map(hosts, host => emergencyShutdownHost(host)), noop)
}
export const isHostTimeConsistentWithXoaTime = host =>
_call('host.isHostServerTimeConsistent', { host: resolveId(host) })
export const isHyperThreadingEnabledHost = host =>
_call('host.isHyperThreadingEnabled', {
id: resolveId(host),
})
// for XCP-ng now
export const installAllPatchesOnHost = ({ host }) =>
confirm({
@@ -1628,15 +1620,14 @@ export const deleteVifs = vifs =>
export const setVif = (
vif,
{ allowedIpv4Addresses, allowedIpv6Addresses, mac, network, rateLimit }
{ network, mac, allowedIpv4Addresses, allowedIpv6Addresses }
) =>
_call('vif.set', {
id: resolveId(vif),
network: resolveId(network),
mac,
allowedIpv4Addresses,
allowedIpv6Addresses,
id: resolveId(vif),
mac,
network: resolveId(network),
rateLimit,
})
// Network -----------------------------------------------------------
@@ -1648,8 +1639,6 @@ export const getBondModes = () => _call('network.getBondModes')
export const createNetwork = params => _call('network.create', params)
export const createBondedNetwork = params =>
_call('network.createBonded', params)
export const createPrivateNetwork = params =>
_call('plugin.SDNController.createPrivateNetwork', params)
export const deleteNetwork = network =>
confirm({
@@ -2294,8 +2283,6 @@ export const probeSrHba = host => _call('sr.probeHba', { host })
export const probeSrHbaExists = (host, scsiId) =>
_call('sr.probeHbaExists', { host, scsiId })
export const probeZfs = host => _call('sr.probeZfs', { host: resolveId(host) })
export const reattachSr = (host, uuid, nameLabel, nameDescription, type) =>
_call('sr.reattach', { host, uuid, nameLabel, nameDescription, type })
@@ -2359,14 +2346,6 @@ export const createSrLvm = (host, nameLabel, nameDescription, device) =>
export const createSrExt = (host, nameLabel, nameDescription, device) =>
_call('sr.createExt', { host, nameLabel, nameDescription, device })
export const createSrZfs = (host, nameLabel, nameDescription, location) =>
_call('sr.createFile', {
host: resolveId(host),
nameDescription,
nameLabel,
location,
})
// Job logs ----------------------------------------------------------
export const deleteJobsLogs = async ids => {

View File

@@ -120,31 +120,28 @@ const createDoesRetentionExist = name => {
return ({ propSettings, settings = propSettings }) => settings.some(predicate)
}
const getInitialState = ({ preSelectedVmIds, setHomeVmIdsSelection }) => {
setHomeVmIdsSelection([]) // Clear preselected vmIds
return {
_displayAdvancedSettings: undefined,
_vmsPattern: undefined,
backupMode: false,
compression: undefined,
crMode: false,
deltaMode: false,
drMode: false,
name: '',
paramsUpdated: false,
remotes: [],
schedules: {},
settings: undefined,
showErrors: false,
smartMode: false,
snapshotMode: false,
srs: [],
tags: {
notValues: ['Continuous Replication', 'Disaster Recovery', 'XOSAN'],
},
vms: preSelectedVmIds,
}
}
const getInitialState = () => ({
_displayAdvancedSettings: undefined,
_vmsPattern: undefined,
backupMode: false,
compression: undefined,
crMode: false,
deltaMode: false,
drMode: false,
name: '',
paramsUpdated: false,
remotes: [],
schedules: {},
settings: undefined,
showErrors: false,
smartMode: false,
snapshotMode: false,
srs: [],
tags: {
notValues: ['Continuous Replication', 'Disaster Recovery', 'XOSAN'],
},
vms: [],
})
const DeleteOldBackupsFirst = ({ handler, handlerParam, value }) => (
<ActionButton
@@ -172,7 +169,6 @@ export default decorate([
hostsById: createGetObjectsOfType('host'),
poolsById: createGetObjectsOfType('pool'),
srsById: createGetObjectsOfType('SR'),
preSelectedVmIds: state => state.homeVmIdsSelection,
})),
injectIntl,
provideState({

View File

@@ -6,7 +6,7 @@ import Icon from 'icon'
import React from 'react'
import SortedTable from 'sorted-table'
import Upgrade from 'xoa-upgrade'
import { addSubscriptions, formatSize, noop } from 'utils'
import { addSubscriptions, noop } from 'utils'
import { confirm } from 'modal'
import { error } from 'notification'
import { FormattedDate } from 'react-intl'
@@ -87,12 +87,6 @@ const BACKUPS_COLUMNS = [
default: true,
sortOrder: 'desc',
},
{
name: _('labelSize'),
itemRenderer: ({ size }) =>
size !== undefined && size !== 0 && formatSize(size),
sortCriteria: 'size',
},
{
name: _('availableBackupsColumn'),
itemRenderer: ({ count }) =>
@@ -155,7 +149,6 @@ export default class Restore extends Component {
})
// TODO: perf
let first, last
let size = 0
forEach(backupDataByVm, (data, vmId) => {
first = { timestamp: Infinity }
last = { timestamp: 0 }
@@ -168,13 +161,9 @@ export default class Restore extends Component {
first = backup
}
count[backup.mode] = (count[backup.mode] || 0) + 1
if (backup.size !== undefined) {
size += backup.size
}
})
assign(data, { first, last, count, id: vmId, size })
assign(data, { first, last, count, id: vmId })
})
forEach(backupDataByVm, ({ backups }, vmId) => {

View File

@@ -1,6 +1,5 @@
import _ from 'intl'
import Component from 'base-component'
import InconsistentHostTimeWarning from 'inconsistent-host-time-warning'
import Ellipsis, { EllipsisContainer } from 'ellipsis'
import Icon from 'icon'
import isEmpty from 'lodash/isEmpty'
@@ -129,8 +128,6 @@ export default class HostItem extends Component {
</Tooltip>
)}
&nbsp;
<InconsistentHostTimeWarning hostId={host.id} />
&nbsp;
{hasLicenseRestrictions(host) && <LicenseWarning />}
</EllipsisContainer>
</Col>

View File

@@ -178,14 +178,6 @@ const OPTIONS = {
icon: 'vm-snapshot',
labelId: 'snapshotVmLabel',
},
{
handler: (vmIds, _, { setHomeVmIdsSelection }, { router }) => {
setHomeVmIdsSelection(vmIds)
router.push('backup-ng/new/vms')
},
icon: 'backup',
labelId: 'backupLabel',
},
{
handler: deleteVms,
icon: 'vm-delete',
@@ -1018,9 +1010,7 @@ export default class Home extends Component {
onClick={() => {
action.handler(
this._getSelectedItemsIds(),
action.params,
this.props,
this.context
action.params
)
}}
>

View File

@@ -1,5 +1,4 @@
import _ from 'intl'
import InconsistentHostTimeWarning from 'inconsistent-host-time-warning'
import Copiable from 'copiable'
import HostActionBar from './action-bar'
import Icon from 'icon'
@@ -255,8 +254,6 @@ export default class Host extends Component {
</Link>
</Tooltip>
)}
&nbsp;
<InconsistentHostTimeWarning hostId={host.id} />
</h2>
<Copiable tagName='pre' className='text-muted mb-0'>
{host.uuid}

View File

@@ -21,7 +21,6 @@ import {
disableHost,
enableHost,
forgetHost,
isHyperThreadingEnabledHost,
installSupplementalPack,
restartHost,
setHostsMultipathing,
@@ -55,7 +54,6 @@ const MultipathableSrs = decorate([
<Container>
{map(pbds, pbd => {
const [nActives, nPaths] = getIscsiPaths(pbd)
const nSessions = pbd.otherConfig.iscsi_sessions
return (
<Row key={pbd.id}>
<Col>
@@ -65,8 +63,8 @@ const MultipathableSrs = decorate([
_('hostMultipathingPaths', {
nActives,
nPaths,
})}{' '}
{nSessions !== undefined && _('iscsiSessions', { nSessions })}
nSessions: pbd.otherConfig.iscsi_sessions,
})}
</Col>
</Row>
)
@@ -97,12 +95,6 @@ MultipathableSrs.propTypes = {
}
})
export default class extends Component {
async componentDidMount() {
this.setState({
isHtEnabled: await isHyperThreadingEnabledHost(this.props.host),
})
}
_getPacks = createSelector(
() => this.props.host.supplementalPacks,
packs => {
@@ -120,12 +112,10 @@ export default class extends Component {
return uniqPacks
}
)
_setRemoteSyslogHost = value => setRemoteSyslogHost(this.props.host, value)
render() {
const { host, pcis, pgpus } = this.props
const { isHtEnabled } = this.state
return (
<Container>
<Row>
@@ -282,16 +272,6 @@ export default class extends Component {
{host.cpus.cores} ({host.cpus.sockets})
</td>
</tr>
<tr>
<th>{_('hyperThreading')}</th>
<td>
{isHtEnabled === null
? _('hyperThreadingNotAvailable')
: isHtEnabled
? _('stateEnabled')
: _('stateDisabled')}
</td>
</tr>
<tr>
<th>{_('hostManufacturerinfo')}</th>
<Copiable tagName='td'>

View File

@@ -87,19 +87,6 @@ const SR_COLUMNS = [
storage.shared ? _('srShared') : _('srNotShared'),
sortCriteria: 'shared',
},
{
name: _('pbdDetails'),
itemRenderer: ({ pbdDeviceConfig }) => {
const keys = Object.keys(pbdDeviceConfig)
return (
<ul className='list-unstyled'>
{keys.map(key => (
<li key={key}>{_.keyValue(key, pbdDeviceConfig[key])}</li>
))}
</ul>
)
},
},
{
name: _('pbdStatus'),
itemRenderer: storage => (
@@ -150,7 +137,6 @@ export default connectStore(() => {
return {
attached: pbd.attached,
pbdDeviceConfig: pbd.device_config,
format: sr.SR_type,
free: size > 0 ? size - usage : 0,
id: sr.id,

View File

@@ -10,7 +10,6 @@ import Link from 'link'
import Page from '../page'
import PropTypes from 'prop-types'
import React from 'react'
import SelectBootFirmware from 'select-boot-firmware'
import store from 'store'
import Tags from 'tags'
import Tooltip from 'tooltip'
@@ -335,7 +334,6 @@ export default class NewVm extends BaseComponent {
cpuWeight: '',
existingDisks: {},
fastClone: true,
hvmBootFirmware: '',
installMethod: 'noConfigDrive',
multipleVms: false,
name_label: '',
@@ -503,8 +501,6 @@ export default class NewVm extends BaseComponent {
tags: state.tags,
vgpuType: get(() => state.vgpuType.id),
gpuGroup: get(() => state.vgpuType.gpuGroup),
hvmBootFirmware:
state.hvmBootFirmware === '' ? undefined : state.hvmBootFirmware,
}
return state.multipleVms
@@ -580,7 +576,6 @@ export default class NewVm extends BaseComponent {
CPUs: template.CPUs.number,
cpuCap: '',
cpuWeight: '',
hvmBootFirmware: defined(() => template.boot.firmware, ''),
memoryDynamicMax: template.memory.dynamic[1],
// installation
installMethod:
@@ -757,11 +752,6 @@ export default class NewVm extends BaseComponent {
template => template && template.name_label === 'CoreOS'
)
_isHvm = createSelector(
() => this.state.template,
template => template && template.virtualizationMode === 'hvm'
)
// On change -------------------------------------------------------------------
_onChangeSshKeys = keys =>
@@ -910,8 +900,6 @@ export default class NewVm extends BaseComponent {
_getRedirectionUrl = id =>
this.state.state.multipleVms ? '/home' : `/vms/${id}`
_handleBootFirmware = value => this._setState({ hvmBootFirmware: value })
// MAIN ------------------------------------------------------------------------
_renderHeader = () => {
@@ -1175,8 +1163,10 @@ export default class NewVm extends BaseComponent {
</LineItem>
<br />
<LineItem>
<Tooltip content={CAN_CLOUD_INIT ? undefined : _('premiumOnly')}>
<label>
<label>
<Tooltip
content={CAN_CLOUD_INIT ? undefined : _('premiumOnly')}
>
<input
checked={installMethod === 'SSH'}
disabled={!CAN_CLOUD_INIT}
@@ -1185,10 +1175,10 @@ export default class NewVm extends BaseComponent {
type='radio'
value='SSH'
/>
&nbsp;
{_('newVmSshKey')}
</label>
</Tooltip>
</Tooltip>
&nbsp;
{_('newVmSshKey')}
</label>
&nbsp;
<span className={classNames('input-group', styles.fixedWidth)}>
<DebounceInput
@@ -1216,8 +1206,10 @@ export default class NewVm extends BaseComponent {
</LineItem>
<br />
<LineItem>
<Tooltip content={CAN_CLOUD_INIT ? undefined : _('premiumOnly')}>
<label>
<label>
<Tooltip
content={CAN_CLOUD_INIT ? undefined : _('premiumOnly')}
>
<input
checked={installMethod === 'customConfig'}
disabled={!CAN_CLOUD_INIT}
@@ -1226,10 +1218,10 @@ export default class NewVm extends BaseComponent {
type='radio'
value='customConfig'
/>
&nbsp;
{_('newVmCustomConfig')}
</label>
</Tooltip>
</Tooltip>
&nbsp;
{_('newVmCustomConfig')}
</label>
&nbsp;
<AvailableTemplateVars />
&nbsp;
@@ -1638,7 +1630,6 @@ export default class NewVm extends BaseComponent {
bootAfterCreate,
cpuCap,
cpuWeight,
hvmBootFirmware,
memoryDynamicMin,
memoryDynamicMax,
memoryStaticMax,
@@ -1650,11 +1641,10 @@ export default class NewVm extends BaseComponent {
share,
showAdvanced,
tags,
template,
} = this.state.state
const { isAdmin } = this.props
const { formatMessage } = this.props.intl
const isHvm = this._isHvm()
return (
<Section
icon='new-vm-advanced'
@@ -1837,7 +1827,7 @@ export default class NewVm extends BaseComponent {
</Item>
</SectionContent>
),
isHvm && (
template && template.virtualizationMode === 'hvm' && (
<SectionContent>
<Item label={_('vmVgpu')}>
<SelectVgpuType
@@ -1847,16 +1837,6 @@ export default class NewVm extends BaseComponent {
</Item>
</SectionContent>
),
isHvm && (
<SectionContent>
<Item label={_('vmBootFirmware')}>
<SelectBootFirmware
onChange={this._handleBootFirmware}
value={hvmBootFirmware}
/>
</Item>
</SectionContent>
),
]}
</Section>
)

View File

@@ -4,14 +4,8 @@ import decorate from 'apply-decorators'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import Wizard, { Section } from 'wizard'
import { addSubscriptions, connectStore } from 'utils'
import {
createBondedNetwork,
createNetwork,
createPrivateNetwork,
getBondModes,
subscribePlugins,
} from 'xo'
import { connectStore } from 'utils'
import { createBondedNetwork, createNetwork, getBondModes } from 'xo'
import { createGetObject, getIsPoolAdmin } from 'selectors'
import { injectIntl } from 'react-intl'
import { injectState, provideState } from 'reaclette'
@@ -27,8 +21,6 @@ const EMPTY = {
bonded: false,
bondMode: undefined,
description: '',
encapsulation: 'gre',
isPrivate: false,
mtu: '',
name: '',
pif: undefined,
@@ -37,9 +29,6 @@ const EMPTY = {
}
const NewNetwork = decorate([
addSubscriptions({
plugins: subscribePlugins,
}),
connectStore(() => ({
isPoolAdmin: getIsPoolAdmin,
pool: createGetObject((_, props) => props.location.query.pool),
@@ -53,26 +42,11 @@ const NewNetwork = decorate([
onChangeMode: (_, bondMode) => ({ bondMode }),
onChangePif: (_, value) => ({ bonded }) =>
bonded ? { pifs: value } : { pif: value },
onChangeEncapsulation(_, encapsulation) {
return { encapsulation: encapsulation.value }
},
reset: () => EMPTY,
toggleBonded() {
const { bonded, isPrivate } = this.state
return {
...EMPTY,
bonded: !bonded,
isPrivate: bonded ? isPrivate : false,
}
},
togglePrivate() {
const { bonded, isPrivate } = this.state
return {
...EMPTY,
isPrivate: !isPrivate,
bonded: isPrivate ? bonded : false,
}
},
toggleBonded: () => ({ bonded }) => ({
...EMPTY,
bonded: !bonded,
}),
},
computed: {
modeOptions: ({ bondModes }) =>
@@ -84,10 +58,6 @@ const NewNetwork = decorate([
: [],
pifPredicate: (_, { pool }) => pif =>
pif.vlan === -1 && pif.$host === (pool && pool.master),
isSdnControllerLoaded: (state, { plugins = [] }) =>
plugins.some(
plugin => plugin.name === 'sdn-controller' && plugin.loaded
),
},
}),
injectState,
@@ -101,9 +71,7 @@ const NewNetwork = decorate([
const {
bonded,
bondMode,
isPrivate,
description,
encapsulation,
mtu,
name,
pif,
@@ -120,13 +88,6 @@ const NewNetwork = decorate([
pool: pool.id,
vlan,
})
: isPrivate
? createPrivateNetwork({
poolId: pool.id,
networkName: name,
networkDescription: description,
encapsulation: encapsulation,
})
: createNetwork({
description,
mtu,
@@ -171,9 +132,7 @@ const NewNetwork = decorate([
const {
bonded,
bondMode,
isPrivate,
description,
encapsulation,
modeOptions,
mtu,
name,
@@ -181,7 +140,6 @@ const NewNetwork = decorate([
pifPredicate,
pifs,
vlan,
isSdnControllerLoaded,
} = state
const { formatMessage } = intl
return (
@@ -194,112 +152,69 @@ const NewNetwork = decorate([
<Toggle onChange={effects.toggleBonded} value={bonded} />{' '}
<label>{_('bondedNetwork')}</label>
</div>
<div>
<Toggle
disabled={!isSdnControllerLoaded}
onChange={effects.togglePrivate}
value={isPrivate}
/>{' '}
<label>{_('privateNetwork')}</label>
</div>
</Section>
<Section icon='info' title='newNetworkInfo'>
{isPrivate ? (
<div className='form-group'>
<label>{_('newNetworkName')}</label>
<input
className='form-control'
name='name'
onChange={effects.linkState}
required
type='text'
value={name}
/>
<label>{_('newNetworkDescription')}</label>
<input
className='form-control'
name='description'
onChange={effects.linkState}
type='text'
value={description}
/>
<label>{_('newNetworkEncapsulation')}</label>
<Select
className='form-control'
name='encapsulation'
onChange={effects.onChangeEncapsulation}
options={[
{ label: 'GRE', value: 'gre' },
{ label: 'VxLAN', value: 'vxlan' },
]}
value={encapsulation}
/>
</div>
) : (
<div className='form-group'>
<label>{_('newNetworkInterface')}</label>
<SelectPif
multi={bonded}
onChange={effects.onChangePif}
predicate={pifPredicate}
required={bonded}
value={bonded ? pifs : pif}
/>
<label>{_('newNetworkName')}</label>
<input
className='form-control'
name='name'
onChange={effects.linkState}
required
type='text'
value={name}
/>
<label>{_('newNetworkDescription')}</label>
<input
className='form-control'
name='description'
onChange={effects.linkState}
type='text'
value={description}
/>
<label>{_('newNetworkMtu')}</label>
<input
className='form-control'
name='mtu'
onChange={effects.linkState}
placeholder={formatMessage(
messages.newNetworkDefaultMtu
)}
type='text'
value={mtu}
/>
{bonded ? (
<div>
<label>{_('newNetworkBondMode')}</label>
<Select
onChange={effects.onChangeMode}
options={modeOptions}
required
value={bondMode}
/>
</div>
) : (
<div>
<label>{_('newNetworkVlan')}</label>
<input
className='form-control'
name='vlan'
onChange={effects.linkState}
placeholder={formatMessage(
messages.newNetworkDefaultVlan
)}
type='text'
value={vlan}
/>
</div>
)}
</div>
)}
<div className='form-group'>
<label>{_('newNetworkInterface')}</label>
<SelectPif
multi={bonded}
onChange={effects.onChangePif}
predicate={pifPredicate}
required={bonded}
value={bonded ? pifs : pif}
/>
<label>{_('newNetworkName')}</label>
<input
className='form-control'
name='name'
onChange={effects.linkState}
required
type='text'
value={name}
/>
<label>{_('newNetworkDescription')}</label>
<input
className='form-control'
name='description'
onChange={effects.linkState}
type='text'
value={description}
/>
<label>{_('newNetworkMtu')}</label>
<input
className='form-control'
name='mtu'
onChange={effects.linkState}
placeholder={formatMessage(messages.newNetworkDefaultMtu)}
type='text'
value={mtu}
/>
{bonded ? (
<div>
<label>{_('newNetworkBondMode')}</label>
<Select
onChange={effects.onChangeMode}
options={modeOptions}
required
value={bondMode}
/>
</div>
) : (
<div>
<label>{_('newNetworkVlan')}</label>
<input
className='form-control'
name='vlan'
onChange={effects.linkState}
placeholder={formatMessage(
messages.newNetworkDefaultVlan
)}
type='text'
value={vlan}
/>
</div>
)}
</div>
</Section>
</Wizard>
<div className='form-group pull-right'>

View File

@@ -18,7 +18,7 @@ import { confirm } from 'modal'
import { adminOnly, connectStore, formatSize } from 'utils'
import { Container, Row, Col } from 'grid'
import { injectIntl } from 'react-intl'
import { Password, Select } from 'form'
import { Password, Select, Toggle } from 'form'
import { SelectHost } from 'select-objects'
import {
createFilter,
@@ -33,7 +33,6 @@ import {
createSrLvm,
createSrNfs,
createSrHba,
createSrZfs,
probeSrIscsiExists,
probeSrIscsiIqns,
probeSrIscsiLuns,
@@ -41,17 +40,12 @@ import {
probeSrNfsExists,
probeSrHba,
probeSrHbaExists,
probeZfs,
reattachSrIso,
reattachSr,
} from 'xo'
// ===================================================================
const NFS_VERSIONS = ['4', '4.1']
// ===================================================================
class SelectScsiId extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
@@ -198,15 +192,14 @@ class SelectLun extends Component {
// ===================================================================
const SR_TYPE_TO_LABEL = {
ext: 'ext (local)',
ext: 'Local Ext',
hba: 'HBA',
iscsi: 'iSCSI',
local: 'Local',
lvm: 'LVM (local)',
lvm: 'Local LVM',
nfs: 'NFS',
nfsiso: 'NFS ISO',
smb: 'SMB',
zfs: 'ZFS (local)',
}
const SR_GROUP_TO_LABEL = {
@@ -215,7 +208,7 @@ const SR_GROUP_TO_LABEL = {
}
const typeGroups = {
vdisr: ['ext', 'hba', 'iscsi', 'lvm', 'nfs', 'zfs'],
vdisr: ['ext', 'hba', 'iscsi', 'lvm', 'nfs'],
isosr: ['local', 'nfsiso', 'smb'],
}
@@ -244,7 +237,6 @@ export default class New extends Component {
lockCreation: undefined,
lun: undefined,
luns: undefined,
nfsVersion: '',
hbaDevices: undefined,
name: undefined,
path: undefined,
@@ -253,7 +245,6 @@ export default class New extends Component {
unused: undefined,
usage: undefined,
used: undefined,
zfsPools: undefined,
}
this.getHostSrs = createFilter(
() => this.props.srs,
@@ -275,18 +266,8 @@ export default class New extends Component {
port,
server,
username,
zfsLocation,
} = this.refs
const {
host,
iqn,
lun,
nfsOptions,
nfsVersion,
path,
scsiId,
type,
} = this.state
const { host, iqn, lun, path, type, scsiId, nfs4, nfsOptions } = this.state
const createMethodFactories = {
nfs: () =>
@@ -296,7 +277,7 @@ export default class New extends Component {
description.value,
server.value,
path,
nfsVersion !== '' ? nfsVersion : undefined,
nfs4 ? '4' : undefined,
nfsOptions
),
hba: async () => {
@@ -349,8 +330,6 @@ export default class New extends Component {
createSrLvm(host.id, name.value, description.value, device.value),
ext: () =>
createSrExt(host.id, name.value, description.value, device.value),
zfs: () =>
createSrZfs(host.id, name.value, description.value, zfsLocation.value),
local: () =>
createSrIso(
host.id,
@@ -388,10 +367,7 @@ export default class New extends Component {
}
}
_handleSrHostSelection = async host => {
this.setState({ host })
await this._probe(host, this.state.type)
}
_handleSrHostSelection = host => this.setState({ host })
_handleNameChange = event => this.setState({ name: event.target.value })
_handleDescriptionChange = event =>
this.setState({ description: event.target.value })
@@ -402,13 +378,20 @@ export default class New extends Component {
hbaDevices: undefined,
iqns: undefined,
paths: undefined,
summary: includes(['ext', 'lvm', 'local', 'smb', 'hba', 'zfs'], type),
summary: includes(['ext', 'lvm', 'local', 'smb', 'hba'], type),
type,
unused: undefined,
usage: undefined,
used: undefined,
})
await this._probe(this.state.host, type)
if (type === 'hba' && this.state.host !== undefined) {
this.setState(({ loading }) => ({ loading: loading + 1 }))
const hbaDevices = await probeSrHba(this.state.host.id)::ignoreErrors()
this.setState(({ loading }) => ({
hbaDevices,
loading: loading - 1,
}))
}
}
_handleSrHbaSelection = async scsiId => {
@@ -539,31 +522,6 @@ export default class New extends Component {
}
}
_handleNfsVersion = ({ target: { value } }) => {
this.setState({
nfsVersion: value,
})
}
_probe = async (host, type) => {
const probeMethodFactories = {
hba: async hostId => ({
hbaDevices: await probeSrHba(hostId)::ignoreErrors(),
}),
zfs: async hostId => ({
zfsPools: await probeZfs(hostId)::ignoreErrors(),
}),
}
if (probeMethodFactories[type] !== undefined && host != null) {
this.setState(({ loading }) => ({ loading: loading + 1 }))
const probeResult = await probeMethodFactories[type](host.id)
this.setState(({ loading }) => ({
loading: loading - 1,
...probeResult,
}))
}
}
_reattach = async uuid => {
const { host, type } = this.state
@@ -608,7 +566,6 @@ export default class New extends Component {
lockCreation,
lun,
luns,
nfsVersion,
path,
paths,
summary,
@@ -616,7 +573,6 @@ export default class New extends Component {
unused,
usage,
used,
zfsPools,
} = this.state
const { formatMessage } = this.props.intl
@@ -701,22 +657,10 @@ export default class New extends Component {
</div>
</fieldset>,
<fieldset>
<label htmlFor='selectNfsVersion'>{_('newSrNfs')}</label>
<select
className='form-control'
id='selectNfsVersion'
onChange={this._handleNfsVersion}
value={nfsVersion}
>
<option value=''>
{formatMessage(messages.newSrNfsDefaultVersion)}
</option>
{map(NFS_VERSIONS, option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<label>{_('newSrUseNfs4')}</label>
<div>
<Toggle onChange={this.toggleState('nfs4')} />
</div>
</fieldset>,
<fieldset>
<label>{_('newSrNfsOptions')}</label>
@@ -915,31 +859,6 @@ export default class New extends Component {
/>
</fieldset>
)}
{type === 'zfs' && (
<fieldset>
<label htmlFor='selectSrLocation'>
{_('srLocation')}
</label>
<select
className='form-control'
defaultValue=''
id='selectSrLocation'
ref='zfsLocation'
required
>
<option value=''>
{isEmpty(zfsPools)
? formatMessage(messages.noSharedZfsAvailable)
: formatMessage(messages.noSelectedValue)}
</option>
{map(zfsPools, (pool, poolName) => (
<option key={poolName} value={pool.mountpoint}>
{poolName} - {pool.mountpoint}
</option>
))}
</select>
</fieldset>
)}
</fieldset>
)}
{loading !== 0 && <Icon icon='loading' />}

View File

@@ -55,19 +55,6 @@ const HOST_COLUMNS = [
},
sortCriteria: (pbd, hosts) => hosts[pbd.host].name_description,
},
{
name: _('pbdDetails'),
itemRenderer: ({ device_config: deviceConfig }) => {
const keys = Object.keys(deviceConfig)
return (
<ul className='list-unstyled'>
{keys.map(key => (
<li key={key}>{_.keyValue(key, deviceConfig[key])}</li>
))}
</ul>
)
},
},
{
name: _('pbdStatus'),
itemRenderer: pbd => (
@@ -111,17 +98,14 @@ const HOST_WITH_PATHS_COLUMNS = [
}
const [nActives, nPaths] = getIscsiPaths(pbd)
const nSessions = pbd.otherConfig.iscsi_sessions
return (
<span>
{nActives !== undefined &&
nPaths !== undefined &&
_('hostMultipathingPaths', {
nActives,
nPaths,
})}{' '}
{nSessions !== undefined && _('iscsiSessions', { nSessions })}
</span>
nActives !== undefined &&
nPaths !== undefined &&
_('hostMultipathingPaths', {
nActives,
nPaths,
nSessions: pbd.otherConfig.iscsi_sessions,
})
)
},
sortCriteria: (pbd, hosts) => get(() => hosts[pbd.host].multipathing),

View File

@@ -8,7 +8,6 @@ import Icon from 'icon'
import Link from 'link'
import React from 'react'
import renderXoItem from 'render-xo-item'
import SelectBootFirmware from 'select-boot-firmware'
import TabButton from 'tab-button'
import Tooltip from 'tooltip'
import { error } from 'notification'
@@ -511,11 +510,6 @@ export default class TabAdvanced extends Component {
_onChangeCpuMask = cpuMask =>
editVm(this.props.vm, { cpuMask: map(cpuMask, 'value') })
_handleBootFirmware = value =>
editVm(this.props.vm, {
hvmBootFirmware: value !== '' ? value : null,
})
_onNicTypeChange = value =>
editVm(this.props.vm, { nicType: value === '' ? null : value })
@@ -839,17 +833,6 @@ export default class TabAdvanced extends Component {
</td>
</tr>
)}
{vm.virtualizationMode === 'hvm' && (
<tr>
<th>{_('vmBootFirmware')}</th>
<td>
<SelectBootFirmware
onChange={this._handleBootFirmware}
value={defined(() => vm.boot.firmware, '')}
/>
</td>
</tr>
)}
</tbody>
</table>
<br />

View File

@@ -13,7 +13,7 @@ import Tooltip from 'tooltip'
import { isIp, isIpV4 } from 'ip-utils'
import { Container, Row, Col } from 'grid'
import { injectIntl } from 'react-intl'
import { Number, Text, XoSelect } from 'editable'
import { XoSelect, Text } from 'editable'
import {
addSubscriptions,
connectStore,
@@ -331,17 +331,6 @@ const COLUMNS = [
name: _('vifNetworkLabel'),
sortCriteria: (vif, userData) => userData.networks[vif.$network].name_label,
},
{
itemRenderer: ({ id, rateLimit }) => (
<Number
nullable
onChange={rateLimit => setVif(id, { rateLimit })}
value={rateLimit === undefined ? '' : rateLimit}
/>
),
name: _('vifRateLimitLabel'),
sortCriteria: 'rateLimit',
},
{
component: VifAllowedIps,
name: _('vifAllowedIps'),

View File

@@ -120,10 +120,7 @@ const Updates = decorate([
linkState,
onChannelChange: (_, channel) => ({ channel }),
async register() {
const {
props: { xoaRegisterState },
state,
} = this
const { state } = this
const { isRegistered } = state
if (isRegistered) {
@@ -133,7 +130,7 @@ const Updates = decorate([
body: (
<p>
{_('alreadyRegisteredModalText', {
email: xoaRegisterState.email,
email: this.props.xoaRegisterState.email,
})}
</p>
),
@@ -147,7 +144,7 @@ const Updates = decorate([
}
state.askRegisterAgain = false
const { email = xoaRegisterState.email, password } = state
const { email, password } = state
await xoaUpdater.register(email, password, isRegistered)
return initialRegistrationState()
@@ -474,7 +471,7 @@ const Updates = decorate([
</div>{' '}
<div className='form-group'>
<Password
disabled={helper(state, xoaRegisterState, 'email') === ''}
disabled={state.email === undefined}
name='password'
onChange={effects.linkState}
placeholder={formatMessage(

451
yarn.lock
View File

@@ -2,7 +2,7 @@
# yarn lockfile v1
"@babel/cli@^7.0.0", "@babel/cli@^7.1.5", "@babel/cli@^7.4.4":
"@babel/cli@^7.0.0", "@babel/cli@^7.1.5":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.4.4.tgz#5454bb7112f29026a4069d8e6f0e1794e651966c"
integrity sha512-XGr5YjQSjgTa6OzQZY57FAJsdeVSAKR/u/KA5exWIz66IKtv/zXtHy+fIZcMry/EgYegwuHE7vzGnrFhjdIAsQ==
@@ -26,17 +26,17 @@
dependencies:
"@babel/highlight" "^7.0.0"
"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.5", "@babel/core@^7.4.4":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.5.tgz#081f97e8ffca65a9b4b0fdc7e274e703f000c06a"
integrity sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==
"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.5":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.4.tgz#84055750b05fcd50f9915a826b44fa347a825250"
integrity sha512-lQgGX3FPRgbz2SKmhMtYgJvVzGZrmjaF4apZ2bLwofAKiSjxU0drPh4S/VasyYXwaTs+A1gvQ45BN8SQJzHsQQ==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.4.4"
"@babel/helpers" "^7.4.4"
"@babel/parser" "^7.4.5"
"@babel/parser" "^7.4.4"
"@babel/template" "^7.4.4"
"@babel/traverse" "^7.4.5"
"@babel/traverse" "^7.4.4"
"@babel/types" "^7.4.4"
convert-source-map "^1.1.0"
debug "^4.1.0"
@@ -242,10 +242,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872"
integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.4.tgz#5977129431b8fe33471730d255ce8654ae1250b6"
integrity sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==
"@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "7.2.0"
@@ -606,12 +606,12 @@
"@babel/helper-module-transforms" "^7.1.0"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106"
integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==
"@babel/plugin-transform-named-capturing-groups-regex@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.4.tgz#5611d96d987dfc4a3a81c4383bb173361037d68d"
integrity sha512-Ki+Y9nXBlKfhD+LXaRS7v95TtTGYRAf9Y1rTDiE75zf8YQz4GDaWRXosMfJBXxnk88mGFjWdCRIeqDbon7spYA==
dependencies:
regexp-tree "^0.1.6"
regexp-tree "^0.1.0"
"@babel/plugin-transform-new-target@^7.4.4":
version "7.4.4"
@@ -644,12 +644,12 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-regenerator@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f"
integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==
"@babel/plugin-transform-regenerator@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.4.tgz#5b4da4df79391895fca9e28f99e87e22cfc02072"
integrity sha512-Zz3w+pX1SI0KMIiqshFZkwnVGUhDZzpX2vtPzfJBKQQq8WsP/Xy9DNdELWivxcKOCX/Pywge4SiEaPaLtoDT4g==
dependencies:
regenerator-transform "^0.14.0"
regenerator-transform "^0.13.4"
"@babel/plugin-transform-reserved-words@^7.2.0":
version "7.2.0"
@@ -712,10 +712,10 @@
core-js "^2.6.5"
regenerator-runtime "^0.13.2"
"@babel/preset-env@^7.0.0", "@babel/preset-env@^7.1.5", "@babel/preset-env@^7.4.4":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.5.tgz#2fad7f62983d5af563b5f3139242755884998a58"
integrity sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==
"@babel/preset-env@^7.0.0", "@babel/preset-env@^7.1.5":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.4.tgz#b6f6825bfb27b3e1394ca3de4f926482722c1d6f"
integrity sha512-FU1H+ACWqZZqfw1x2G1tgtSSYSfxJLkpaUQL37CenULFARDo+h4xJoVHzRoHbK+85ViLciuI7ME4WTIhFRBBlw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
@@ -746,12 +746,12 @@
"@babel/plugin-transform-modules-commonjs" "^7.4.4"
"@babel/plugin-transform-modules-systemjs" "^7.4.4"
"@babel/plugin-transform-modules-umd" "^7.2.0"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.4.4"
"@babel/plugin-transform-new-target" "^7.4.4"
"@babel/plugin-transform-object-super" "^7.2.0"
"@babel/plugin-transform-parameters" "^7.4.4"
"@babel/plugin-transform-property-literals" "^7.2.0"
"@babel/plugin-transform-regenerator" "^7.4.5"
"@babel/plugin-transform-regenerator" "^7.4.4"
"@babel/plugin-transform-reserved-words" "^7.2.0"
"@babel/plugin-transform-shorthand-properties" "^7.2.0"
"@babel/plugin-transform-spread" "^7.2.0"
@@ -760,8 +760,8 @@
"@babel/plugin-transform-typeof-symbol" "^7.2.0"
"@babel/plugin-transform-unicode-regex" "^7.4.4"
"@babel/types" "^7.4.4"
browserslist "^4.6.0"
core-js-compat "^3.1.1"
browserslist "^4.5.2"
core-js-compat "^3.0.0"
invariant "^2.2.2"
js-levenshtein "^1.1.3"
semver "^5.5.0"
@@ -794,9 +794,9 @@
regenerator-runtime "^0.12.0"
"@babel/runtime@^7.1.2":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12"
integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==
dependencies:
regenerator-runtime "^0.13.2"
@@ -809,16 +809,16 @@
"@babel/parser" "^7.4.4"
"@babel/types" "^7.4.4"
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.5.tgz#4e92d1728fd2f1897dafdd321efbff92156c3216"
integrity sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.4.tgz#0776f038f6d78361860b6823887d4f3937133fe8"
integrity sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.4.4"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.4.4"
"@babel/parser" "^7.4.5"
"@babel/parser" "^7.4.4"
"@babel/types" "^7.4.4"
debug "^4.1.0"
globals "^11.1.0"
@@ -1138,14 +1138,14 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*", "@types/node@^12.0.2":
version "12.0.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.4.tgz#46832183115c904410c275e34cf9403992999c32"
integrity sha512-j8YL2C0fXq7IONwl/Ud5Kt0PeXw22zGERt+HSSnwbKOJVsAGkEz3sFCYwaF9IOuoG1HOtE0vKCj6sXF7Q0+Vaw==
version "12.0.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40"
integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==
"@types/node@^8.0.7":
version "8.10.49"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.49.tgz#f331afc5efed0796798e5591d6e0ece636969b7b"
integrity sha512-YX30JVx0PvSmJ3Eqr74fYLGeBxD+C7vIL20ek+GGGLJeUbVYRUW3EzyAXpIRA0K8c8o0UWqR/GwEFYiFoz1T8w==
version "8.10.48"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.48.tgz#e385073561643a9ba6199a1985ffc03530f90781"
integrity sha512-c35YEBTkL4rzXY2ucpSKy+UYHjUBIIkuJbWYbsGIrKLEWU5dgJMmLkkIb3qeC3O3Tpb1ZQCwecscvJTDjDjkRw==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@@ -1276,16 +1276,16 @@ acorn-jsx@^5.0.0:
integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==
acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.7.0.tgz#aac6a559d27af6176b076ab6fb13c5974c213e3b"
integrity sha512-XhahLSsCB6X6CJbe+uNu3Mn9sJBNFxtBN9NLgAOQovfS6Kh0lDUtmlclhjn9CvEK7A7YyRU13PXlNcpSiLI9Yw==
version "1.6.2"
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.6.2.tgz#b7d7ceca6f22e6417af933a62cad4de01048d5d2"
integrity sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==
dependencies:
acorn "^6.1.1"
acorn "^6.0.2"
acorn-dynamic-import "^4.0.0"
acorn-walk "^6.1.1"
acorn-walk "^6.1.0"
xtend "^4.0.1"
acorn-walk@^6.0.1, acorn-walk@^6.1.1:
acorn-walk@^6.0.1, acorn-walk@^6.1.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913"
integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==
@@ -1305,7 +1305,7 @@ acorn@^4.0.4, acorn@~4.0.2:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=
acorn@^6.0.1, acorn@^6.0.7, acorn@^6.1.1:
acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.7:
version "6.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f"
integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==
@@ -1702,12 +1702,12 @@ array-unique@^0.3.2:
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
array.prototype.find@^2.0.4:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7"
integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==
version "2.0.4"
resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90"
integrity sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=
dependencies:
define-properties "^1.1.3"
es-abstract "^1.13.0"
define-properties "^1.1.2"
es-abstract "^1.7.0"
array.prototype.flat@^1.2.1:
version "1.2.1"
@@ -2907,9 +2907,9 @@ blocked@^1.2.1:
integrity sha1-4i7+dnhjxlq4GX9iUpKRBOHsnOI=
bluebird@^3.5.1:
version "3.5.5"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
version "3.5.4"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714"
integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.8"
@@ -3169,14 +3169,14 @@ browserslist@^3.2.6:
caniuse-lite "^1.0.30000844"
electron-to-chromium "^1.3.47"
browserslist@^4.5.4, browserslist@^4.6.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.1.tgz#ee5059b1aec18cbec9d055d6cb5e24ae50343a9b"
integrity sha512-1MC18ooMPRG2UuVFJTHFIAkk6mpByJfxCrnUyvSlu/hyQSFHMrlhM02SzNuCV+quTP4CKmqtOMAIjrifrpBJXQ==
browserslist@^4.5.2, browserslist@^4.5.4:
version "4.6.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.0.tgz#5274028c26f4d933d5b1323307c1d1da5084c9ff"
integrity sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==
dependencies:
caniuse-lite "^1.0.30000971"
electron-to-chromium "^1.3.137"
node-releases "^1.1.21"
caniuse-lite "^1.0.30000967"
electron-to-chromium "^1.3.133"
node-releases "^1.1.19"
bser@^2.0.0:
version "2.0.0"
@@ -3318,7 +3318,7 @@ cache-base@^1.0.1:
union-value "^1.0.0"
unset-value "^1.0.0"
cached-path-relative@^1.0.0, cached-path-relative@^1.0.2:
cached-path-relative@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db"
integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==
@@ -3405,10 +3405,10 @@ can-promise@0.0.1:
dependencies:
window-or-global "^1.0.1"
caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000971:
version "1.0.30000971"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000971.tgz#d1000e4546486a6977756547352bc96a4cfd2b13"
integrity sha512-TQFYFhRS0O5rdsmSbF1Wn+16latXYsQJat66f7S7lizXW1PVpWJeZw9wqqVLIjuxDRz7s7xRUj13QCfd8hKn6g==
caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000967:
version "1.0.30000969"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz#7664f571f2072657bde70b00a1fc1ba41f1942a9"
integrity sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==
capture-exit@^2.0.0:
version "2.0.0"
@@ -3648,9 +3648,9 @@ clone@^2.1.1:
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
cloneable-readable@^1.0.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec"
integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==
version "1.1.2"
resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65"
integrity sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==
dependencies:
inherits "^2.0.1"
process-nextick-args "^2.0.0"
@@ -3921,19 +3921,25 @@ copy-to-clipboard@^3, copy-to-clipboard@^3.0.8:
dependencies:
toggle-selection "^1.0.6"
core-js-compat@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.1.3.tgz#0cc3ba4c7f62928c2837e1cffbe8dc78b4f1ae14"
integrity sha512-EP018pVhgwsKHz3YoN1hTq49aRe+h017Kjz0NQz3nXV0cCRMvH3fLQl+vEPGr4r4J5sk4sU3tUC7U1aqTCeJeA==
core-js-compat@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.0.1.tgz#bff73ba31ca8687431b9c88f78d3362646fb76f0"
integrity sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g==
dependencies:
browserslist "^4.6.0"
core-js-pure "3.1.3"
semver "^6.1.0"
browserslist "^4.5.4"
core-js "3.0.1"
core-js-pure "3.0.1"
semver "^6.0.0"
core-js-pure@3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.3.tgz#4c90752d5b9471f641514f3728f51c1e0783d0b5"
integrity sha512-k3JWTrcQBKqjkjI0bkfXS0lbpWPxYuHWfMMjC1VDmzU4Q58IwSbuXSo99YO/hUHlw/EB4AlfA2PVxOGkrIq6dA==
core-js-pure@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.0.1.tgz#37358fb0d024e6b86d443d794f4e37e949098cbe"
integrity sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g==
core-js@3.0.1, core-js@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.1.tgz#1343182634298f7f38622f95e73f54e48ddf4738"
integrity sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==
core-js@^1.0.0:
version "1.2.7"
@@ -3941,14 +3947,9 @@ core-js@^1.0.0:
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5:
version "2.6.9"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
core-js@^3.0.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.3.tgz#95700bca5f248f5f78c0ec63e784eca663ec4138"
integrity sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA==
version "2.6.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
@@ -4020,7 +4021,7 @@ create-react-class@^15.5.1, create-react-class@^15.6.0:
loose-envify "^1.3.1"
object-assign "^4.1.1"
cross-env@^5.1.1, cross-env@^5.1.3, cross-env@^5.1.4, cross-env@^5.2.0:
cross-env@^5.1.1, cross-env@^5.1.3, cross-env@^5.1.4:
version "5.2.0"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2"
integrity sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==
@@ -4962,10 +4963,10 @@ ejs@^2.5.6:
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
electron-to-chromium@^1.3.137, electron-to-chromium@^1.3.47:
version "1.3.143"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.143.tgz#8b2a631ab75157aa53d0c2933275643b99ef580b"
integrity sha512-J9jOpxIljQZlV6GIP2fwAWq0T69syawU0sH3EW3O2Bgxquiy+veeIT5mBDRz+i3oHUSL1tvVgRKH3/4QiQh9Pg==
electron-to-chromium@^1.3.133, electron-to-chromium@^1.3.47:
version "1.3.135"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.135.tgz#f5799b95f2bcd8de17cde47d63392d83a4477041"
integrity sha512-xXLNstRdVsisPF3pL3H9TVZo2XkMILfqtD6RiWIUmDK2sFX1Bjwqmd8LBp0Kuo2FgKO63JXPoEVGm8WyYdwP0Q==
elliptic@^6.0.0:
version "6.4.1"
@@ -5091,7 +5092,7 @@ error-ex@^1.2.0, error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0:
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==
@@ -5232,9 +5233,9 @@ eslint-plugin-eslint-comments@^3.1.1:
ignore "^5.0.5"
eslint-plugin-import@^2.8.0:
version "2.17.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz#00548b4434c18faebaba04b24ae6198f280de189"
integrity sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==
version "2.17.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz#d227d5c6dc67eca71eb590d2bb62fb38d86e9fcb"
integrity sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g==
dependencies:
array-includes "^3.0.3"
contains-path "^0.1.0"
@@ -5246,19 +5247,19 @@ eslint-plugin-import@^2.8.0:
lodash "^4.17.11"
minimatch "^3.0.4"
read-pkg-up "^2.0.0"
resolve "^1.11.0"
resolve "^1.10.0"
eslint-plugin-node@^9.0.1:
version "9.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-9.1.0.tgz#f2fd88509a31ec69db6e9606d76dabc5adc1b91a"
integrity sha512-ZwQYGm6EoV2cfLpE1wxJWsfnKUIXfM/KM09/TlorkukgCAwmkgajEJnPCmyzoFPQQkmvo5DrW/nyKutNIw36Mw==
version "9.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-9.0.1.tgz#93e44626fa62bcb6efea528cee9687663dc03b62"
integrity sha512-fljT5Uyy3lkJzuqhxrYanLSsvaILs9I7CmQ31atTtZ0DoIzRbbvInBh4cQ1CrthFHInHYBQxfPmPt6KLHXNXdw==
dependencies:
eslint-plugin-es "^1.4.0"
eslint-utils "^1.3.1"
ignore "^5.1.1"
minimatch "^3.0.4"
resolve "^1.10.1"
semver "^6.1.0"
semver "^6.0.0"
eslint-plugin-promise@^4.0.0:
version "4.1.1"
@@ -5606,9 +5607,9 @@ express-session@^1.15.6:
uid-safe "~2.1.5"
express@^4.16.2:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
version "4.17.0"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.0.tgz#288af62228a73f4c8ea2990ba3b791bb87cd4438"
integrity sha512-1Z7/t3Z5ZnBG252gKUPyItc4xdeaA0X934ca2ewckAsVsw9EG71i++ZHZPYnus8g/s5Bty8IMpSVEuRkmwwPRQ==
dependencies:
accepts "~1.3.7"
array-flatten "1.1.1"
@@ -5753,9 +5754,9 @@ fast-future@~1.0.2:
integrity sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo=
fast-glob@^2.2.6:
version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
version "2.2.6"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295"
integrity sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==
dependencies:
"@mrmlnc/readdir-enhanced" "^2.2.1"
"@nodelib/fs.stat" "^1.1.2"
@@ -5909,13 +5910,6 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
find-up@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.0.0.tgz#c367f8024de92efb75f2d4906536d24682065c3a"
integrity sha512-zoH7ZWPkRdgwYCDVoQTzqjG8JSPANhtvLhh4KVUHyKnaUJJrNeFmWIkTcNuJmR3GLMEmGYEf2S2bjgx26JTF+Q==
dependencies:
locate-path "^5.0.0"
findup-sync@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc"
@@ -5973,10 +5967,10 @@ flatted@^2.0.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916"
integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==
flow-bin@^0.100.0:
version "0.100.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.100.0.tgz#729902726658cfa0a81425d6401f9625cf9f5534"
integrity sha512-jcethhgrslBJukH7Z7883ohFFpzLrdsOEwHxvn5NwuTWbNaE71GAl55/PEBRJwYpDvYkRlqgcNkANTv0x5XjqA==
flow-bin@^0.98.0:
version "0.98.1"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.98.1.tgz#a8d781621c91703df69928acc83c9777e2fcbb49"
integrity sha512-y1YzQgbFUX4EG6h2EO8PhyJeS0VxNgER8XsTwU8IXw4KozfneSmGVgw8y3TwAOza7rVhTlHEoli1xNuNW1rhPw==
flush-write-stream@^1.0.2:
version "1.1.1"
@@ -6258,13 +6252,6 @@ get-stream@^4.0.0:
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==
dependencies:
pump "^3.0.0"
get-uri@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.3.tgz#fa13352269781d75162c6fc813c9e905323fbab5"
@@ -7080,9 +7067,9 @@ ignore@^4.0.3, ignore@^4.0.6:
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
ignore@^5.0.5, ignore@^5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.2.tgz#e28e584d43ad7e92f96995019cc43b9e1ac49558"
integrity sha512-vdqWBp7MyzdmHkkRWV5nY+PfGRbYbahfuvsBCh277tq+w9zyNi7h5CYJCK0kmzti9kU+O/cB7sE8HvKv6aXAKQ==
version "5.1.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.1.tgz#2fc6b8f518aff48fef65a7f348ed85632448e4a5"
integrity sha512-DWjnQIFLenVrwyRCKZT+7a7/U4Cqgar4WG8V++K3hw+lrW1hc/SIwdiGmtxKCVACmHULTuGeBbHJmbwW7/sAvA==
immutable@^4.0.0-rc.9:
version "4.0.0-rc.12"
@@ -7221,9 +7208,9 @@ interpret@^1.1.0:
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
intl-format-cache@^2.0.5:
version "2.2.9"
resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.2.9.tgz#fb560de20c549cda20b569cf1ffb6dc62b5b93b4"
integrity sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ==
version "2.1.0"
resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.1.0.tgz#04a369fecbfad6da6005bae1f14333332dcf9316"
integrity sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY=
intl-messageformat-parser@1.4.0:
version "1.4.0"
@@ -7238,9 +7225,9 @@ intl-messageformat@^2.0.0, intl-messageformat@^2.1.0:
intl-messageformat-parser "1.4.0"
intl-relativeformat@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz#6aca95d019ec8d30b6c5653b6629f9983ea5b6c5"
integrity sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==
version "2.1.0"
resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#010f1105802251f40ac47d0e3e1a201348a255df"
integrity sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8=
dependencies:
intl-messageformat "^2.0.0"
@@ -7617,7 +7604,7 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isarray@^2.0.1:
isarray@^2.0.1, isarray@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.4.tgz#38e7bcbb0f3ba1b7933c86ba1894ddfc3781bbb7"
integrity sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==
@@ -8386,11 +8373,12 @@ l33teral@^3.0.3:
integrity sha1-mh3FJvvf9cSw80in41+6Zvdd6gc=
labeled-stream-splicer@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21"
integrity sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==
version "2.0.1"
resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz#9cffa32fd99e1612fd1d86a8db962416d5292926"
integrity sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==
dependencies:
inherits "^2.0.1"
isarray "^2.0.4"
stream-splicer "^2.0.0"
last-run@^1.1.0:
@@ -8732,13 +8720,6 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
lodash-compat@^3.10.1:
version "3.10.2"
resolved "https://registry.yarnpkg.com/lodash-compat/-/lodash-compat-3.10.2.tgz#c6940128a9d30f8e902cd2cf99fd0cba4ecfc183"
@@ -9322,7 +9303,7 @@ minimist@~0.0.1:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
minipass@^2.2.1, minipass@^2.3.5:
minipass@^2.2.1, minipass@^2.3.4:
version "2.3.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
@@ -9330,7 +9311,7 @@ minipass@^2.2.1, minipass@^2.3.5:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minizlib@^1.2.1:
minizlib@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
@@ -9386,13 +9367,13 @@ modular-cssify@^12:
through2 "^2.0.3"
module-deps@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.1.tgz#cfe558784060e926824f474b4e647287837cda50"
integrity sha512-UnEn6Ah36Tu4jFiBbJVUtt0h+iXqxpLqDvPS8nllbw5RZFmNJ1+Mz5BjYnM9ieH80zyxHkARGLnMIHlPK5bu6A==
version "6.2.0"
resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.0.tgz#d41a2e790245ce319171e4e7c4d8c73993ba3cd5"
integrity sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==
dependencies:
JSONStream "^1.0.3"
browser-resolve "^1.7.0"
cached-path-relative "^1.0.2"
cached-path-relative "^1.0.0"
concat-stream "~1.6.0"
defined "^1.0.0"
detective "^5.0.2"
@@ -9413,7 +9394,7 @@ moment-timezone@^0.5.13, moment-timezone@^0.5.14:
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0", moment@^2.10.6, moment@^2.20.1, moment@^2.22.1:
"moment@>= 2.9.0", moment@^2.10.6, moment@^2.20.1:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
@@ -9682,14 +9663,6 @@ node-notifier@^5.2.1:
shellwords "^0.1.1"
which "^1.3.0"
node-openssl-cert@^0.0.81:
version "0.0.81"
resolved "https://registry.yarnpkg.com/node-openssl-cert/-/node-openssl-cert-0.0.81.tgz#79b5c0d9767116799bc74c4bd94b965a4789f8ad"
integrity sha512-KPDU2mx+dL9Ryj9Pse2KkcwGQXoXmg8FrSluIMcw/l6l2PiOYd6k+91SON359XWIS/x32WFmeFGUOCRHEuoQtQ==
dependencies:
moment "^2.22.1"
tmp "^0.0.33"
node-pre-gyp@0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz#f11c07516dd92f87199dbc7e1838eab7cd56c9e0"
@@ -9722,10 +9695,10 @@ node-pre-gyp@^0.12.0:
semver "^5.3.0"
tar "^4"
node-releases@^1.1.21:
version "1.1.22"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.22.tgz#d90cd5adc59ab9b0f377d4f532b09656399c88bf"
integrity sha512-O6XpteBuntW1j86mw6LlovBIwTe+sO2+7vi9avQffNeIW4upgnaCVm6xrBWH+KATz7mNNRNNeEpuWB7dT6Cr3w==
node-releases@^1.1.19:
version "1.1.19"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.19.tgz#c492d1e381fea0350b338b646c27867e88e91b3d"
integrity sha512-SH/B4WwovHbulIALsQllAVwqZZD1kPmKCqrhGfR29dXjLAVZMHvBjD3S6nL9D/J9QkmZ1R92/0wCMDKXUUvyyA==
dependencies:
semver "^5.3.0"
@@ -9797,9 +9770,9 @@ nodemailer-markdown@^1.0.1:
marked "^0.6.2"
nodemailer@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.2.1.tgz#20d773925eb8f7a06166a0b62c751dc8290429f3"
integrity sha512-TagB7iuIi9uyNgHExo8lUDq3VK5/B0BpbkcjIgNvxbtVrjNqq0DwAOTuzALPVkK76kMhTSzIgHqg8X1uklVs6g==
version "6.1.1"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.1.1.tgz#09e88ef4b3646f01089c5d84d007b872141fb575"
integrity sha512-/x5MRIh56VyuuhLfcz+DL2SlBARpZpgQIf2A4Ao4hMb69MHSgDIMPwYmFwesGT1lkRDZ0eBSoym5+JoIZ3N+cQ==
noop-logger@^0.1.1:
version "0.1.1"
@@ -10186,9 +10159,9 @@ osenv@0, osenv@^0.1.4:
os-tmpdir "^1.0.0"
otplib@^11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/otplib/-/otplib-11.0.1.tgz#7d64aa87029f07c99c7f96819fb10cdb67dea886"
integrity sha512-oi57teljNyWTC/JqJztHOtSGeFNDiDh5C1myd+faocUtFAX27Sm1mbx69kpEJ8/JqrblI3kAm4Pqd6tZJoOIBQ==
version "11.0.0"
resolved "https://registry.yarnpkg.com/otplib/-/otplib-11.0.0.tgz#1b8b8691184e6022dac0b569ebd104c1e5451498"
integrity sha512-B9qxrYj0FU22nF39XOcKiK+yHdeg4uoensQQsRRlVnGJ26jugZNmJS2Wc9VinR+SjTfVpjDhzxZzpcXzlRkk5Q==
dependencies:
thirty-two "1.0.2"
@@ -10242,7 +10215,7 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
p-limit@^2.0.0, p-limit@^2.2.0:
p-limit@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2"
integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==
@@ -10263,13 +10236,6 @@ p-locate@^3.0.0:
dependencies:
p-limit "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-reduce@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
@@ -10464,7 +10430,7 @@ passport-oauth2@1.x.x:
uid2 "0.0.x"
utils-merge "1.x.x"
passport-saml@^1.1.0:
passport-saml@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/passport-saml/-/passport-saml-1.1.0.tgz#0fdd639b905b8f5db172bc07f2e73819a043c321"
integrity sha512-T2dKp++HQDg9dyJvb+5dyhIVVdIb5FX6DKMex0RALU16D65fiIbZ01vKZ2qtD+nW5nSOkbkfKJUolr9U689EdQ==
@@ -10667,11 +10633,11 @@ pkg-dir@^3.0.0:
find-up "^3.0.0"
pkg-dir@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
version "4.1.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.1.0.tgz#aaeb91c0d3b9c4f74a44ad849f4de34781ae01de"
integrity sha512-55k9QN4saZ8q518lE6EFgYiu95u3BWkSajCifhdQjvLvmr8IpnRbhI+UGpWJQfa0KzDguHeeWT1ccO1PmkOi3A==
dependencies:
find-up "^4.0.0"
find-up "^3.0.0"
platform@1.3.5, platform@^1.3.0, platform@^1.3.3:
version "1.3.5"
@@ -10894,13 +10860,6 @@ promise-toolbox@^0.12.1:
dependencies:
make-error "^1.3.2"
promise-toolbox@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/promise-toolbox/-/promise-toolbox-0.13.0.tgz#f4c73167be3f3b51d92167e9db888f1718a75b59"
integrity sha512-Z6u7EL9/QyY1zZqeqpEiKS7ygKwZyl0JL0ouno/en6vMliZZc4AmM0aFCrDAVxEyKqj2f3SpkW0lXEfAZsNWiQ==
dependencies:
make-error "^1.3.2"
promise-toolbox@^0.8.0:
version "0.8.3"
resolved "https://registry.yarnpkg.com/promise-toolbox/-/promise-toolbox-0.8.3.tgz#b757232a21d246d8702df50da6784932dd0f5348"
@@ -10923,9 +10882,9 @@ promise@^7.0.1, promise@^7.1.1:
asap "~2.0.3"
prompts@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.1.0.tgz#bf90bc71f6065d255ea2bdc0fe6520485c1b45db"
integrity sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==
version "2.0.4"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.0.4.tgz#179f9d4db3128b9933aa35f93a800d8fce76a682"
integrity sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==
dependencies:
kleur "^3.0.2"
sisteransi "^1.0.0"
@@ -11007,9 +10966,9 @@ pseudomap@^1.0.2:
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.24, psl@^1.1.28:
version "1.1.32"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.32.tgz#3f132717cf2f9c169724b2b6caf373cf694198db"
integrity sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==
version "1.1.31"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
public-encrypt@^4.0.0:
version "4.0.3"
@@ -11165,9 +11124,9 @@ pull-pushable@^2.0.0:
integrity sha1-Xy867UethpGfAbEqLpnW8b13ZYE=
pull-stream@^3.2.3, pull-stream@^3.4.0, pull-stream@^3.6.8:
version "3.6.12"
resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.12.tgz#11231c8dd77afe4ae30d1cd543873179e3b30a32"
integrity sha512-+LO1XIVyTMmeoH26UHznpgrgX2npTVYccTkMpgk/EyiQjFt1FmoNm+w+/zMLuz9U3bpvT5sSUicMKEe/2JjgEA==
version "3.6.11"
resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.11.tgz#601956610952a76defdcb18e4435e2478659cead"
integrity sha512-43brwtqO0OSltctKbW1mgzzKH4TNE8egkW+Y4BFzlDWiG2Ayl7VKr4SeuoKacfgPfUWcSwcPlHsf40BEqNR32A==
pull-window@^2.1.4:
version "2.1.4"
@@ -11742,9 +11701,9 @@ readable-stream@1.1.x, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1
string_decoder "~0.10.x"
"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.1, readable-stream@^3.0.2, readable-stream@^3.0.5, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.2.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
version "3.3.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9"
integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
@@ -11898,10 +11857,10 @@ regenerator-transform@^0.10.0:
babel-types "^6.19.0"
private "^0.1.6"
regenerator-transform@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.0.tgz#2ca9aaf7a2c239dd32e4761218425b8c7a86ecaf"
integrity sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==
regenerator-transform@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb"
integrity sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==
dependencies:
private "^0.1.6"
@@ -11920,10 +11879,10 @@ regex-not@^1.0.0, regex-not@^1.0.2:
extend-shallow "^3.0.2"
safe-regex "^1.1.0"
regexp-tree@^0.1.6:
version "0.1.10"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.10.tgz#d837816a039c7af8a8d64d7a7c3cf6a1d93450bc"
integrity sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==
regexp-tree@^0.1.0:
version "0.1.6"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.6.tgz#84900fa12fdf428a2ac25f04300382a7c0148479"
integrity sha512-LFrA98Dw/heXqDojz7qKFdygZmFoiVlvE1Zp7Cq2cvF+ZA+03Gmhy0k0PQlsC1jvHPiTUSs+pDHEuSWv6+6D7w==
regexpp@^2.0.1:
version "2.0.1"
@@ -12162,7 +12121,7 @@ resolve@1.1.7:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0:
resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232"
integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==
@@ -12332,10 +12291,10 @@ semver-greatest-satisfied-range@^1.1.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
semver@^6.0.0, semver@^6.1.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
semver@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65"
integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==
semver@~5.1.0:
version "5.1.1"
@@ -12823,9 +12782,9 @@ stream-shift@^1.0.0:
integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
stream-splicer@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.1.tgz#0b13b7ee2b5ac7e0609a7463d83899589a363fcd"
integrity sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==
version "2.0.0"
resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83"
integrity sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=
dependencies:
inherits "^2.0.1"
readable-stream "^2.0.2"
@@ -13074,9 +13033,9 @@ syntax-error@^1.1.1:
acorn-node "^1.2.0"
table@^5.2.3:
version "5.4.0"
resolved "https://registry.yarnpkg.com/table/-/table-5.4.0.tgz#d772a3216e68829920a41a32c18eda286c95d780"
integrity sha512-nHFDrxmbrkU7JAFKqKbDJXfzrX2UBsWmrieXFTGxiI5e4ncg3VqsZeI4EzNmX0ncp4XNGVeoxIWJXfCIXwrsvw==
version "5.3.3"
resolved "https://registry.yarnpkg.com/table/-/table-5.3.3.tgz#eae560c90437331b74200e011487a33442bd28b4"
integrity sha512-3wUNCgdWX6PNpOe3amTTPWPuF6VGvgzjKCaO1snFj0z7Y3mUPWf5+zDtxUVGispJkDECPmR29wbzh6bVMOHbcw==
dependencies:
ajv "^6.9.1"
lodash "^4.17.11"
@@ -13127,17 +13086,17 @@ tar@^2.0.0:
inherits "2"
tar@^4:
version "4.4.9"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.9.tgz#058fbb152f6fc45733e84585a40c39e59302e1b3"
integrity sha512-xisFa7Q2i3HOgfn+nmnWLGHD6Tm23hxjkx6wwGmgxkJFr6wxwXnJOdJYcZjL453PSdF0+bemO03+flAzkIdLBQ==
version "4.4.8"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
minipass "^2.3.5"
minizlib "^1.2.1"
minipass "^2.3.4"
minizlib "^1.1.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
yallist "^3.0.3"
yallist "^3.0.2"
test-exclude@^5.2.3:
version "5.2.3"
@@ -13418,9 +13377,9 @@ tslint-eslint-rules@^5.3.1:
tsutils "^3.0.0"
tslint@^5.9.1:
version "5.17.0"
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.17.0.tgz#f9f0ce2011d8e90debaa6e9b4975f24cd16852b8"
integrity sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w==
version "5.16.0"
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.16.0.tgz#ae61f9c5a98d295b9a4f4553b1b1e831c1984d67"
integrity sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==
dependencies:
"@babel/code-frame" "^7.0.0"
builtin-modules "^1.1.1"
@@ -13428,7 +13387,7 @@ tslint@^5.9.1:
commander "^2.12.1"
diff "^3.2.0"
glob "^7.1.1"
js-yaml "^3.13.1"
js-yaml "^3.13.0"
minimatch "^3.0.4"
mkdirp "^0.5.1"
resolve "^1.3.2"
@@ -13444,9 +13403,9 @@ tsutils@^2.29.0:
tslib "^1.8.1"
tsutils@^3.0.0:
version "3.13.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.13.0.tgz#904ae58d0d81a1d5c16112da7bca059f9283b0e2"
integrity sha512-wRtEjVU8Su72sDIDoqno5Scwt8x4eaF0teKO3m4hu8K1QFPnIZMM88CLafs2tapUeWnY9SwwO3bWeOt2uauBcg==
version "3.10.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.10.0.tgz#6f1c95c94606e098592b0dff06590cf9659227d6"
integrity sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==
dependencies:
tslib "^1.8.1"
@@ -13498,9 +13457,9 @@ typedarray@^0.0.6:
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.1.6:
version "3.5.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202"
integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==
version "3.4.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
typewise-core@^1.2, typewise-core@^1.2.0:
version "1.2.0"
@@ -13543,9 +13502,9 @@ uglify-js@^2.6.1:
uglify-to-browserify "~1.0.0"
uglify-js@^3.0.5, uglify-js@^3.1.4, uglify-js@^3.5.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
version "3.5.13"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.13.tgz#d2d8857b598d77f8764ae3bfcf90bb1df134d2bd"
integrity sha512-Lho+IJlquX6sdJgyKSJx/M9y4XbDd3ekPjD8S6HYmT5yVSwDtlSuca2w5hV4g2dIsp0Y/4orbfWxKexodmFv7w==
dependencies:
commander "~2.20.0"
source-map "~0.6.1"
@@ -14269,7 +14228,7 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.3:
yallist@^3.0.0, yallist@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==