Skip to content

Commit

Permalink
feat(xo-web/host/advanced): list PCIs known by the XAPI (#7455)
Browse files Browse the repository at this point in the history
See #7432
  • Loading branch information
MathieuRA committed Apr 29, 2024
1 parent c32ca3d commit e2a4960
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [i18n] Japanese translation (PR [#7582](https://github.com/vatesfr/xen-orchestra/pull/7582))
- [REST API] [Watch mode for the tasks collection](./packages/xo-server/docs/rest-api.md#all-tasks) (PR [#7565](https://github.com/vatesfr/xen-orchestra/pull/7565))
- [Home/SR] Display _Pro Support_ status for XOSTOR SR (PR [#7601](https://github.com/vatesfr/xen-orchestra/pull/7601))
- [Host/Advanced] Ability to `enable/disable` passthrough for PCIs [#7432](https://github.com/vatesfr/xen-orchestra/issues/7432) (PR [#7455](https://github.com/vatesfr/xen-orchestra/pull/7455))

### Bug fixes

Expand Down
9 changes: 9 additions & 0 deletions packages/xo-web/src/common/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,10 @@ const messages = {
// ----- host stat tab -----
statLoad: 'Load average',
// ----- host advanced tab -----
applyChangeOnPcis:
'This operation will reboot the host in order to apply the change on the PCI{nPcis, plural, one {} other {s}}. Are you sure you want to continue?',
className: 'Class name',
deviceName: 'Device name',
enabled: 'Enabled',
disksSystemHealthy: 'All disks are healthy ✅',
editHostIscsiIqnTitle: 'Edit iSCSI IQN',
Expand Down Expand Up @@ -1078,6 +1082,11 @@ const messages = {
hostRemoteSyslog: 'Remote syslog',
hostIommu: 'IOMMU',
hostNoCertificateInstalled: 'No certificates installed on this host',
'onlyAvailableXcp8.3OrHigher': 'Only available for XCP-ng 8.3.0 or higher',
pciDevices: 'PCI Devices',
pciId: 'PCI ID',
pcisEnable: 'PCI{nPcis, plural, one {} other {s}} enable',
pcisDisable: 'PCI{nPcis, plural, one {} other {s}} disable',
pusbDevices: 'PUSB Devices',
smartctlPluginNotInstalled: 'Smartctl plugin not installed',
supplementalPacks: 'Installed supplemental packs',
Expand Down
21 changes: 21 additions & 0 deletions packages/xo-web/src/common/xo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cookies from 'js-cookie'
import copy from 'copy-to-clipboard'
import fpSortBy from 'lodash/fp/sortBy'
import React from 'react'
import semver from 'semver'
import updater from 'xoa-updater'
import URL from 'url-parse'
import Xo from 'xo-lib'
Expand Down Expand Up @@ -1372,6 +1373,26 @@ export const installSupplementalPackOnAllHosts = (pool, file) => {
)
}

export const hidePcis = async (pcis, hide) => {
try {
await confirm({
body: _('applyChangeOnPcis', { nPcis: pcis.length }),
// hide `true` means that we will disable dom0's PCI access, so we will "enable" the possibility of passthrough this PCI
title: _(hide ? 'pcisEnable' : 'pcisDisable', { nPcis: pcis.length }),
})
} catch (error) {
return
}
return _call('pci.disableDom0Access', { pcis: resolveIds(pcis), disable: hide })
}

export const isPciHidden = async pci => (await _call('pci.getDom0AccessStatus', { id: resolveId(pci) })) === 'disabled'

// ATM, unknown date for the availablity on XS, since they are doing rolling release
// FIXME: When XS release methods to do PCI passthrough, update this check
export const isPciPassthroughAvailable = host =>
host.productBrand === 'XCP-ng' && semver.satisfies(host.version, '>=8.3.0')

// Containers --------------------------------------------------------

export const pauseContainer = (vm, container) => _call('docker.pause', { vm: resolveId(vm), container })
Expand Down
109 changes: 108 additions & 1 deletion packages/xo-web/src/xo-app/host/tab-advanced.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ import {
enableAdvancedLiveTelemetry,
enableHost,
forgetHost,
hidePcis,
installSupplementalPack,
isHyperThreadingEnabledHost,
isPciHidden,
isPciPassthroughAvailable,
isNetDataInstalledOnHost,
getPlugin,
getSmartctlHealth,
Expand Down Expand Up @@ -76,6 +79,78 @@ const PUSBS_COLUMNS = [
},
},
]
const PCIS_COLUMNS = [
{
name: _('id'),
itemRenderer: pci => {
const { uuid } = pci
return (
<Copiable data={uuid} tagName='p'>
{uuid.slice(4, 8)}
</Copiable>
)
},
},
{
default: true,
name: _('pciId'),
itemRenderer: pci => pci.pci_id,
sortCriteria: pci => pci.pci_id,
},
{
name: _('className'),
itemRenderer: pci => pci.class_name,
sortCriteria: pci => pci.class_name,
},
{
name: _('deviceName'),
itemRenderer: pci => pci.device_name,
sortCriteria: pci => pci.device_name,
},
{
name: _('enabled'),
itemRenderer: (pci, { pciStateById, isPciPassthroughAvailable }) => {
if (pciStateById === undefined) {
return <Icon icon='loading' />
}

if (!isPciPassthroughAvailable) {
return (
<Tooltip content={_('onlyAvailableXcp8.3OrHigher')}>
<Toggle disabled />
</Tooltip>
)
}

const { error, isHidden } = pciStateById[pci.id]
if (error !== undefined) {
return (
<Tooltip content={error}>
<Icon icon='alarm' color='text-danger' />
</Tooltip>
)
}

const _hidePcis = value => hidePcis([pci], value)
return <Toggle value={isHidden} onChange={_hidePcis} />
},
sortCriteria: (pci, { pciStateById }) => pciStateById?.[pci.id]?.isHidden,
},
]
const PCIS_ACTIONS = [
{
handler: pcis => hidePcis(pcis, false),
icon: 'toggle-off',
label: _('disable'),
level: 'primary',
},
{
handler: pcis => hidePcis(pcis, true),
icon: 'toggle-on',
label: _('enable'),
level: 'primary',
},
]

const SCHED_GRAN_TYPE_OPTIONS = [
{
Expand Down Expand Up @@ -181,7 +256,11 @@ MultipathableSrs.propTypes = {
.pick((_, { host }) => host.$PGPUs)
.sort()

const getPcis = createGetObjectsOfType('PCI').pick(createSelector(getPgpus, pgpus => map(pgpus, 'pci')))
const getPcis = createGetObjectsOfType('PCI').filter(
(_, { host }) =>
pci =>
pci.$host === host.id
)

const getPusbs = createGetObjectsOfType('PUSB').filter(
(_, { host }) =>
Expand Down Expand Up @@ -223,11 +302,30 @@ export default class extends Component {
}))
}

const _isPciPassthroughAvailable = isPciPassthroughAvailable(host)
const pciStateById = {}
if (_isPciPassthroughAvailable) {
await Promise.all(
Object.keys(this.props.pcis).map(async id => {
const pciSate = {}
try {
pciSate.isHidden = await isPciHidden(id)
} catch (error) {
console.error(error)
pciSate.error = error.message
}
pciStateById[id] = pciSate
})
)
}

this.setState({
isHtEnabled: await isHyperThreadingEnabledHost(host).catch(() => null),
isSmartctlHealthEnabled,
pciStateById,
smartctlUnhealthyDevices,
unhealthyDevicesAlerts,
isPciPassthroughAvailable: _isPciPassthroughAvailable,
})
}

Expand Down Expand Up @@ -596,6 +694,15 @@ export default class extends Component {
<h3>{_('pusbDevices')}</h3>
<SortedTable collection={pusbs} columns={PUSBS_COLUMNS} />
<br />
<h3>{_('pciDevices')}</h3>
<SortedTable
groupedActions={PCIS_ACTIONS}
collection={pcis}
columns={PCIS_COLUMNS}
data-pciStateById={this.state.pciStateById}
data-isPciPassthroughAvailable={this.state.isPciPassthroughAvailable}
stateUrlParam='s_pcis'
/>
<h3>{_('licenseHostSettingsLabel')}</h3>
<table className='table'>
<tbody>
Expand Down

0 comments on commit e2a4960

Please sign in to comment.