Compare commits
3 Commits
feat_remov
...
xo5/fix-bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e118ac43f | ||
|
|
d98ee9665d | ||
|
|
9ce5b017a9 |
@@ -15,6 +15,9 @@
|
||||
- [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 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
|
||||
|
||||
@@ -35,5 +38,6 @@
|
||||
- @xen-orchestra/immutable-backups patch
|
||||
- @xen-orchestra/xva patch
|
||||
- xo-server patch
|
||||
- xo-web patch
|
||||
|
||||
<!--packages-end-->
|
||||
|
||||
@@ -495,10 +495,8 @@ export default class Xapi extends XapiBase {
|
||||
bypassAssert = false,
|
||||
}
|
||||
) {
|
||||
const srRef = sr !== undefined ? hostXapi.getObject(sr).$ref : undefined
|
||||
const getDefaultSrRef = once(() => {
|
||||
if (sr !== undefined) {
|
||||
return hostXapi.getObject(sr).$ref
|
||||
}
|
||||
const defaultSr = host.$pool.$default_SR
|
||||
if (defaultSr === undefined) {
|
||||
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
|
||||
})
|
||||
|
||||
// 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 connectedSrs = new Map()
|
||||
const isSrConnected = sr => {
|
||||
@@ -518,10 +538,6 @@ export default class Xapi extends XapiBase {
|
||||
}
|
||||
|
||||
// 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:
|
||||
// - 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)
|
||||
@@ -534,12 +550,7 @@ export default class Xapi extends XapiBase {
|
||||
if (vdi.$snapshot_of !== undefined) {
|
||||
continue
|
||||
}
|
||||
vdis[vdi.$ref] =
|
||||
mapVdisSrs[vdi.$id] !== undefined
|
||||
? hostXapi.getObject(mapVdisSrs[vdi.$id]).$ref
|
||||
: isSrConnected(vdi.$SR)
|
||||
? vdi.$SR.$ref
|
||||
: getDefaultSrRef()
|
||||
vdis[vdi.$ref] = getMigrationSrRef(vdi)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2046,6 +2046,7 @@ const messages = {
|
||||
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',
|
||||
selectDestinationSr: 'Select a destination SR',
|
||||
|
||||
// ----- Servers -----
|
||||
enableServerErrorTitle: 'Enable server',
|
||||
|
||||
@@ -59,6 +59,7 @@ export default class ChooseSrForEachVdisModal extends Component {
|
||||
ignorableVdis = false,
|
||||
mainSrPredicate = isSrWritable,
|
||||
placeholder,
|
||||
required,
|
||||
srPredicate = mainSrPredicate,
|
||||
value: { mainSr, mapVdisSrs },
|
||||
vdis,
|
||||
@@ -66,15 +67,21 @@ export default class ChooseSrForEachVdisModal extends Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SelectSr
|
||||
onChange={this._onChangeMainSr}
|
||||
placeholder={placeholder !== undefined ? placeholder : _('chooseSrForEachVdisModalMainSr')}
|
||||
predicate={mainSrPredicate}
|
||||
required
|
||||
value={mainSr}
|
||||
/>
|
||||
<SingleLineRow>
|
||||
<Col size={6}>{_('selectDestinationSr')}</Col>
|
||||
<Col size={6}>
|
||||
<SelectSr
|
||||
onChange={this._onChangeMainSr}
|
||||
placeholder={placeholder !== undefined ? placeholder : _('chooseSrForEachVdisModalMainSr')}
|
||||
predicate={mainSrPredicate}
|
||||
required
|
||||
value={mainSr}
|
||||
/>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
{!required && <i>{_('optionalEntry')}</i>}
|
||||
<br />
|
||||
{!isEmpty(vdis) && mainSr != null && (
|
||||
{!isEmpty(vdis) && (
|
||||
<Collapsible buttonText={_('chooseSrForEachVdisModalSelectSr')} collapsible size='small'>
|
||||
<br />
|
||||
<Container>
|
||||
|
||||
@@ -1688,17 +1688,16 @@ export const migrateVm = async (vm, host) => {
|
||||
return
|
||||
}
|
||||
|
||||
const { migrationNetwork, sr, targetHost } = params
|
||||
const { sr, srRequired, targetHost } = params
|
||||
|
||||
if (!targetHost) {
|
||||
return error(_('migrateVmNoTargetHost'), _('migrateVmNoTargetHostMessage'))
|
||||
}
|
||||
|
||||
// Workaround to prevent VM's VDIs from unexpectedly migrating to the default SR
|
||||
// if migration network is defined, the SR is required.
|
||||
if (migrationNetwork !== undefined && sr === undefined) {
|
||||
if (srRequired && sr === undefined) {
|
||||
return error(_('migrateVmNoSr'), _('migrateVmNoSrMessage'))
|
||||
}
|
||||
delete params.srRequired
|
||||
|
||||
try {
|
||||
await _call('vm.migrate', { vm: vm.id, ...params })
|
||||
@@ -1733,6 +1732,11 @@ export const migrateVms = vms =>
|
||||
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
|
||||
Promise.all(
|
||||
map(vms, ({ id }) =>
|
||||
|
||||
@@ -122,6 +122,7 @@ export default class MigrateVmModalBody extends BaseComponent {
|
||||
migrationNetwork: this.state.migrationNetworkId,
|
||||
sr: resolveId(this.state.targetSrs.mainSr),
|
||||
targetHost: this.state.host && this.state.host.id,
|
||||
srRequired: !this.state.doNotMigrateVdis,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,14 +223,14 @@ export default class MigrateVmModalBody extends BaseComponent {
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</div>
|
||||
{host && (!doNotMigrateVdis || migrationNetworkId != null) && (
|
||||
{host && (
|
||||
<div className={styles.groupBlock}>
|
||||
<SingleLineRow>
|
||||
<Col size={12}>
|
||||
<ChooseSrForEachVdisModal
|
||||
mainSrPredicate={this._getSrPredicate()}
|
||||
onChange={this.linkState('targetSrs')}
|
||||
required
|
||||
required={!doNotMigrateVdis}
|
||||
value={targetSrs}
|
||||
vdis={vdis}
|
||||
/>
|
||||
|
||||
@@ -101,10 +101,6 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._selectHost(this.props.host)
|
||||
}
|
||||
|
||||
get value() {
|
||||
const { host } = this.state
|
||||
const vms = filter(this.props.vms, vm => vm.$container !== host.id)
|
||||
@@ -112,7 +108,15 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
||||
return { vms }
|
||||
}
|
||||
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 )
|
||||
// 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 => {
|
||||
const vdi = vbd.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
|
||||
}
|
||||
}
|
||||
})
|
||||
mapVmsMapVdisSrs[vm] = mapVdisSrs
|
||||
if (!isEmpty(mapVdisSrs)) {
|
||||
mapVmsMapVdisSrs[vm] = mapVdisSrs
|
||||
}
|
||||
})
|
||||
|
||||
const defaultNetwork =
|
||||
@@ -160,6 +168,7 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
||||
mapVmsMapVifsNetworks,
|
||||
migrationNetwork: migrationNetworkId,
|
||||
sr: srId,
|
||||
srRequired: !noVdisMigration,
|
||||
targetHost: host.id,
|
||||
vms,
|
||||
}
|
||||
@@ -212,7 +221,7 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
||||
networkId: defaultMigrationNetworkId,
|
||||
noVdisMigration,
|
||||
smartVifMapping: true,
|
||||
srId: defaultSrConnectedToHost ? defaultSrId : undefined,
|
||||
srId: !noVdisMigration && defaultSrConnectedToHost ? defaultSrId : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -254,27 +263,11 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
</div>
|
||||
{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) && (
|
||||
{host !== undefined && [
|
||||
<div key='sr' style={LINE_STYLE}>
|
||||
<SingleLineRow>
|
||||
<Col size={6}>
|
||||
{!intraPool ? _('migrateVmsSelectSr') : _('migrateVmsSelectSrIntraPool')}{' '}
|
||||
{_('selectDestinationSr')}{' '}
|
||||
{(defaultSrId === undefined || !defaultSrConnectedToHost) && (
|
||||
<Tooltip
|
||||
content={
|
||||
@@ -292,11 +285,31 @@ export default class MigrateVmsModalBody extends BaseComponent {
|
||||
)}
|
||||
</Col>
|
||||
<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>
|
||||
</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 && (
|
||||
<div key='network' style={LINE_STYLE}>
|
||||
<SingleLineRow>
|
||||
|
||||
Reference in New Issue
Block a user