feat(xo-server-netbox): new plugin to synchronize pools with Netbox (#5783)
Fixes #5633
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
- [Metadata Backup] Add a warning on restoring a metadata backup (PR [#5769](https://github.com/vatesfr/xen-orchestra/pull/5769))
|
||||
- [SAML] Compatible with users created with other authentication providers (PR [#5781](https://github.com/vatesfr/xen-orchestra/pull/5781))
|
||||
- [Netbox] [Plugin](https://xen-orchestra.com/docs/advanced.html#netbox) to synchronize pools, VMs and IPs with [Netbox](https://netbox.readthedocs.io/en/stable/) (PR [#5783](https://github.com/vatesfr/xen-orchestra/pull/5783))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -40,5 +41,6 @@
|
||||
- xen-api minor
|
||||
- xo-server-auth-saml minor
|
||||
- xo-server-backup-reports patch
|
||||
- xo-server-netbox minor
|
||||
- xo-web minor
|
||||
- xo-server patch
|
||||
|
||||
@@ -320,6 +320,39 @@ It works with few steps:
|
||||
|
||||
From there, you can even manage your existing resources with Terraform!
|
||||
|
||||
## Netbox
|
||||
|
||||
Synchronize your pools, VMs, network interfaces and IP addresses with your [Netbox](https://netbox.readthedocs.io/en/stable/) instance.
|
||||
|
||||

|
||||
|
||||
- Go to your Netbox interface
|
||||
- Configure prefixes:
|
||||
- Go to IPAM > Prefixes > Add
|
||||
- Manually create as many prefixes as needed for your infrastructure's IP addresses
|
||||
|
||||
:::warning
|
||||
XO will try to find the right prefix for each IP address. If it can't find a prefix that fits, the IP address won't be synchronized.
|
||||
:::
|
||||
|
||||
- Generate a token:
|
||||
- Go to Admin > Tokens > Add token
|
||||
- Create a token with "Write enabled"
|
||||
- Add a UUID custom field:
|
||||
- Got to Admin > Custom fields > Add custom field
|
||||
- Create a custom field called "uuid"
|
||||
- Assign it to object types `virtualization > cluster` and `virtualization > virtual machine`
|
||||
|
||||

|
||||
|
||||
- Go to Xen Orchestra > Settings > Plugins > Netbox and fill out the configuration:
|
||||
- Endpoint: the URL of your Netbox instance (e.g.: `https://netbox.company.net`)
|
||||
- Token: the token you generated earlier
|
||||
- Pools: the pools you wish to automatically synchronize with Netbox
|
||||
- Interval: the time interval (in hours) between 2 auto-synchronizations. Leave empty if you don't want to synchronize automatically.
|
||||
- Load the plugin (button next to the plugin's name)
|
||||
- Manual synchronization: if you correctly configured and loaded the plugin, a "Synchronize with Netbox" button will appear in every pool's Advanced tab, which allows you to manually synchronize it with Netbox
|
||||
|
||||
## Recipes
|
||||
|
||||
:::tip
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 120 KiB |
@@ -0,0 +1 @@
|
||||
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../scripts/babel-eslintrc.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
@@ -0,0 +1,25 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# xo-server-netbox
|
||||
|
||||
> Synchronizes pools managed by Xen Orchestra with Netbox
|
||||
|
||||
## Usage
|
||||
|
||||
Like all other xo-server plugins, it can be configured directly via
|
||||
the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).
|
||||
|
||||
## 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
|
||||
|
||||
[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)
|
||||
@@ -0,0 +1,2 @@
|
||||
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).
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "xo-server-netbox",
|
||||
"version": "0.0.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"description": "Synchronizes pools managed by Xen Orchestra with Netbox",
|
||||
"keywords": [
|
||||
"netbox",
|
||||
"orchestra",
|
||||
"plugin",
|
||||
"web",
|
||||
"xen",
|
||||
"xen-orchestra",
|
||||
"xo-server"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-netbox",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "packages/xo-server-netbox",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"engines": {
|
||||
"node": ">=14.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.13.16",
|
||||
"@babel/core": "^7.14.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@babel/preset-env": "^7.14.1",
|
||||
"@xen-orchestra/log": "^0.2.0",
|
||||
"assert": "^2.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"is-in-subnet": "^4.0.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,580 @@
|
||||
import assert from 'assert'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import { find, flatten, forEach, groupBy, isEmpty, keyBy, mapValues, trimEnd, zipObject } from 'lodash'
|
||||
import { isInSubnet } from 'is-in-subnet'
|
||||
|
||||
const log = createLogger('xo:netbox')
|
||||
|
||||
const CLUSTER_TYPE = 'XCP-ng Pool'
|
||||
const CHUNK_SIZE = 100
|
||||
const NAME_MAX_LENGTH = 64
|
||||
const REQUEST_TIMEOUT = 120e3 // 2min
|
||||
const M = 1024 ** 2
|
||||
const G = 1024 ** 3
|
||||
|
||||
const { push } = Array.prototype
|
||||
|
||||
const diff = (newer, older) => {
|
||||
if (typeof newer !== 'object') {
|
||||
return newer === older ? undefined : newer
|
||||
}
|
||||
|
||||
newer = { ...newer }
|
||||
Object.keys(newer).forEach(key => {
|
||||
if (diff(newer[key], older[key]) === undefined) {
|
||||
delete newer[key]
|
||||
}
|
||||
})
|
||||
|
||||
return isEmpty(newer) ? undefined : newer
|
||||
}
|
||||
|
||||
const indexName = (name, index) => {
|
||||
const suffix = ` (${index})`
|
||||
return name.slice(0, NAME_MAX_LENGTH - suffix.length) + suffix
|
||||
}
|
||||
|
||||
const onRequest = req => {
|
||||
req.setTimeout(REQUEST_TIMEOUT)
|
||||
req.on('timeout', req.abort)
|
||||
}
|
||||
|
||||
class Netbox {
|
||||
#endpoint
|
||||
#intervalToken
|
||||
#loaded
|
||||
#pools
|
||||
#removeApiMethods
|
||||
#syncInterval
|
||||
#token
|
||||
#xo
|
||||
|
||||
constructor({ xo }) {
|
||||
this.#xo = xo
|
||||
}
|
||||
|
||||
configure(configuration) {
|
||||
this.#endpoint = trimEnd(configuration.endpoint, '/')
|
||||
if (!/^https?:\/\//.test(this.#endpoint)) {
|
||||
this.#endpoint = 'http://' + this.#endpoint
|
||||
}
|
||||
this.#token = configuration.token
|
||||
this.#pools = configuration.pools
|
||||
this.#syncInterval = configuration.syncInterval && configuration.syncInterval * 60 * 60 * 1e3
|
||||
|
||||
// We don't want to start the auto-sync if the plugin isn't loaded
|
||||
if (this.#loaded) {
|
||||
clearInterval(this.#intervalToken)
|
||||
if (this.#syncInterval !== undefined) {
|
||||
this.#intervalToken = setInterval(this.#synchronize.bind(this), this.#syncInterval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load() {
|
||||
const synchronize = ({ pools }) => this.#synchronize(pools)
|
||||
synchronize.description = 'Synchronize XO pools with Netbox'
|
||||
synchronize.params = {
|
||||
pools: { type: 'array', optional: true, items: { type: 'string' } },
|
||||
}
|
||||
|
||||
this.#removeApiMethods = this.#xo.addApiMethods({
|
||||
netbox: { synchronize },
|
||||
})
|
||||
|
||||
if (this.#syncInterval !== undefined) {
|
||||
this.#intervalToken = setInterval(this.#synchronize.bind(this), this.#syncInterval)
|
||||
}
|
||||
|
||||
this.#loaded = true
|
||||
}
|
||||
|
||||
unload() {
|
||||
this.#removeApiMethods()
|
||||
clearInterval(this.#intervalToken)
|
||||
|
||||
this.#loaded = false
|
||||
}
|
||||
|
||||
async #makeRequest(path, method, data) {
|
||||
log.debug(
|
||||
`${method} ${path}`,
|
||||
Array.isArray(data) && data.length > 2 ? [...data.slice(0, 2), `and ${data.length - 2} others`] : data
|
||||
)
|
||||
let url = this.#endpoint + '/api' + path
|
||||
const options = {
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Token ${this.#token}` },
|
||||
method,
|
||||
onRequest,
|
||||
}
|
||||
|
||||
const httpRequest = async () => {
|
||||
try {
|
||||
const response = await this.#xo.httpRequest(url, options)
|
||||
const body = await response.readAll()
|
||||
if (body.length > 0) {
|
||||
return JSON.parse(body)
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
const body = await error.response.readAll()
|
||||
if (body.length > 0) {
|
||||
log.error(body.toString())
|
||||
}
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
let response = []
|
||||
// Split long POST request into chunks of CHUNK_SIZE objects to avoid a Bad Gateway errors
|
||||
if (Array.isArray(data)) {
|
||||
let offset = 0
|
||||
while (offset < data.length) {
|
||||
options.body = JSON.stringify(data.slice(offset, offset + CHUNK_SIZE))
|
||||
push.apply(response, await httpRequest())
|
||||
offset += CHUNK_SIZE
|
||||
}
|
||||
} else {
|
||||
if (data !== undefined) {
|
||||
options.body = JSON.stringify(data)
|
||||
}
|
||||
response = await httpRequest()
|
||||
}
|
||||
|
||||
if (method !== 'GET') {
|
||||
return response
|
||||
}
|
||||
|
||||
// Handle pagination for GET requests
|
||||
const { results } = response
|
||||
while (response.next !== null) {
|
||||
const { pathname, search } = new URL(response.next)
|
||||
url = this.#endpoint + pathname + search
|
||||
response = await httpRequest()
|
||||
push.apply(results, response.results)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
async #synchronize(pools = this.#pools) {
|
||||
const xo = this.#xo
|
||||
log.debug('synchronizing')
|
||||
// Cluster type
|
||||
const clusterTypes = await this.#makeRequest(
|
||||
`/virtualization/cluster-types/?name=${encodeURIComponent(CLUSTER_TYPE)}`,
|
||||
'GET'
|
||||
)
|
||||
if (clusterTypes.length > 1) {
|
||||
throw new Error('Found more than 1 "XCP-ng Pool" cluster type')
|
||||
}
|
||||
let clusterType
|
||||
if (clusterTypes.length === 0) {
|
||||
clusterType = await this.#makeRequest('/virtualization/cluster-types/', 'POST', {
|
||||
name: CLUSTER_TYPE,
|
||||
slug: CLUSTER_TYPE.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
||||
description: 'Created by Xen Orchestra',
|
||||
})
|
||||
} else {
|
||||
clusterType = clusterTypes[0]
|
||||
}
|
||||
|
||||
// Clusters
|
||||
const clusters = keyBy(
|
||||
await this.#makeRequest(`/virtualization/clusters/?type_id=${clusterType.id}`, 'GET'),
|
||||
'custom_fields.uuid'
|
||||
)
|
||||
|
||||
const clustersToCreate = []
|
||||
const clustersToUpdate = []
|
||||
for (const poolId of pools) {
|
||||
const pool = xo.getObject(poolId)
|
||||
const cluster = clusters[pool.uuid]
|
||||
|
||||
const updatedCluster = {
|
||||
name: pool.name_label.slice(0, NAME_MAX_LENGTH),
|
||||
type: clusterType.id,
|
||||
custom_fields: { uuid: pool.uuid },
|
||||
}
|
||||
|
||||
if (cluster === undefined) {
|
||||
clustersToCreate.push(updatedCluster)
|
||||
} else {
|
||||
// `type` needs to be flattened so we can compare the 2 objects
|
||||
const patch = diff(updatedCluster, { ...cluster, type: cluster.type.id })
|
||||
if (patch !== undefined) {
|
||||
clustersToUpdate.push({ ...patch, id: cluster.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(
|
||||
clusters,
|
||||
keyBy(
|
||||
flatten(
|
||||
await Promise.all(
|
||||
clustersToCreate.length === 0
|
||||
? []
|
||||
: await this.#makeRequest('/virtualization/clusters/', 'POST', clustersToCreate),
|
||||
clustersToUpdate.length === 0
|
||||
? []
|
||||
: await this.#makeRequest('/virtualization/clusters/', 'PATCH', clustersToUpdate)
|
||||
)
|
||||
),
|
||||
'custom_fields.uuid'
|
||||
)
|
||||
)
|
||||
|
||||
// VMs
|
||||
const vms = xo.getObjects({ filter: object => object.type === 'VM' && pools.includes(object.$pool) })
|
||||
const oldNetboxVms = keyBy(
|
||||
flatten(
|
||||
// FIXME: It should be doable with one request:
|
||||
// `cluster_id=1&cluster_id=2` but it doesn't work
|
||||
// https://netbox.readthedocs.io/en/stable/rest-api/filtering/#filtering-objects
|
||||
await Promise.all(
|
||||
pools.map(poolId =>
|
||||
this.#makeRequest(`/virtualization/virtual-machines/?cluster_id=${clusters[poolId].id}`, 'GET')
|
||||
)
|
||||
)
|
||||
),
|
||||
'custom_fields.uuid'
|
||||
)
|
||||
|
||||
// Build collections for later
|
||||
const netboxVms = {} // VM UUID → Netbox VM
|
||||
const vifsByVm = {} // VM UUID → VIF
|
||||
const ipsByDeviceByVm = {} // VM UUID → (VIF device → IP)
|
||||
|
||||
const vmsToCreate = []
|
||||
const vmsToUpdate = []
|
||||
for (const vm of Object.values(vms)) {
|
||||
vifsByVm[vm.uuid] = vm.VIFs
|
||||
const vmIpsByDevice = (ipsByDeviceByVm[vm.uuid] = {})
|
||||
forEach(vm.addresses, (address, key) => {
|
||||
const device = key.split('/')[0]
|
||||
if (vmIpsByDevice[device] === undefined) {
|
||||
vmIpsByDevice[device] = []
|
||||
}
|
||||
vmIpsByDevice[device].push(address)
|
||||
})
|
||||
|
||||
const oldNetboxVm = oldNetboxVms[vm.uuid]
|
||||
delete oldNetboxVms[vm.uuid]
|
||||
const cluster = clusters[vm.$pool]
|
||||
assert(cluster !== undefined)
|
||||
|
||||
const disk = Math.floor(
|
||||
vm.$VBDs
|
||||
.map(vbdId => xo.getObject(vbdId))
|
||||
.filter(vbd => !vbd.is_cd_drive)
|
||||
.map(vbd => xo.getObject(vbd.VDI))
|
||||
.reduce((total, vdi) => total + vdi.size, 0) / G
|
||||
)
|
||||
|
||||
const updatedVm = {
|
||||
name: vm.name_label.slice(0, NAME_MAX_LENGTH),
|
||||
cluster: cluster.id,
|
||||
vcpus: vm.CPUs.number,
|
||||
disk,
|
||||
memory: Math.floor(vm.memory.dynamic[1] / M),
|
||||
status: vm.power_state === 'Running' ? 'active' : 'offline',
|
||||
custom_fields: { uuid: vm.uuid },
|
||||
}
|
||||
|
||||
if (oldNetboxVm === undefined) {
|
||||
vmsToCreate.push(updatedVm)
|
||||
} else {
|
||||
// Some properties need to be flattened to match the expected POST
|
||||
// request objects
|
||||
let patch = diff(updatedVm, {
|
||||
...oldNetboxVm,
|
||||
cluster: oldNetboxVm.cluster.id,
|
||||
status: oldNetboxVm.status?.value,
|
||||
})
|
||||
|
||||
// Check if a name mismatch is due to a name deduplication
|
||||
if (patch?.name !== undefined) {
|
||||
let match
|
||||
if ((match = oldNetboxVm.name.match(/.* \((\d+)\)$/)) !== null) {
|
||||
if (indexName(patch.name, match[1]) === oldNetboxVm.name) {
|
||||
delete patch.name
|
||||
if (isEmpty(patch)) {
|
||||
patch = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (patch !== undefined) {
|
||||
// $cluster is needed to deduplicate the VM names within the same
|
||||
// cluster. It will be removed at that step.
|
||||
vmsToUpdate.push({ ...patch, id: oldNetboxVm.id, $cluster: cluster.id })
|
||||
} else {
|
||||
netboxVms[vm.uuid] = oldNetboxVm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deduplicate VM names
|
||||
vmsToCreate.forEach((vm, i) => {
|
||||
const name = vm.name
|
||||
let nameIndex = 1
|
||||
while (
|
||||
find(netboxVms, netboxVm => netboxVm.cluster.id === vm.cluster && netboxVm.name === vm.name) !== undefined ||
|
||||
find(
|
||||
vmsToCreate,
|
||||
(vmToCreate, j) => vmToCreate.cluster === vm.cluster && vmToCreate.name === vm.name && i !== j
|
||||
) !== undefined
|
||||
) {
|
||||
if (nameIndex >= 1e3) {
|
||||
throw new Error(`Cannot deduplicate name of VM ${name}`)
|
||||
}
|
||||
vm.name = indexName(name, nameIndex++)
|
||||
}
|
||||
})
|
||||
vmsToUpdate.forEach((vm, i) => {
|
||||
const name = vm.name
|
||||
if (name === undefined) {
|
||||
delete vm.$cluster
|
||||
return
|
||||
}
|
||||
let nameIndex = 1
|
||||
while (
|
||||
find(netboxVms, netboxVm => netboxVm.cluster.id === vm.$cluster && netboxVm.name === vm.name) !== undefined ||
|
||||
find(vmsToCreate, vmToCreate => vmToCreate.cluster === vm.$cluster && vmToCreate.name === vm.name) !==
|
||||
undefined ||
|
||||
find(
|
||||
vmsToUpdate,
|
||||
(vmToUpdate, j) => vmToUpdate.$cluster === vm.$cluster && vmToUpdate.name === vm.name && i !== j
|
||||
) !== undefined
|
||||
) {
|
||||
if (nameIndex >= 1e3) {
|
||||
throw new Error(`Cannot deduplicate name of VM ${name}`)
|
||||
}
|
||||
vm.name = indexName(name, nameIndex++)
|
||||
}
|
||||
delete vm.$cluster
|
||||
})
|
||||
|
||||
const vmsToDelete = Object.values(oldNetboxVms).map(vm => ({ id: vm.id }))
|
||||
Object.assign(
|
||||
netboxVms,
|
||||
keyBy(
|
||||
flatten(
|
||||
(
|
||||
await Promise.all([
|
||||
vmsToDelete.length !== 0 &&
|
||||
(await this.#makeRequest('/virtualization/virtual-machines/', 'DELETE', vmsToDelete)),
|
||||
vmsToCreate.length === 0
|
||||
? []
|
||||
: await this.#makeRequest('/virtualization/virtual-machines/', 'POST', vmsToCreate),
|
||||
vmsToUpdate.length === 0
|
||||
? []
|
||||
: await this.#makeRequest('/virtualization/virtual-machines/', 'PATCH', vmsToUpdate),
|
||||
])
|
||||
).slice(1)
|
||||
),
|
||||
'custom_fields.uuid'
|
||||
)
|
||||
)
|
||||
|
||||
// Interfaces
|
||||
// { vmUuid: { ifName: if } }
|
||||
const oldInterfaces = mapValues(
|
||||
groupBy(
|
||||
flatten(
|
||||
await Promise.all(
|
||||
pools.map(poolId =>
|
||||
this.#makeRequest(`/virtualization/interfaces/?cluster_id=${clusters[poolId].id}`, 'GET')
|
||||
)
|
||||
)
|
||||
),
|
||||
'virtual_machine.id'
|
||||
),
|
||||
interfaces => keyBy(interfaces, 'name')
|
||||
)
|
||||
|
||||
const interfaces = {} // VIF UUID → interface
|
||||
|
||||
const interfacesToCreateByVif = {} // VIF UUID → interface
|
||||
const interfacesToUpdateByVif = {} // VIF UUID → interface
|
||||
for (const [vmUuid, vifs] of Object.entries(vifsByVm)) {
|
||||
const netboxVmId = netboxVms[vmUuid].id
|
||||
const vmInterfaces = oldInterfaces[netboxVmId] ?? {}
|
||||
for (const vifId of vifs) {
|
||||
const vif = xo.getObject(vifId)
|
||||
const name = `eth${vif.device}`
|
||||
|
||||
const oldInterface = vmInterfaces[name]
|
||||
delete vmInterfaces[name]
|
||||
|
||||
const updatedInterface = {
|
||||
name,
|
||||
mac_address: vif.MAC.toUpperCase(),
|
||||
virtual_machine: netboxVmId,
|
||||
}
|
||||
|
||||
if (oldInterface === undefined) {
|
||||
interfacesToCreateByVif[vif.uuid] = updatedInterface
|
||||
} else {
|
||||
const patch = diff(updatedInterface, {
|
||||
...oldInterface,
|
||||
virtual_machine: oldInterface.virtual_machine.id,
|
||||
})
|
||||
if (patch !== undefined) {
|
||||
interfacesToUpdateByVif[vif.uuid] = { ...patch, id: oldInterface.id }
|
||||
} else {
|
||||
interfaces[vif.uuid] = oldInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const interfacesToDelete = flatten(
|
||||
Object.values(oldInterfaces).map(oldInterfacesByName =>
|
||||
Object.values(oldInterfacesByName).map(oldInterface => ({ id: oldInterface.id }))
|
||||
)
|
||||
)
|
||||
;(
|
||||
await Promise.all([
|
||||
interfacesToDelete.length !== 0 &&
|
||||
this.#makeRequest('/virtualization/interfaces/', 'DELETE', interfacesToDelete),
|
||||
isEmpty(interfacesToCreateByVif)
|
||||
? {}
|
||||
: this.#makeRequest(
|
||||
'/virtualization/interfaces/',
|
||||
'POST',
|
||||
Object.values(interfacesToCreateByVif)
|
||||
).then(interfaces => zipObject(Object.keys(interfacesToCreateByVif), interfaces)),
|
||||
isEmpty(interfacesToUpdateByVif)
|
||||
? {}
|
||||
: this.#makeRequest(
|
||||
'/virtualization/interfaces/',
|
||||
'PATCH',
|
||||
Object.values(interfacesToUpdateByVif)
|
||||
).then(interfaces => zipObject(Object.keys(interfacesToUpdateByVif), interfaces)),
|
||||
])
|
||||
)
|
||||
.slice(1)
|
||||
.forEach(newInterfaces => Object.assign(interfaces, newInterfaces))
|
||||
|
||||
// IPs
|
||||
const [oldNetboxIps, prefixes] = await Promise.all([
|
||||
this.#makeRequest('/ipam/ip-addresses/', 'GET').then(addresses => groupBy(addresses, 'assigned_object_id')),
|
||||
this.#makeRequest('/ipam/prefixes/', 'GET'),
|
||||
])
|
||||
|
||||
const ipsToDelete = []
|
||||
const ipsToCreate = []
|
||||
const ignoredIps = []
|
||||
for (const [vmUuid, vifs] of Object.entries(vifsByVm)) {
|
||||
const vmIpsByDevice = ipsByDeviceByVm[vmUuid]
|
||||
if (vmIpsByDevice === undefined) {
|
||||
continue
|
||||
}
|
||||
for (const vifId of vifs) {
|
||||
const vif = xo.getObject(vifId)
|
||||
const vifIps = vmIpsByDevice[vif.device]
|
||||
if (vifIps === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
const interface_ = interfaces[vif.uuid]
|
||||
const interfaceOldIps = oldNetboxIps[interface_.id] ?? []
|
||||
|
||||
for (const ip of vifIps) {
|
||||
// FIXME: Should we compare the IPs with their range? ie: can 2 IPs
|
||||
// look identical but belong to 2 different ranges?
|
||||
const netboxIpIndex = interfaceOldIps.findIndex(netboxIp => netboxIp.address.split('/')[0] === ip)
|
||||
if (netboxIpIndex >= 0) {
|
||||
interfaceOldIps.splice(netboxIpIndex, 1)
|
||||
} else {
|
||||
const prefix = prefixes.find(({ prefix }) => isInSubnet(ip, prefix))
|
||||
if (prefix === undefined) {
|
||||
ignoredIps.push(ip)
|
||||
continue
|
||||
}
|
||||
|
||||
ipsToCreate.push({
|
||||
address: `${ip}/${prefix.prefix.split('/')[1]}`,
|
||||
assigned_object_type: 'virtualization.vminterface',
|
||||
assigned_object_id: interface_.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
ipsToDelete.push(...interfaceOldIps.map(oldIp => ({ id: oldIp.id })))
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoredIps.length > 0) {
|
||||
log.warn('Could not find prefix for some IPs: ignoring them.', { ips: ignoredIps })
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
ipsToDelete.length !== 0 && this.#makeRequest('/ipam/ip-addresses/', 'DELETE', ipsToDelete),
|
||||
ipsToCreate.length !== 0 && this.#makeRequest('/ipam/ip-addresses/', 'POST', ipsToCreate),
|
||||
])
|
||||
|
||||
log.debug('synchronized')
|
||||
}
|
||||
|
||||
async test() {
|
||||
const randomSuffix = Math.random().toString(36).slice(2)
|
||||
const name = '[TMP] Xen Orchestra Netbox plugin test - ' + randomSuffix
|
||||
await this.#makeRequest('/virtualization/cluster-types/', 'POST', {
|
||||
name,
|
||||
slug: 'xo-test-' + randomSuffix,
|
||||
description:
|
||||
"This type has been created by Xen Orchestra's Netbox plugin test. If it hasn't been properly deleted, you may delete it manually.",
|
||||
})
|
||||
const clusterTypes = await this.#makeRequest(
|
||||
`/virtualization/cluster-types/?name=${encodeURIComponent(name)}`,
|
||||
'GET'
|
||||
)
|
||||
|
||||
if (clusterTypes.length !== 1) {
|
||||
throw new Error('Could not properly write and read Netbox')
|
||||
}
|
||||
|
||||
await this.#makeRequest('/virtualization/cluster-types/', 'DELETE', [{ id: clusterTypes[0].id }])
|
||||
}
|
||||
}
|
||||
|
||||
export const configurationSchema = ({ xo: { apiMethods } }) => ({
|
||||
description:
|
||||
'Synchronize pools managed by Xen Orchestra with Netbox. Configuration steps: https://xen-orchestra.com/docs/advanced.html#netbox.',
|
||||
type: 'object',
|
||||
properties: {
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
title: 'Endpoint',
|
||||
description: 'Netbox URI',
|
||||
},
|
||||
token: {
|
||||
type: 'string',
|
||||
title: 'Token',
|
||||
description: 'Generate a token with write permissions from your Netbox interface',
|
||||
},
|
||||
pools: {
|
||||
type: 'array',
|
||||
title: 'Pools',
|
||||
description: 'Pools to synchronize with Netbox',
|
||||
items: {
|
||||
type: 'string',
|
||||
$type: 'pool',
|
||||
},
|
||||
},
|
||||
syncInterval: {
|
||||
type: 'number',
|
||||
title: 'Interval',
|
||||
description: 'Synchronization interval in hours - leave empty to disable auto-sync',
|
||||
},
|
||||
},
|
||||
required: ['endpoint', 'token', 'pools'],
|
||||
})
|
||||
|
||||
export default opts => new Netbox(opts)
|
||||
@@ -794,6 +794,8 @@ const messages = {
|
||||
setpoolMaster: 'Master',
|
||||
syslogRemoteHost: 'Remote syslog host',
|
||||
defaultMigrationNetwork: 'Default migration network',
|
||||
syncNetbox: 'Synchronize with Netbox',
|
||||
syncNetboxWarning: 'Are you sure you want to synchronize with Netbox?',
|
||||
// ----- Pool host tab -----
|
||||
hostNameLabel: 'Name',
|
||||
hostDescription: 'Description',
|
||||
|
||||
@@ -3001,3 +3001,12 @@ export const synchronizeLdapGroups = () =>
|
||||
body: _('syncLdapGroupsWarning'),
|
||||
icon: 'refresh',
|
||||
}).then(() => _call('ldap.synchronizeGroups')::tap(subscribeGroups.forceRefresh), noop)
|
||||
|
||||
// Netbox plugin ---------------------------------------------------------------
|
||||
|
||||
export const synchronizeNetbox = pools =>
|
||||
confirm({
|
||||
title: _('syncNetbox'),
|
||||
body: _('syncNetboxWarning'),
|
||||
icon: 'refresh',
|
||||
}).then(() => _call('netbox.synchronize', { pools: resolveIds(pools) }))
|
||||
|
||||
@@ -7,8 +7,9 @@ import Component from 'base-component'
|
||||
import Icon from 'icon'
|
||||
import renderXoItem, { Network } from 'render-xo-item'
|
||||
import SelectFiles from 'select-files'
|
||||
import TabButton from 'tab-button'
|
||||
import Upgrade from 'xoa-upgrade'
|
||||
import { connectStore } from 'utils'
|
||||
import { addSubscriptions, connectStore } from 'utils'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import { CustomFields } from 'custom-fields'
|
||||
import { injectIntl } from 'react-intl'
|
||||
@@ -28,6 +29,8 @@ import {
|
||||
setPoolMaster,
|
||||
setRemoteSyslogHost,
|
||||
setRemoteSyslogHosts,
|
||||
subscribePlugins,
|
||||
synchronizeNetbox,
|
||||
} from 'xo'
|
||||
|
||||
@connectStore(() => ({
|
||||
@@ -65,6 +68,9 @@ class PoolMaster extends Component {
|
||||
migrationNetwork: createGetObject((_, { pool }) => pool.otherConfig['xo:migrationNetwork']),
|
||||
}
|
||||
})
|
||||
@addSubscriptions({
|
||||
plugins: subscribePlugins,
|
||||
})
|
||||
export default class TabAdvanced extends Component {
|
||||
_getMigrationNetworkPredicate = createSelector(
|
||||
createCollectionWrapper(
|
||||
@@ -84,6 +90,11 @@ export default class TabAdvanced extends Component {
|
||||
networkIds => network => networkIds.has(network.id)
|
||||
)
|
||||
|
||||
_isNetboxPluginLoaded = createSelector(
|
||||
() => this.props.plugins,
|
||||
plugins => plugins !== undefined && plugins.some(plugin => plugin.name === 'netbox' && plugin.loaded)
|
||||
)
|
||||
|
||||
_onChangeMigrationNetwork = migrationNetwork => editPool(this.props.pool, { migrationNetwork: migrationNetwork.id })
|
||||
|
||||
_removeMigrationNetwork = () => editPool(this.props.pool, { migrationNetwork: null })
|
||||
@@ -101,6 +112,19 @@ export default class TabAdvanced extends Component {
|
||||
return (
|
||||
<div>
|
||||
<Container>
|
||||
{this._isNetboxPluginLoaded() && (
|
||||
<Row>
|
||||
<Col className='text-xs-right'>
|
||||
<TabButton
|
||||
btnStyle='primary'
|
||||
handler={synchronizeNetbox}
|
||||
handlerParam={[pool]}
|
||||
icon='refresh'
|
||||
labelId='syncNetbox'
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
<Row>
|
||||
<Col>
|
||||
<h3>{_('xenSettingsLabel')}</h3>
|
||||
|
||||
@@ -19,6 +19,22 @@
|
||||
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents"
|
||||
chokidar "^3.4.0"
|
||||
|
||||
"@babel/cli@^7.13.16":
|
||||
version "7.13.16"
|
||||
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.13.16.tgz#9d372e943ced0cc291f068204a9b010fd9cfadbc"
|
||||
integrity sha512-cL9tllhqvsQ6r1+d9Invf7nNXg/3BlfL1vvvL/AdH9fZ2l5j0CeBcoq6UjsqHpvyN1v5nXSZgqJZoGeK+ZOAbw==
|
||||
dependencies:
|
||||
commander "^4.0.1"
|
||||
convert-source-map "^1.1.0"
|
||||
fs-readdir-recursive "^1.1.0"
|
||||
glob "^7.0.0"
|
||||
make-dir "^2.1.0"
|
||||
slash "^2.0.0"
|
||||
source-map "^0.5.0"
|
||||
optionalDependencies:
|
||||
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents"
|
||||
chokidar "^3.4.0"
|
||||
|
||||
"@babel/code-frame@7.12.11":
|
||||
version "7.12.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
||||
@@ -38,6 +54,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.15.tgz#7e8eea42d0b64fda2b375b22d06c605222e848f4"
|
||||
integrity sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==
|
||||
|
||||
"@babel/compat-data@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919"
|
||||
integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==
|
||||
|
||||
"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.5", "@babel/core@^7.1.6", "@babel/core@^7.11.0", "@babel/core@^7.13.8", "@babel/core@^7.3.3", "@babel/core@^7.4.4", "@babel/core@^7.7.2", "@babel/core@^7.7.4", "@babel/core@^7.7.5", "@babel/core@^7.8.4":
|
||||
version "7.13.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.15.tgz#a6d40917df027487b54312202a06812c4f7792d0"
|
||||
@@ -59,6 +80,27 @@
|
||||
semver "^6.3.0"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/core@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.0.tgz#47299ff3ec8d111b493f1a9d04bf88c04e728d88"
|
||||
integrity sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.12.13"
|
||||
"@babel/generator" "^7.14.0"
|
||||
"@babel/helper-compilation-targets" "^7.13.16"
|
||||
"@babel/helper-module-transforms" "^7.14.0"
|
||||
"@babel/helpers" "^7.14.0"
|
||||
"@babel/parser" "^7.14.0"
|
||||
"@babel/template" "^7.12.13"
|
||||
"@babel/traverse" "^7.14.0"
|
||||
"@babel/types" "^7.14.0"
|
||||
convert-source-map "^1.7.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.1.2"
|
||||
semver "^6.3.0"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/eslint-parser@^7.13.8":
|
||||
version "7.13.14"
|
||||
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.13.14.tgz#f80fd23bdd839537221914cb5d17720a5ea6ba3a"
|
||||
@@ -77,6 +119,15 @@
|
||||
jsesc "^2.5.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.14.0":
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.1.tgz#1f99331babd65700183628da186f36f63d615c93"
|
||||
integrity sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==
|
||||
dependencies:
|
||||
"@babel/types" "^7.14.1"
|
||||
jsesc "^2.5.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab"
|
||||
@@ -102,6 +153,16 @@
|
||||
browserslist "^4.14.5"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.13.16":
|
||||
version "7.13.16"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c"
|
||||
integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.13.15"
|
||||
"@babel/helper-validator-option" "^7.12.17"
|
||||
browserslist "^4.14.5"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.13.11":
|
||||
version "7.13.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6"
|
||||
@@ -113,6 +174,18 @@
|
||||
"@babel/helper-replace-supers" "^7.13.0"
|
||||
"@babel/helper-split-export-declaration" "^7.12.13"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.14.0":
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.1.tgz#1fe11b376f3c41650ad9fedc665b0068722ea76c"
|
||||
integrity sha512-r8rsUahG4ywm0QpGcCrLaUSOuNAISR3IZCg4Fx05Ozq31aCUrQsTLH6KPxy0N5ULoQ4Sn9qjNdGNtbPWAC6hYg==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.12.13"
|
||||
"@babel/helper-function-name" "^7.12.13"
|
||||
"@babel/helper-member-expression-to-functions" "^7.13.12"
|
||||
"@babel/helper-optimise-call-expression" "^7.12.13"
|
||||
"@babel/helper-replace-supers" "^7.13.12"
|
||||
"@babel/helper-split-export-declaration" "^7.12.13"
|
||||
|
||||
"@babel/helper-create-regexp-features-plugin@^7.12.13":
|
||||
version "7.12.17"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz#a2ac87e9e319269ac655b8d4415e94d38d663cb7"
|
||||
@@ -194,6 +267,20 @@
|
||||
"@babel/traverse" "^7.13.13"
|
||||
"@babel/types" "^7.13.14"
|
||||
|
||||
"@babel/helper-module-transforms@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz#8fcf78be220156f22633ee204ea81f73f826a8ad"
|
||||
integrity sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.13.12"
|
||||
"@babel/helper-replace-supers" "^7.13.12"
|
||||
"@babel/helper-simple-access" "^7.13.12"
|
||||
"@babel/helper-split-export-declaration" "^7.12.13"
|
||||
"@babel/helper-validator-identifier" "^7.14.0"
|
||||
"@babel/template" "^7.12.13"
|
||||
"@babel/traverse" "^7.14.0"
|
||||
"@babel/types" "^7.14.0"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea"
|
||||
@@ -251,6 +338,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
|
||||
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
|
||||
integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==
|
||||
|
||||
"@babel/helper-validator-option@^7.12.17":
|
||||
version "7.12.17"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831"
|
||||
@@ -275,6 +367,15 @@
|
||||
"@babel/traverse" "^7.13.0"
|
||||
"@babel/types" "^7.13.0"
|
||||
|
||||
"@babel/helpers@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.0.tgz#ea9b6be9478a13d6f961dbb5f36bf75e2f3b8f62"
|
||||
integrity sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==
|
||||
dependencies:
|
||||
"@babel/template" "^7.12.13"
|
||||
"@babel/traverse" "^7.14.0"
|
||||
"@babel/types" "^7.14.0"
|
||||
|
||||
"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13":
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1"
|
||||
@@ -289,6 +390,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8"
|
||||
integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==
|
||||
|
||||
"@babel/parser@^7.14.0":
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.1.tgz#1bd644b5db3f5797c4479d89ec1817fe02b84c47"
|
||||
integrity sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==
|
||||
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12":
|
||||
version "7.13.12"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a"
|
||||
@@ -315,6 +421,14 @@
|
||||
"@babel/helper-create-class-features-plugin" "^7.13.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-proposal-class-static-block@^7.13.11":
|
||||
version "7.13.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz#6fcbba4a962702c17e5371a0c7b39afde186d703"
|
||||
integrity sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
"@babel/plugin-syntax-class-static-block" "^7.12.13"
|
||||
|
||||
"@babel/plugin-proposal-decorators@^7.0.0", "@babel/plugin-proposal-decorators@^7.1.6", "@babel/plugin-proposal-decorators@^7.13.0", "@babel/plugin-proposal-decorators@^7.3.0", "@babel/plugin-proposal-decorators@^7.4.0", "@babel/plugin-proposal-decorators@^7.8.0", "@babel/plugin-proposal-decorators@^7.8.3":
|
||||
version "7.13.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.13.15.tgz#e91ccfef2dc24dd5bd5dcc9fc9e2557c684ecfb8"
|
||||
@@ -432,6 +546,16 @@
|
||||
"@babel/helper-create-class-features-plugin" "^7.13.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-proposal-private-property-in-object@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz#b1a1f2030586b9d3489cc26179d2eb5883277636"
|
||||
integrity sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.12.13"
|
||||
"@babel/helper-create-class-features-plugin" "^7.14.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
"@babel/plugin-syntax-private-property-in-object" "^7.14.0"
|
||||
|
||||
"@babel/plugin-proposal-throw-expressions@^7.0.0":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.12.13.tgz#48a6e4a5988041d16b0a2f1568a3b518f8b6c1d4"
|
||||
@@ -469,6 +593,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.12.13"
|
||||
|
||||
"@babel/plugin-syntax-class-static-block@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz#8e3d674b0613e67975ceac2776c97b60cafc5c9c"
|
||||
integrity sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.12.13"
|
||||
|
||||
"@babel/plugin-syntax-decorators@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.13.tgz#fac829bf3c7ef4a1bc916257b403e58c6bdaf648"
|
||||
@@ -574,6 +705,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.12.13"
|
||||
|
||||
"@babel/plugin-syntax-private-property-in-object@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz#762a4babec61176fec6c88480dec40372b140c0b"
|
||||
integrity sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-syntax-throw-expressions@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.12.13.tgz#bb02bfbaf57d71ab69280ebf6a53aa45ad4c3f1a"
|
||||
@@ -618,6 +756,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.12.13"
|
||||
|
||||
"@babel/plugin-transform-block-scoping@^7.14.1":
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.1.tgz#ac1b3a8e3d8cbb31efc6b9be2f74eb9823b74ab2"
|
||||
integrity sha512-2mQXd0zBrwfp0O1moWIhPpEeTKDvxyHcnma3JATVP1l+CctWBuot6OJG8LQ4DnBj4ZZPSmlb/fm4mu47EOAnVA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.13.0":
|
||||
version "7.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b"
|
||||
@@ -645,6 +790,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-transform-destructuring@^7.13.17":
|
||||
version "7.13.17"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz#678d96576638c19d5b36b332504d3fd6e06dea27"
|
||||
integrity sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-transform-dotall-regex@^7.12.13", "@babel/plugin-transform-dotall-regex@^7.4.4":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad"
|
||||
@@ -706,6 +858,15 @@
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-amd@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.0.tgz#589494b5b290ff76cf7f59c798011f6d77026553"
|
||||
integrity sha512-CF4c5LX4LQ03LebQxJ5JZes2OYjzBuk1TdiF7cG7d5dK4lAdw9NZmaxq5K/mouUdNeqwz3TNjnW6v01UqUNgpQ==
|
||||
dependencies:
|
||||
"@babel/helper-module-transforms" "^7.14.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-commonjs@^7.13.8":
|
||||
version "7.13.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b"
|
||||
@@ -716,6 +877,16 @@
|
||||
"@babel/helper-simple-access" "^7.12.13"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-commonjs@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz#52bc199cb581e0992edba0f0f80356467587f161"
|
||||
integrity sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==
|
||||
dependencies:
|
||||
"@babel/helper-module-transforms" "^7.14.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
"@babel/helper-simple-access" "^7.13.12"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-systemjs@^7.13.8":
|
||||
version "7.13.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3"
|
||||
@@ -735,6 +906,14 @@
|
||||
"@babel/helper-module-transforms" "^7.13.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-transform-modules-umd@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz#2f8179d1bbc9263665ce4a65f305526b2ea8ac34"
|
||||
integrity sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==
|
||||
dependencies:
|
||||
"@babel/helper-module-transforms" "^7.14.0"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz#2213725a5f5bbbe364b50c3ba5998c9599c5c9d9"
|
||||
@@ -956,6 +1135,85 @@
|
||||
core-js-compat "^3.9.0"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/preset-env@^7.14.1":
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.1.tgz#b55914e2e68885ea03f69600b2d3537e54574a93"
|
||||
integrity sha512-0M4yL1l7V4l+j/UHvxcdvNfLB9pPtIooHTbEhgD/6UGyh8Hy3Bm1Mj0buzjDXATCSz3JFibVdnoJZCrlUCanrQ==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.14.0"
|
||||
"@babel/helper-compilation-targets" "^7.13.16"
|
||||
"@babel/helper-plugin-utils" "^7.13.0"
|
||||
"@babel/helper-validator-option" "^7.12.17"
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.13.15"
|
||||
"@babel/plugin-proposal-class-properties" "^7.13.0"
|
||||
"@babel/plugin-proposal-class-static-block" "^7.13.11"
|
||||
"@babel/plugin-proposal-dynamic-import" "^7.13.8"
|
||||
"@babel/plugin-proposal-export-namespace-from" "^7.12.13"
|
||||
"@babel/plugin-proposal-json-strings" "^7.13.8"
|
||||
"@babel/plugin-proposal-logical-assignment-operators" "^7.13.8"
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8"
|
||||
"@babel/plugin-proposal-numeric-separator" "^7.12.13"
|
||||
"@babel/plugin-proposal-object-rest-spread" "^7.13.8"
|
||||
"@babel/plugin-proposal-optional-catch-binding" "^7.13.8"
|
||||
"@babel/plugin-proposal-optional-chaining" "^7.13.12"
|
||||
"@babel/plugin-proposal-private-methods" "^7.13.0"
|
||||
"@babel/plugin-proposal-private-property-in-object" "^7.14.0"
|
||||
"@babel/plugin-proposal-unicode-property-regex" "^7.12.13"
|
||||
"@babel/plugin-syntax-async-generators" "^7.8.4"
|
||||
"@babel/plugin-syntax-class-properties" "^7.12.13"
|
||||
"@babel/plugin-syntax-class-static-block" "^7.12.13"
|
||||
"@babel/plugin-syntax-dynamic-import" "^7.8.3"
|
||||
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
|
||||
"@babel/plugin-syntax-json-strings" "^7.8.3"
|
||||
"@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
|
||||
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
|
||||
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
|
||||
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
|
||||
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
|
||||
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
|
||||
"@babel/plugin-syntax-private-property-in-object" "^7.14.0"
|
||||
"@babel/plugin-syntax-top-level-await" "^7.12.13"
|
||||
"@babel/plugin-transform-arrow-functions" "^7.13.0"
|
||||
"@babel/plugin-transform-async-to-generator" "^7.13.0"
|
||||
"@babel/plugin-transform-block-scoped-functions" "^7.12.13"
|
||||
"@babel/plugin-transform-block-scoping" "^7.14.1"
|
||||
"@babel/plugin-transform-classes" "^7.13.0"
|
||||
"@babel/plugin-transform-computed-properties" "^7.13.0"
|
||||
"@babel/plugin-transform-destructuring" "^7.13.17"
|
||||
"@babel/plugin-transform-dotall-regex" "^7.12.13"
|
||||
"@babel/plugin-transform-duplicate-keys" "^7.12.13"
|
||||
"@babel/plugin-transform-exponentiation-operator" "^7.12.13"
|
||||
"@babel/plugin-transform-for-of" "^7.13.0"
|
||||
"@babel/plugin-transform-function-name" "^7.12.13"
|
||||
"@babel/plugin-transform-literals" "^7.12.13"
|
||||
"@babel/plugin-transform-member-expression-literals" "^7.12.13"
|
||||
"@babel/plugin-transform-modules-amd" "^7.14.0"
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.14.0"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.13.8"
|
||||
"@babel/plugin-transform-modules-umd" "^7.14.0"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.12.13"
|
||||
"@babel/plugin-transform-new-target" "^7.12.13"
|
||||
"@babel/plugin-transform-object-super" "^7.12.13"
|
||||
"@babel/plugin-transform-parameters" "^7.13.0"
|
||||
"@babel/plugin-transform-property-literals" "^7.12.13"
|
||||
"@babel/plugin-transform-regenerator" "^7.13.15"
|
||||
"@babel/plugin-transform-reserved-words" "^7.12.13"
|
||||
"@babel/plugin-transform-shorthand-properties" "^7.12.13"
|
||||
"@babel/plugin-transform-spread" "^7.13.0"
|
||||
"@babel/plugin-transform-sticky-regex" "^7.12.13"
|
||||
"@babel/plugin-transform-template-literals" "^7.13.0"
|
||||
"@babel/plugin-transform-typeof-symbol" "^7.12.13"
|
||||
"@babel/plugin-transform-unicode-escapes" "^7.12.13"
|
||||
"@babel/plugin-transform-unicode-regex" "^7.12.13"
|
||||
"@babel/preset-modules" "^0.1.4"
|
||||
"@babel/types" "^7.14.1"
|
||||
babel-plugin-polyfill-corejs2 "^0.2.0"
|
||||
babel-plugin-polyfill-corejs3 "^0.2.0"
|
||||
babel-plugin-polyfill-regenerator "^0.2.0"
|
||||
core-js-compat "^3.9.0"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/preset-modules@^0.1.4":
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e"
|
||||
@@ -1020,6 +1278,20 @@
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/traverse@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef"
|
||||
integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.12.13"
|
||||
"@babel/generator" "^7.14.0"
|
||||
"@babel/helper-function-name" "^7.12.13"
|
||||
"@babel/helper-split-export-declaration" "^7.12.13"
|
||||
"@babel/parser" "^7.14.0"
|
||||
"@babel/types" "^7.14.0"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.6.1", "@babel/types@^7.9.6":
|
||||
version "7.13.14"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d"
|
||||
@@ -1029,6 +1301,14 @@
|
||||
lodash "^4.17.19"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.14.0", "@babel/types@^7.14.1":
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.1.tgz#095bd12f1c08ab63eff6e8f7745fa7c9cc15a9db"
|
||||
integrity sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.14.0"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@bcoe/v8-coverage@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
@@ -2654,6 +2934,16 @@ assert@^1.1.1, assert@^1.4.0:
|
||||
object-assign "^4.1.1"
|
||||
util "0.10.3"
|
||||
|
||||
assert@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32"
|
||||
integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==
|
||||
dependencies:
|
||||
es6-object-assign "^1.1.0"
|
||||
is-nan "^1.2.1"
|
||||
object-is "^1.0.1"
|
||||
util "^0.12.0"
|
||||
|
||||
assign-symbols@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
|
||||
@@ -2759,6 +3049,13 @@ autoprefixer@^9.5.1, autoprefixer@^9.6.1:
|
||||
postcss "^7.0.32"
|
||||
postcss-value-parser "^4.1.0"
|
||||
|
||||
available-typed-arrays@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
|
||||
integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==
|
||||
dependencies:
|
||||
array-filter "^1.0.0"
|
||||
|
||||
aws-sdk@^2.686.0:
|
||||
version "2.889.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.889.0.tgz#513457f488305a8ff5593747741b67e7b874bdf7"
|
||||
@@ -4557,7 +4854,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@^7.0.2:
|
||||
cross-env@^7.0.2, cross-env@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
|
||||
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
|
||||
@@ -6085,6 +6382,11 @@ es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3:
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-object-assign@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||
|
||||
es6-promise@^4.1.0:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||
@@ -9023,6 +9325,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-in-subnet@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-in-subnet/-/is-in-subnet-4.0.1.tgz#7a93bf67636021598dc483e934d8841cb8b7a537"
|
||||
integrity sha512-D3mAuAo6vZ+/AxsLkEIZ3moTx7AIGQLLzLQslV6n0RRO/CzdUemXap+lj3OPAehKCbdkGPikxOVUYqRo0GGJAA==
|
||||
|
||||
is-installed-globally@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
|
||||
@@ -9038,6 +9345,14 @@ is-ip@^3.1.0:
|
||||
dependencies:
|
||||
ip-regex "^4.0.0"
|
||||
|
||||
is-nan@^1.2.1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
|
||||
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
|
||||
dependencies:
|
||||
call-bind "^1.0.0"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
is-negated-glob@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
|
||||
@@ -9210,6 +9525,17 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-typed-array@^1.1.3:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e"
|
||||
integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.2"
|
||||
call-bind "^1.0.2"
|
||||
es-abstract "^1.18.0-next.2"
|
||||
foreach "^2.0.5"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
@@ -10349,11 +10675,6 @@ limit-concurrency-decorator@^0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/limit-concurrency-decorator/-/limit-concurrency-decorator-0.5.0.tgz#7455fc7c8d12e93ce725cb98dc18b861397dd726"
|
||||
integrity sha512-s5HqdnTpRJhvK/vleMY3qJ3yEfIQ1BUCUqbBJwtXCKngMSc+qpS1Rl6/rxdhr1Z/oQz3keYho6G4XCFSHb7nbA==
|
||||
|
||||
limit-concurrency-decorator@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/limit-concurrency-decorator/-/limit-concurrency-decorator-0.5.0.tgz#7455fc7c8d12e93ce725cb98dc18b861397dd726"
|
||||
integrity sha512-s5HqdnTpRJhvK/vleMY3qJ3yEfIQ1BUCUqbBJwtXCKngMSc+qpS1Rl6/rxdhr1Z/oQz3keYho6G4XCFSHb7nbA==
|
||||
|
||||
lines-and-columns@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||
@@ -16926,6 +17247,18 @@ util@^0.11.0:
|
||||
dependencies:
|
||||
inherits "2.0.3"
|
||||
|
||||
util@^0.12.0:
|
||||
version "0.12.3"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888"
|
||||
integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
is-arguments "^1.0.4"
|
||||
is-generator-function "^1.0.7"
|
||||
is-typed-array "^1.1.3"
|
||||
safe-buffer "^5.1.2"
|
||||
which-typed-array "^1.1.2"
|
||||
|
||||
util@~0.10.1:
|
||||
version "0.10.4"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||
@@ -17520,6 +17853,19 @@ which-pm-runs@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
|
||||
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
|
||||
|
||||
which-typed-array@^1.1.2:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff"
|
||||
integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.2"
|
||||
call-bind "^1.0.0"
|
||||
es-abstract "^1.18.0-next.1"
|
||||
foreach "^2.0.5"
|
||||
function-bind "^1.1.1"
|
||||
has-symbols "^1.0.1"
|
||||
is-typed-array "^1.1.3"
|
||||
|
||||
which@1, which@^1.2.14, which@^1.2.9:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
|
||||
Reference in New Issue
Block a user