feat(xo-web/backup/restore): ability to ignore certain VDIs (#6143)
Fixes #4605
This commit is contained in:
parent
fbb5c47358
commit
dfa5009a9b
@ -30,7 +30,12 @@ exports.ImportVmBackup = class ImportVmBackup {
|
||||
} else {
|
||||
assert.strictEqual(metadata.mode, 'delta')
|
||||
|
||||
backup = await adapter.readDeltaVmBackup(metadata)
|
||||
const ignoredVdis = new Set(
|
||||
Object.entries(this._importDeltaVmSettings.mapVdisSrs)
|
||||
.filter(([_, srUuid]) => srUuid === null)
|
||||
.map(([vdiUuid]) => vdiUuid)
|
||||
)
|
||||
backup = await adapter.readDeltaVmBackup(metadata, ignoredVdis)
|
||||
Object.values(backup.streams).forEach(stream => watchStreamSize(stream, sizeContainer))
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ const fromCallback = require('promise-toolbox/fromCallback')
|
||||
const fromEvent = require('promise-toolbox/fromEvent')
|
||||
const pDefer = require('promise-toolbox/defer')
|
||||
const groupBy = require('lodash/groupBy.js')
|
||||
const pickBy = require('lodash/pickBy.js')
|
||||
const { dirname, join, normalize, resolve } = require('path')
|
||||
const { createLogger } = require('@xen-orchestra/log')
|
||||
const { Constants, createVhdDirectoryFromStream, openVhd, VhdAbstract, VhdDirectory, VhdSynthetic } = require('vhd-lib')
|
||||
@ -576,14 +577,15 @@ class RemoteAdapter {
|
||||
return stream
|
||||
}
|
||||
|
||||
async readDeltaVmBackup(metadata) {
|
||||
async readDeltaVmBackup(metadata, ignoredVdis) {
|
||||
const handler = this._handler
|
||||
const { vbds, vdis, vhds, vifs, vm } = metadata
|
||||
const { vbds, vhds, vifs, vm } = metadata
|
||||
const dir = dirname(metadata._filename)
|
||||
const vdis = ignoredVdis === undefined ? metadata.vdis : pickBy(metadata.vdis, vdi => !ignoredVdis.has(vdi.uuid))
|
||||
|
||||
const streams = {}
|
||||
await asyncMapSettled(Object.keys(vdis), async id => {
|
||||
streams[`${id}.vhd`] = await this._createSyntheticStream(handler, join(dir, vhds[id]))
|
||||
await asyncMapSettled(Object.keys(vdis), async ref => {
|
||||
streams[`${ref}.vhd`] = await this._createSyntheticStream(handler, join(dir, vhds[ref]))
|
||||
})
|
||||
|
||||
return {
|
||||
|
@ -20,6 +20,9 @@ exports.TAG_COPY_SRC = TAG_COPY_SRC
|
||||
|
||||
const ensureArray = value => (value === undefined ? [] : Array.isArray(value) ? value : [value])
|
||||
const resolveUuid = async (xapi, cache, uuid, type) => {
|
||||
if (uuid == null) {
|
||||
return uuid
|
||||
}
|
||||
let ref = cache.get(uuid)
|
||||
if (ref === undefined) {
|
||||
ref = await xapi.call(`${type}.get_by_uuid`, uuid)
|
||||
|
@ -10,6 +10,7 @@
|
||||
- [REST API] Expose networks, VBDs, VDIs and VIFs
|
||||
- [Console] Supports host and VM consoles behind HTTP proxies [#6133](https://github.com/vatesfr/xen-orchestra/pull/6133)
|
||||
- [Install patches] Disable patch installation when `High Availability` is enabled (PR [#6145](https://github.com/vatesfr/xen-orchestra/pull/6145))
|
||||
- [Delta Backup/Restore] Ability to ignore some VDIs (PR [#6143](https://github.com/vatesfr/xen-orchestra/pull/6143))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@ -44,5 +45,5 @@
|
||||
- vhd-cli minor
|
||||
- @xen-orchestra/backups minor
|
||||
- @xen-orchestra/proxy minor
|
||||
- xo-server patch
|
||||
- xo-server minor
|
||||
- xo-web minor
|
||||
|
@ -1826,6 +1826,7 @@ const messages = {
|
||||
'This VM contains a duplicate MAC address or has the same MAC address as another running VM. Do you want to continue?',
|
||||
vmsWithDuplicatedMacAddressesMessage:
|
||||
'{nVms, number} VM{nVms, plural, one {} other {s}} contain{nVms, plural, one {s} other {}} duplicate MAC addresses or {nVms, plural, one {has} other {have}} the same MAC addresses as other running VMs. Do you want to continue?',
|
||||
ignoreVdi: 'Ignore this VDI',
|
||||
|
||||
// ----- Servers -----
|
||||
enableServerErrorTitle: 'Enable server',
|
||||
|
@ -1,14 +1,16 @@
|
||||
import Collapse from 'collapse'
|
||||
import Component from 'base-component'
|
||||
import Icon from 'icon'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import Tooltip from 'tooltip'
|
||||
import { Container, Col } from 'grid'
|
||||
import { isEmpty, map } from 'lodash'
|
||||
import { isSrWritable } from 'xo'
|
||||
import { Vdi } from 'render-xo-item'
|
||||
|
||||
import _ from '../../intl'
|
||||
import SingleLineRow from '../../single-line-row'
|
||||
import { Container, Col } from 'grid'
|
||||
import { isSrWritable } from 'xo'
|
||||
import { SelectSr } from '../../select-objects'
|
||||
|
||||
const Collapsible = ({ collapsible, children, ...props }) =>
|
||||
@ -29,6 +31,7 @@ Collapsible.propTypes = {
|
||||
|
||||
export default class ChooseSrForEachVdisModal extends Component {
|
||||
static propTypes = {
|
||||
ignorableVdis: PropTypes.bool,
|
||||
mainSrPredicate: PropTypes.func,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
srPredicate: PropTypes.func,
|
||||
@ -53,6 +56,7 @@ export default class ChooseSrForEachVdisModal extends Component {
|
||||
render() {
|
||||
const { props } = this
|
||||
const {
|
||||
ignorableVdis = false,
|
||||
mainSrPredicate = isSrWritable,
|
||||
placeholder,
|
||||
srPredicate = mainSrPredicate,
|
||||
@ -64,7 +68,7 @@ export default class ChooseSrForEachVdisModal extends Component {
|
||||
<div>
|
||||
<SelectSr
|
||||
onChange={this._onChangeMainSr}
|
||||
placeholder={placeholder !== undefined ? placeholder : 'chooseSrForEachVdisModalMainSr'}
|
||||
placeholder={placeholder !== undefined ? placeholder : _('chooseSrForEachVdisModalMainSr')}
|
||||
predicate={mainSrPredicate}
|
||||
required
|
||||
value={mainSr}
|
||||
@ -84,7 +88,9 @@ export default class ChooseSrForEachVdisModal extends Component {
|
||||
</SingleLineRow>
|
||||
{map(vdis, vdi => (
|
||||
<SingleLineRow key={vdi.uuid}>
|
||||
<Col size={6}>{vdi.name !== undefined ? vdi.name : <Vdi id={vdi.id} showSize />}</Col>
|
||||
<Col size={ignorableVdis ? 5 : 6}>
|
||||
{vdi.name !== undefined ? vdi.name : <Vdi id={vdi.id} showSize />}
|
||||
</Col>
|
||||
<Col size={6}>
|
||||
<SelectSr
|
||||
onChange={sr =>
|
||||
@ -96,6 +102,22 @@ export default class ChooseSrForEachVdisModal extends Component {
|
||||
value={mapVdisSrs !== undefined && mapVdisSrs[vdi.uuid]}
|
||||
/>
|
||||
</Col>
|
||||
{ignorableVdis && (
|
||||
<Col size={1}>
|
||||
<Tooltip content={_('ignoreVdi')}>
|
||||
<a
|
||||
role='button'
|
||||
onClick={() =>
|
||||
this._onChange({
|
||||
mapVdisSrs: { ...mapVdisSrs, [vdi.uuid]: null },
|
||||
})
|
||||
}
|
||||
>
|
||||
<Icon icon='remove' />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
)}
|
||||
</SingleLineRow>
|
||||
))}
|
||||
<i>{_('optionalEntry')}</i>
|
||||
|
@ -19,9 +19,14 @@ export default class RestoreBackupsModalBody extends Component {
|
||||
|
||||
_getDisks = createSelector(
|
||||
() => this.state.backup,
|
||||
backup =>
|
||||
() => this.state.targetSrs.mapVdisSrs,
|
||||
(backup, mapVdisSrs) =>
|
||||
backup !== undefined && backup.mode === 'delta'
|
||||
? backup.disks.reduce((vdis, vdi) => ({ ...vdis, [vdi.uuid]: vdi }), {})
|
||||
? backup.disks.reduce(
|
||||
(vdis, vdi) =>
|
||||
mapVdisSrs !== undefined && mapVdisSrs[vdi.uuid] === null ? vdis : { ...vdis, [vdi.uuid]: vdi },
|
||||
{}
|
||||
)
|
||||
: {}
|
||||
)
|
||||
|
||||
@ -40,6 +45,7 @@ export default class RestoreBackupsModalBody extends Component {
|
||||
<div>
|
||||
<div className='mb-1'>
|
||||
<ChooseSrForEachVdisModal
|
||||
ignorableVdis
|
||||
onChange={this.linkState('targetSrs')}
|
||||
placeholder={_('importBackupModalSelectSr')}
|
||||
required
|
||||
|
Loading…
Reference in New Issue
Block a user