Compare commits
41 Commits
xo-web-v5.
...
cr-seed-cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cf04aca72 | ||
|
|
410d6762bf | ||
|
|
0a95426e63 | ||
|
|
4ec4970d49 | ||
|
|
e57ae0a8ce | ||
|
|
1e7852369f | ||
|
|
bdefd0bcd8 | ||
|
|
fa88e1789c | ||
|
|
df90094cae | ||
|
|
efdbc18a0a | ||
|
|
fc1dd3ce09 | ||
|
|
10aff53d2c | ||
|
|
85c3d64c04 | ||
|
|
5a71ab53be | ||
|
|
d022b40732 | ||
|
|
e105c0aad1 | ||
|
|
eb9655125c | ||
|
|
a10fea2823 | ||
|
|
0c05d89d3f | ||
|
|
d600d4cc28 | ||
|
|
4f0e5317ed | ||
|
|
fce7c7fd49 | ||
|
|
929ca767ca | ||
|
|
342c1bc6fa | ||
|
|
317e301067 | ||
|
|
ac5741a341 | ||
|
|
3f1fb7092b | ||
|
|
7298a8b8f0 | ||
|
|
856924c970 | ||
|
|
9a285d280f | ||
|
|
9c3fc56d4a | ||
|
|
aaaee45eeb | ||
|
|
ac2ab21826 | ||
|
|
b22514646e | ||
|
|
c7ab1ddb0c | ||
|
|
7ddc595a1c | ||
|
|
4147800266 | ||
|
|
99e6d54647 | ||
|
|
dac5901c6b | ||
|
|
308928a7d4 | ||
|
|
e6e3d2cd52 |
@@ -30,10 +30,10 @@
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
@@ -44,6 +44,7 @@
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepare": "yarn run build",
|
||||
"prepublishOnly": "yarn run build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,9 @@ ${cliName} v${pkg.version}
|
||||
|
||||
await Promise.all([
|
||||
srcXapi.setFieldEntries(srcSnapshot, 'other_config', metadata),
|
||||
srcXapi.setFieldEntries(srcSnapshot, 'other_config', {
|
||||
'xo:backup:exported': 'true',
|
||||
}),
|
||||
tgtXapi.setField(
|
||||
tgtVm,
|
||||
'name_label',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xen-orchestra/cr-seed-cli",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/cr-seed-cli",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
@@ -15,6 +15,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"golike-defer": "^0.4.1",
|
||||
"xen-api": "^0.18.0"
|
||||
"xen-api": "^0.19.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
"moment-timezone": "^0.5.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xen-orchestra/fs",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "The File System for Xen Orchestra backups.",
|
||||
"keywords": [],
|
||||
@@ -31,11 +31,11 @@
|
||||
"xo-remote-parser": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"index-modules": "^0.3.0",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import execa from 'execa'
|
||||
import fs from 'fs-extra'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import LocalHandler from './local'
|
||||
|
||||
@@ -15,63 +14,38 @@ export default class NfsHandler extends LocalHandler {
|
||||
return `/run/xo-server/mounts/${this._remote.id}`
|
||||
}
|
||||
|
||||
async _loadRealMounts () {
|
||||
let stdout
|
||||
const mounted = {}
|
||||
try {
|
||||
stdout = await execa.stdout('findmnt', [
|
||||
'-P',
|
||||
'-t',
|
||||
'nfs,nfs4',
|
||||
'--output',
|
||||
'SOURCE,TARGET',
|
||||
'--noheadings',
|
||||
])
|
||||
const regex = /^SOURCE="([^:]*):(.*)" TARGET="(.*)"$/
|
||||
forEach(stdout.split('\n'), m => {
|
||||
if (m) {
|
||||
const match = regex.exec(m)
|
||||
mounted[match[3]] = {
|
||||
host: match[1],
|
||||
share: match[2],
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (exc) {
|
||||
// When no mounts are found, the call pretends to fail...
|
||||
if (exc.stderr !== '') {
|
||||
throw exc
|
||||
}
|
||||
}
|
||||
|
||||
this._realMounts = mounted
|
||||
return mounted
|
||||
}
|
||||
|
||||
_matchesRealMount () {
|
||||
return this._getRealPath() in this._realMounts
|
||||
}
|
||||
|
||||
async _mount () {
|
||||
await fs.ensureDir(this._getRealPath())
|
||||
const { host, path, port, options } = this._remote
|
||||
return execa('mount', [
|
||||
'-t',
|
||||
'nfs',
|
||||
'-o',
|
||||
DEFAULT_NFS_OPTIONS + (options !== undefined ? `,${options}` : ''),
|
||||
`${host}${port !== undefined ? ':' + port : ''}:${path}`,
|
||||
this._getRealPath(),
|
||||
])
|
||||
return execa(
|
||||
'mount',
|
||||
[
|
||||
'-t',
|
||||
'nfs',
|
||||
'-o',
|
||||
DEFAULT_NFS_OPTIONS + (options !== undefined ? `,${options}` : ''),
|
||||
`${host}${port !== undefined ? ':' + port : ''}:${path}`,
|
||||
this._getRealPath(),
|
||||
],
|
||||
{
|
||||
env: {
|
||||
LANG: 'C',
|
||||
},
|
||||
}
|
||||
).catch(error => {
|
||||
if (!error.stderr.includes('already mounted')) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async _sync () {
|
||||
await this._loadRealMounts()
|
||||
if (this._matchesRealMount() && !this._remote.enabled) {
|
||||
await this._umount(this._remote)
|
||||
} else if (!this._matchesRealMount() && this._remote.enabled) {
|
||||
if (this._remote.enabled) {
|
||||
await this._mount()
|
||||
} else {
|
||||
await this._umount()
|
||||
}
|
||||
|
||||
return this._remote
|
||||
}
|
||||
|
||||
@@ -83,7 +57,15 @@ export default class NfsHandler extends LocalHandler {
|
||||
}
|
||||
}
|
||||
|
||||
async _umount (remote) {
|
||||
await execa('umount', ['--force', this._getRealPath()])
|
||||
async _umount () {
|
||||
await execa('umount', ['--force', this._getRealPath()], {
|
||||
env: {
|
||||
LANG: 'C',
|
||||
},
|
||||
}).catch(error => {
|
||||
if (!error.stderr.includes('not mounted')) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,13 @@
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"index-modules": "^0.3.0",
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"bind-property-descriptor": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-dev": "^1.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -4,6 +4,47 @@
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [Host/Networks] Remove "Add network" button [#3386](https://github.com/vatesfr/xen-orchestra/issues/3386) (PR [#3478](https://github.com/vatesfr/xen-orchestra/pull/3478))
|
||||
- [Host/networks] Private networks table [#3387](https://github.com/vatesfr/xen-orchestra/issues/3387) (PR [#3481](https://github.com/vatesfr/xen-orchestra/pull/3481))
|
||||
- [Home/pool] Patch count pill now shows the number of unique patches in the pool [#3321](https://github.com/vatesfr/xen-orchestra/issues/3321) (PR [#3483](https://github.com/vatesfr/xen-orchestra/pull/3483))
|
||||
- [Patches] Pre-install checks to avoid errors [#3252](https://github.com/vatesfr/xen-orchestra/issues/3252)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [Backup NG] Fix `Cannot read property 'uuid' of undefined` when a disk is removed from a VM to backup (PR [#3479](https://github.com/vatesfr/xen-orchestra/pull/3479))
|
||||
- [Backup NG] Fix unexpected full after failure, interruption or basic rolling snapshot (PR [#3485](https://github.com/vatesfr/xen-orchestra/pull/3485))
|
||||
|
||||
### Released packages
|
||||
|
||||
- xo-acl-resolver v0.3.0
|
||||
- xo-server v5.28.0
|
||||
- xo-web v5.28.0
|
||||
|
||||
## **5.27.1** (2018-09-28)
|
||||
|
||||
### Enhancements
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [OVA Import] Allow import of files bigger than 127GB (PR [#3451](https://github.com/vatesfr/xen-orchestra/pull/3451))
|
||||
- [File restore] Fix a path issue when going back to the parent folder (PR [#3446](https://github.com/vatesfr/xen-orchestra/pull/3446))
|
||||
- [File restore] Fix a minor issue when showing which selected files are redundant (PR [#3447](https://github.com/vatesfr/xen-orchestra/pull/3447))
|
||||
- [Memory] Fix a major leak [#2580](https://github.com/vatesfr/xen-orchestra/issues/2580) [#2820](https://github.com/vatesfr/xen-orchestra/issues/2820) (PR [#3453](https://github.com/vatesfr/xen-orchestra/pull/3453))
|
||||
- [NFS Remotes] Fix `already mounted` race condition [#3380](https://github.com/vatesfr/xen-orchestra/issues/3380) (PR [#3460](https://github.com/vatesfr/xen-orchestra/pull/3460))
|
||||
- Fix `Cannot read property 'type' of undefined` when deleting a VM (PR [#3465](https://github.com/vatesfr/xen-orchestra/pull/3465))
|
||||
|
||||
### Released packages
|
||||
|
||||
- @xen-orchestra/fs v0.3.1
|
||||
- vhd-lib v0.3.1
|
||||
- xo-vmdk-to-vhd v0.1.4
|
||||
- xo-server v5.27.2
|
||||
- xo-web v5.27.1
|
||||
|
||||
## **5.27.0** (2018-09-24)
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [Remotes] Test the remote automatically on changes [#3323](https://github.com/vatesfr/xen-orchestra/issues/3323) (PR [#3397](https://github.com/vatesfr/xen-orchestra/pull/3397))
|
||||
- [Remotes] Use *WORKGROUP* as default domain for new SMB remote (PR [#3398](https://github.com/vatesfr/xen-orchestra/pull/3398))
|
||||
- [Backup NG form] Display a tip to encourage users to create vms on a thin-provisioned storage [#3334](https://github.com/vatesfr/xen-orchestra/issues/3334) (PR [#3402](https://github.com/vatesfr/xen-orchestra/pull/3402))
|
||||
@@ -13,6 +54,7 @@
|
||||
- [Backup reports] Ability to test the plugin (PR [#3421](https://github.com/vatesfr/xen-orchestra/pull/3421))
|
||||
- [Backup NG] Ability to restart failed VMs' backup [#3339](https://github.com/vatesfr/xen-orchestra/issues/3339) (PR [#3420](https://github.com/vatesfr/xen-orchestra/pull/3420))
|
||||
- [VM] Ability to change the NIC type [#3423](https://github.com/vatesfr/xen-orchestra/issues/3423) (PR [#3440](https://github.com/vatesfr/xen-orchestra/pull/3440))
|
||||
- [Backup NG Overview] Display the schedule's name [#3444](https://github.com/vatesfr/xen-orchestra/issues/3444) (PR [#3445](https://github.com/vatesfr/xen-orchestra/pull/3445))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -24,6 +66,7 @@
|
||||
- [Backup NG] Fix issue when *Delete first* was enabled for some of the remotes [#3424](https://github.com/vatesfr/xen-orchestra/issues/3424) (PR [#3427](https://github.com/vatesfr/xen-orchestra/pull/3427))
|
||||
- [VM/host consoles] Work around a XenServer/XCP-ng issue which lead to some consoles not working [#3432](https://github.com/vatesfr/xen-orchestra/issues/3432) (PR [#3435](https://github.com/vatesfr/xen-orchestra/pull/3435))
|
||||
- [Backup NG] Remove extraneous snapshots in case of multiple schedules [#3132](https://github.com/vatesfr/xen-orchestra/issues/3132) (PR [#3439](https://github.com/vatesfr/xen-orchestra/pull/3439))
|
||||
- [Backup NG] Fix page reloaded on creating a schedule [#3461](https://github.com/vatesfr/xen-orchestra/issues/3461) (PR [#3462](https://github.com/vatesfr/xen-orchestra/pull/3462))
|
||||
|
||||
### Released packages
|
||||
|
||||
|
||||
16
package.json
16
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/register": "7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/register": "^7.0.0",
|
||||
"babel-core": "^7.0.0-0",
|
||||
"babel-eslint": "^9.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.0.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"eslint": "^5.1.0",
|
||||
@@ -15,9 +15,9 @@
|
||||
"eslint-plugin-react": "^7.6.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"flow-bin": "^0.80.0",
|
||||
"flow-bin": "^0.82.0",
|
||||
"globby": "^8.0.0",
|
||||
"husky": "^0.14.3",
|
||||
"husky": "^1.0.0-rc.15",
|
||||
"jest": "^23.0.1",
|
||||
"lodash": "^4.17.4",
|
||||
"prettier": "^1.10.2",
|
||||
@@ -27,6 +27,11 @@
|
||||
"engines": {
|
||||
"yarn": "^1.7.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "scripts/lint-staged"
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverage": true,
|
||||
"projects": [
|
||||
@@ -49,7 +54,6 @@
|
||||
"dev": "scripts/run-script --parallel dev",
|
||||
"dev-test": "jest --bail --watch \"^(?!.*\\.integ\\.spec\\.js$)\"",
|
||||
"posttest": "scripts/run-script test",
|
||||
"precommit": "scripts/lint-staged",
|
||||
"prepare": "scripts/run-script prepare",
|
||||
"pretest": "eslint --ignore-path .gitignore .",
|
||||
"test": "jest \"^(?!.*\\.integ\\.spec\\.js$)\"",
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.1",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
"vhd-lib": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vhd-lib",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "Primitives for VHD file handling",
|
||||
"keywords": [],
|
||||
@@ -29,11 +29,11 @@
|
||||
"uuid": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@xen-orchestra/fs": "^0.3.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"@xen-orchestra/fs": "^0.3.1",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { SECTOR_SIZE } from './_constants'
|
||||
|
||||
export default function computeGeometryForSize (size) {
|
||||
const totalSectors = Math.ceil(size / 512)
|
||||
const totalSectors = Math.min(Math.ceil(size / 512), 65535 * 16 * 255)
|
||||
let sectorsPerTrackCylinder
|
||||
let heads
|
||||
let cylinderTimesHeads
|
||||
if (totalSectors > 65535 * 16 * 255) {
|
||||
throw Error('disk is too big')
|
||||
}
|
||||
// straight copypasta from the file spec appendix on CHS Calculation
|
||||
if (totalSectors >= 65535 * 16 * 63) {
|
||||
sectorsPerTrackCylinder = 255
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xen-api",
|
||||
"version": "0.18.0",
|
||||
"version": "0.19.0",
|
||||
"license": "ISC",
|
||||
"description": "Connector to the Xen API",
|
||||
"keywords": [
|
||||
@@ -33,10 +33,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"blocked": "^1.2.1",
|
||||
"debug": "^3.1.0",
|
||||
"debug": "^4.0.1",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"http-request-plus": "^0.5.0",
|
||||
"http-request-plus": "^0.6.0",
|
||||
"iterable-backoff": "^0.0.0",
|
||||
"jest-diff": "^23.5.0",
|
||||
"json-rpc-protocol": "^0.12.0",
|
||||
@@ -51,10 +51,10 @@
|
||||
"xo-collection": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
|
||||
@@ -86,7 +86,7 @@ const checkAuthorizationByTypes = {
|
||||
|
||||
'VM-controller': checkMember('$container'),
|
||||
|
||||
'VM-snapshot': checkMember('$snapshot_of'),
|
||||
'VM-snapshot': or(checkSelf, checkMember('$snapshot_of')),
|
||||
|
||||
'VM-template': or(checkSelf, checkMember('$pool')),
|
||||
}
|
||||
|
||||
@@ -28,31 +28,32 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.0.0",
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"bluebird": "^3.5.1",
|
||||
"chalk": "^2.2.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"fs-promise": "^2.0.3",
|
||||
"got": "^8.0.1",
|
||||
"http-request-plus": "^0.6.0",
|
||||
"human-format": "^0.10.0",
|
||||
"l33teral": "^3.0.3",
|
||||
"lodash": "^4.17.4",
|
||||
"micromatch": "^3.1.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"nice-pipe": "0.0.0",
|
||||
"pretty-ms": "^3.0.1",
|
||||
"pretty-ms": "^4.0.0",
|
||||
"progress-stream": "^2.0.0",
|
||||
"promise-toolbox": "^0.10.1",
|
||||
"pump": "^3.0.0",
|
||||
"pw": "^0.0.4",
|
||||
"strip-indent": "^2.0.0",
|
||||
"xdg-basedir": "^3.0.0",
|
||||
"xo-lib": "^0.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -11,10 +11,10 @@ const resolveUrl = require('url').resolve
|
||||
const stat = require('fs-promise').stat
|
||||
|
||||
const chalk = require('chalk')
|
||||
const eventToPromise = require('event-to-promise')
|
||||
const forEach = require('lodash/forEach')
|
||||
const fromCallback = require('promise-toolbox/fromCallback')
|
||||
const getKeys = require('lodash/keys')
|
||||
const got = require('got')
|
||||
const hrp = require('http-request-plus').default
|
||||
const humanFormat = require('human-format')
|
||||
const identity = require('lodash/identity')
|
||||
const isArray = require('lodash/isArray')
|
||||
@@ -23,6 +23,7 @@ const micromatch = require('micromatch')
|
||||
const nicePipe = require('nice-pipe')
|
||||
const pairs = require('lodash/toPairs')
|
||||
const pick = require('lodash/pick')
|
||||
const pump = require('pump')
|
||||
const startsWith = require('lodash/startsWith')
|
||||
const prettyMs = require('pretty-ms')
|
||||
const progressStream = require('progress-stream')
|
||||
@@ -362,50 +363,43 @@ async function call (args) {
|
||||
ensurePathParam(method, file)
|
||||
url = resolveUrl(baseUrl, result[key])
|
||||
const output = createWriteStream(file)
|
||||
const response = await hrp(url)
|
||||
|
||||
const progress = progressStream({ time: 1e3 }, printProgress)
|
||||
|
||||
return eventToPromise(
|
||||
nicePipe([
|
||||
got.stream(url).on('response', function (response) {
|
||||
const length = response.headers['content-length']
|
||||
if (length !== undefined) {
|
||||
progress.length(length)
|
||||
}
|
||||
}),
|
||||
progress,
|
||||
output,
|
||||
]),
|
||||
'finish'
|
||||
const progress = progressStream(
|
||||
{
|
||||
length: response.headers['content-length'],
|
||||
time: 1e3,
|
||||
},
|
||||
printProgress
|
||||
)
|
||||
|
||||
return fromCallback(cb => pump(response, progress, output, cb))
|
||||
}
|
||||
|
||||
if (key === '$sendTo') {
|
||||
ensurePathParam(method, file)
|
||||
url = resolveUrl(baseUrl, result[key])
|
||||
|
||||
const stats = await stat(file)
|
||||
const length = stats.size
|
||||
|
||||
const { size: length } = await stat(file)
|
||||
const input = nicePipe([
|
||||
createReadStream(file),
|
||||
progressStream(
|
||||
{
|
||||
length: length,
|
||||
length,
|
||||
time: 1e3,
|
||||
},
|
||||
printProgress
|
||||
),
|
||||
])
|
||||
|
||||
const response = await got.post(url, {
|
||||
body: input,
|
||||
headers: {
|
||||
'content-length': length,
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
return response.body
|
||||
return hrp
|
||||
.post(url, {
|
||||
body: input,
|
||||
headers: {
|
||||
'content-length': length,
|
||||
},
|
||||
})
|
||||
.readAll('utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"make-error": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"rimraf": "^2.6.1"
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
"lodash": "^4.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"deep-freeze": "^0.0.1",
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
"promise-toolbox": "^0.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
|
||||
@@ -40,9 +40,9 @@
|
||||
"moment-timezone": "^0.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
|
||||
@@ -31,14 +31,13 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"event-to-promise": "^0.8.0",
|
||||
"jsonrpc-websocket-client": "^0.4.1",
|
||||
"superagent": "^3.8.2"
|
||||
"http-request-plus": "^0.6.0",
|
||||
"jsonrpc-websocket-client": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.5.4"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import Client, { createBackoff } from 'jsonrpc-websocket-client'
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import request from 'superagent'
|
||||
import { PassThrough } from 'stream'
|
||||
import hrp from 'http-request-plus'
|
||||
|
||||
const UPDATER_URL = 'localhost'
|
||||
const WS_PORT = 9001
|
||||
@@ -145,17 +143,16 @@ class XoServerCloud {
|
||||
throw new Error('cannot get download token')
|
||||
}
|
||||
|
||||
const req = request
|
||||
.get(`${UPDATER_URL}:${HTTP_PORT}/`)
|
||||
.set('Authorization', `Bearer ${downloadToken}`)
|
||||
const response = await hrp(`${UPDATER_URL}:${HTTP_PORT}/`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${downloadToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// Impossible to pipe the response directly: https://github.com/visionmedia/superagent/issues/1187
|
||||
const pt = new PassThrough()
|
||||
req.pipe(pt)
|
||||
const { headers } = await eventToPromise(req, 'response')
|
||||
pt.length = headers['content-length']
|
||||
// currently needed for XenApi#putResource()
|
||||
response.length = response.headers['content-length']
|
||||
|
||||
return pt
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
"lodash": "^4.16.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
"promise-toolbox": "^0.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server",
|
||||
"version": "5.27.0",
|
||||
"version": "5.27.2",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "Server part of Xen-Orchestra",
|
||||
"keywords": [
|
||||
@@ -31,11 +31,10 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.0.0",
|
||||
"@xen-orchestra/async-map": "^0.0.0",
|
||||
"@xen-orchestra/cron": "^1.0.3",
|
||||
"@xen-orchestra/emit-async": "^0.0.0",
|
||||
"@xen-orchestra/fs": "^0.3.0",
|
||||
"@xen-orchestra/fs": "^0.3.1",
|
||||
"@xen-orchestra/mixin": "^0.0.0",
|
||||
"ajv": "^6.1.1",
|
||||
"app-conf": "^0.5.0",
|
||||
@@ -49,7 +48,7 @@
|
||||
"cookie": "^0.3.1",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"d3-time-format": "^2.1.1",
|
||||
"debug": "^3.1.0",
|
||||
"debug": "^4.0.1",
|
||||
"decorator-synchronized": "^0.3.0",
|
||||
"deptree": "^1.0.0",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
@@ -67,7 +66,7 @@
|
||||
"helmet": "^3.9.0",
|
||||
"highland": "^2.11.1",
|
||||
"http-proxy": "^1.16.2",
|
||||
"http-request-plus": "^0.5.0",
|
||||
"http-request-plus": "^0.6.0",
|
||||
"http-server-plus": "^0.10.0",
|
||||
"human-format": "^0.10.0",
|
||||
"is-redirect": "^1.0.0",
|
||||
@@ -113,29 +112,30 @@
|
||||
"tmp": "^0.0.33",
|
||||
"uuid": "^3.0.1",
|
||||
"value-matcher": "^0.2.0",
|
||||
"vhd-lib": "^0.3.0",
|
||||
"vhd-lib": "^0.3.1",
|
||||
"ws": "^6.0.0",
|
||||
"xen-api": "^0.18.0",
|
||||
"xen-api": "^0.19.0",
|
||||
"xml2js": "^0.4.19",
|
||||
"xo-acl-resolver": "^0.2.4",
|
||||
"xo-collection": "^0.4.1",
|
||||
"xo-common": "^0.1.1",
|
||||
"xo-remote-parser": "^0.5.0",
|
||||
"xo-vmdk-to-vhd": "^0.1.3",
|
||||
"xo-vmdk-to-vhd": "^0.1.4",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "7.0.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.0.0",
|
||||
"@babel/plugin-proposal-pipeline-operator": "7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "^7.0.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"index-modules": "^0.3.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import appConf from 'app-conf'
|
||||
import assert from 'assert'
|
||||
import bind from 'lodash/bind'
|
||||
import blocked from 'blocked'
|
||||
import createExpress from 'express'
|
||||
@@ -540,6 +541,9 @@ ${name} v${version}`)(require('../package.json'))
|
||||
// ===================================================================
|
||||
|
||||
export default async function main (args) {
|
||||
// makes sure the global Promise has not been changed by a lib
|
||||
assert(global.Promise === require('bluebird'))
|
||||
|
||||
if (includes(args, '--help') || includes(args, '-h')) {
|
||||
return USAGE
|
||||
}
|
||||
|
||||
@@ -230,6 +230,16 @@ const STATS = {
|
||||
getPath: matches => ['xvds', 'w', matches[1]],
|
||||
},
|
||||
},
|
||||
iops: {
|
||||
r: {
|
||||
test: /^vbd_xvd(.)_iops_read$/,
|
||||
getPath: matches => ['iops', 'r', matches[1]],
|
||||
},
|
||||
w: {
|
||||
test: /^vbd_xvd(.)_iops_write$/,
|
||||
getPath: matches => ['iops', 'w', matches[1]],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from 'promise-toolbox'
|
||||
import { PassThrough } from 'stream'
|
||||
import { forbiddenOperation } from 'xo-common/api-errors'
|
||||
import { Xapi as XapiBase } from 'xen-api'
|
||||
import { Xapi as XapiBase, NULL_REF } from 'xen-api'
|
||||
import {
|
||||
every,
|
||||
find,
|
||||
@@ -63,7 +63,6 @@ import {
|
||||
canSrHaveNewVdiOfSize,
|
||||
isVmHvm,
|
||||
isVmRunning,
|
||||
NULL_REF,
|
||||
optional,
|
||||
parseDateTime,
|
||||
prepareXapiParam,
|
||||
@@ -674,6 +673,9 @@ export default class Xapi extends XapiBase {
|
||||
})
|
||||
}
|
||||
|
||||
// must be done before destroying the VM
|
||||
const disks = getVmDisks(vm)
|
||||
|
||||
// this cannot be done in parallel, otherwise disks and snapshots will be
|
||||
// destroyed even if this fails
|
||||
await this.call('VM.destroy', $ref)
|
||||
@@ -684,7 +686,7 @@ export default class Xapi extends XapiBase {
|
||||
)::ignoreErrors(),
|
||||
|
||||
deleteDisks &&
|
||||
asyncMap(getVmDisks(vm), ({ $ref: vdiRef }) => {
|
||||
asyncMap(disks, ({ $ref: vdiRef }) => {
|
||||
let onFailure = () => {
|
||||
onFailure = vdi => {
|
||||
console.error(
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import deferrable from 'golike-defer'
|
||||
import { ignoreErrors, pCatch } from 'promise-toolbox'
|
||||
import { find, gte, includes, isEmpty, lte, noop } from 'lodash'
|
||||
import { ignoreErrors, pCatch } from 'promise-toolbox'
|
||||
import { NULL_REF } from 'xen-api'
|
||||
|
||||
import { forEach, mapToArray, parseSize } from '../../utils'
|
||||
|
||||
import { isVmHvm, isVmRunning, makeEditObject, NULL_REF } from '../utils'
|
||||
import { isVmHvm, isVmRunning, makeEditObject } from '../utils'
|
||||
|
||||
// According to: https://xenserver.org/blog/entry/vga-over-cirrus-in-xenserver-6-2.html.
|
||||
const XEN_VGA_VALUES = ['std', 'cirrus']
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NULL_REF } from './utils'
|
||||
import { NULL_REF } from 'xen-api'
|
||||
|
||||
const OTHER_CONFIG_TEMPLATE = {
|
||||
actions_after_crash: 'restart',
|
||||
|
||||
@@ -366,10 +366,6 @@ export const makeEditObject = specs => {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export const NULL_REF = 'OpaqueRef:NULL'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export const useUpdateSystem = host => {
|
||||
// Match Xen Center's condition: https://github.com/xenserver/xenadmin/blob/f3a64fc54bbff239ca6f285406d9034f57537d64/XenModel/Utils/Helpers.cs#L420
|
||||
return versionSatisfies(host.software_version.platform_version, '^2.1.1')
|
||||
|
||||
@@ -11,8 +11,9 @@ import { AssertionError } from 'assert'
|
||||
import { basename, dirname } from 'path'
|
||||
import {
|
||||
countBy,
|
||||
findLast,
|
||||
flatMap,
|
||||
forEach,
|
||||
forOwn,
|
||||
groupBy,
|
||||
isEmpty,
|
||||
last,
|
||||
@@ -769,6 +770,7 @@ export default class BackupNg {
|
||||
},
|
||||
xapi._updateObjectMapProperty(vm, 'other_config', {
|
||||
'xo:backup:datetime': null,
|
||||
'xo:backup:exported': null,
|
||||
'xo:backup:job': null,
|
||||
'xo:backup:schedule': null,
|
||||
'xo:backup:vm': null,
|
||||
@@ -889,7 +891,19 @@ export default class BackupNg {
|
||||
|
||||
snapshot = await xapi.barrier(snapshot.$ref)
|
||||
|
||||
let baseSnapshot = mode === 'delta' ? last(snapshots) : undefined
|
||||
let baseSnapshot
|
||||
if (mode === 'delta') {
|
||||
baseSnapshot = findLast(
|
||||
snapshots,
|
||||
_ => 'xo:backup:exported' in _.other_config
|
||||
)
|
||||
|
||||
// JFT 2018-10-02: support previous snapshots which did not have this
|
||||
// entry, can be removed after 2018-12.
|
||||
if (baseSnapshot === undefined) {
|
||||
baseSnapshot = last(snapshots)
|
||||
}
|
||||
}
|
||||
snapshots.push(snapshot)
|
||||
|
||||
// snapshots to delete due to the snapshot retention settings
|
||||
@@ -1135,6 +1149,15 @@ export default class BackupNg {
|
||||
const fullRequired = { __proto__: null }
|
||||
const vdis: $Dict<Vdi> = getVmDisks(baseSnapshot)
|
||||
|
||||
// ignore VDI snapshots which no longer have a parent
|
||||
forOwn(vdis, (vdi, key, vdis) => {
|
||||
// `vdi.snapshot_of` is not always set to the null ref, it can contain
|
||||
// an invalid ref, that's why the test is on `vdi.$snapshot_of`
|
||||
if (vdi.$snapshot_of === undefined) {
|
||||
delete vdis[key]
|
||||
}
|
||||
})
|
||||
|
||||
for (const { $id: srId, xapi } of srs) {
|
||||
const replicatedVm = listReplicatedVms(
|
||||
xapi,
|
||||
@@ -1151,7 +1174,7 @@ export default class BackupNg {
|
||||
getVmDisks(replicatedVm),
|
||||
vdi => vdi.other_config[TAG_COPY_SRC]
|
||||
)
|
||||
forEach(vdis, vdi => {
|
||||
forOwn(vdis, vdi => {
|
||||
if (!(vdi.uuid in replicatedVdis)) {
|
||||
fullRequired[vdi.$snapshot_of.$id] = true
|
||||
}
|
||||
@@ -1439,6 +1462,17 @@ export default class BackupNg {
|
||||
} else {
|
||||
throw new Error(`no exporter for backup mode ${mode}`)
|
||||
}
|
||||
|
||||
await wrapTask(
|
||||
{
|
||||
logger,
|
||||
message: 'set snapshot.other_config[xo:backup:exported]',
|
||||
parentId: taskId,
|
||||
},
|
||||
xapi._updateObjectMapProperty(snapshot, 'other_config', {
|
||||
'xo:backup:exported': 'true',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async _deleteDeltaVmBackups (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-vmdk-to-vhd",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "JS lib streaming a vmdk file to a vhd",
|
||||
"keywords": [
|
||||
@@ -27,12 +27,12 @@
|
||||
"pipette": "^0.9.3",
|
||||
"promise-toolbox": "^0.10.1",
|
||||
"tmp": "^0.0.33",
|
||||
"vhd-lib": "^0.3.0"
|
||||
"vhd-lib": "^0.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/preset-env": "7.0.0",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.3",
|
||||
"event-to-promise": "^0.8.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "xo-web",
|
||||
"version": "5.27.0",
|
||||
"version": "5.27.1",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "Web interface client for Xen-Orchestra",
|
||||
"keywords": [
|
||||
@@ -139,7 +139,7 @@
|
||||
"xo-common": "^0.1.1",
|
||||
"xo-lib": "^0.8.0",
|
||||
"xo-remote-parser": "^0.5.0",
|
||||
"xo-vmdk-to-vhd": "^0.1.3"
|
||||
"xo-vmdk-to-vhd": "^0.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production gulp build",
|
||||
|
||||
@@ -676,6 +676,8 @@ const messages = {
|
||||
poolNetworkNameLabel: 'Name',
|
||||
poolNetworkDescription: 'Description',
|
||||
poolNetworkPif: 'PIFs',
|
||||
privateNetworks: 'Private networks',
|
||||
manage: 'Manage',
|
||||
poolNoNetwork: 'No networks',
|
||||
poolNetworkMTU: 'MTU',
|
||||
poolNetworkPifAttached: 'Connected',
|
||||
@@ -829,9 +831,17 @@ const messages = {
|
||||
// ----- Pool patch tabs -----
|
||||
refreshPatches: 'Refresh patches',
|
||||
installPoolPatches: 'Install pool patches',
|
||||
confirmPoolPatch:
|
||||
'Are you sure you want to install all the patches on this pool?',
|
||||
poolNeedsDefaultSr: 'The pool needs a default SR to install the patches.',
|
||||
selectDefaultSr: 'Select a default SR',
|
||||
vmsHaveCds:
|
||||
'{nVms, number} VM{nVms, plural, one {} other {s}} {nVms, plural, one {has} other {have}} CDs',
|
||||
ejectCds: 'Eject CDs',
|
||||
// ----- Pool storage tabs -----
|
||||
defaultSr: 'Default SR',
|
||||
setAsDefaultSr: 'Set as default SR',
|
||||
setDefaultSr: 'Set default SR:',
|
||||
|
||||
// ----- VM tabs -----
|
||||
generalTabName: 'General',
|
||||
|
||||
@@ -259,6 +259,8 @@ export const confirm = ({ body, icon = 'alarm', title, strongConfirm }) =>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const preventDefault = event => event.preventDefault()
|
||||
|
||||
class FormModal extends BaseComponent {
|
||||
state = {
|
||||
value: this.props.defaultValue,
|
||||
@@ -271,7 +273,7 @@ class FormModal extends BaseComponent {
|
||||
render () {
|
||||
const { body, formId } = this.props
|
||||
return (
|
||||
<form id={formId}>
|
||||
<form id={formId} onSubmit={preventDefault}>
|
||||
{cloneElement(body, {
|
||||
value: this.state.value,
|
||||
onChange: this.linkState('value'),
|
||||
|
||||
@@ -715,10 +715,21 @@ export const installAllHostPatches = host =>
|
||||
subscribeHostMissingPatches.forceRefresh(host)
|
||||
)
|
||||
|
||||
export const installAllPatchesOnPool = pool =>
|
||||
_call('pool.installAllPatches', { pool: resolveId(pool) })::tap(() =>
|
||||
subscribeHostMissingPatches.forceRefresh()
|
||||
import InstallPoolPatchesModalBody from './install-pool-patches-modal' // eslint-disable-line import/first
|
||||
export const installAllPatchesOnPool = pool => {
|
||||
const poolId = resolveId(pool)
|
||||
return confirm({
|
||||
body: <InstallPoolPatchesModalBody pool={poolId} />,
|
||||
title: _('installPoolPatches'),
|
||||
icon: 'host-patch-update',
|
||||
}).then(
|
||||
() =>
|
||||
_call('pool.installAllPatches', { pool: poolId })::tap(() =>
|
||||
subscribeHostMissingPatches.forceRefresh()
|
||||
),
|
||||
noop
|
||||
)
|
||||
}
|
||||
|
||||
export const installSupplementalPack = (host, file) => {
|
||||
info(
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import _ from 'intl'
|
||||
import ActionButton from 'action-button'
|
||||
import Component from 'base-component'
|
||||
import Icon from 'icon'
|
||||
import React from 'react'
|
||||
import SingleLineRow from 'single-line-row'
|
||||
import Tooltip from 'tooltip'
|
||||
import { Container, Col } from 'grid'
|
||||
import { connectStore } from 'utils'
|
||||
import {
|
||||
createCollectionWrapper,
|
||||
createCounter,
|
||||
createGetObject,
|
||||
createGetObjectsOfType,
|
||||
createSelector,
|
||||
} from 'selectors'
|
||||
import { forEach } from 'lodash'
|
||||
import { SelectSr } from 'select-objects'
|
||||
import { VmItem } from 'render-xo-item'
|
||||
import { ejectCd, isSrWritable, setDefaultSr } from 'xo'
|
||||
|
||||
@connectStore(() => {
|
||||
const getPool = createGetObject((_, props) => props.pool)
|
||||
return {
|
||||
pool: getPool,
|
||||
poolMaster: createGetObject(
|
||||
createSelector(getPool, ({ master }) => master)
|
||||
),
|
||||
vbds: createGetObjectsOfType('VBD').filter(
|
||||
createSelector(getPool, ({ id }) => vbd => vbd.$pool === id)
|
||||
),
|
||||
}
|
||||
})
|
||||
export default class InstallPoolPatchesModalBody extends Component {
|
||||
_getVmsWithCds = createSelector(
|
||||
() => this.props.vbds,
|
||||
createCollectionWrapper(vbds => {
|
||||
const vmIds = []
|
||||
forEach(vbds, vbd => {
|
||||
if (
|
||||
vbd.is_cd_drive &&
|
||||
vbd.VDI !== undefined &&
|
||||
!vmIds.includes(vbd.VM)
|
||||
) {
|
||||
vmIds.push(vbd.VM)
|
||||
}
|
||||
})
|
||||
return vmIds
|
||||
})
|
||||
)
|
||||
|
||||
_getNVmsWithCds = createCounter(this._getVmsWithCds)
|
||||
|
||||
_ejectCds = () => Promise.all(this._getVmsWithCds().map(ejectCd))
|
||||
|
||||
_getTooltip = createSelector(this._getVmsWithCds, vmIds =>
|
||||
vmIds.map(vmId => (
|
||||
<p className='m-0' key={vmId}>
|
||||
<VmItem id={vmId} />
|
||||
</p>
|
||||
))
|
||||
)
|
||||
|
||||
render () {
|
||||
const { pool, poolMaster } = this.props
|
||||
const needDefaultSr =
|
||||
poolMaster.productBrand !== 'XCP-ng' && pool.default_SR === undefined
|
||||
const someCdsInserted = this._getNVmsWithCds() > 0
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{!needDefaultSr &&
|
||||
!someCdsInserted && (
|
||||
<SingleLineRow>
|
||||
<Col>{_('confirmPoolPatch')}</Col>
|
||||
</SingleLineRow>
|
||||
)}
|
||||
{needDefaultSr && [
|
||||
<SingleLineRow className='mt-1' key='message'>
|
||||
<Col>
|
||||
<Icon icon='alarm' /> {_('poolNeedsDefaultSr')}
|
||||
</Col>
|
||||
</SingleLineRow>,
|
||||
<SingleLineRow className='mt-1' key='select'>
|
||||
<Col size={6}>{_('setDefaultSr')}</Col>
|
||||
<Col size={5}>
|
||||
<div className='input-group'>
|
||||
<SelectSr
|
||||
onChange={this.linkState('sr')}
|
||||
predicate={sr => sr.$pool === pool.id && isSrWritable(sr)}
|
||||
value={this.state.sr}
|
||||
/>
|
||||
<span className='input-group-btn'>
|
||||
<ActionButton
|
||||
handler={setDefaultSr}
|
||||
handlerParam={this.state.sr}
|
||||
icon='save'
|
||||
>
|
||||
{_('formOk')}
|
||||
</ActionButton>
|
||||
</span>
|
||||
</div>
|
||||
</Col>
|
||||
</SingleLineRow>,
|
||||
]}
|
||||
{someCdsInserted && (
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Tooltip content={this._getTooltip()}>
|
||||
<Col size={6}>
|
||||
<Icon icon='alarm' />{' '}
|
||||
{_('vmsHaveCds', { nVms: this._getNVmsWithCds() })}
|
||||
</Col>
|
||||
</Tooltip>
|
||||
<Col size={6}>
|
||||
<ActionButton icon='vm-eject' handler={this._ejectCds}>
|
||||
{_('ejectCds')}
|
||||
</ActionButton>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ const fileOptionRenderer = ({ isFile, name }) => (
|
||||
</span>
|
||||
)
|
||||
|
||||
const ensureTrailingSlash = path => path + (endsWith(path, '/') ? '' : '/')
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const formatFilesOptions = (rawFiles, path) => {
|
||||
@@ -56,7 +58,7 @@ const formatFilesOptions = (rawFiles, path) => {
|
||||
id: '..',
|
||||
isFile: false,
|
||||
name: '..',
|
||||
path: dirname(path),
|
||||
path: ensureTrailingSlash(dirname(path)),
|
||||
},
|
||||
]
|
||||
: []
|
||||
@@ -258,8 +260,10 @@ export default class RestoreFileModalBody extends Component {
|
||||
const redundantFiles = {}
|
||||
forEach(files, file => {
|
||||
redundantFiles[file.path] =
|
||||
find(files, f => f !== file && startsWith(file.path, f.path)) !==
|
||||
undefined
|
||||
find(
|
||||
files,
|
||||
f => !f.isFile && f !== file && startsWith(file.path, f.path)
|
||||
) !== undefined
|
||||
})
|
||||
return redundantFiles
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ const _runBackupNgJob = ({ id, name, schedule }) =>
|
||||
const SchedulePreviewBody = ({ item: job, userData: { schedulesByJob } }) => (
|
||||
<table>
|
||||
<tr className='text-muted'>
|
||||
<th>{_('scheduleName')}</th>
|
||||
<th>{_('scheduleCron')}</th>
|
||||
<th>{_('scheduleTimezone')}</th>
|
||||
<th>{_('scheduleExportRetention')}</th>
|
||||
@@ -64,6 +65,7 @@ const SchedulePreviewBody = ({ item: job, userData: { schedulesByJob } }) => (
|
||||
</tr>
|
||||
{map(schedulesByJob && schedulesByJob[job.id], schedule => (
|
||||
<tr key={schedule.id}>
|
||||
<td>{schedule.name}</td>
|
||||
<td>{schedule.cron}</td>
|
||||
<td>{schedule.timezone}</td>
|
||||
<td>{job.settings[schedule.id].exportRetention}</td>
|
||||
|
||||
@@ -590,10 +590,18 @@ export default [
|
||||
setting.snapshotRetention
|
||||
)
|
||||
? {
|
||||
copyRetention: setting.copyRetention || DEFAULT_RETENTION,
|
||||
exportRetention: setting.exportRetention || DEFAULT_RETENTION,
|
||||
snapshotRetention:
|
||||
setting.snapshotRetention || DEFAULT_RETENTION,
|
||||
copyRetention: defined(
|
||||
setting.copyRetention,
|
||||
DEFAULT_RETENTION
|
||||
),
|
||||
exportRetention: defined(
|
||||
setting.exportRetention,
|
||||
DEFAULT_RETENTION
|
||||
),
|
||||
snapshotRetention: defined(
|
||||
setting.snapshotRetention,
|
||||
DEFAULT_RETENTION
|
||||
),
|
||||
}
|
||||
: setting
|
||||
),
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import _ from 'intl'
|
||||
import Component from 'base-component'
|
||||
import Ellipsis, { EllipsisContainer } from 'ellipsis'
|
||||
import flatMap from 'lodash/flatMap'
|
||||
import Icon from 'icon'
|
||||
import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
import SingleLineRow from 'single-line-row'
|
||||
import size from 'lodash/size'
|
||||
import HomeTags from 'home-tags'
|
||||
import Tooltip from 'tooltip'
|
||||
import Link, { BlockLink } from 'link'
|
||||
@@ -14,6 +11,7 @@ import { Col } from 'grid'
|
||||
import { Text } from 'editable'
|
||||
import { addTag, editPool, getHostMissingPatches, removeTag } from 'xo'
|
||||
import { connectStore, formatSizeShort } from 'utils'
|
||||
import { flatten, map, size, uniq } from 'lodash'
|
||||
import {
|
||||
createGetObjectsOfType,
|
||||
createGetHostMetrics,
|
||||
@@ -32,7 +30,7 @@ import styles from './index.css'
|
||||
|
||||
const getMissingPatches = createSelector(getPoolHosts, hosts => {
|
||||
return Promise.all(map(hosts, host => getHostMissingPatches(host))).then(
|
||||
patches => flatMap(patches)
|
||||
patches => uniq(map(flatten(patches), 'name'))
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { editHost, fetchHostStats, subscribeHostMissingPatches } from 'xo'
|
||||
import { connectStore, routes } from 'utils'
|
||||
import {
|
||||
createDoesHostNeedRestart,
|
||||
createFilter,
|
||||
createGetObject,
|
||||
createGetObjectsOfType,
|
||||
createSelector,
|
||||
@@ -76,6 +77,13 @@ const isRunning = host => host && host.power_state === 'Running'
|
||||
createSelector(getPifs, pifs => map(pifs, pif => pif.$network))
|
||||
)
|
||||
|
||||
const getPrivateNetworks = createFilter(
|
||||
createGetObjectsOfType('network'),
|
||||
createSelector(getPool, pool => network =>
|
||||
network.$pool === pool.id && isEmpty(network.PIFs)
|
||||
)
|
||||
)
|
||||
|
||||
const getHostPatches = createSelector(
|
||||
createGetObjectsOfType('pool_patch'),
|
||||
createGetObjectsOfType('host_patch').pick(
|
||||
@@ -114,6 +122,7 @@ const isRunning = host => host && host.power_state === 'Running'
|
||||
nVms: getNumberOfVms(state, props),
|
||||
pifs: getPifs(state, props),
|
||||
pool: getPool(state, props),
|
||||
privateNetworks: getPrivateNetworks(state, props),
|
||||
vmController: getVmController(state, props),
|
||||
vms: getHostVms(state, props),
|
||||
}
|
||||
@@ -317,6 +326,7 @@ export default class Host extends Component {
|
||||
'nVms',
|
||||
'pbds',
|
||||
'pifs',
|
||||
'privateNetworks',
|
||||
'srs',
|
||||
'vmController',
|
||||
'vms',
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import _ from 'intl'
|
||||
import ActionButton from 'action-button'
|
||||
import Component from 'base-component'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import React from 'react'
|
||||
import Icon from 'icon'
|
||||
import pick from 'lodash/pick'
|
||||
import SingleLineRow from 'single-line-row'
|
||||
import some from 'lodash/some'
|
||||
import SortedTable from 'sorted-table'
|
||||
import StateButton from 'state-button'
|
||||
import TabButton from 'tab-button'
|
||||
import Tooltip from 'tooltip'
|
||||
import { confirm } from 'modal'
|
||||
import { connectStore, noop } from 'utils'
|
||||
@@ -18,9 +16,9 @@ import { error } from 'notification'
|
||||
import { get } from '@xen-orchestra/defined'
|
||||
import { Select, Number } from 'editable'
|
||||
import { Toggle } from 'form'
|
||||
import { isEmpty, pick, some } from 'lodash'
|
||||
import {
|
||||
connectPif,
|
||||
createNetwork,
|
||||
deletePif,
|
||||
deletePifs,
|
||||
disconnectPif,
|
||||
@@ -211,7 +209,7 @@ class PifItemLock extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const COLUMNS = [
|
||||
const PIF_COLUMNS = [
|
||||
{
|
||||
default: true,
|
||||
itemRenderer: pif => pif.device,
|
||||
@@ -293,7 +291,7 @@ const COLUMNS = [
|
||||
},
|
||||
]
|
||||
|
||||
const INDIVIDUAL_ACTIONS = [
|
||||
const PIF_INDIVIDUAL_ACTIONS = [
|
||||
{
|
||||
handler: pif => copy(pif.uuid),
|
||||
icon: 'clipboard',
|
||||
@@ -307,7 +305,7 @@ const INDIVIDUAL_ACTIONS = [
|
||||
},
|
||||
]
|
||||
|
||||
const GROUPED_ACTIONS = [
|
||||
const PIF_GROUPED_ACTIONS = [
|
||||
{
|
||||
handler: deletePifs,
|
||||
icon: 'delete',
|
||||
@@ -316,34 +314,79 @@ const GROUPED_ACTIONS = [
|
||||
},
|
||||
]
|
||||
|
||||
export default class TabNetwork extends Component {
|
||||
render () {
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Col className='text-xs-right'>
|
||||
<TabButton
|
||||
btnStyle='primary'
|
||||
handler={createNetwork}
|
||||
handlerParam={this.props.host}
|
||||
icon='add'
|
||||
labelId='networkCreateButton'
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<SortedTable
|
||||
collection={this.props.pifs}
|
||||
columns={COLUMNS}
|
||||
groupedActions={GROUPED_ACTIONS}
|
||||
individualActions={INDIVIDUAL_ACTIONS}
|
||||
stateUrlParam='s'
|
||||
userData={{ networks: this.props.networks }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
const PVT_NETWORK_COLUMNS = [
|
||||
{
|
||||
name: _('poolNetworkNameLabel'),
|
||||
itemRenderer: network => network.name_label,
|
||||
},
|
||||
{
|
||||
name: _('poolNetworkDescription'),
|
||||
itemRenderer: network => network.name_description,
|
||||
},
|
||||
{
|
||||
name: _('poolNetworkMTU'),
|
||||
itemRenderer: network => network.MTU,
|
||||
},
|
||||
{
|
||||
name: (
|
||||
<div className='text-xs-center'>
|
||||
<Tooltip content={_('defaultLockingMode')}>
|
||||
<Icon size='lg' icon='lock' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
itemRenderer: network => (
|
||||
<Icon icon={network.defaultIsLocked ? 'lock' : 'unlock'} />
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const PVT_NETWORK_ACTIONS = [
|
||||
{
|
||||
handler: network => copy(network.uuid),
|
||||
icon: 'clipboard',
|
||||
label: network => _('copyUuid', { uuid: network.uuid }),
|
||||
},
|
||||
]
|
||||
|
||||
export default ({ host, networks, pifs, privateNetworks }) => (
|
||||
<Container>
|
||||
<Row>
|
||||
<Col>
|
||||
<h1>{_('poolNetworkPif')}</h1>
|
||||
<SortedTable
|
||||
collection={pifs}
|
||||
columns={PIF_COLUMNS}
|
||||
data-networks={networks}
|
||||
groupedActions={PIF_GROUPED_ACTIONS}
|
||||
individualActions={PIF_INDIVIDUAL_ACTIONS}
|
||||
stateUrlParam='s'
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
{!isEmpty(privateNetworks) && (
|
||||
<Row>
|
||||
<Col>
|
||||
<h1>
|
||||
{_('privateNetworks')}
|
||||
<ActionButton
|
||||
className='ml-1'
|
||||
handler={noop}
|
||||
icon='edit'
|
||||
redirectOnSuccess={`/pools/${
|
||||
host.$pool
|
||||
}/network?s=${encodeURIComponent('!PIFs:length?')}`}
|
||||
>
|
||||
{_('manage')}
|
||||
</ActionButton>
|
||||
</h1>
|
||||
<SortedTable
|
||||
collection={privateNetworks}
|
||||
columns={PVT_NETWORK_COLUMNS}
|
||||
individualActions={PVT_NETWORK_ACTIONS}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
|
||||
@@ -376,7 +376,11 @@ export default class TabNetworks extends Component {
|
||||
<Row>
|
||||
<Col>
|
||||
{!isEmpty(networks) ? (
|
||||
<SortedTable collection={networks} columns={NETWORKS_COLUMNS} />
|
||||
<SortedTable
|
||||
collection={networks}
|
||||
columns={NETWORKS_COLUMNS}
|
||||
stateUrlParam='s'
|
||||
/>
|
||||
) : (
|
||||
<h4 className='text-xs-center'>{_('poolNoNetwork')}</h4>
|
||||
)}
|
||||
|
||||
@@ -153,11 +153,7 @@ export default class Vm extends BaseComponent {
|
||||
() => this.props.checkPermissions,
|
||||
() => this.props.vm,
|
||||
() => this.props.srs,
|
||||
(checkPermissions, vm, srs) =>
|
||||
checkPermissions([
|
||||
[vm.id, 'administrate'],
|
||||
...map(srs, sr => [sr.id, 'operate']),
|
||||
])
|
||||
(checkPermissions, vm, srs) => checkPermissions(vm.id, 'administrate')
|
||||
)
|
||||
|
||||
_setNameDescription = nameDescription =>
|
||||
|
||||
Reference in New Issue
Block a user