feat(xo-web/VM/Snapshots): add fast clone option when creating a VM (#3136)
Fixes #3120
This commit is contained in:
parent
3370014ddf
commit
3ed081f04c
@ -12,6 +12,7 @@
|
||||
- [Backup NG overview] Display concurrency and offline snapshot value [3087](https://github.com/vatesfr/xen-orchestra/issues/3087) (PR [3145](https://github.com/vatesfr/xen-orchestra/pull/3145))
|
||||
- [VM revert] notify the result of reverting a VM [3095](https://github.com/vatesfr/xen-orchestra/issues/3095) (PR [3150](https://github.com/vatesfr/xen-orchestra/pull/3150))
|
||||
- [Backup NG logs] Link XO items in the details modal [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) (PR [#3171](https://github.com/vatesfr/xen-orchestra/pull/3171))
|
||||
- [VM/Snapshots] Add fast clone option when creating a VM [#3120](https://github.com/vatesfr/xen-orchestra/issues/3120) (PR [#3136](https://github.com/vatesfr/xen-orchestra/pull/3136))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
@ -87,12 +87,13 @@ export default class ActionButton extends Component {
|
||||
const result = await handler(handlerParam)
|
||||
|
||||
const { redirectOnSuccess } = props
|
||||
if (redirectOnSuccess) {
|
||||
return this.context.router.push(
|
||||
isFunction(redirectOnSuccess)
|
||||
? redirectOnSuccess(result, handlerParam)
|
||||
: redirectOnSuccess
|
||||
)
|
||||
if (redirectOnSuccess !== undefined) {
|
||||
const to = isFunction(redirectOnSuccess)
|
||||
? redirectOnSuccess(result, handlerParam)
|
||||
: redirectOnSuccess
|
||||
if (to !== undefined) {
|
||||
return this.context.router.push(to)
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
@ -1532,6 +1532,8 @@ const messages = {
|
||||
copyVmCompress: 'Use compression',
|
||||
copyVmsNoTargetSr: 'No target SR',
|
||||
copyVmsNoTargetSrMessage: 'A target SR is required to copy a VM',
|
||||
fastCloneMode: 'Fast clone',
|
||||
fullCopyMode: 'Full copy',
|
||||
|
||||
// ----- Detach host -----
|
||||
detachHostModalTitle: 'Detach host',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { Component } from 'react'
|
||||
import BaseComponent from 'base-component'
|
||||
import React from 'react'
|
||||
|
||||
import _, { messages } from '../../intl'
|
||||
import SingleLineRow from '../../single-line-row'
|
||||
@ -8,51 +9,98 @@ import { SelectSr } from '../../select-objects'
|
||||
import { Toggle } from '../../form'
|
||||
import { injectIntl } from 'react-intl'
|
||||
|
||||
class CopyVmModalBody extends Component {
|
||||
state = { compress: false }
|
||||
class CopyVmModalBody extends BaseComponent {
|
||||
state = {
|
||||
compress: false,
|
||||
copyMode: 'fullCopy',
|
||||
}
|
||||
|
||||
get value () {
|
||||
const { state } = this
|
||||
const { props, state } = this
|
||||
return {
|
||||
compress: state.compress,
|
||||
name: this.state.name || this.props.vm.name_label,
|
||||
sr: state.sr.id,
|
||||
copyMode: state.copyMode,
|
||||
name: state.name || props.vm.name_label,
|
||||
sr: state.sr && state.sr.id,
|
||||
}
|
||||
}
|
||||
|
||||
_onChangeSr = sr => this.setState({ sr })
|
||||
_onChangeName = event => this.setState({ name: event.target.value })
|
||||
_onChangeCompress = compress => this.setState({ compress })
|
||||
|
||||
render () {
|
||||
const { formatMessage } = this.props.intl
|
||||
const { compress, copyMode, name, sr } = this.state
|
||||
|
||||
return process.env.XOA_PLAN > 2 ? (
|
||||
<div>
|
||||
<SingleLineRow>
|
||||
<Col size={6}>{_('copyVmSelectSr')}</Col>
|
||||
<Col size={6}>
|
||||
<SelectSr onChange={this._onChangeSr} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
|
||||
<SingleLineRow>
|
||||
<Col size={6}>{_('copyVmName')}</Col>
|
||||
<Col size={6}>
|
||||
<input
|
||||
className='form-control'
|
||||
onChange={this._onChangeName}
|
||||
placeholder={formatMessage(messages.copyVmNamePlaceholder)}
|
||||
type='text'
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
|
||||
<SingleLineRow>
|
||||
<Col size={6}>{_('copyVmCompress')}</Col>
|
||||
<Col size={6}>
|
||||
<Toggle onChange={this._onChangeCompress} />
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<div>
|
||||
<SingleLineRow>
|
||||
<Col size={4}>{_('copyVmName')}</Col>
|
||||
<Col size={6} className='ml-2'>
|
||||
<input
|
||||
className='form-control'
|
||||
onChange={this.linkState('name')}
|
||||
placeholder={formatMessage(messages.copyVmNamePlaceholder)}
|
||||
type='text'
|
||||
value={name}
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</div>
|
||||
<div className='mt-1'>
|
||||
<SingleLineRow>
|
||||
<Col>
|
||||
<label>
|
||||
<input
|
||||
checked={copyMode === 'fullCopy'}
|
||||
name='copyMode'
|
||||
onChange={this.linkState('copyMode')}
|
||||
type='radio'
|
||||
value='fullCopy'
|
||||
/>
|
||||
<span> {_('fullCopyMode')} </span>
|
||||
</label>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={4} className='ml-2'>
|
||||
{_('copyVmSelectSr')}
|
||||
</Col>
|
||||
<Col size={6}>
|
||||
<SelectSr
|
||||
disabled={copyMode !== 'fullCopy'}
|
||||
onChange={this.linkState('sr')}
|
||||
value={sr}
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col size={4} className='ml-2'>
|
||||
{_('copyVmCompress')}
|
||||
</Col>
|
||||
<Col size={6}>
|
||||
<Toggle
|
||||
disabled={copyMode !== 'fullCopy'}
|
||||
onChange={this.linkState('compress')}
|
||||
value={compress}
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</div>
|
||||
<div>
|
||||
<SingleLineRow className='mt-1'>
|
||||
<Col>
|
||||
<label>
|
||||
<input
|
||||
checked={copyMode === 'fastClone'}
|
||||
name='copyMode'
|
||||
onChange={this.linkState('copyMode')}
|
||||
type='radio'
|
||||
value='fastClone'
|
||||
/>
|
||||
<span> {_('fastCloneMode')} </span>
|
||||
</label>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
|
@ -939,30 +939,33 @@ export const cloneVm = ({ id, name_label: nameLabel }, fullCopy = false) =>
|
||||
full_copy: fullCopy,
|
||||
})
|
||||
|
||||
const _copyVm = ({ vm, sr, name, compress }) =>
|
||||
_call('vm.copy', {
|
||||
vm: resolveId(vm),
|
||||
sr,
|
||||
name: name || vm.name_label + '_COPY',
|
||||
compress,
|
||||
})
|
||||
|
||||
import CopyVmModalBody from './copy-vm-modal' // eslint-disable-line import/first
|
||||
export const copyVm = (vm, sr, name, compress) => {
|
||||
const vmId = resolveId(vm)
|
||||
return sr !== undefined
|
||||
? confirm({
|
||||
title: _('copyVm'),
|
||||
body: _('copyVmConfirm', { SR: sr.name_label }),
|
||||
}).then(() =>
|
||||
_call('vm.copy', {
|
||||
vm: vmId,
|
||||
sr: sr.id,
|
||||
name: name || vm.name_label + '_COPY',
|
||||
compress,
|
||||
})
|
||||
)
|
||||
}).then(() => _copyVm({ vm, sr: sr.id, name, compress }))
|
||||
: confirm({
|
||||
title: _('copyVm'),
|
||||
body: <CopyVmModalBody vm={vm} />,
|
||||
}).then(params => {
|
||||
if (!params.sr) {
|
||||
error('copyVmsNoTargetSr', 'copyVmsNoTargetSrMessage')
|
||||
return
|
||||
if (params.copyMode === 'fullCopy') {
|
||||
if (!params.sr) {
|
||||
error(_('copyVmsNoTargetSr'), _('copyVmsNoTargetSrMessage'))
|
||||
return
|
||||
}
|
||||
return _copyVm({ vm, ...params })
|
||||
}
|
||||
return _call('vm.copy', { vm: vmId, ...params })
|
||||
return cloneVm({ id: vm.id, name_label: params.name })
|
||||
}, noop)
|
||||
}
|
||||
|
||||
|
@ -6,17 +6,12 @@ import SortedTable from 'sorted-table'
|
||||
import TabButton from 'tab-button'
|
||||
import Tooltip from 'tooltip'
|
||||
import { connectStore } from 'utils'
|
||||
import { FormattedRelative, FormattedTime } from 'react-intl'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import { Text } from 'editable'
|
||||
import { createGetObjectsOfType } from 'selectors'
|
||||
import { FormattedRelative, FormattedTime } from 'react-intl'
|
||||
import { includes, isEmpty } from 'lodash'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import {
|
||||
createSelector,
|
||||
createGetObjectsOfType,
|
||||
getCheckPermissions,
|
||||
} from 'selectors'
|
||||
import {
|
||||
cloneVm,
|
||||
copyVm,
|
||||
deleteSnapshot,
|
||||
deleteSnapshots,
|
||||
@ -86,13 +81,7 @@ const INDIVIDUAL_ACTIONS = [
|
||||
handler: snapshot => copyVm(snapshot),
|
||||
icon: 'vm-copy',
|
||||
label: _('copySnapshot'),
|
||||
},
|
||||
{
|
||||
disabled: (snapshot, { canAdministrate }) => !canAdministrate(snapshot),
|
||||
handler: snapshot => cloneVm(snapshot, false),
|
||||
icon: 'vm-fast-clone',
|
||||
label: _('fastCloneVmLabel'),
|
||||
redirectOnSuccess: snapshot => `/vms/${snapshot}/general`,
|
||||
redirectOnSuccess: vm => vm && `/vms/${vm}/general`,
|
||||
},
|
||||
{
|
||||
handler: exportVm,
|
||||
@ -119,17 +108,11 @@ const INDIVIDUAL_ACTIONS = [
|
||||
]
|
||||
|
||||
@connectStore(() => ({
|
||||
checkPermissions: getCheckPermissions,
|
||||
snapshots: createGetObjectsOfType('VM-snapshot')
|
||||
.pick((_, props) => props.vm.snapshots)
|
||||
.sort(),
|
||||
}))
|
||||
export default class TabSnapshot extends Component {
|
||||
_getCanAdministrate = createSelector(
|
||||
() => this.props.checkPermissions,
|
||||
check => vm => check(vm.id, 'administrate')
|
||||
)
|
||||
|
||||
render () {
|
||||
const { snapshots, vm } = this.props
|
||||
return (
|
||||
@ -164,7 +147,6 @@ export default class TabSnapshot extends Component {
|
||||
<SortedTable
|
||||
collection={snapshots}
|
||||
columns={COLUMNS}
|
||||
data-canAdministrate={this._getCanAdministrate()}
|
||||
groupedActions={GROUPED_ACTIONS}
|
||||
individualActions={INDIVIDUAL_ACTIONS}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user