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-web/host/advanced): list PCIs known by the XAPI #7455

Merged
merged 2 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading