feat(xo-web/host): pro support icon at host level (#6633)

- Host is not XCP-ng: no icon
- Host doesn't have license or is part of a pool that contains at least one host that doesn't have a license: orange life buoy
- Host has license and all hosts in its pool has license (=supported host): green life buoy
- Host doesn't have license and at least one host in its pool has a license: red triangle
This commit is contained in:
Mathieu
2023-03-29 15:54:15 +02:00
committed by GitHub
parent 954b29cb61
commit 97790313eb
5 changed files with 68 additions and 8 deletions

View File

@@ -12,6 +12,7 @@
- [Backup] Display the SR name label in the log even if the SR is not currently connected
- [Import VM] Ability to import multiple VMs from ESXi (PR [#6718](https://github.com/vatesfr/xen-orchestra/pull/6718))
- [Backup/Advanced setting] Ability to add transfer limit per job (PRs [#6737](https://github.com/vatesfr/xen-orchestra/pull/6737), [#6728](https://github.com/vatesfr/xen-orchestra/pull/6728))
- [License] Show Pro Support status icon at host level (PR [#6633](https://github.com/vatesfr/xen-orchestra/pull/6633))
### Bug fixes

View File

@@ -917,6 +917,10 @@ const messages = {
// ----- Host item ------
host: 'Host',
hostNoLicensePartialProSupport:
'This host does not have an active license, even though it is in a pool with licensed hosts. In order for XCP-ng Pro Support to be enabled on a pool, all hosts within the pool must have an active license',
hostNoSupport: 'No XCP-ng Pro Support enabled on this host',
hostSupportEnabled: 'XCP-ng Pro Support enabled on this host',
noMoreMaintained: 'This host version is no longer maintained',
// ----- Host actions ------

View File

@@ -29,12 +29,14 @@ import {
createGetObjectsOfType,
createSelector,
} from 'selectors'
import { injectState } from 'reaclette'
import MiniStats from './mini-stats'
import styles from './index.css'
import BulkIcons from '../../common/bulk-icons'
import { LICENSE_WARNING_BODY } from '../host/license-warning'
import { getXoaPlan, SOURCES } from '../../common/xoa-plans'
@addSubscriptions({
hvSupportedVersions: subscribeHvSupportedVersions,
@@ -48,8 +50,9 @@ import { LICENSE_WARNING_BODY } from '../host/license-warning'
hostId => obj => obj.$container === hostId
)
),
state: createGetHostState((_, props) => props.item),
hostState: createGetHostState((_, props) => props.item),
}))
@injectState
export default class HostItem extends Component {
get _isRunning() {
const host = this.props.item
@@ -75,6 +78,42 @@ export default class HostItem extends Component {
_stop = () => stopHost(this.props.item)
_toggleExpanded = () => this.setState({ expanded: !this.state.expanded })
_onSelect = () => this.props.onSelect(this.props.item.id)
_getProSupportStatus = () => {
const { state: reacletteState, item: host } = this.props
if (host.productBrand !== 'XCP-ng') {
return
}
const { supportLevel } = reacletteState.poolLicenseInfoByPoolId[host.$poolId]
const license = reacletteState.xcpngLicenseByBoundObjectId[host.id]
if (license !== undefined) {
license.expires = license.expires ?? Infinity
}
let level = 'warning'
let message = 'hostNoSupport'
if (getXoaPlan() === SOURCES) {
message = 'poolSupportSourceUsers'
level = 'warning'
}
if (supportLevel === 'total') {
message = 'hostSupportEnabled'
level = 'success'
}
if (supportLevel === 'partial' && (license === undefined || license.expires < Date.now())) {
message = 'hostNoLicensePartialProSupport'
level = 'danger'
}
return {
level,
icon: <Icon icon='menu-support' className={`text-${level}`} />,
message,
}
}
_getAlerts = createSelector(
() => this.props.needsRestart,
@@ -126,13 +165,25 @@ export default class HostItem extends Component {
),
})
}
const proSupportStatus = this._getProSupportStatus()
if (proSupportStatus !== undefined && proSupportStatus.level !== 'success') {
alerts.push({
level: proSupportStatus.level,
render: (
<span>
{proSupportStatus.icon} {_(proSupportStatus.message)}
</span>
),
})
}
return alerts
}
)
render() {
const { container, expandAll, item: host, nVms, selected, state } = this.props
const { container, expandAll, item: host, nVms, selected, hostState } = this.props
const proSupportStatus = this._getProSupportStatus()
return (
<div className={styles.item}>
<BlockLink to={`/hosts/${host.id}`}>
@@ -144,8 +195,8 @@ export default class HostItem extends Component {
<Tooltip
content={
<span>
{_(`powerState${state}`)}
{state === 'Busy' && (
{_(`powerState${hostState}`)}
{hostState === 'Busy' && (
<span>
{' ('}
{map(host.current_operations)[0]}
@@ -155,7 +206,7 @@ export default class HostItem extends Component {
</span>
}
>
<Icon icon={state.toLowerCase()} />
<Icon icon={hostState.toLowerCase()} />
</Tooltip>
&nbsp;&nbsp;
<Ellipsis>
@@ -167,6 +218,10 @@ export default class HostItem extends Component {
)}
&nbsp;
<BulkIcons alerts={this._getAlerts()} />
&nbsp;
{proSupportStatus?.level === 'success' && (
<Tooltip content={_(proSupportStatus.message)}>{proSupportStatus.icon}</Tooltip>
)}
</EllipsisContainer>
</Col>
<Col mediumSize={3} className='hidden-lg-down'>

View File

@@ -110,7 +110,7 @@ export default class PoolItem extends Component {
if (isAdmin && this._isXcpngPool()) {
const { icon, supportLevel } = poolLicenseInfo
if (supportLevel !== 'total') {
const level = supportLevel === 'partial' ? 'warning' : 'danger'
const level = supportLevel === 'partial' || getXoaPlan() === SOURCES ? 'warning' : 'danger'
alerts.push({
level,
render: (

View File

@@ -187,7 +187,7 @@ export const ICON_POOL_LICENSE = {
if (getXoaPlan() === SOURCES.name) {
poolLicenseInfoByPoolId[poolId] = {
nHostsUnderLicense,
icon: () => <Icon icon='unknown-status' className='text-danger' />,
icon: () => <Icon icon='unknown-status' className='text-warning' />,
nHosts,
}
return