feat(xo-web/vm/disk): notify user before breaking action (#4035)
See #3911 - New disk: warning if the selected SR is local to another host than another VDI - Migrate VDI (row action only): warning if the selected SR is local to another host than another VDI
This commit is contained in:
committed by
Pierre Donias
parent
d82c951db6
commit
f27170ff0e
@@ -4,6 +4,7 @@
|
||||
|
||||
- [Self/New VM] Display confirmation modal when user will use a large amount of resources [#4044](https://github.com/vatesfr/xen-orchestra/issues/4044) (PR [#4127](https://github.com/vatesfr/xen-orchestra/pull/4127))
|
||||
- [Home] No more false positives when select Tag on Home page [#4087](https://github.com/vatesfr/xen-orchestra/issues/4087) (PR [#4112](https://github.com/vatesfr/xen-orchestra/pull/4112))
|
||||
- [VDI migration, New disk] Warning when SR host is different from the other disks [#3911](https://github.com/vatesfr/xen-orchestra/issues/3911) (PR [#4035](https://github.com/vatesfr/xen-orchestra/pull/4035))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
||||
@@ -1051,12 +1051,12 @@ const messages = {
|
||||
importVdi: 'Import VDI content',
|
||||
importVdiNoFile: 'No file selected',
|
||||
selectVdiMessage: 'Drop VHD file here',
|
||||
srsNotOnSameHost:
|
||||
'The SRs must either be shared or on the same host for the VM to be able to start.',
|
||||
useQuotaWarning:
|
||||
'Creating this disk will use the disk space quota from the resource set {resourceSet} ({spaceLeft} left)',
|
||||
notEnoughSpaceInResourceSet:
|
||||
'Not enough space in resource set {resourceSet} ({spaceLeft} left)',
|
||||
warningVdiSr:
|
||||
"The VDIs' SRs must either be shared or on the same host for the VM to be able to start.",
|
||||
|
||||
// ----- VM network tab -----
|
||||
vifCreateDeviceButton: 'New device',
|
||||
|
||||
@@ -257,6 +257,7 @@ const parseBootOrder = bootOrder => {
|
||||
})
|
||||
class NewDisk extends Component {
|
||||
static propTypes = {
|
||||
checkSr: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func,
|
||||
vm: PropTypes.object.isRequired,
|
||||
}
|
||||
@@ -299,6 +300,12 @@ class NewDisk extends Component {
|
||||
resourceSet => get(resourceSet, 'limits.disk.available')
|
||||
)
|
||||
|
||||
_checkSr = createSelector(
|
||||
() => this.props.checkSr,
|
||||
() => this.state.sr,
|
||||
(check, sr) => check(sr)
|
||||
)
|
||||
|
||||
render() {
|
||||
const { vm, isAdmin } = this.props
|
||||
const { formatMessage } = this.props.intl
|
||||
@@ -370,6 +377,13 @@ class NewDisk extends Component {
|
||||
</ActionButton>
|
||||
</span>
|
||||
</fieldset>
|
||||
{!this._checkSr() && (
|
||||
<div>
|
||||
<span className='text-danger'>
|
||||
<Icon icon='alarm' /> {_('warningVdiSr')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{resourceSet != null &&
|
||||
diskLimit != null &&
|
||||
(diskLimit < size ? (
|
||||
@@ -609,6 +623,7 @@ class BootOrder extends Component {
|
||||
|
||||
class MigrateVdiModalBody extends Component {
|
||||
static propTypes = {
|
||||
checkSr: PropTypes.func.isRequired,
|
||||
pool: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
@@ -621,6 +636,12 @@ class MigrateVdiModalBody extends Component {
|
||||
poolId => createCompareContainers(poolId)
|
||||
)
|
||||
|
||||
_checkSr = createSelector(
|
||||
() => this.props.checkSr,
|
||||
() => this.state.sr,
|
||||
(check, sr) => check(sr)
|
||||
)
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
@@ -643,6 +664,15 @@ class MigrateVdiModalBody extends Component {
|
||||
</label>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
{!this._checkSr() && (
|
||||
<SingleLineRow>
|
||||
<Col>
|
||||
<span className='text-danger'>
|
||||
<Icon icon='alarm' /> {_('warningVdiSr')}
|
||||
</span>
|
||||
</Col>
|
||||
</SingleLineRow>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@@ -663,11 +693,13 @@ export default class TabDisks extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
_getVdiSrs = createSelector(
|
||||
() => this.props.vdis,
|
||||
createCollectionWrapper(vdis => sortedUniq(map(vdis, '$SR').sort()))
|
||||
)
|
||||
|
||||
_areSrsOnSameHost = createSelector(
|
||||
createSelector(
|
||||
() => this.props.vdis,
|
||||
createCollectionWrapper(vdis => sortedUniq(map(vdis, '$SR').sort()))
|
||||
),
|
||||
this._getVdiSrs,
|
||||
() => this.props.srs,
|
||||
(vdiSrs, srs) => {
|
||||
if (some(vdiSrs, srId => srs[srId] === undefined)) {
|
||||
@@ -711,7 +743,12 @@ export default class TabDisks extends Component {
|
||||
_migrateVdi = vdi => {
|
||||
return confirm({
|
||||
title: _('vdiMigrate'),
|
||||
body: <MigrateVdiModalBody pool={this.props.vm.$pool} />,
|
||||
body: (
|
||||
<MigrateVdiModalBody
|
||||
checkSr={this._getCheckSr()}
|
||||
pool={this.props.vm.$pool}
|
||||
/>
|
||||
),
|
||||
}).then(({ sr, migrateAll }) => {
|
||||
if (!sr) {
|
||||
return error(_('vdiMigrateNoSr'), _('vdiMigrateNoSrMessage'))
|
||||
@@ -736,6 +773,37 @@ export default class TabDisks extends Component {
|
||||
isAdmin || (resourceSet == null && isVmAdmin)
|
||||
)
|
||||
|
||||
_getRequiredHost = createSelector(
|
||||
this._areSrsOnSameHost,
|
||||
this._getVdiSrs,
|
||||
() => this.props.srs,
|
||||
(areSrsOnSameHost, vdiSrs, srs) => {
|
||||
if (!areSrsOnSameHost) {
|
||||
return
|
||||
}
|
||||
|
||||
let container
|
||||
let sr
|
||||
forEach(vdiSrs, srId => {
|
||||
sr = srs[srId]
|
||||
if (sr !== undefined && !isSrShared(sr)) {
|
||||
container = sr.$container
|
||||
return false
|
||||
}
|
||||
})
|
||||
return container
|
||||
}
|
||||
)
|
||||
|
||||
_getCheckSr = createSelector(
|
||||
this._getRequiredHost,
|
||||
requiredHost => sr =>
|
||||
sr === undefined ||
|
||||
isSrShared(sr) ||
|
||||
requiredHost === undefined ||
|
||||
sr.$container === requiredHost
|
||||
)
|
||||
|
||||
_getVbdsByVdi = createSelector(
|
||||
() => this.props.vdis,
|
||||
() => this.props.vbds,
|
||||
@@ -817,7 +885,11 @@ export default class TabDisks extends Component {
|
||||
<Col>
|
||||
{newDisk && (
|
||||
<div>
|
||||
<NewDisk vm={vm} onClose={this._toggleNewDisk} />
|
||||
<NewDisk
|
||||
checkSr={this._getCheckSr()}
|
||||
vm={vm}
|
||||
onClose={this._toggleNewDisk}
|
||||
/>
|
||||
<hr />
|
||||
</div>
|
||||
)}
|
||||
@@ -843,7 +915,7 @@ export default class TabDisks extends Component {
|
||||
{!this._areSrsOnSameHost() && (
|
||||
<div>
|
||||
<span className='text-danger'>
|
||||
<Icon icon='alarm' /> {_('srsNotOnSameHost')}
|
||||
<Icon icon='alarm' /> {_('warningVdiSr')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user