Skip to content

Commit

Permalink
feat(xo-web/VM/Snapshots): add fast clone option when creating a VM (#…
Browse files Browse the repository at this point in the history
…3136)

Fixes #3120
  • Loading branch information
Rajaa-BARHTAOUI authored and pdonias committed Jul 12, 2018
1 parent 3370014 commit 3ed081f
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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

Expand Down
13 changes: 7 additions & 6 deletions packages/xo-web/src/common/action-button.js
Expand Up @@ -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({
Expand Down
2 changes: 2 additions & 0 deletions packages/xo-web/src/common/intl/messages.js
Expand Up @@ -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',
Expand Down
118 changes: 83 additions & 35 deletions packages/xo-web/src/common/xo/copy-vm-modal/index.js
@@ -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'
Expand All @@ -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>
&nbsp;
<SingleLineRow>
<Col size={6}>{_('copyVmName')}</Col>
<Col size={6}>
<input
className='form-control'
onChange={this._onChangeName}
placeholder={formatMessage(messages.copyVmNamePlaceholder)}
type='text'
/>
</Col>
</SingleLineRow>
&nbsp;
<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>
Expand Down
29 changes: 16 additions & 13 deletions packages/xo-web/src/common/xo/index.js
Expand Up @@ -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)
}

Expand Down
26 changes: 4 additions & 22 deletions packages/xo-web/src/xo-app/vm/tab-snapshots.js
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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 (
Expand Down Expand Up @@ -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}
/>
Expand Down

0 comments on commit 3ed081f

Please sign in to comment.