feat(xo-web/VM/advanced): VTPM management (#7085)

See #7066
See #6802
See #7074
This commit is contained in:
Mathieu 2023-10-19 13:46:03 +00:00 committed by GitHub
parent 8752487280
commit 1a1dd0531d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 0 deletions

View File

@ -10,6 +10,7 @@
- [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))
- [Plugin/backup-report] Errors are now listed in XO tasks
- [PIF] Show network name in PIF selectors (PR [#7081](https://github.com/vatesfr/xen-orchestra/pull/7081))
- [VM/Advanced] Possibility to create/delete VTPM [#7066](https://github.com/vatesfr/xen-orchestra/issues/7066) [Forum#6578](https://xcp-ng.org/forum/topic/6578/xcp-ng-8-3-public-alpha/109) (PR [#7085](https://github.com/vatesfr/xen-orchestra/pull/7085))
### Bug fixes

View File

@ -25,6 +25,7 @@ const messages = {
esxiImportStopOnErrorDescription: 'Stop on the first error when importing VMs',
nImportVmsInParallel: 'Number of VMs to import in parallel',
stopOnError: 'Stop on error',
uuid: 'UUID',
vmSrUsage: 'Storage: {used} used of {total} ({free} free)',
notDefined: 'Not defined',
@ -1367,6 +1368,9 @@ const messages = {
logAction: 'Action',
// ----- VM advanced tab -----
createVtpm: 'Create a VTPM',
deleteVtpm: 'Delete the VTPM',
deleteVtpmWarning: 'If the VTPM is in use, removing it will result in a dangerous data loss. Are you sure you want to remove the VTPM?',
vmRemoveButton: 'Remove',
vmConvertToTemplateButton: 'Convert to template',
vmSwitchVirtualizationMode: 'Convert to {mode}',
@ -1396,9 +1400,12 @@ const messages = {
srHaTooltip: 'SR used for High Availability',
nestedVirt: 'Nested virtualization',
vmAffinityHost: 'Affinity host',
vmNeedToBeHalted: 'The VM needs to be halted',
vmVga: 'VGA',
vmVideoram: 'Video RAM',
vmNicType: 'NIC type',
vtpm: 'VTPM',
vtpmRequireUefi: 'A UEFI boot firmware is necessary to use a VTPM',
noAffinityHost: 'None',
originalTemplate: 'Original template',
unknownOsName: 'Unknown',
@ -1644,8 +1651,10 @@ const messages = {
newVmNetworkConfigDoc: 'Network config documentation',
templateHasBiosStrings: 'The template already contains the BIOS strings',
secureBootLinkToDocumentationMessage: 'Click for more information about Guest UEFI Secure Boot.',
seeVtpmDocumentation: 'See VTPM documentation',
vmBootFirmwareIsUefi: 'The boot firmware is UEFI',
destroyCloudConfigVdiAfterBoot: 'Destroy cloud config drive after first boot',
vtpmNotSupported: 'VTPM is only supported on pools running XCP-ng/XS 8.3 or later.',
// ----- Self -----
resourceSets: 'Resource sets',

View File

@ -2155,6 +2155,11 @@ export const deleteAclRule = ({ protocol = undefined, port = undefined, ipRange
vifId: resolveId(vif),
})
// VTPM -----------------------------------------------------------
export const createVtpm = vm => _call('vtpm.create', { id: resolveId(vm) })
export const deleteVtpm = vtpm => _call('vtpm.destroy', { id: resolveId(vtpm) })
// Network -----------------------------------------------------------
export const editNetwork = (network, props) => _call('network.set', { ...props, id: resolveId(network) })

View File

@ -2,6 +2,7 @@ import _ from 'intl'
import ActionButton from 'action-button'
import Component from 'base-component'
import decorate from 'apply-decorators'
import Copiable from 'copiable'
import defined, { get } from '@xen-orchestra/defined'
import getEventValue from 'get-event-value'
import Icon from 'icon'
@ -28,8 +29,10 @@ import {
cloneVm,
convertVmToTemplate,
createVgpu,
createVtpm,
deleteVgpu,
deleteVm,
deleteVtpm,
editVm,
getVmsHaValues,
isVmRunning,
@ -450,9 +453,48 @@ export default class TabAdvanced extends Component {
_onNicTypeChange = value => editVm(this.props.vm, { nicType: value === '' ? null : value })
_getDisabledAddVtpmReason = createSelector(
() => this.props.vm,
() => this.props.pool,
(vm, pool) => {
if (!pool.vtpmSupported) {
return _('vtpmNotSupported')
}
if (vm.boot.firmware !== 'uefi') {
return _('vtpmRequireUefi')
}
if (vm.power_state !== 'Halted') {
return _('vmNeedToBeHalted')
}
}
)
_getDisabledDeleteVtpmReason = () => {
if (this.props.vm.power_state !== 'Halted') {
return _('vmNeedToBeHalted')
}
}
_handleDeleteVtpm = async vtpm => {
await confirm({
icon: 'delete',
title: _('deleteVtpm'),
body: <p>{_('deleteVtpmWarning')}</p>,
strongConfirm: {
messageId: 'deleteVtpm',
},
})
return deleteVtpm(vtpm)
}
render() {
const { container, isAdmin, vgpus, vm, vmPool } = this.props
const isWarmMigrationAvailable = getXoaPlan().value >= PREMIUM.value
const addVtpmTooltip = this._getDisabledAddVtpmReason()
const deleteVtpmTooltip = this._getDisabledDeleteVtpmReason()
const isAddVtpmAvailable = addVtpmTooltip === undefined
const isDeleteVtpmAvailable = deleteVtpmTooltip === undefined
const vtpmId = vm.VTPMs[0]
return (
<Container>
<Row>
@ -798,6 +840,59 @@ export default class TabAdvanced extends Component {
</td>
</tr>
)}
<tr>
<th>{_('vtpm')}</th>
<td>
{/*
FIXME: add documentation link
<a
className='text-muted'
href='#'
rel='noopener noreferrer'
style={{ display: 'block' }}
target='_blank'
>
<Icon icon='info' /> {_('seeVtpmDocumentation')}
</a> */}
{vtpmId === undefined ? (
<Tooltip content={addVtpmTooltip}>
<ActionButton
btnStyle='primary'
disabled={!isAddVtpmAvailable}
handler={createVtpm}
handlerParam={vm}
icon='add'
>
{_('createVtpm')}
</ActionButton>
</Tooltip>
) : (
<div>
<Tooltip content={deleteVtpmTooltip}>
<ActionButton
btnStyle='danger'
disabled={!isDeleteVtpmAvailable}
handler={this._handleDeleteVtpm}
handlerParam={vtpmId}
icon='delete'
>
{_('deleteVtpm')}
</ActionButton>
</Tooltip>
<table className='table mt-1'>
<tbody>
<tr>
<th>{_('uuid')}</th>
<Copiable tagName='td' data={vtpmId}>
{vtpmId.slice(0, 4)}
</Copiable>
</tr>
</tbody>
</table>
</div>
)}
</td>
</tr>
{vm.boot.firmware === 'uefi' && (
<tr>
<th>{_('secureBoot')}</th>