feat(xo-web/pool/host): add warning if hosts don't have the same version (#7280)
Fixes #7059
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
- [REST API] New pool action: `emergency_shutdown`, it suspends all the VMs and then shuts down all the host [#7277](https://github.com/vatesfr/xen-orchestra/issues/7277) (PR [#7279](https://github.com/vatesfr/xen-orchestra/pull/7279))
|
||||
- [Tasks] Hide `/rrd_updates` tasks by default
|
||||
- [Sign in] Support _Remember me_ feature with external providers (PR [#7298](https://github.com/vatesfr/xen-orchestra/pull/7298))
|
||||
- [Pool/Host] Add a warning if hosts do not have the same version within a pool [#7059](https://github.com/vatesfr/xen-orchestra/issues/7059) (PR [#7280](https://github.com/vatesfr/xen-orchestra/pull/7280))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
||||
@@ -328,6 +328,7 @@ const messages = {
|
||||
powerState: 'Power state',
|
||||
srSharedType: 'Shared {type}',
|
||||
warningHostTimeTooltip: 'Host time and XOA time are not consistent with each other',
|
||||
notAllHostsHaveTheSameVersion: 'Not all hosts within {pool} have the same version',
|
||||
selectExistingTags: 'Select from existing tags',
|
||||
sortByDisksUsage: 'Disks usage',
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
createSelector,
|
||||
} from 'selectors'
|
||||
import { injectState } from 'reaclette'
|
||||
import { Host, Pool } from 'render-xo-item'
|
||||
|
||||
import MiniStats from './mini-stats'
|
||||
import styles from './index.css'
|
||||
@@ -126,13 +127,16 @@ export default class HostItem extends Component {
|
||||
message,
|
||||
}
|
||||
}
|
||||
_getAreHostsVersionsEqual = () => this.props.state.areHostsVersionsEqualByPool[this.props.item.$pool]
|
||||
|
||||
_getAlerts = createSelector(
|
||||
() => this.props.needsRestart,
|
||||
() => this.props.item,
|
||||
this._isMaintained,
|
||||
() => this.state.isHostTimeConsistentWithXoaTime,
|
||||
(needsRestart, host, isMaintained, isHostTimeConsistentWithXoaTime) => {
|
||||
this._getAreHostsVersionsEqual,
|
||||
() => this.props.state.hostsByPoolId[this.props.item.$pool],
|
||||
(needsRestart, host, isMaintained, isHostTimeConsistentWithXoaTime, areHostsVersionsEqual, poolHosts) => {
|
||||
const alerts = []
|
||||
|
||||
if (needsRestart) {
|
||||
@@ -201,6 +205,25 @@ export default class HostItem extends Component {
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
if (!areHostsVersionsEqual) {
|
||||
alerts.push({
|
||||
level: 'danger',
|
||||
render: (
|
||||
<div>
|
||||
<p>
|
||||
<Icon icon='alarm' /> {_('notAllHostsHaveTheSameVersion', { pool: <Pool id={host.$pool} link /> })}
|
||||
</p>
|
||||
<ul>
|
||||
{map(poolHosts, host => (
|
||||
<li>{_('keyValue', { key: <Host id={host.id} />, value: host.version })}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
return alerts
|
||||
}
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ import { addTag, editPool, getHostMissingPatches, removeTag } from 'xo'
|
||||
import { connectStore, formatSizeShort } from 'utils'
|
||||
import { compact, flatten, map, size, uniq } from 'lodash'
|
||||
import { createGetObjectsOfType, createGetHostMetrics, createSelector } from 'selectors'
|
||||
import { Host, Pool } from 'render-xo-item'
|
||||
import { injectState } from 'reaclette'
|
||||
|
||||
import styles from './index.css'
|
||||
@@ -101,10 +102,15 @@ export default class PoolItem extends Component {
|
||||
|
||||
_getPoolLicenseInfo = () => this.props.state.poolLicenseInfoByPoolId[this.props.item.id]
|
||||
|
||||
_getAreHostsVersionsEqual = () => this.props.state.areHostsVersionsEqualByPool[this.props.item.id]
|
||||
|
||||
_getAlerts = createSelector(
|
||||
() => this.props.isAdmin,
|
||||
this._getPoolLicenseInfo,
|
||||
(isAdmin, poolLicenseInfo) => {
|
||||
this._getAreHostsVersionsEqual,
|
||||
() => this.props.poolHosts,
|
||||
() => this.props.item.id,
|
||||
(isAdmin, poolLicenseInfo, areHostsVersionsEqual, hosts, poolId) => {
|
||||
const alerts = []
|
||||
|
||||
if (isAdmin && this._isXcpngPool()) {
|
||||
@@ -120,6 +126,25 @@ export default class PoolItem extends Component {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!areHostsVersionsEqual) {
|
||||
alerts.push({
|
||||
level: 'danger',
|
||||
render: (
|
||||
<div>
|
||||
<p>
|
||||
<Icon icon='alarm' /> {_('notAllHostsHaveTheSameVersion', { pool: <Pool id={poolId} link /> })}
|
||||
</p>
|
||||
<ul>
|
||||
{map(hosts, host => (
|
||||
<li>{_('keyValue', { key: <Host id={host.id} />, value: host.version })}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
return alerts
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as CM from 'complex-matcher'
|
||||
import _ from 'intl'
|
||||
import Copiable from 'copiable'
|
||||
import decorate from 'apply-decorators'
|
||||
import Icon from 'icon'
|
||||
import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
@@ -11,151 +12,169 @@ import { BlockLink } from 'link'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import { FormattedRelative } from 'react-intl'
|
||||
import { formatSize, formatSizeShort, hasLicenseRestrictions } from 'utils'
|
||||
import { injectState, provideState } from 'reaclette'
|
||||
import Usage, { UsageElement } from 'usage'
|
||||
import { getObject } from 'selectors'
|
||||
import { CpuSparkLines, MemorySparkLines, NetworkSparkLines, LoadSparkLines } from 'xo-sparklines'
|
||||
import { Pool } from 'render-xo-item'
|
||||
|
||||
import LicenseWarning from './license-warning'
|
||||
|
||||
export default ({ statsOverview, host, nVms, vmController, vms }) => {
|
||||
const pool = getObject(store.getState(), host.$pool)
|
||||
const vmsFilter = encodeURIComponent(new CM.Property('$container', new CM.String(host.id)).toString())
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<br />
|
||||
<Row className='text-xs-center'>
|
||||
<Col mediumSize={3}>
|
||||
<h2>
|
||||
{host.CPUs.cpu_count}x <Icon icon='cpu' size='lg' />
|
||||
</h2>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <CpuSparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<h2>
|
||||
{formatSize(host.memory.size)} <Icon icon='memory' size='lg' />
|
||||
</h2>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <MemorySparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<BlockLink to={`/hosts/${host.id}/network`}>
|
||||
export default decorate([
|
||||
provideState({
|
||||
computed: {
|
||||
areHostsVersionsEqual: ({ areHostsVersionsEqualByPool }, { host }) => areHostsVersionsEqualByPool[host.$pool],
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
({ statsOverview, host, nVms, vmController, vms, state: { areHostsVersionsEqual } }) => {
|
||||
const pool = getObject(store.getState(), host.$pool)
|
||||
const vmsFilter = encodeURIComponent(new CM.Property('$container', new CM.String(host.id)).toString())
|
||||
return (
|
||||
<Container>
|
||||
<br />
|
||||
<Row className='text-xs-center'>
|
||||
<Col mediumSize={3}>
|
||||
<h2>
|
||||
{host.$PIFs.length}x <Icon icon='network' size='lg' />
|
||||
{host.CPUs.cpu_count}x <Icon icon='cpu' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <NetworkSparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<BlockLink to={`/hosts/${host.id}/storage`}>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <CpuSparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<h2>
|
||||
{host.$PBDs.length}x <Icon icon='disk' size='lg' />
|
||||
{formatSize(host.memory.size)} <Icon icon='memory' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <LoadSparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row className='text-xs-center'>
|
||||
<Col mediumSize={3}>
|
||||
<p className='text-xs-center'>
|
||||
{_('started', {
|
||||
ago: <FormattedRelative value={host.startTime * 1000} />,
|
||||
})}
|
||||
</p>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<p>
|
||||
{host.productBrand} {host.version} (
|
||||
{host.productBrand !== 'XCP-ng' ? host.license_params.sku_type : 'GPLv2'}){' '}
|
||||
{hasLicenseRestrictions(host) && <LicenseWarning iconSize='lg' />}
|
||||
</p>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<Copiable tagName='p'>{host.address}</Copiable>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<p>
|
||||
{host.bios_strings['system-manufacturer']} {host.bios_strings['system-product-name']}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<BlockLink to={`/home?t=VM&s=${vmsFilter}`}>
|
||||
<h2>
|
||||
{nVms}x <Icon icon='vm' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>{_('hostTitleRamUsage')}</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col smallOffset={1} mediumSize={10}>
|
||||
<Usage total={host.memory.size}>
|
||||
<UsageElement
|
||||
highlight
|
||||
tooltip={`${host.productBrand} (${formatSize(vmController.memory.size)})`}
|
||||
value={vmController.memory.size}
|
||||
/>
|
||||
{map(vms, vm => (
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <MemorySparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<BlockLink to={`/hosts/${host.id}/network`}>
|
||||
<h2>
|
||||
{host.$PIFs.length}x <Icon icon='network' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <NetworkSparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<BlockLink to={`/hosts/${host.id}/storage`}>
|
||||
<h2>
|
||||
{host.$PBDs.length}x <Icon icon='disk' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
<BlockLink to={`/hosts/${host.id}/stats`}>
|
||||
{statsOverview && <LoadSparkLines data={statsOverview} />}
|
||||
</BlockLink>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row className='text-xs-center'>
|
||||
<Col mediumSize={3}>
|
||||
<p className='text-xs-center'>
|
||||
{_('started', {
|
||||
ago: <FormattedRelative value={host.startTime * 1000} />,
|
||||
})}
|
||||
</p>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<p>
|
||||
{host.productBrand} {host.version} (
|
||||
{host.productBrand !== 'XCP-ng' ? host.license_params.sku_type : 'GPLv2'}){' '}
|
||||
{hasLicenseRestrictions(host) && <LicenseWarning iconSize='lg' />}
|
||||
</p>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<Copiable tagName='p'>{host.address}</Copiable>
|
||||
</Col>
|
||||
<Col mediumSize={3}>
|
||||
<p>
|
||||
{host.bios_strings['system-manufacturer']} {host.bios_strings['system-product-name']}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<BlockLink to={`/home?t=VM&s=${vmsFilter}`}>
|
||||
<h2>
|
||||
{nVms}x <Icon icon='vm' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>{_('hostTitleRamUsage')}</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col smallOffset={1} mediumSize={10}>
|
||||
<Usage total={host.memory.size}>
|
||||
<UsageElement
|
||||
tooltip={`${vm.name_label} (${formatSize(vm.memory.size)})`}
|
||||
key={vm.id}
|
||||
value={vm.memory.size}
|
||||
href={`#/vms/${vm.id}`}
|
||||
highlight
|
||||
tooltip={`${host.productBrand} (${formatSize(vmController.memory.size)})`}
|
||||
value={vmController.memory.size}
|
||||
/>
|
||||
))}
|
||||
</Usage>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>
|
||||
{_('memoryHostState', {
|
||||
memoryUsed: formatSizeShort(host.memory.usage),
|
||||
memoryTotal: formatSizeShort(host.memory.size),
|
||||
memoryFree: formatSizeShort(host.memory.size - host.memory.usage),
|
||||
})}
|
||||
</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
{pool && host.id === pool.master && (
|
||||
<Row className='text-xs-center'>
|
||||
{map(vms, vm => (
|
||||
<UsageElement
|
||||
tooltip={`${vm.name_label} (${formatSize(vm.memory.size)})`}
|
||||
key={vm.id}
|
||||
value={vm.memory.size}
|
||||
href={`#/vms/${vm.id}`}
|
||||
/>
|
||||
))}
|
||||
</Usage>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>
|
||||
{_('memoryHostState', {
|
||||
memoryUsed: formatSizeShort(host.memory.usage),
|
||||
memoryTotal: formatSizeShort(host.memory.size),
|
||||
memoryFree: formatSizeShort(host.memory.size - host.memory.usage),
|
||||
})}
|
||||
</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
{pool && host.id === pool.master && (
|
||||
<Row className='text-xs-center'>
|
||||
<Col>
|
||||
<h3>
|
||||
<span className='tag tag-pill tag-info'>{_('pillMaster')}</span>
|
||||
</h3>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<h2 className='text-xs-center'>
|
||||
<HomeTags
|
||||
type='host'
|
||||
labels={host.tags}
|
||||
onDelete={tag => removeTag(host.id, tag)}
|
||||
onAdd={tag => addTag(host.id, tag)}
|
||||
/>
|
||||
</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
{!areHostsVersionsEqual && (
|
||||
<Row className='text-xs-center text-danger'>
|
||||
<Col>
|
||||
<h3>
|
||||
<span className='tag tag-pill tag-info'>{_('pillMaster')}</span>
|
||||
</h3>
|
||||
<p>
|
||||
<Icon icon='alarm' /> {_('notAllHostsHaveTheSameVersion', { pool: <Pool id={host.$pool} link /> })}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<h2 className='text-xs-center'>
|
||||
<HomeTags
|
||||
type='host'
|
||||
labels={host.tags}
|
||||
onDelete={tag => removeTag(host.id, tag)}
|
||||
onAdd={tag => addTag(host.id, tag)}
|
||||
/>
|
||||
</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
</Container>
|
||||
)
|
||||
},
|
||||
])
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import Component from 'base-component'
|
||||
import cookies from 'js-cookie'
|
||||
import DocumentTitle from 'react-document-title'
|
||||
import every from 'lodash/every'
|
||||
import Icon from 'icon'
|
||||
import Link from 'link'
|
||||
import map from 'lodash/map'
|
||||
import mapValues from 'lodash/mapValues'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import Shortcuts from 'shortcuts'
|
||||
@@ -172,7 +174,7 @@ export const ICON_POOL_LICENSE = {
|
||||
xcpngLicenseById: (_, { xcpLicenses }) => keyBy(xcpLicenses, 'id'),
|
||||
hostsByPoolId: createCollectionWrapper((_, { hosts }) =>
|
||||
groupBy(
|
||||
map(hosts, host => pick(host, ['$poolId', 'id'])),
|
||||
map(hosts, host => pick(host, ['$poolId', 'id', 'version'])),
|
||||
'$poolId'
|
||||
)
|
||||
),
|
||||
@@ -230,6 +232,8 @@ export const ICON_POOL_LICENSE = {
|
||||
placeholder: '',
|
||||
},
|
||||
isXoaStatusOk: ({ xoaStatus }) => !xoaStatus.includes('✖'),
|
||||
areHostsVersionsEqualByPool: ({ hostsByPoolId }) =>
|
||||
mapValues(hostsByPoolId, hosts => every(hosts, host => host.version === hosts[0].version)),
|
||||
},
|
||||
})
|
||||
export default class XoApp extends Component {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import _ from 'intl'
|
||||
import decorate from 'apply-decorators'
|
||||
import find from 'lodash/find'
|
||||
import Icon from 'icon'
|
||||
import map from 'lodash/map'
|
||||
@@ -11,87 +12,106 @@ import { Container, Row, Col } from 'grid'
|
||||
import Usage, { UsageElement } from 'usage'
|
||||
import { formatSizeShort } from 'utils'
|
||||
import Tooltip from 'tooltip'
|
||||
import { injectState, provideState } from 'reaclette'
|
||||
import { Pool } from 'render-xo-item'
|
||||
|
||||
export default ({ hosts, nVms, pool, srs }) => (
|
||||
<Container>
|
||||
<br />
|
||||
<Row className='text-xs-center'>
|
||||
<Col mediumSize={4}>
|
||||
<Tooltip content={_('displayAllHosts')}>
|
||||
<BlockLink to={`/home?s=$pool:${pool.id}&t=host`}>
|
||||
<h2>
|
||||
{hosts.length}x <Icon icon='host' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<Tooltip content={_('displayAllStorages')}>
|
||||
<BlockLink to={`/home?s=$pool:${pool.id}&t=SR`}>
|
||||
<h2>
|
||||
{srs.length}x <Icon icon='sr' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<Tooltip content={_('displayAllVMs')}>
|
||||
<BlockLink to={`/home?s=$pool:${pool.id}`}>
|
||||
<h2>
|
||||
{nVms}x <Icon icon='vm' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>{_('poolTitleRamUsage')}</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col smallOffset={1} mediumSize={10}>
|
||||
<Usage total={sumBy(hosts, 'memory.size')}>
|
||||
{map(hosts, host => (
|
||||
<UsageElement
|
||||
tooltip={`${host.name_label} (${formatSizeShort(host.memory.usage)})`}
|
||||
key={host.id}
|
||||
value={host.memory.usage}
|
||||
href={`#/hosts/${host.id}`}
|
||||
export default decorate([
|
||||
provideState({
|
||||
computed: {
|
||||
areHostsVersionsEqual: ({ areHostsVersionsEqualByPool }, { pool }) => areHostsVersionsEqualByPool[pool.id],
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
({ hosts, nVms, pool, srs, state: { areHostsVersionsEqual } }) => (
|
||||
<Container>
|
||||
<br />
|
||||
<Row className='text-xs-center'>
|
||||
<Col mediumSize={4}>
|
||||
<Tooltip content={_('displayAllHosts')}>
|
||||
<BlockLink to={`/home?s=$pool:${pool.id}&t=host`}>
|
||||
<h2>
|
||||
{hosts.length}x <Icon icon='host' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<Tooltip content={_('displayAllStorages')}>
|
||||
<BlockLink to={`/home?s=$pool:${pool.id}&t=SR`}>
|
||||
<h2>
|
||||
{srs.length}x <Icon icon='sr' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<Tooltip content={_('displayAllVMs')}>
|
||||
<BlockLink to={`/home?s=$pool:${pool.id}`}>
|
||||
<h2>
|
||||
{nVms}x <Icon icon='vm' size='lg' />
|
||||
</h2>
|
||||
</BlockLink>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>{_('poolTitleRamUsage')}</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col smallOffset={1} mediumSize={10}>
|
||||
<Usage total={sumBy(hosts, 'memory.size')}>
|
||||
{map(hosts, host => (
|
||||
<UsageElement
|
||||
tooltip={`${host.name_label} (${formatSizeShort(host.memory.usage)})`}
|
||||
key={host.id}
|
||||
value={host.memory.usage}
|
||||
href={`#/hosts/${host.id}`}
|
||||
/>
|
||||
))}
|
||||
</Usage>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>
|
||||
{_('poolRamUsage', {
|
||||
used: formatSizeShort(sumBy(hosts, 'memory.usage')),
|
||||
total: formatSizeShort(sumBy(hosts, 'memory.size')),
|
||||
free: formatSizeShort(sumBy(hosts, host => host.memory.size - host.memory.usage)),
|
||||
})}
|
||||
</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className='text-xs-center'>
|
||||
<Col>
|
||||
{_('poolMaster')}{' '}
|
||||
<Link to={`/hosts/${pool.master}`}>{find(hosts, host => host.id === pool.master).name_label}</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className='text-xs-center'>
|
||||
<Col>
|
||||
<h2>
|
||||
<HomeTags
|
||||
type='pool'
|
||||
labels={pool.tags}
|
||||
onDelete={tag => removeTag(pool.id, tag)}
|
||||
onAdd={tag => addTag(pool.id, tag)}
|
||||
/>
|
||||
))}
|
||||
</Usage>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className='text-xs-center'>
|
||||
<h5>
|
||||
{_('poolRamUsage', {
|
||||
used: formatSizeShort(sumBy(hosts, 'memory.usage')),
|
||||
total: formatSizeShort(sumBy(hosts, 'memory.size')),
|
||||
free: formatSizeShort(sumBy(hosts, host => host.memory.size - host.memory.usage)),
|
||||
})}
|
||||
</h5>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className='text-xs-center'>
|
||||
<Col>
|
||||
{_('poolMaster')}{' '}
|
||||
<Link to={`/hosts/${pool.master}`}>{find(hosts, host => host.id === pool.master).name_label}</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className='text-xs-center'>
|
||||
<Col>
|
||||
<h2>
|
||||
<HomeTags
|
||||
type='pool'
|
||||
labels={pool.tags}
|
||||
onDelete={tag => removeTag(pool.id, tag)}
|
||||
onAdd={tag => addTag(pool.id, tag)}
|
||||
/>
|
||||
</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
{!areHostsVersionsEqual && (
|
||||
<Row className='text-xs-center text-danger'>
|
||||
<Col>
|
||||
<p>
|
||||
<Icon icon='alarm' /> {_('notAllHostsHaveTheSameVersion', { pool: <Pool id={pool.id} link /> })}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Container>
|
||||
),
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user