feat(xo-web/host): allow to force smartReboot

This commit is contained in:
mathieuRA 2023-09-07 17:00:16 +02:00 committed by Pierre Donias
parent 3bf6aae103
commit 549d9b70a9
4 changed files with 90 additions and 35 deletions

View File

@ -7,6 +7,8 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it” > Users must be able to say: “Nice enhancement, I'm eager to test it”
- [Host/Advanced] Allow to force _Smart reboot_ if some resident VMs have the suspend operation blocked [Forum#7136](https://xcp-ng.org/forum/topic/7136/suspending-vms-during-host-reboot/23) (PR [#7025](https://github.com/vatesfr/xen-orchestra/pull/7025))
### Bug fixes ### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed” > Users must be able to say: “I had this issue, happy to know it's fixed”
@ -29,5 +31,6 @@
- @xen-orchestra/xapi minor - @xen-orchestra/xapi minor
- xo-server minor - xo-server minor
- xo-web minor
<!--packages-end--> <!--packages-end-->

View File

@ -963,9 +963,13 @@ const messages = {
enableHostLabel: 'Enable', enableHostLabel: 'Enable',
disableHostLabel: 'Disable', disableHostLabel: 'Disable',
restartHostAgent: 'Restart toolstack', restartHostAgent: 'Restart toolstack',
smartRebootBypassCurrentVmCheck:
'As the XOA is hosted on the host that is scheduled for a reboot, it will also be restarted. Consequently, XO won\'t be able to resume VMs, and VMs with the "Protect from accidental shutdown" option enabled will not have this option reactivated automatically.',
smartRebootHostLabel: 'Smart reboot', smartRebootHostLabel: 'Smart reboot',
smartRebootHostTooltip: 'Suspend resident VMs, reboot host and resume VMs automatically', smartRebootHostTooltip: 'Suspend resident VMs, reboot host and resume VMs automatically',
forceRebootHostLabel: 'Force reboot', forceRebootHostLabel: 'Force reboot',
forceSmartRebootHost:
'Smart Reboot failed because {nVms, number} VM{nVms, plural, one {} other {s}} ha{nVms, plural, one {s} other {ve}} {nVms, plural, one {its} other {their}} Suspend operation blocked. Would you like to force?',
rebootHostLabel: 'Reboot', rebootHostLabel: 'Reboot',
noHostsAvailableErrorTitle: 'Error while restarting host', noHostsAvailableErrorTitle: 'Error while restarting host',
noHostsAvailableErrorMessage: noHostsAvailableErrorMessage:

View File

@ -16,6 +16,7 @@ import {
incorrectState, incorrectState,
noHostsAvailable, noHostsAvailable,
operationBlocked, operationBlocked,
operationFailed,
vmLacksFeature, vmLacksFeature,
} from 'xo-common/api-errors' } from 'xo-common/api-errors'
@ -821,42 +822,89 @@ export const setRemoteSyslogHost = (host, syslogDestination) =>
export const setRemoteSyslogHosts = (hosts, syslogDestination) => export const setRemoteSyslogHosts = (hosts, syslogDestination) =>
Promise.all(map(hosts, host => setRemoteSyslogHost(host, syslogDestination))) Promise.all(map(hosts, host => setRemoteSyslogHost(host, syslogDestination)))
export const restartHost = (host, force = false, suspendResidentVms = false) => export const restartHost = async (
confirm({ host,
force = false,
suspendResidentVms = false,
bypassBlockedSuspend = false,
bypassCurrentVmCheck = false
) => {
await confirm({
title: _('restartHostModalTitle'), title: _('restartHostModalTitle'),
body: _('restartHostModalMessage'), body: _('restartHostModalMessage'),
}).then( })
() => return _restartHost({ host, force, suspendResidentVms, bypassBlockedSuspend, bypassCurrentVmCheck })
_call('host.restart', { id: resolveId(host), force, suspendResidentVms }) }
.catch(async error => {
if ( const _restartHost = async ({ host, ...opts }) => {
forbiddenOperation.is(error, { opts = { ...opts, id: resolveId(host) }
reason: `A backup may run on the pool: ${host.$poolId}`,
}) || try {
forbiddenOperation.is(error, { await _call('host.restart', opts)
reason: `A backup is running on the pool: ${host.$poolId}`, } catch (error) {
}) if (cantSuspend(error)) {
) { await confirm({
await confirm({ body: (
body: ( <p>
<p className='text-warning'> <Icon icon='alarm' /> {_('forceSmartRebootHost', { nVms: error.data.actual.length })}
<Icon icon='alarm' /> {_('bypassBackupHostModalMessage')} </p>
</p> ),
), title: _('restartHostModalTitle'),
title: _('restartHostModalTitle'), })
}) return _restartHost({ ...opts, host, bypassBlockedSuspend: true })
return _call('host.restart', { id: resolveId(host), force, suspendResidentVms, bypassBackupCheck: true }) }
}
throw error if (xoaOnHost(error)) {
}) await confirm({
.catch(error => { body: (
if (noHostsAvailable.is(error)) { <p>
alert(_('noHostsAvailableErrorTitle'), _('noHostsAvailableErrorMessage')) <Icon icon='alarm' /> {_('smartRebootBypassCurrentVmCheck')}
} </p>
throw error ),
}), title: _('restartHostModalTitle'),
noop })
) return _restartHost({ ...opts, host, bypassCurrentVmCheck: true })
}
if (backupIsRunning(error, host.$poolId)) {
await confirm({
body: (
<p className='text-warning'>
<Icon icon='alarm' /> {_('bypassBackupHostModalMessage')}
</p>
),
title: _('restartHostModalTitle'),
})
return _restartHost({ ...opts, host, bypassBackupCheck: true })
}
if (noHostsAvailableErrCheck(error)) {
alert(_('noHostsAvailableErrorTitle'), _('noHostsAvailableErrorMessage'))
}
throw error
}
}
// ---- Restart Host errors
const cantSuspend = err =>
err !== undefined &&
incorrectState.is(err, {
object: 'suspendBlocked',
})
const xoaOnHost = err =>
err !== undefined &&
operationFailed.is(err, {
code: 'xoaOnHost',
})
const backupIsRunning = (err, poolId) =>
err !== undefined &&
(forbiddenOperation.is(err, {
reason: `A backup may run on the pool: ${poolId}`,
}) ||
forbiddenOperation.is(err, {
reason: `A backup is running on the pool: ${poolId}`,
}))
const noHostsAvailableErrCheck = err => err !== undefined && noHostsAvailable.is(err)
export const restartHosts = (hosts, force = false) => { export const restartHosts = (hosts, force = false) => {
const nHosts = size(hosts) const nHosts = size(hosts)

View File

@ -76,7 +76,7 @@ const downloadLogs = async uuid => {
const forceReboot = host => restartHost(host, true) const forceReboot = host => restartHost(host, true)
const smartReboot = ALLOW_SMART_REBOOT const smartReboot = ALLOW_SMART_REBOOT
? host => restartHost(host, false, true) // don't force, suspend resident VMs ? host => restartHost(host, false, true, false, false) // don't force, suspend resident VMs, don't bypass blocked suspend, don't bypass current VM check
: () => {} : () => {}
const formatPack = ({ name, author, description, version }, key) => ( const formatPack = ({ name, author, description, version }, key) => (