feat(xo-server,xo-web/snapshot): ability to snapshot the VM memory (#3812)
Fixes #3795
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
- [Backup NG] Restore logs moved to restore tab [#3772](https://github.com/vatesfr/xen-orchestra/issues/3772) (PR [#3802](https://github.com/vatesfr/xen-orchestra/pull/3802))
|
- [Backup NG] Restore logs moved to restore tab [#3772](https://github.com/vatesfr/xen-orchestra/issues/3772) (PR [#3802](https://github.com/vatesfr/xen-orchestra/pull/3802))
|
||||||
- [Remotes] New SMB implementation that provides better stability and performance [#2257](https://github.com/vatesfr/xen-orchestra/issues/2257) (PR [#3708](https://github.com/vatesfr/xen-orchestra/pull/3708))
|
- [Remotes] New SMB implementation that provides better stability and performance [#2257](https://github.com/vatesfr/xen-orchestra/issues/2257) (PR [#3708](https://github.com/vatesfr/xen-orchestra/pull/3708))
|
||||||
- [VM/advanced] ACL management from VM view [#3040](https://github.com/vatesfr/xen-orchestra/issues/3727) (PR [#3040](https://github.com/vatesfr/xen-orchestra/pull/3774))
|
- [VM/advanced] ACL management from VM view [#3040](https://github.com/vatesfr/xen-orchestra/issues/3727) (PR [#3040](https://github.com/vatesfr/xen-orchestra/pull/3774))
|
||||||
|
- [VM / snapshots] Ability to save the VM memory [#3795](https://github.com/vatesfr/xen-orchestra/issues/3795) (PR [#3812](https://github.com/vatesfr/xen-orchestra/pull/3812))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,19 @@ export default {
|
|||||||
// https://xapi-project.github.io/xen-api/classes/vm.html#checkpoint
|
// https://xapi-project.github.io/xen-api/classes/vm.html#checkpoint
|
||||||
async checkpointVm(vmId, nameLabel) {
|
async checkpointVm(vmId, nameLabel) {
|
||||||
const vm = this.getObject(vmId)
|
const vm = this.getObject(vmId)
|
||||||
const ref = await this.callAsync(
|
try {
|
||||||
'VM.checkpoint',
|
const ref = await this.callAsync(
|
||||||
vm.$ref,
|
'VM.checkpoint',
|
||||||
nameLabel != null ? nameLabel : vm.name_label
|
vm.$ref,
|
||||||
).then(extractOpaqueRef)
|
nameLabel != null ? nameLabel : vm.name_label
|
||||||
return this.barrier(ref)
|
).then(extractOpaqueRef)
|
||||||
|
return this.barrier(ref)
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'VM_BAD_POWER_STATE') {
|
||||||
|
return this._snapshotVm(vm, nameLabel)
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: clean up on error.
|
// TODO: clean up on error.
|
||||||
|
|||||||
@@ -1001,6 +1001,8 @@ const messages = {
|
|||||||
|
|
||||||
// ----- VM snapshot tab -----
|
// ----- VM snapshot tab -----
|
||||||
noSnapshots: 'No snapshots',
|
noSnapshots: 'No snapshots',
|
||||||
|
newSnapshotWithMemory: 'New snapshot with memory',
|
||||||
|
snapshotMemorySaved: 'memory saved',
|
||||||
snapshotCreateButton: 'New snapshot',
|
snapshotCreateButton: 'New snapshot',
|
||||||
tipCreateSnapshotLabel: 'Just click on the snapshot button to create one!',
|
tipCreateSnapshotLabel: 'Just click on the snapshot button to create one!',
|
||||||
revertSnapshot: 'Revert VM to this snapshot',
|
revertSnapshot: 'Revert VM to this snapshot',
|
||||||
@@ -1477,9 +1479,8 @@ const messages = {
|
|||||||
restartVmsModalTitle: 'Restart VM{vms, plural, one {} other {s}}',
|
restartVmsModalTitle: 'Restart VM{vms, plural, one {} other {s}}',
|
||||||
restartVmsModalMessage:
|
restartVmsModalMessage:
|
||||||
'Are you sure you want to restart {vms, number} VM{vms, plural, one {} other {s}}?',
|
'Are you sure you want to restart {vms, number} VM{vms, plural, one {} other {s}}?',
|
||||||
|
snapshotSaveMemory: 'save memory',
|
||||||
snapshotVmsModalTitle: 'Snapshot VM{vms, plural, one {} other {s}}',
|
snapshotVmsModalTitle: 'Snapshot VM{vms, plural, one {} other {s}}',
|
||||||
snapshotVmsModalMessage:
|
|
||||||
'Are you sure you want to snapshot {vms, number} VM{vms, plural, one {} other {s}}?',
|
|
||||||
deleteVmsModalTitle: 'Delete VM{vms, plural, one {} other {s}}',
|
deleteVmsModalTitle: 'Delete VM{vms, plural, one {} other {s}}',
|
||||||
deleteVmsModalMessage:
|
deleteVmsModalMessage:
|
||||||
'Are you sure you want to delete {vms, number} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED',
|
'Are you sure you want to delete {vms, number} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED',
|
||||||
|
|||||||
@@ -1116,13 +1116,19 @@ export const deleteTemplates = templates =>
|
|||||||
}, noop)
|
}, noop)
|
||||||
}, noop)
|
}, noop)
|
||||||
|
|
||||||
export const snapshotVm = vm => _call('vm.snapshot', { id: resolveId(vm) })
|
export const snapshotVm = (vm, saveMemory) =>
|
||||||
|
_call('vm.snapshot', { id: resolveId(vm), saveMemory })
|
||||||
|
|
||||||
|
import SnapshotVmModalBody from './snapshot-vm-modal' // eslint-disable-line import/first
|
||||||
export const snapshotVms = vms =>
|
export const snapshotVms = vms =>
|
||||||
confirm({
|
confirm({
|
||||||
|
icon: 'memory',
|
||||||
title: _('snapshotVmsModalTitle', { vms: vms.length }),
|
title: _('snapshotVmsModalTitle', { vms: vms.length }),
|
||||||
body: _('snapshotVmsModalMessage', { vms: vms.length }),
|
body: <SnapshotVmModalBody />,
|
||||||
}).then(() => map(vms, vmId => snapshotVm({ id: vmId })), noop)
|
}).then(
|
||||||
|
saveMemory => Promise.all(map(vms, vm => snapshotVm(vm, saveMemory))),
|
||||||
|
noop
|
||||||
|
)
|
||||||
|
|
||||||
export const deleteSnapshot = vm =>
|
export const deleteSnapshot = vm =>
|
||||||
confirm({
|
confirm({
|
||||||
|
|||||||
23
packages/xo-web/src/common/xo/snapshot-vm-modal/index.js
Normal file
23
packages/xo-web/src/common/xo/snapshot-vm-modal/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import Component from 'base-component'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import _ from '../../intl'
|
||||||
|
|
||||||
|
export default class SnapshotVmModalBody extends Component {
|
||||||
|
get value() {
|
||||||
|
return this.state.saveMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='checkbox'
|
||||||
|
onChange={this.linkState('saveMemory')}
|
||||||
|
checked={this.state.saveMemory}
|
||||||
|
/>{' '}
|
||||||
|
{_('snapshotSaveMemory')}
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,10 +48,18 @@ const COLUMNS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
itemRenderer: snapshot => (
|
itemRenderer: snapshot => (
|
||||||
<Text
|
<div>
|
||||||
onChange={value => editVm(snapshot, { name_label: value })}
|
<Text
|
||||||
value={snapshot.name_label}
|
onChange={value => editVm(snapshot, { name_label: value })}
|
||||||
/>
|
value={snapshot.name_label}
|
||||||
|
/>{' '}
|
||||||
|
{/* checkpoint snapshots are in a Suspended state */}
|
||||||
|
{snapshot.power_state === 'Suspended' && (
|
||||||
|
<Tooltip content={_('snapshotMemorySaved')}>
|
||||||
|
<Icon icon='memory' color='text-success' />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
name: _('snapshotName'),
|
name: _('snapshotName'),
|
||||||
sortCriteria: _ => _.name_label,
|
sortCriteria: _ => _.name_label,
|
||||||
@@ -107,6 +115,8 @@ const INDIVIDUAL_ACTIONS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const _snapshotVmWithMemory = vm => snapshotVm(vm, true)
|
||||||
|
|
||||||
@connectStore(() => ({
|
@connectStore(() => ({
|
||||||
snapshots: createGetObjectsOfType('VM-snapshot')
|
snapshots: createGetObjectsOfType('VM-snapshot')
|
||||||
.pick((_, props) => props.vm.snapshots)
|
.pick((_, props) => props.vm.snapshots)
|
||||||
@@ -119,6 +129,16 @@ export default class TabSnapshot extends Component {
|
|||||||
<Container>
|
<Container>
|
||||||
<Row>
|
<Row>
|
||||||
<Col className='text-xs-right'>
|
<Col className='text-xs-right'>
|
||||||
|
{vm.power_state !== 'Halted' && (
|
||||||
|
<TabButton
|
||||||
|
btnStyle='primary'
|
||||||
|
handler={_snapshotVmWithMemory}
|
||||||
|
handlerParam={vm}
|
||||||
|
icon='memory'
|
||||||
|
labelId='newSnapshotWithMemory'
|
||||||
|
pending={includes(vm.current_operations, 'checkpoint')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<TabButton
|
<TabButton
|
||||||
btnStyle='primary'
|
btnStyle='primary'
|
||||||
handler={snapshotVm}
|
handler={snapshotVm}
|
||||||
|
|||||||
Reference in New Issue
Block a user