Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xo-server/xo-web/VM/new): VTPM creation #7077

Merged
merged 11 commits into from
Oct 19, 2023
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [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))
- [VM/New] Possibility to create and attach a _VTPM_ to a VM [#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 [#7077](https://github.com/vatesfr/xen-orchestra/pull/7077))

### Bug fixes

Expand Down
10 changes: 10 additions & 0 deletions packages/xo-server/src/api/vm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ export const create = defer(async function ($defer, params) {
await this.allocIpAddresses(vif.$id, concat(vif.ipv4_allowed, vif.ipv6_allowed)).catch(() => xapi.deleteVif(vif))
}

if (params.createVtpm) {
const vtpmRef = await xapi.VTPM_create({ VM: xapiVm.$ref })
$defer.onFailure(() => xapi.call('VTPM.destroy', vtpmRef))
}

if (params.bootAfterCreate) {
startVmAndDestroyCloudConfigVdi(xapi, xapiVm, cloudConfigVdiUuid, params)
}
Expand All @@ -258,6 +263,11 @@ create.params = {
optional: true,
},

createVtpm: {
type: 'boolean',
default: false,
},

networkConfig: {
type: 'string',
optional: true,
Expand Down
2 changes: 2 additions & 0 deletions packages/xo-web/src/common/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ const messages = {
createVmModalWarningMessage:
"You're about to use a large amount of resources available on the resource set. Are you sure you want to continue?",
copyHostBiosStrings: 'Copy host BIOS strings to VM',
enableVtpm: 'Enable VTPM',
newVmCreateNewVmOn: 'Create a new VM on {select}',
newVmCreateNewVmNoPermission: 'You have no permission to create a VM',
newVmInfoPanel: 'Info',
Expand Down Expand Up @@ -1655,6 +1656,7 @@ const messages = {
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.',
warningVtpmRequired: 'This template requires a VTPM, if you proceed, the VM will likely not be able to boot.',

// ----- Self -----
resourceSets: 'Resource sets',
Expand Down
8 changes: 8 additions & 0 deletions packages/xo-web/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ $select-input-height: 40px; // Bootstrap input height
color: #333;
}

.d-inline-flex {
display: inline-flex;
}

.align-self-center {
align-self: center;
}

// COLORS ======================================================================

.xo-status-running {
Expand Down
32 changes: 31 additions & 1 deletion packages/xo-web/src/xo-app/new-vm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ export default class NewVm extends BaseComponent {
seqStart: 1,
share: this._getResourceSet()?.shareByDefault ?? false,
tags: [],
createVtpm: this._templateNeedsVtpm(),
},
callback
)
Expand Down Expand Up @@ -493,6 +494,7 @@ export default class NewVm extends BaseComponent {
bootAfterCreate: state.bootAfterCreate,
copyHostBiosStrings:
state.hvmBootFirmware !== 'uefi' && !this._templateHasBiosStrings() && state.copyHostBiosStrings,
createVtpm: state.createVtpm,
destroyCloudConfigVdiAfterBoot: state.destroyCloudConfigVdiAfterBoot,
secureBoot: state.secureBoot,
share: state.share,
Expand Down Expand Up @@ -599,6 +601,7 @@ export default class NewVm extends BaseComponent {
}),
// settings
secureBoot: template.secureBoot,
createVtpm: this._templateNeedsVtpm(),
})

if (this._isCoreOs()) {
Expand Down Expand Up @@ -748,6 +751,8 @@ export default class NewVm extends BaseComponent {
template => template && template.virtualizationMode === 'hvm'
)

_templateNeedsVtpm = () => this.props.template?.platform?.vtpm === 'true'

// On change -------------------------------------------------------------------

_onChangeSshKeys = keys => this._setState({ sshKeys: map(keys, key => key.id) })
Expand Down Expand Up @@ -881,7 +886,12 @@ export default class NewVm extends BaseComponent {

_getRedirectionUrl = id => (this.state.state.multipleVms ? '/home' : `/vms/${id}`)

_handleBootFirmware = value => this._setState({ hvmBootFirmware: value, secureBoot: false })
_handleBootFirmware = value =>
this._setState({
hvmBootFirmware: value,
secureBoot: false,
createVtpm: value === 'uefi' ? this._templateNeedsVtpm() : false,
})

// MAIN ------------------------------------------------------------------------

Expand Down Expand Up @@ -1531,6 +1541,7 @@ export default class NewVm extends BaseComponent {
cpuCap,
cpusMax,
cpuWeight,
createVtpm,
destroyCloudConfigVdiAfterBoot,
hvmBootFirmware,
installMethod,
Expand Down Expand Up @@ -1565,6 +1576,8 @@ export default class NewVm extends BaseComponent {
</label>
) : null

const isVtpmSupported = pool.vtpmSupported

return (
<Section icon='new-vm-advanced' title='newVmAdvancedPanel' done={this._isAdvancedDone()}>
<SectionContent column>
Expand Down Expand Up @@ -1769,6 +1782,23 @@ export default class NewVm extends BaseComponent {
<Item label={_('secureBoot')}>
<Toggle onChange={this._toggleState('secureBoot')} value={secureBoot} />
</Item>
<Item label={_('enableVtpm')} className='d-inline-flex'>
<Tooltip content={!isVtpmSupported ? _('vtpmNotSupported') : undefined}>
<Toggle onChange={this._toggleState('createVtpm')} value={createVtpm} disabled={!isVtpmSupported} />
</Tooltip>
{/* FIXME: link to VTPM documentation when ready */}
{/* &nbsp;
<Tooltip content={_('seeVtpmDocumentation')}>
<a className='text-info align-self-center' style={{ cursor: 'pointer' }} href='#'>
<Icon icon='info' />
</a>
</Tooltip> */}
{!createVtpm && this._templateNeedsVtpm() && (
<span className='align-self-center text-warning ml-1'>
<Icon icon='alarm' /> {_('warningVtpmRequired')}
</span>
)}
</Item>
</SectionContent>
),
isAdmin && isHvm && (
Expand Down