diff --git a/@xen-orchestra/backups/ImportVmBackup.js b/@xen-orchestra/backups/ImportVmBackup.js index 5b00e740185..11694257123 100644 --- a/@xen-orchestra/backups/ImportVmBackup.js +++ b/@xen-orchestra/backups/ImportVmBackup.js @@ -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)) } diff --git a/@xen-orchestra/backups/RemoteAdapter.js b/@xen-orchestra/backups/RemoteAdapter.js index 7ae0d95a779..b1deec4f600 100644 --- a/@xen-orchestra/backups/RemoteAdapter.js +++ b/@xen-orchestra/backups/RemoteAdapter.js @@ -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 { diff --git a/@xen-orchestra/backups/_deltaVm.js b/@xen-orchestra/backups/_deltaVm.js index 0717c16a374..d75475f5dd4 100644 --- a/@xen-orchestra/backups/_deltaVm.js +++ b/@xen-orchestra/backups/_deltaVm.js @@ -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) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 44cac70a165..f33bc07c0c8 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -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 diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js index 08dfeed9fca..da23ebdf5b9 100644 --- a/packages/xo-web/src/common/intl/messages.js +++ b/packages/xo-web/src/common/intl/messages.js @@ -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', diff --git a/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js b/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js index a0966884030..28b0c6b345a 100644 --- a/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js +++ b/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js @@ -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 {
{map(vdis, vdi => ( - {vdi.name !== undefined ? vdi.name : } + + {vdi.name !== undefined ? vdi.name : } + @@ -96,6 +102,22 @@ export default class ChooseSrForEachVdisModal extends Component { value={mapVdisSrs !== undefined && mapVdisSrs[vdi.uuid]} /> + {ignorableVdis && ( + + + + this._onChange({ + mapVdisSrs: { ...mapVdisSrs, [vdi.uuid]: null }, + }) + } + > + + + + + )} ))} {_('optionalEntry')} diff --git a/packages/xo-web/src/xo-app/backup/restore/restore-backups-modal-body.js b/packages/xo-web/src/xo-app/backup/restore/restore-backups-modal-body.js index fe5f7c04db0..d2d8fbcc393 100644 --- a/packages/xo-web/src/xo-app/backup/restore/restore-backups-modal-body.js +++ b/packages/xo-web/src/xo-app/backup/restore/restore-backups-modal-body.js @@ -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 {