feat(xo-web/host/advanced): display system disks health

This commit is contained in:
mathieuRA 2023-09-28 11:28:14 +02:00 committed by Julien Fontanet
parent eb69234a8e
commit 0e934c1413
4 changed files with 58 additions and 4 deletions

View File

@ -7,6 +7,8 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [Host/Advanced] Display system disks health based on the _smartctl_ plugin. [#4458](https://github.com/vatesfr/xen-orchestra/issues/4458) (PR [#7060](https://github.com/vatesfr/xen-orchestra/pull/7060))
### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed”
@ -35,6 +37,6 @@
- vhd-lib patch
- xo-server-backup-reports patch
- xo-server minor
- xo-web patch
- xo-web minor
<!--packages-end-->

View File

@ -981,6 +981,7 @@ const messages = {
// ----- host stat tab -----
statLoad: 'Load average',
// ----- host advanced tab -----
disksSystemHealthy: 'All disks are healthy ✅',
editHostIscsiIqnTitle: 'Edit iSCSI IQN',
editHostIscsiIqnMessage:
'Are you sure you want to edit the iSCSI IQN? This may result in failures connecting to existing SRs if the host is attached to iSCSI SRs.',
@ -1031,6 +1032,7 @@ const messages = {
hostRemoteSyslog: 'Remote syslog',
hostIommu: 'IOMMU',
hostNoCertificateInstalled: 'No certificates installed on this host',
smartctlPluginNotInstalled: 'Smartctl plugin not installed',
supplementalPacks: 'Installed supplemental packs',
supplementalPackNew: 'Install new supplemental pack',
supplementalPackPoolNew: 'Install supplemental pack on every host',
@ -1041,6 +1043,7 @@ const messages = {
supplementalPackInstallErrorMessage: 'The installation of the supplemental pack failed.',
supplementalPackInstallSuccessTitle: 'Installation success',
supplementalPackInstallSuccessMessage: 'Supplemental pack successfully installed.',
systemDisksHealth: 'System disks health',
uniqueHostIscsiIqnInfo: 'The iSCSI IQN must be unique. ',
// ----- Host net tabs -----
networkCreateButton: 'Add a network',

View File

@ -1024,6 +1024,11 @@ export const isHyperThreadingEnabledHost = host =>
id: resolveId(host),
})
export const getSmartctlHealth = host => _call('host.getSmartctlHealth', { id: resolveId(host) })
export const getSmartctlInformation = (host, deviceNames) =>
_call('host.getSmartctlInformation', { id: resolveId(host), deviceNames })
export const installCertificateOnHost = (id, props) => _call('host.installCertificate', { id, ...props })
export const setControlDomainMemory = (id, memory) => _call('host.setControlDomainMemory', { id, memory })

View File

@ -1,5 +1,6 @@
import _ from 'intl'
import ActionButton from 'action-button'
import BulkIcons from 'bulk-icons'
import Component from 'base-component'
import Copiable from 'copiable'
import decorate from 'apply-decorators'
@ -33,6 +34,8 @@ import {
isHyperThreadingEnabledHost,
isNetDataInstalledOnHost,
getPlugin,
getSmartctlHealth,
getSmartctlInformation,
restartHost,
setControlDomainMemory,
setHostsMultipathing,
@ -161,17 +164,36 @@ MultipathableSrs.propTypes = {
})
export default class extends Component {
async componentDidMount() {
const { host } = this.props
const plugin = await getPlugin('netdata')
const isNetDataPluginCorrectlySet = plugin !== undefined && plugin.loaded
this.setState({ isNetDataPluginCorrectlySet })
if (isNetDataPluginCorrectlySet) {
this.setState({
isNetDataPluginInstalledOnHost: await isNetDataInstalledOnHost(this.props.host),
isNetDataPluginInstalledOnHost: await isNetDataInstalledOnHost(host),
})
}
const smartctlHealth = await getSmartctlHealth(host)
const isSmartctlHealthEnabled = smartctlHealth !== null
const smartctlUnhealthyDevices = isSmartctlHealthEnabled
? Object.keys(smartctlHealth).filter(deviceName => smartctlHealth[deviceName] !== 'PASSED')
: undefined
let unhealthyDevicesAlerts
if (smartctlUnhealthyDevices?.length > 0) {
const unhealthyDeviceInformations = await getSmartctlInformation(host, smartctlUnhealthyDevices)
unhealthyDevicesAlerts = map(unhealthyDeviceInformations, (value, key) => ({
level: 'warning',
render: <pre>{_('keyValue', { key, value: JSON.stringify(value, null, 2) })}</pre>,
}))
}
this.setState({
isHtEnabled: await isHyperThreadingEnabledHost(this.props.host).catch(() => null),
isHtEnabled: await isHyperThreadingEnabledHost(host).catch(() => null),
isSmartctlHealthEnabled,
smartctlUnhealthyDevices,
unhealthyDevicesAlerts,
})
}
@ -233,7 +255,14 @@ export default class extends Component {
render() {
const { controlDomain, host, pcis, pgpus, schedGran } = this.props
const { isHtEnabled, isNetDataPluginInstalledOnHost, isNetDataPluginCorrectlySet } = this.state
const {
isHtEnabled,
isNetDataPluginInstalledOnHost,
isNetDataPluginCorrectlySet,
isSmartctlHealthEnabled,
unhealthyDevicesAlerts,
smartctlUnhealthyDevices,
} = this.state
const _isXcpNgHost = host.productBrand === 'XCP-ng'
@ -512,6 +541,21 @@ export default class extends Component {
{host.bios_strings['bios-vendor']} ({host.bios_strings['bios-version']})
</td>
</tr>
<tr>
<th>{_('systemDisksHealth')}</th>
<td>
{isSmartctlHealthEnabled !== undefined &&
(isSmartctlHealthEnabled ? (
smartctlUnhealthyDevices?.length === 0 ? (
_('disksSystemHealthy')
) : (
<BulkIcons alerts={unhealthyDevicesAlerts ?? []} />
)
) : (
_('smartctlPluginNotInstalled')
))}
</td>
</tr>
</tbody>
</table>
<br />