Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e118ac43f | ||
|
|
d98ee9665d | ||
|
|
9ce5b017a9 |
@@ -15,6 +15,9 @@
|
|||||||
- [Proxies] Fix `xapi.getOrWaitObject is not a function` is not a function during deployment
|
- [Proxies] Fix `xapi.getOrWaitObject is not a function` is not a function during deployment
|
||||||
- [REST API] Fix empty object's tasks list
|
- [REST API] Fix empty object's tasks list
|
||||||
- [REST API] Fix incorrect `href` in `/:collection/:object/tasks`
|
- [REST API] Fix incorrect `href` in `/:collection/:object/tasks`
|
||||||
|
- [VM/Migration] Fix VDIs that were not migrated to the destination SR (PR [#7360](https://github.com/vatesfr/xen-orchestra/pull/7360))
|
||||||
|
- [Home/VM] VMs migration from the home view will no longer execute a `migrate_send` unless it is necessary [Forum#8279](https://xcp-ng.org/forum/topic/8279/getting-errors-when-migrating-4-out-5-vmguest/)(PR [#7360](https://github.com/vatesfr/xen-orchestra/pull/7360))
|
||||||
|
- [VM/migration] SR is no longer required if you select a migration network (PR [#7360](https://github.com/vatesfr/xen-orchestra/pull/7360))
|
||||||
|
|
||||||
### Packages to release
|
### Packages to release
|
||||||
|
|
||||||
@@ -35,5 +38,6 @@
|
|||||||
- @xen-orchestra/immutable-backups patch
|
- @xen-orchestra/immutable-backups patch
|
||||||
- @xen-orchestra/xva patch
|
- @xen-orchestra/xva patch
|
||||||
- xo-server patch
|
- xo-server patch
|
||||||
|
- xo-web patch
|
||||||
|
|
||||||
<!--packages-end-->
|
<!--packages-end-->
|
||||||
|
|||||||
@@ -495,10 +495,8 @@ export default class Xapi extends XapiBase {
|
|||||||
bypassAssert = false,
|
bypassAssert = false,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
const srRef = sr !== undefined ? hostXapi.getObject(sr).$ref : undefined
|
||||||
const getDefaultSrRef = once(() => {
|
const getDefaultSrRef = once(() => {
|
||||||
if (sr !== undefined) {
|
|
||||||
return hostXapi.getObject(sr).$ref
|
|
||||||
}
|
|
||||||
const defaultSr = host.$pool.$default_SR
|
const defaultSr = host.$pool.$default_SR
|
||||||
if (defaultSr === undefined) {
|
if (defaultSr === undefined) {
|
||||||
throw new Error(`This operation requires a default SR to be set on the pool ${host.$pool.name_label}`)
|
throw new Error(`This operation requires a default SR to be set on the pool ${host.$pool.name_label}`)
|
||||||
@@ -506,6 +504,28 @@ export default class Xapi extends XapiBase {
|
|||||||
return defaultSr.$ref
|
return defaultSr.$ref
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// VDIs/SRs mapping
|
||||||
|
// For VDI:
|
||||||
|
// - If a map of VDI -> SR was explicitly passed: use it
|
||||||
|
// - Else if SR was explicitly passed: use it
|
||||||
|
// - Else if VDI SR is reachable from the destination host: use it
|
||||||
|
// - Else: use the migration main SR or the pool's default SR (error if none of them is defined)
|
||||||
|
function getMigrationSrRef(vdi) {
|
||||||
|
if (mapVdisSrs[vdi.$id] !== undefined) {
|
||||||
|
return hostXapi.getObject(mapVdisSrs[vdi.$id]).$ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srRef !== undefined) {
|
||||||
|
return srRef
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSrConnected(vdi.$SR)) {
|
||||||
|
return vdi.$SR.$ref
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultSrRef()
|
||||||
|
}
|
||||||
|
|
||||||
const hostPbds = new Set(host.PBDs)
|
const hostPbds = new Set(host.PBDs)
|
||||||
const connectedSrs = new Map()
|
const connectedSrs = new Map()
|
||||||
const isSrConnected = sr => {
|
const isSrConnected = sr => {
|
||||||
@@ -518,10 +538,6 @@ export default class Xapi extends XapiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VDIs/SRs mapping
|
// VDIs/SRs mapping
|
||||||
// For VDI:
|
|
||||||
// - If SR was explicitly passed: use it
|
|
||||||
// - Else if VDI SR is reachable from the destination host: use it
|
|
||||||
// - Else: use the migration main SR or the pool's default SR (error if none of them is defined)
|
|
||||||
// For VDI-snapshot:
|
// For VDI-snapshot:
|
||||||
// - If VDI-snapshot is an orphan snapshot: same logic as a VDI
|
// - If VDI-snapshot is an orphan snapshot: same logic as a VDI
|
||||||
// - Else: don't add it to the map (VDI -> SR). It will be managed by the XAPI (snapshot will be migrated to the same SR as its parent active VDI)
|
// - Else: don't add it to the map (VDI -> SR). It will be managed by the XAPI (snapshot will be migrated to the same SR as its parent active VDI)
|
||||||
@@ -534,12 +550,7 @@ export default class Xapi extends XapiBase {
|
|||||||
if (vdi.$snapshot_of !== undefined) {
|
if (vdi.$snapshot_of !== undefined) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
vdis[vdi.$ref] =
|
vdis[vdi.$ref] = getMigrationSrRef(vdi)
|
||||||
mapVdisSrs[vdi.$id] !== undefined
|
|
||||||
? hostXapi.getObject(mapVdisSrs[vdi.$id]).$ref
|
|
||||||
: isSrConnected(vdi.$SR)
|
|
||||||
? vdi.$SR.$ref
|
|
||||||
: getDefaultSrRef()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2046,6 +2046,7 @@ const messages = {
|
|||||||
vmsWithDuplicatedMacAddressesMessage:
|
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?',
|
'{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',
|
ignoreVdi: 'Ignore this VDI',
|
||||||
|
selectDestinationSr: 'Select a destination SR',
|
||||||
|
|
||||||
// ----- Servers -----
|
// ----- Servers -----
|
||||||
enableServerErrorTitle: 'Enable server',
|
enableServerErrorTitle: 'Enable server',
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export default class ChooseSrForEachVdisModal extends Component {
|
|||||||
ignorableVdis = false,
|
ignorableVdis = false,
|
||||||
mainSrPredicate = isSrWritable,
|
mainSrPredicate = isSrWritable,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
required,
|
||||||
srPredicate = mainSrPredicate,
|
srPredicate = mainSrPredicate,
|
||||||
value: { mainSr, mapVdisSrs },
|
value: { mainSr, mapVdisSrs },
|
||||||
vdis,
|
vdis,
|
||||||
@@ -66,6 +67,9 @@ export default class ChooseSrForEachVdisModal extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<SingleLineRow>
|
||||||
|
<Col size={6}>{_('selectDestinationSr')}</Col>
|
||||||
|
<Col size={6}>
|
||||||
<SelectSr
|
<SelectSr
|
||||||
onChange={this._onChangeMainSr}
|
onChange={this._onChangeMainSr}
|
||||||
placeholder={placeholder !== undefined ? placeholder : _('chooseSrForEachVdisModalMainSr')}
|
placeholder={placeholder !== undefined ? placeholder : _('chooseSrForEachVdisModalMainSr')}
|
||||||
@@ -73,8 +77,11 @@ export default class ChooseSrForEachVdisModal extends Component {
|
|||||||
required
|
required
|
||||||
value={mainSr}
|
value={mainSr}
|
||||||
/>
|
/>
|
||||||
|
</Col>
|
||||||
|
</SingleLineRow>
|
||||||
|
{!required && <i>{_('optionalEntry')}</i>}
|
||||||
<br />
|
<br />
|
||||||
{!isEmpty(vdis) && mainSr != null && (
|
{!isEmpty(vdis) && (
|
||||||
<Collapsible buttonText={_('chooseSrForEachVdisModalSelectSr')} collapsible size='small'>
|
<Collapsible buttonText={_('chooseSrForEachVdisModalSelectSr')} collapsible size='small'>
|
||||||
<br />
|
<br />
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
@@ -1688,17 +1688,16 @@ export const migrateVm = async (vm, host) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { migrationNetwork, sr, targetHost } = params
|
const { sr, srRequired, targetHost } = params
|
||||||
|
|
||||||
if (!targetHost) {
|
if (!targetHost) {
|
||||||
return error(_('migrateVmNoTargetHost'), _('migrateVmNoTargetHostMessage'))
|
return error(_('migrateVmNoTargetHost'), _('migrateVmNoTargetHostMessage'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround to prevent VM's VDIs from unexpectedly migrating to the default SR
|
if (srRequired && sr === undefined) {
|
||||||
// if migration network is defined, the SR is required.
|
|
||||||
if (migrationNetwork !== undefined && sr === undefined) {
|
|
||||||
return error(_('migrateVmNoSr'), _('migrateVmNoSrMessage'))
|
return error(_('migrateVmNoSr'), _('migrateVmNoSrMessage'))
|
||||||
}
|
}
|
||||||
|
delete params.srRequired
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _call('vm.migrate', { vm: vm.id, ...params })
|
await _call('vm.migrate', { vm: vm.id, ...params })
|
||||||
@@ -1733,6 +1732,11 @@ export const migrateVms = vms =>
|
|||||||
return error(_('migrateVmNoTargetHost'), _('migrateVmNoTargetHostMessage'))
|
return error(_('migrateVmNoTargetHost'), _('migrateVmNoTargetHostMessage'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.srRequired && params.sr === undefined) {
|
||||||
|
return error(_('migrateVmNoTargetHost'), _('migrateVmNoTargetHostMessage'))
|
||||||
|
}
|
||||||
|
delete params.srRequired
|
||||||
|
|
||||||
const { mapVmsMapVdisSrs, mapVmsMapVifsNetworks, migrationNetwork, sr, targetHost, vms } = params
|
const { mapVmsMapVdisSrs, mapVmsMapVifsNetworks, migrationNetwork, sr, targetHost, vms } = params
|
||||||
Promise.all(
|
Promise.all(
|
||||||
map(vms, ({ id }) =>
|
map(vms, ({ id }) =>
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ export default class MigrateVmModalBody extends BaseComponent {
|
|||||||
migrationNetwork: this.state.migrationNetworkId,
|
migrationNetwork: this.state.migrationNetworkId,
|
||||||
sr: resolveId(this.state.targetSrs.mainSr),
|
sr: resolveId(this.state.targetSrs.mainSr),
|
||||||
targetHost: this.state.host && this.state.host.id,
|
targetHost: this.state.host && this.state.host.id,
|
||||||
|
srRequired: !this.state.doNotMigrateVdis,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,14 +223,14 @@ export default class MigrateVmModalBody extends BaseComponent {
|
|||||||
</Col>
|
</Col>
|
||||||
</SingleLineRow>
|
</SingleLineRow>
|
||||||
</div>
|
</div>
|
||||||
{host && (!doNotMigrateVdis || migrationNetworkId != null) && (
|
{host && (
|
||||||
<div className={styles.groupBlock}>
|
<div className={styles.groupBlock}>
|
||||||
<SingleLineRow>
|
<SingleLineRow>
|
||||||
<Col size={12}>
|
<Col size={12}>
|
||||||
<ChooseSrForEachVdisModal
|
<ChooseSrForEachVdisModal
|
||||||
mainSrPredicate={this._getSrPredicate()}
|
mainSrPredicate={this._getSrPredicate()}
|
||||||
onChange={this.linkState('targetSrs')}
|
onChange={this.linkState('targetSrs')}
|
||||||
required
|
required={!doNotMigrateVdis}
|
||||||
value={targetSrs}
|
value={targetSrs}
|
||||||
vdis={vdis}
|
vdis={vdis}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -101,10 +101,6 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this._selectHost(this.props.host)
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
const { host } = this.state
|
const { host } = this.state
|
||||||
const vms = filter(this.props.vms, vm => vm.$container !== host.id)
|
const vms = filter(this.props.vms, vm => vm.$container !== host.id)
|
||||||
@@ -112,7 +108,15 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
return { vms }
|
return { vms }
|
||||||
}
|
}
|
||||||
const { networks, pifs, vbdsByVm, vifsByVm } = this.props
|
const { networks, pifs, vbdsByVm, vifsByVm } = this.props
|
||||||
const { doNotMigrateVdi, doNotMigrateVmVdis, migrationNetworkId, networkId, smartVifMapping, srId } = this.state
|
const {
|
||||||
|
doNotMigrateVdi,
|
||||||
|
doNotMigrateVmVdis,
|
||||||
|
migrationNetworkId,
|
||||||
|
noVdisMigration,
|
||||||
|
networkId,
|
||||||
|
smartVifMapping,
|
||||||
|
srId,
|
||||||
|
} = this.state
|
||||||
|
|
||||||
// Map VM --> ( Map VDI --> SR )
|
// Map VM --> ( Map VDI --> SR )
|
||||||
// 2021-02-16: Fill the map (VDI -> SR) with *all* the VDIs to avoid unexpectedly migrating them to the wrong SRs:
|
// 2021-02-16: Fill the map (VDI -> SR) with *all* the VDIs to avoid unexpectedly migrating them to the wrong SRs:
|
||||||
@@ -124,10 +128,14 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
forEach(vbds, vbd => {
|
forEach(vbds, vbd => {
|
||||||
const vdi = vbd.VDI
|
const vdi = vbd.VDI
|
||||||
if (!vbd.is_cd_drive && vdi) {
|
if (!vbd.is_cd_drive && vdi) {
|
||||||
mapVdisSrs[vdi] = doNotMigrateVmVdis[vm] || doNotMigrateVdi[vdi] ? this._getObject(vdi).$SR : srId
|
if (!doNotMigrateVmVdis[vm] && !doNotMigrateVdi[vdi]) {
|
||||||
|
mapVdisSrs[vdi] = srId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if (!isEmpty(mapVdisSrs)) {
|
||||||
mapVmsMapVdisSrs[vm] = mapVdisSrs
|
mapVmsMapVdisSrs[vm] = mapVdisSrs
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultNetwork =
|
const defaultNetwork =
|
||||||
@@ -160,6 +168,7 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
mapVmsMapVifsNetworks,
|
mapVmsMapVifsNetworks,
|
||||||
migrationNetwork: migrationNetworkId,
|
migrationNetwork: migrationNetworkId,
|
||||||
sr: srId,
|
sr: srId,
|
||||||
|
srRequired: !noVdisMigration,
|
||||||
targetHost: host.id,
|
targetHost: host.id,
|
||||||
vms,
|
vms,
|
||||||
}
|
}
|
||||||
@@ -212,7 +221,7 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
networkId: defaultMigrationNetworkId,
|
networkId: defaultMigrationNetworkId,
|
||||||
noVdisMigration,
|
noVdisMigration,
|
||||||
smartVifMapping: true,
|
smartVifMapping: true,
|
||||||
srId: defaultSrConnectedToHost ? defaultSrId : undefined,
|
srId: !noVdisMigration && defaultSrConnectedToHost ? defaultSrId : undefined,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,27 +263,11 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
</Col>
|
</Col>
|
||||||
</SingleLineRow>
|
</SingleLineRow>
|
||||||
</div>
|
</div>
|
||||||
{host !== undefined && (
|
{host !== undefined && [
|
||||||
<div style={LINE_STYLE}>
|
|
||||||
<SingleLineRow>
|
|
||||||
<Col size={6}>{_('migrateVmSelectMigrationNetwork')}</Col>
|
|
||||||
<Col size={6}>
|
|
||||||
<SelectNetwork
|
|
||||||
onChange={this._selectMigrationNetwork}
|
|
||||||
predicate={this._getMigrationNetworkPredicate()}
|
|
||||||
required={!intraPool}
|
|
||||||
value={migrationNetworkId}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</SingleLineRow>
|
|
||||||
{intraPool && <i>{_('optionalEntry')}</i>}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{host && (!noVdisMigration || migrationNetworkId != null) && (
|
|
||||||
<div key='sr' style={LINE_STYLE}>
|
<div key='sr' style={LINE_STYLE}>
|
||||||
<SingleLineRow>
|
<SingleLineRow>
|
||||||
<Col size={6}>
|
<Col size={6}>
|
||||||
{!intraPool ? _('migrateVmsSelectSr') : _('migrateVmsSelectSrIntraPool')}{' '}
|
{_('selectDestinationSr')}{' '}
|
||||||
{(defaultSrId === undefined || !defaultSrConnectedToHost) && (
|
{(defaultSrId === undefined || !defaultSrConnectedToHost) && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
@@ -292,11 +285,31 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
|||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col size={6}>
|
<Col size={6}>
|
||||||
<SelectSr onChange={this._selectSr} predicate={this._getSrPredicate()} required value={srId} />
|
<SelectSr
|
||||||
|
onChange={this._selectSr}
|
||||||
|
predicate={this._getSrPredicate()}
|
||||||
|
required={!noVdisMigration}
|
||||||
|
value={srId}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</SingleLineRow>
|
</SingleLineRow>
|
||||||
</div>
|
{noVdisMigration && <i>{_('optionalEntry')}</i>}
|
||||||
)}
|
</div>,
|
||||||
|
<div style={LINE_STYLE} key='network'>
|
||||||
|
<SingleLineRow>
|
||||||
|
<Col size={6}>{_('migrateVmSelectMigrationNetwork')}</Col>
|
||||||
|
<Col size={6}>
|
||||||
|
<SelectNetwork
|
||||||
|
onChange={this._selectMigrationNetwork}
|
||||||
|
predicate={this._getMigrationNetworkPredicate()}
|
||||||
|
required={!intraPool}
|
||||||
|
value={migrationNetworkId}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</SingleLineRow>
|
||||||
|
{intraPool && <i>{_('optionalEntry')}</i>}
|
||||||
|
</div>,
|
||||||
|
]}
|
||||||
{host && !intraPool && (
|
{host && !intraPool && (
|
||||||
<div key='network' style={LINE_STYLE}>
|
<div key='network' style={LINE_STYLE}>
|
||||||
<SingleLineRow>
|
<SingleLineRow>
|
||||||
|
|||||||
Reference in New Issue
Block a user