parent
d99555a4a8
commit
53477be12d
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
- Export VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3194](https://github.com/vatesfr/xen-orchestra/pull/3194))
|
- Export VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3194](https://github.com/vatesfr/xen-orchestra/pull/3194))
|
||||||
- Search syntax support wildcard (`*`) and regular expressions [#3190](https://github.com/vatesfr/xen-orchestra/issues/3190) (PRs [#3198](https://github.com/vatesfr/xen-orchestra/pull/3198) & [#3199](https://github.com/vatesfr/xen-orchestra/pull/3199))
|
- Search syntax support wildcard (`*`) and regular expressions [#3190](https://github.com/vatesfr/xen-orchestra/issues/3190) (PRs [#3198](https://github.com/vatesfr/xen-orchestra/pull/3198) & [#3199](https://github.com/vatesfr/xen-orchestra/pull/3199))
|
||||||
|
- Import VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3216](https://github.com/vatesfr/xen-orchestra/pull/3216))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
@ -8,16 +8,18 @@ import styles from './index.css'
|
|||||||
@propTypes({
|
@propTypes({
|
||||||
onDrop: propTypes.func,
|
onDrop: propTypes.func,
|
||||||
message: propTypes.node,
|
message: propTypes.node,
|
||||||
|
multiple: propTypes.bool,
|
||||||
})
|
})
|
||||||
export default class Dropzone extends Component {
|
export default class Dropzone extends Component {
|
||||||
render () {
|
render () {
|
||||||
const { onDrop, message } = this.props
|
const { onDrop, message, multiple } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactDropzone
|
<ReactDropzone
|
||||||
onDrop={onDrop}
|
|
||||||
className={styles.dropzone}
|
|
||||||
activeClassName={styles.activeDropzone}
|
activeClassName={styles.activeDropzone}
|
||||||
|
className={styles.dropzone}
|
||||||
|
multiple={multiple}
|
||||||
|
onDrop={onDrop}
|
||||||
>
|
>
|
||||||
<div className={styles.dropzoneText}>{message}</div>
|
<div className={styles.dropzoneText}>{message}</div>
|
||||||
</ReactDropzone>
|
</ReactDropzone>
|
||||||
|
@ -918,6 +918,9 @@ const messages = {
|
|||||||
deleteSelectedVdis: 'Delete selected VDIs',
|
deleteSelectedVdis: 'Delete selected VDIs',
|
||||||
deleteSelectedVdi: 'Delete selected VDI',
|
deleteSelectedVdi: 'Delete selected VDI',
|
||||||
exportVdi: 'Export VDI content',
|
exportVdi: 'Export VDI content',
|
||||||
|
importVdi: 'Import VDI content',
|
||||||
|
importVdiNoFile: 'No file selected',
|
||||||
|
selectVdiMessage: 'Drop VHD file here',
|
||||||
useQuotaWarning:
|
useQuotaWarning:
|
||||||
'Creating this disk will use the disk space quota from the resource set {resourceSet} ({spaceLeft} left)',
|
'Creating this disk will use the disk space quota from the resource set {resourceSet} ({spaceLeft} left)',
|
||||||
notEnoughSpaceInResourceSet:
|
notEnoughSpaceInResourceSet:
|
||||||
@ -1242,8 +1245,11 @@ const messages = {
|
|||||||
importVmsCleanList: 'Reset',
|
importVmsCleanList: 'Reset',
|
||||||
vmImportSuccess: 'VM import success',
|
vmImportSuccess: 'VM import success',
|
||||||
vmImportFailed: 'VM import failed',
|
vmImportFailed: 'VM import failed',
|
||||||
|
vdiImportSuccess: 'VDI import success',
|
||||||
|
vdiImportFailed: 'VDI import failed',
|
||||||
setVmFailed: 'Error on setting the VM: {vm}',
|
setVmFailed: 'Error on setting the VM: {vm}',
|
||||||
startVmImport: 'Import starting…',
|
startVmImport: 'Import starting…',
|
||||||
|
startVdiImport: 'VDI import starting…',
|
||||||
startVmExport: 'Export starting…',
|
startVmExport: 'Export starting…',
|
||||||
startVdiExport: 'VDI export starting…',
|
startVdiExport: 'VDI export starting…',
|
||||||
nCpus: 'N CPUs',
|
nCpus: 'N CPUs',
|
||||||
|
21
packages/xo-web/src/common/xo/import-vdi-modal/index.js
Normal file
21
packages/xo-web/src/common/xo/import-vdi-modal/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import _ from 'intl'
|
||||||
|
import Component from 'base-component'
|
||||||
|
import Dropzone from 'dropzone'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default class ImportVdiModalBody extends Component {
|
||||||
|
get value () {
|
||||||
|
return this.state.file
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { file } = this.state
|
||||||
|
return (
|
||||||
|
<Dropzone
|
||||||
|
onDrop={this.linkState('file', '0')}
|
||||||
|
message={file === undefined ? _('selectVdiMessage') : file.name}
|
||||||
|
multiple={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1256,6 +1256,38 @@ export const importVm = (file, type = 'xva', data = undefined, sr) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import ImportVdiModalBody from './import-vdi-modal' // eslint-disable-line import/first
|
||||||
|
export const importVdi = async vdi => {
|
||||||
|
const file = await confirm({
|
||||||
|
body: <ImportVdiModalBody />,
|
||||||
|
icon: 'import',
|
||||||
|
title: _('importVdi'),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (file === undefined) {
|
||||||
|
error(_('importVdi'), _('importVdiNoFile'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name } = file
|
||||||
|
info(_('startVdiImport'), name)
|
||||||
|
|
||||||
|
return _call('disk.importContent', { id: resolveId(vdi) }).then(
|
||||||
|
({ $sendTo }) =>
|
||||||
|
post($sendTo, file)
|
||||||
|
.then(res => {
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw res.status
|
||||||
|
}
|
||||||
|
success(_('vdiImportSuccess'), name)
|
||||||
|
return res.json().then(body => body.result)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
error(_('vdiImportFailed'), err)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const importVms = (vms, sr) =>
|
export const importVms = (vms, sr) =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
map(vms, ({ file, type, data }) =>
|
map(vms, ({ file, type, data }) =>
|
||||||
|
@ -16,7 +16,7 @@ import { Text } from 'editable'
|
|||||||
import { SizeInput, Toggle } from 'form'
|
import { SizeInput, Toggle } from 'form'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
import { connectStore, formatSize, noop } from 'utils'
|
import { connectStore, formatSize, noop } from 'utils'
|
||||||
import { concat, isEmpty, map, some } from 'lodash'
|
import { concat, groupBy, isEmpty, map, mapValues, pick, some } from 'lodash'
|
||||||
import {
|
import {
|
||||||
createGetObjectsOfType,
|
createGetObjectsOfType,
|
||||||
createSelector,
|
createSelector,
|
||||||
@ -31,6 +31,7 @@ import {
|
|||||||
disconnectVbd,
|
disconnectVbd,
|
||||||
editVdi,
|
editVdi,
|
||||||
exportVdi,
|
exportVdi,
|
||||||
|
importVdi,
|
||||||
isVmRunning,
|
isVmRunning,
|
||||||
} from 'xo'
|
} from 'xo'
|
||||||
|
|
||||||
@ -191,6 +192,12 @@ const INDIVIDUAL_ACTIONS = [
|
|||||||
icon: 'export',
|
icon: 'export',
|
||||||
label: _('exportVdi'),
|
label: _('exportVdi'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
disabled: ({ id }, { isVdiAttached }) => isVdiAttached[id],
|
||||||
|
handler: importVdi,
|
||||||
|
icon: 'import',
|
||||||
|
label: _('importVdi'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
handler: vdi => copy(vdi.uuid),
|
handler: vdi => copy(vdi.uuid),
|
||||||
icon: 'clipboard',
|
icon: 'clipboard',
|
||||||
@ -276,6 +283,7 @@ class NewDisk extends Component {
|
|||||||
|
|
||||||
@connectStore(() => ({
|
@connectStore(() => ({
|
||||||
checkPermissions: getCheckPermissions,
|
checkPermissions: getCheckPermissions,
|
||||||
|
vbds: createGetObjectsOfType('VBD'),
|
||||||
}))
|
}))
|
||||||
export default class SrDisks extends Component {
|
export default class SrDisks extends Component {
|
||||||
_closeNewDiskForm = () => this.setState({ newDisk: false })
|
_closeNewDiskForm = () => this.setState({ newDisk: false })
|
||||||
@ -293,6 +301,15 @@ export default class SrDisks extends Component {
|
|||||||
(check, id) => check(id, 'administrate')
|
(check, id) => check(id, 'administrate')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_getIsVdiAttached = createSelector(
|
||||||
|
createSelector(
|
||||||
|
() => this.props.vbds,
|
||||||
|
() => map(this.props.vdis, 'id'),
|
||||||
|
(vbds, vdis) => pick(groupBy(vbds, 'VDI'), vdis)
|
||||||
|
),
|
||||||
|
vbdsByVdi => mapValues(vbdsByVdi, vbds => some(vbds, 'attached'))
|
||||||
|
)
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const vdis = this._getAllVdis()
|
const vdis = this._getAllVdis()
|
||||||
const { newDisk } = this.state
|
const { newDisk } = this.state
|
||||||
@ -325,6 +342,7 @@ export default class SrDisks extends Component {
|
|||||||
<SortedTable
|
<SortedTable
|
||||||
collection={vdis}
|
collection={vdis}
|
||||||
columns={COLUMNS}
|
columns={COLUMNS}
|
||||||
|
data-isVdiAttached={this._getIsVdiAttached()}
|
||||||
defaultFilter='filterOnlyManaged'
|
defaultFilter='filterOnlyManaged'
|
||||||
filters={FILTERS}
|
filters={FILTERS}
|
||||||
groupedActions={GROUPED_ACTIONS}
|
groupedActions={GROUPED_ACTIONS}
|
||||||
|
@ -14,6 +14,7 @@ import SortedTable from 'sorted-table'
|
|||||||
import TabButton from 'tab-button'
|
import TabButton from 'tab-button'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
import {
|
import {
|
||||||
|
createGetObjectsOfType,
|
||||||
createSelector,
|
createSelector,
|
||||||
createFinder,
|
createFinder,
|
||||||
getCheckPermissions,
|
getCheckPermissions,
|
||||||
@ -33,7 +34,17 @@ import { SizeInput, Toggle } from 'form'
|
|||||||
import { XoSelect, Size, Text } from 'editable'
|
import { XoSelect, Size, Text } from 'editable'
|
||||||
import { confirm } from 'modal'
|
import { confirm } from 'modal'
|
||||||
import { error } from 'notification'
|
import { error } from 'notification'
|
||||||
import { filter, find, forEach, get, map, mapValues, some } from 'lodash'
|
import {
|
||||||
|
filter,
|
||||||
|
find,
|
||||||
|
forEach,
|
||||||
|
get,
|
||||||
|
groupBy,
|
||||||
|
map,
|
||||||
|
mapValues,
|
||||||
|
pick,
|
||||||
|
some,
|
||||||
|
} from 'lodash'
|
||||||
import {
|
import {
|
||||||
attachDiskToVm,
|
attachDiskToVm,
|
||||||
createDisk,
|
createDisk,
|
||||||
@ -45,6 +56,7 @@ import {
|
|||||||
disconnectVbd,
|
disconnectVbd,
|
||||||
editVdi,
|
editVdi,
|
||||||
exportVdi,
|
exportVdi,
|
||||||
|
importVdi,
|
||||||
isSrWritable,
|
isSrWritable,
|
||||||
isVmRunning,
|
isVmRunning,
|
||||||
migrateVdi,
|
migrateVdi,
|
||||||
@ -585,6 +597,7 @@ class MigrateVdiModalBody extends Component {
|
|||||||
@connectStore(() => ({
|
@connectStore(() => ({
|
||||||
checkPermissions: getCheckPermissions,
|
checkPermissions: getCheckPermissions,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
allVbds: createGetObjectsOfType('VBD'),
|
||||||
}))
|
}))
|
||||||
export default class TabDisks extends Component {
|
export default class TabDisks extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@ -652,12 +665,27 @@ export default class TabDisks extends Component {
|
|||||||
(vdis, vbds, vm) => mapValues(vdis, vdi => find(vbds, { VDI: vdi.id }))
|
(vdis, vbds, vm) => mapValues(vdis, vdi => find(vbds, { VDI: vdi.id }))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_getIsVdiAttached = createSelector(
|
||||||
|
createSelector(
|
||||||
|
() => this.props.allVbds,
|
||||||
|
() => Object.keys(this.props.vdis),
|
||||||
|
(vbds, vdis) => pick(groupBy(vbds, 'VDI'), vdis)
|
||||||
|
),
|
||||||
|
vbdsByVdi => mapValues(vbdsByVdi, vbds => some(vbds, 'attached'))
|
||||||
|
)
|
||||||
|
|
||||||
individualActions = [
|
individualActions = [
|
||||||
{
|
{
|
||||||
handler: exportVdi,
|
handler: exportVdi,
|
||||||
icon: 'export',
|
icon: 'export',
|
||||||
label: _('exportVdi'),
|
label: _('exportVdi'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
disabled: ({ id }, { isVdiAttached }) => isVdiAttached[id],
|
||||||
|
handler: importVdi,
|
||||||
|
icon: 'import',
|
||||||
|
label: _('importVdi'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
handler: this._migrateVdi,
|
handler: this._migrateVdi,
|
||||||
icon: 'vdi-migrate',
|
icon: 'vdi-migrate',
|
||||||
@ -735,6 +763,7 @@ export default class TabDisks extends Component {
|
|||||||
actions={ACTIONS}
|
actions={ACTIONS}
|
||||||
collection={vdis}
|
collection={vdis}
|
||||||
columns={vm.virtualizationMode === 'pv' ? COLUMNS_VM_PV : COLUMNS}
|
columns={vm.virtualizationMode === 'pv' ? COLUMNS_VM_PV : COLUMNS}
|
||||||
|
data-isVdiAttached={this._getIsVdiAttached()}
|
||||||
data-srs={srs}
|
data-srs={srs}
|
||||||
data-vbdsByVdi={this._getVbdsByVdi()}
|
data-vbdsByVdi={this._getVbdsByVdi()}
|
||||||
data-vm={vm}
|
data-vm={vm}
|
||||||
|
Loading…
Reference in New Issue
Block a user