feat(dashboard): allow the user in self service to see its quotas (#2268)
Fixes #1538
This commit is contained in:
parent
c89c7dab60
commit
add10ea556
@ -904,12 +904,22 @@ const messages = {
|
|||||||
hostPanel: 'Host{hosts, plural, one {} other {s}}',
|
hostPanel: 'Host{hosts, plural, one {} other {s}}',
|
||||||
vmPanel: 'VM{vms, plural, one {} other {s}}',
|
vmPanel: 'VM{vms, plural, one {} other {s}}',
|
||||||
memoryStatePanel: 'RAM Usage:',
|
memoryStatePanel: 'RAM Usage:',
|
||||||
|
usedMemory: 'Used Memory',
|
||||||
|
totalMemory: 'Total Memory',
|
||||||
|
totalCpus: 'CPUs Total',
|
||||||
|
usedVCpus: 'Used vCPUs',
|
||||||
|
usedSpace: 'Used Space',
|
||||||
|
totalSpace: 'Total Space',
|
||||||
cpuStatePanel: 'CPUs Usage',
|
cpuStatePanel: 'CPUs Usage',
|
||||||
vmStatePanel: 'VMs Power state',
|
vmStatePanel: 'VMs Power state',
|
||||||
|
vmStateHalted: 'Halted',
|
||||||
|
vmStateOther: 'Other',
|
||||||
|
vmStateRunning: 'Running',
|
||||||
taskStatePanel: 'Pending tasks',
|
taskStatePanel: 'Pending tasks',
|
||||||
usersStatePanel: 'Users',
|
usersStatePanel: 'Users',
|
||||||
srStatePanel: 'Storage state',
|
srStatePanel: 'Storage state',
|
||||||
ofUsage: '{usage} (of {total})',
|
ofUsage: '{usage} (of {total})',
|
||||||
|
ofCpusUsage: '{nVcpus, number} vCPU{nVcpus, plural, one {} other {s}} (of {nCpus, number} CPU{nCpus, plural, one {} other {s}})',
|
||||||
noSrs: 'No storage',
|
noSrs: 'No storage',
|
||||||
srName: 'Name',
|
srName: 'Name',
|
||||||
srPool: 'Pool',
|
srPool: 'Pool',
|
||||||
@ -921,6 +931,7 @@ const messages = {
|
|||||||
srFree: 'free',
|
srFree: 'free',
|
||||||
srUsageStatePanel: 'Storage Usage',
|
srUsageStatePanel: 'Storage Usage',
|
||||||
srTopUsageStatePanel: 'Top 5 SR Usage (in %)',
|
srTopUsageStatePanel: 'Top 5 SR Usage (in %)',
|
||||||
|
notEnoughPermissionsError: 'Not enough permissions!',
|
||||||
vmsStates: '{running, number} running ({halted, number} halted)',
|
vmsStates: '{running, number} running ({halted, number} halted)',
|
||||||
dashboardStatsButtonRemoveAll: 'Clear selection',
|
dashboardStatsButtonRemoveAll: 'Clear selection',
|
||||||
dashboardStatsButtonAddAllHost: 'Add all hosts',
|
dashboardStatsButtonAddAllHost: 'Add all hosts',
|
||||||
@ -1048,9 +1059,10 @@ const messages = {
|
|||||||
ipPool: 'IP pool',
|
ipPool: 'IP pool',
|
||||||
quantity: 'Quantity',
|
quantity: 'Quantity',
|
||||||
noResourceSetLimits: 'No limits.',
|
noResourceSetLimits: 'No limits.',
|
||||||
totalResource: 'Total:',
|
|
||||||
remainingResource: 'Remaining:',
|
remainingResource: 'Remaining:',
|
||||||
usedResource: 'Used:',
|
usedResourceLabel: 'Used',
|
||||||
|
availableResourceLabel: 'Available',
|
||||||
|
resourceSetQuota: 'Used: {usage} (Total: {total})',
|
||||||
resourceSetNew: 'New',
|
resourceSetNew: 'New',
|
||||||
|
|
||||||
// ---- VM import ---
|
// ---- VM import ---
|
||||||
|
127
src/common/resource-set-quotas.js
Normal file
127
src/common/resource-set-quotas.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import _, { messages } from 'intl'
|
||||||
|
import ChartistGraph from 'react-chartist'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React from 'react'
|
||||||
|
import { Card, CardBlock, CardHeader } from 'card'
|
||||||
|
import { Container, Row, Col } from 'grid'
|
||||||
|
import { forEach, map } from 'lodash'
|
||||||
|
import { injectIntl } from 'react-intl'
|
||||||
|
|
||||||
|
import Component from './base-component'
|
||||||
|
import Icon from './icon'
|
||||||
|
import { createSelector } from './selectors'
|
||||||
|
import { formatSize } from './utils'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const RESOURCES = ['disk', 'memory', 'cpus']
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
@injectIntl
|
||||||
|
export default class ResourceSetQuotas extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
limits: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
_getQuotas = createSelector(
|
||||||
|
() => this.props.limits,
|
||||||
|
limits => {
|
||||||
|
const quotas = {}
|
||||||
|
|
||||||
|
forEach(RESOURCES, resource => {
|
||||||
|
if (limits[resource] != null) {
|
||||||
|
const { available, total } = limits[resource]
|
||||||
|
quotas[resource] = {
|
||||||
|
available,
|
||||||
|
total,
|
||||||
|
usage: total - available,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return quotas
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { intl: { formatMessage } } = this.props
|
||||||
|
const labels = [
|
||||||
|
formatMessage(messages.availableResourceLabel),
|
||||||
|
formatMessage(messages.usedResourceLabel),
|
||||||
|
]
|
||||||
|
const { cpus, disk, memory } = this._getQuotas()
|
||||||
|
const quotas = [
|
||||||
|
{
|
||||||
|
header: (
|
||||||
|
<span>
|
||||||
|
<Icon icon='cpu' /> {_('cpuStatePanel')}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
validFormat: true,
|
||||||
|
quota: cpus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: (
|
||||||
|
<span>
|
||||||
|
<Icon icon='memory' /> {_('memoryStatePanel')}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
validFormat: false,
|
||||||
|
quota: memory,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: (
|
||||||
|
<span>
|
||||||
|
<Icon icon='disk' /> {_('srUsageStatePanel')}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
validFormat: false,
|
||||||
|
quota: disk,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Row>
|
||||||
|
{map(quotas, ({ header, validFormat, quota }, key) => (
|
||||||
|
<Col key={key} mediumSize={4}>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>{header}</CardHeader>
|
||||||
|
<CardBlock className='text-center'>
|
||||||
|
{quota !== undefined ? (
|
||||||
|
<div>
|
||||||
|
<ChartistGraph
|
||||||
|
data={{
|
||||||
|
labels,
|
||||||
|
series: [quota.available, quota.usage],
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
donut: true,
|
||||||
|
donutWidth: 40,
|
||||||
|
showLabel: false,
|
||||||
|
}}
|
||||||
|
type='Pie'
|
||||||
|
/>
|
||||||
|
<p className='text-xs-center'>
|
||||||
|
{_('resourceSetQuota', {
|
||||||
|
total: validFormat
|
||||||
|
? quota.total.toString()
|
||||||
|
: formatSize(quota.total),
|
||||||
|
usage: validFormat
|
||||||
|
? quota.usage.toString()
|
||||||
|
: formatSize(quota.usage),
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className='text-xs-center display-1'>∞</p>
|
||||||
|
)}
|
||||||
|
</CardBlock>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
import _ from 'intl'
|
import _, { messages } from 'intl'
|
||||||
import ButtonGroup from 'button-group'
|
import ButtonGroup from 'button-group'
|
||||||
import ChartistGraph from 'react-chartist'
|
import ChartistGraph from 'react-chartist'
|
||||||
import Component from 'base-component'
|
import Component from 'base-component'
|
||||||
import forEach from 'lodash/forEach'
|
|
||||||
import Icon from 'icon'
|
|
||||||
import propTypes from 'prop-types-decorator'
|
|
||||||
import Link, { BlockLink } from 'link'
|
|
||||||
import map from 'lodash/map'
|
|
||||||
import HostsPatchesTable from 'hosts-patches-table'
|
import HostsPatchesTable from 'hosts-patches-table'
|
||||||
|
import Icon from 'icon'
|
||||||
|
import Link, { BlockLink } from 'link'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import size from 'lodash/size'
|
import ResourceSetQuotas from 'resource-set-quotas'
|
||||||
import Upgrade from 'xoa-upgrade'
|
import Upgrade from 'xoa-upgrade'
|
||||||
import { Card, CardBlock, CardHeader } from 'card'
|
import { Card, CardBlock, CardHeader } from 'card'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
|
import { forEach, isEmpty, map, size } from 'lodash'
|
||||||
|
import { injectIntl } from 'react-intl'
|
||||||
import {
|
import {
|
||||||
createCollectionWrapper,
|
createCollectionWrapper,
|
||||||
createCounter,
|
createCounter,
|
||||||
@ -22,17 +22,27 @@ import {
|
|||||||
createTop,
|
createTop,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
} from 'selectors'
|
} from 'selectors'
|
||||||
import { connectStore, formatSize } from 'utils'
|
import { addSubscriptions, connectStore, formatSize } from 'utils'
|
||||||
import { isSrWritable, subscribeUsers } from 'xo'
|
import {
|
||||||
|
isSrWritable,
|
||||||
|
subscribePermissions,
|
||||||
|
subscribeResourceSets,
|
||||||
|
subscribeUsers,
|
||||||
|
} from 'xo'
|
||||||
|
|
||||||
import styles from './index.css'
|
import styles from './index.css'
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
@propTypes({
|
const PIE_GRAPH_OPTIONS = { donut: true, donutWidth: 40, showLabel: false }
|
||||||
hosts: propTypes.object.isRequired,
|
|
||||||
})
|
// ===================================================================
|
||||||
|
|
||||||
class PatchesCard extends Component {
|
class PatchesCard extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
hosts: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
_getContainer = () => this.refs.container
|
_getContainer = () => this.refs.container
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -55,8 +65,6 @@ class PatchesCard extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===================================================================
|
|
||||||
|
|
||||||
@connectStore(() => {
|
@connectStore(() => {
|
||||||
const getHosts = createGetObjectsOfType('host')
|
const getHosts = createGetObjectsOfType('host')
|
||||||
const getVms = createGetObjectsOfType('VM')
|
const getVms = createGetObjectsOfType('VM')
|
||||||
@ -111,7 +119,6 @@ class PatchesCard extends Component {
|
|||||||
return {
|
return {
|
||||||
hostMetrics: getHostMetrics,
|
hostMetrics: getHostMetrics,
|
||||||
hosts: getHosts,
|
hosts: getHosts,
|
||||||
isAdmin,
|
|
||||||
nAlarmMessages: getNumberOfAlarmMessages,
|
nAlarmMessages: getNumberOfAlarmMessages,
|
||||||
nHosts: getNumberOfHosts,
|
nHosts: getNumberOfHosts,
|
||||||
nPools: getNumberOfPools,
|
nPools: getNumberOfPools,
|
||||||
@ -126,18 +133,22 @@ class PatchesCard extends Component {
|
|||||||
vmMetrics: getVmMetrics,
|
vmMetrics: getVmMetrics,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export default class Overview extends Component {
|
@injectIntl
|
||||||
|
class DefaultCard extends Component {
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
this.componentWillUnmount = subscribeUsers(users => {
|
this.componentWillUnmount = subscribeUsers(users => {
|
||||||
this.setState({ users })
|
this.setState({ users })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { props, state } = this
|
const { props, state } = this
|
||||||
const users = state && state.users
|
const users = state && state.users
|
||||||
const nUsers = size(users)
|
const nUsers = size(users)
|
||||||
|
|
||||||
return process.env.XOA_PLAN > 2 ? (
|
const { formatMessage } = props.intl
|
||||||
|
|
||||||
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Row>
|
<Row>
|
||||||
<Col mediumSize={4}>
|
<Col mediumSize={4}>
|
||||||
@ -186,14 +197,17 @@ export default class Overview extends Component {
|
|||||||
<CardBlock className='dashboardItem'>
|
<CardBlock className='dashboardItem'>
|
||||||
<ChartistGraph
|
<ChartistGraph
|
||||||
data={{
|
data={{
|
||||||
labels: ['Used Memory', 'Total Memory'],
|
labels: [
|
||||||
|
formatMessage(messages.usedMemory),
|
||||||
|
formatMessage(messages.totalMemory),
|
||||||
|
],
|
||||||
series: [
|
series: [
|
||||||
props.hostMetrics.memoryUsage,
|
props.hostMetrics.memoryUsage,
|
||||||
props.hostMetrics.memoryTotal -
|
props.hostMetrics.memoryTotal -
|
||||||
props.hostMetrics.memoryUsage,
|
props.hostMetrics.memoryUsage,
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
options={{ donut: true, donutWidth: 40, showLabel: false }}
|
options={PIE_GRAPH_OPTIONS}
|
||||||
type='Pie'
|
type='Pie'
|
||||||
/>
|
/>
|
||||||
<p className='text-xs-center'>
|
<p className='text-xs-center'>
|
||||||
@ -214,7 +228,10 @@ export default class Overview extends Component {
|
|||||||
<div className='ct-chart dashboardItem'>
|
<div className='ct-chart dashboardItem'>
|
||||||
<ChartistGraph
|
<ChartistGraph
|
||||||
data={{
|
data={{
|
||||||
labels: ['vCPUs', 'CPUs'],
|
labels: [
|
||||||
|
formatMessage(messages.usedVCpus),
|
||||||
|
formatMessage(messages.totalCpus),
|
||||||
|
],
|
||||||
series: [props.vmMetrics.vcpus, props.hostMetrics.cpus],
|
series: [props.vmMetrics.vcpus, props.hostMetrics.cpus],
|
||||||
}}
|
}}
|
||||||
options={{
|
options={{
|
||||||
@ -225,9 +242,9 @@ export default class Overview extends Component {
|
|||||||
type='Bar'
|
type='Bar'
|
||||||
/>
|
/>
|
||||||
<p className='text-xs-center'>
|
<p className='text-xs-center'>
|
||||||
{_('ofUsage', {
|
{_('ofCpusUsage', {
|
||||||
total: `${props.hostMetrics.cpus} CPUs`,
|
nCpus: props.hostMetrics.cpus,
|
||||||
usage: `${props.vmMetrics.vcpus} vCPUs`,
|
nVcpus: props.vmMetrics.vcpus,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -244,17 +261,16 @@ export default class Overview extends Component {
|
|||||||
<BlockLink to='/dashboard/health'>
|
<BlockLink to='/dashboard/health'>
|
||||||
<ChartistGraph
|
<ChartistGraph
|
||||||
data={{
|
data={{
|
||||||
labels: ['Used Space', 'Total Space'],
|
labels: [
|
||||||
|
formatMessage(messages.usedSpace),
|
||||||
|
formatMessage(messages.totalSpace),
|
||||||
|
],
|
||||||
series: [
|
series: [
|
||||||
props.srMetrics.srUsage,
|
props.srMetrics.srUsage,
|
||||||
props.srMetrics.srTotal - props.srMetrics.srUsage,
|
props.srMetrics.srTotal - props.srMetrics.srUsage,
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
options={{
|
options={PIE_GRAPH_OPTIONS}
|
||||||
donut: true,
|
|
||||||
donutWidth: 40,
|
|
||||||
showLabel: false,
|
|
||||||
}}
|
|
||||||
type='Pie'
|
type='Pie'
|
||||||
/>
|
/>
|
||||||
<p className='text-xs-center'>
|
<p className='text-xs-center'>
|
||||||
@ -326,7 +342,11 @@ export default class Overview extends Component {
|
|||||||
<BlockLink to='/home?t=VM'>
|
<BlockLink to='/home?t=VM'>
|
||||||
<ChartistGraph
|
<ChartistGraph
|
||||||
data={{
|
data={{
|
||||||
labels: ['Running', 'Halted', 'Other'],
|
labels: [
|
||||||
|
formatMessage(messages.vmStateRunning),
|
||||||
|
formatMessage(messages.vmStateHalted),
|
||||||
|
formatMessage(messages.vmStateOther),
|
||||||
|
],
|
||||||
series: [
|
series: [
|
||||||
props.vmMetrics.running,
|
props.vmMetrics.running,
|
||||||
props.vmMetrics.halted,
|
props.vmMetrics.halted,
|
||||||
@ -381,10 +401,50 @@ export default class Overview extends Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
) : (
|
)
|
||||||
<Container>
|
}
|
||||||
<Upgrade place='dashboard' available={3} />
|
}
|
||||||
</Container>
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
@addSubscriptions({
|
||||||
|
resourceSets: subscribeResourceSets,
|
||||||
|
permissions: subscribePermissions,
|
||||||
|
})
|
||||||
|
@connectStore({
|
||||||
|
isAdmin,
|
||||||
|
})
|
||||||
|
export default class Overview extends Component {
|
||||||
|
render () {
|
||||||
|
const { props } = this
|
||||||
|
const showResourceSets = !isEmpty(props.resourceSets) && !props.isAdmin
|
||||||
|
const authorized = !isEmpty(props.permissions) || props.isAdmin
|
||||||
|
|
||||||
|
if (!authorized && !showResourceSets) {
|
||||||
|
return <em>{_('notEnoughPermissionsError')}</em>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Upgrade place='dashboard' required={3}>
|
||||||
|
<Container>
|
||||||
|
{showResourceSets ? (
|
||||||
|
map(props.resourceSets, resourceSet => (
|
||||||
|
<Row key={resourceSet.id}>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<Icon icon='menu-self-service' /> {resourceSet.name}
|
||||||
|
</CardHeader>
|
||||||
|
<CardBlock>
|
||||||
|
<ResourceSetQuotas limits={resourceSet.limits} />
|
||||||
|
</CardBlock>
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<DefaultCard isAdmin={props.isAdmin} />
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
</Upgrade>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import _ from 'intl'
|
import _ from 'intl'
|
||||||
import ActionButton from 'action-button'
|
import ActionButton from 'action-button'
|
||||||
import ChartistGraph from 'react-chartist'
|
|
||||||
import Collapse from 'collapse'
|
import Collapse from 'collapse'
|
||||||
import Component from 'base-component'
|
import Component from 'base-component'
|
||||||
import defined from 'xo-defined'
|
import defined from 'xo-defined'
|
||||||
@ -15,13 +14,15 @@ import isEmpty from 'lodash/isEmpty'
|
|||||||
import keys from 'lodash/keys'
|
import keys from 'lodash/keys'
|
||||||
import map from 'lodash/map'
|
import map from 'lodash/map'
|
||||||
import mapKeys from 'lodash/mapKeys'
|
import mapKeys from 'lodash/mapKeys'
|
||||||
import propTypes from 'prop-types-decorator'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import remove from 'lodash/remove'
|
import remove from 'lodash/remove'
|
||||||
import renderXoItem from 'render-xo-item'
|
import renderXoItem from 'render-xo-item'
|
||||||
|
import ResourceSetQuotas from 'resource-set-quotas'
|
||||||
import Upgrade from 'xoa-upgrade'
|
import Upgrade from 'xoa-upgrade'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
import { createGetObjectsOfType, createSelector } from 'selectors'
|
import { createGetObjectsOfType, createSelector } from 'selectors'
|
||||||
|
import { injectIntl } from 'react-intl'
|
||||||
import { SizeInput } from 'form'
|
import { SizeInput } from 'form'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -36,13 +37,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
addSubscriptions,
|
addSubscriptions,
|
||||||
connectStore,
|
connectStore,
|
||||||
formatSize,
|
|
||||||
resolveIds,
|
resolveIds,
|
||||||
resolveResourceSets,
|
resolveResourceSets,
|
||||||
} from 'utils'
|
} from 'utils'
|
||||||
|
|
||||||
import { Card, CardBlock, CardHeader } from 'card'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SelectIpPool,
|
SelectIpPool,
|
||||||
SelectNetwork,
|
SelectNetwork,
|
||||||
@ -72,10 +70,7 @@ const HEADER = (
|
|||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
const Hosts = propTypes({
|
const Hosts = ({ eligibleHosts, excludedHosts }) => (
|
||||||
eligibleHosts: propTypes.array.isRequired,
|
|
||||||
excludedHosts: propTypes.array.isRequired,
|
|
||||||
})(({ eligibleHosts, excludedHosts }) => (
|
|
||||||
<div>
|
<div>
|
||||||
<Row>
|
<Row>
|
||||||
<Col mediumSize={6}>
|
<Col mediumSize={6}>
|
||||||
@ -117,14 +112,15 @@ const Hosts = propTypes({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
))
|
)
|
||||||
|
|
||||||
|
Hosts.propTypes = {
|
||||||
|
eligibleHosts: PropTypes.array.isRequired,
|
||||||
|
excludedHosts: PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
@propTypes({
|
|
||||||
onSave: propTypes.func,
|
|
||||||
resourceSet: propTypes.object,
|
|
||||||
})
|
|
||||||
@connectStore(() => {
|
@connectStore(() => {
|
||||||
const getHosts = createGetObjectsOfType('host').sort()
|
const getHosts = createGetObjectsOfType('host').sort()
|
||||||
const getHostsByPool = getHosts.groupBy('$pool')
|
const getHostsByPool = getHosts.groupBy('$pool')
|
||||||
@ -135,6 +131,11 @@ const Hosts = propTypes({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class Edit extends Component {
|
export class Edit extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
onSave: PropTypes.func,
|
||||||
|
resourceSet: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
@ -588,16 +589,12 @@ export class Edit extends Component {
|
|||||||
@addSubscriptions({
|
@addSubscriptions({
|
||||||
ipPools: subscribeIpPools,
|
ipPools: subscribeIpPools,
|
||||||
})
|
})
|
||||||
|
@injectIntl
|
||||||
class ResourceSet extends Component {
|
class ResourceSet extends Component {
|
||||||
_renderDisplay = () => {
|
_renderDisplay = () => {
|
||||||
const { resourceSet } = this.props
|
const { resourceSet } = this.props
|
||||||
const resolvedIpPools = mapKeys(this.props.ipPools, 'id')
|
const resolvedIpPools = mapKeys(this.props.ipPools, 'id')
|
||||||
const {
|
const { limits, ipPools, subjects, objectsByType } = resourceSet
|
||||||
limits: { cpus, disk, memory } = {},
|
|
||||||
ipPools,
|
|
||||||
subjects,
|
|
||||||
objectsByType,
|
|
||||||
} = resourceSet
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<li key='subjects' className='list-group-item'>
|
<li key='subjects' className='list-group-item'>
|
||||||
@ -614,16 +611,16 @@ class ResourceSet extends Component {
|
|||||||
<li key='ipPools' className='list-group-item'>
|
<li key='ipPools' className='list-group-item'>
|
||||||
{map(ipPools, pool => {
|
{map(ipPools, pool => {
|
||||||
const resolvedIpPool = resolvedIpPools[pool]
|
const resolvedIpPool = resolvedIpPools[pool]
|
||||||
const limits = get(resourceSet, `limits[ipPool:${pool}]`)
|
const ipPoolLimits = limits && get(limits, `[ipPool:${pool}]`)
|
||||||
const available = limits && limits.available
|
const available = ipPoolLimits && ipPoolLimits.available
|
||||||
const total = limits && limits.total
|
const total = ipPoolLimits && ipPoolLimits.total
|
||||||
return (
|
return (
|
||||||
<span className='mr-1'>
|
<span className='mr-1'>
|
||||||
{renderXoItem({
|
{renderXoItem({
|
||||||
name: resolvedIpPool && resolvedIpPool.name,
|
name: resolvedIpPool && resolvedIpPool.name,
|
||||||
type: 'ipPool',
|
type: 'ipPool',
|
||||||
})}
|
})}
|
||||||
{limits && (
|
{ipPoolLimits && (
|
||||||
<span>
|
<span>
|
||||||
{' '}
|
{' '}
|
||||||
({available}/{total})
|
({available}/{total})
|
||||||
@ -635,112 +632,7 @@ class ResourceSet extends Component {
|
|||||||
</li>
|
</li>
|
||||||
),
|
),
|
||||||
<li key='graphs' className='list-group-item'>
|
<li key='graphs' className='list-group-item'>
|
||||||
<Row>
|
<ResourceSetQuotas limits={limits} />
|
||||||
<Col mediumSize={4}>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<Icon icon='cpu' /> {_('resourceSetVcpus')}
|
|
||||||
</CardHeader>
|
|
||||||
<CardBlock className='text-center'>
|
|
||||||
{cpus ? (
|
|
||||||
<div>
|
|
||||||
<ChartistGraph
|
|
||||||
data={{
|
|
||||||
labels: ['Available', 'Used'],
|
|
||||||
series: [cpus.available, cpus.total - cpus.available],
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
donut: true,
|
|
||||||
donutWidth: 40,
|
|
||||||
showLabel: false,
|
|
||||||
}}
|
|
||||||
type='Pie'
|
|
||||||
/>
|
|
||||||
<p className='text-xs-center'>
|
|
||||||
{_('usedResource')} {cpus.total - cpus.available} ({_(
|
|
||||||
'totalResource'
|
|
||||||
)}{' '}
|
|
||||||
{cpus.total})
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className='text-xs-center display-1'>∞</p>
|
|
||||||
)}
|
|
||||||
</CardBlock>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
<Col mediumSize={4}>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<Icon icon='memory' /> {_('resourceSetMemory')}
|
|
||||||
</CardHeader>
|
|
||||||
<CardBlock className='text-center'>
|
|
||||||
{memory ? (
|
|
||||||
<div>
|
|
||||||
<ChartistGraph
|
|
||||||
data={{
|
|
||||||
labels: ['Available', 'Used'],
|
|
||||||
series: [
|
|
||||||
memory.available,
|
|
||||||
memory.total - memory.available,
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
donut: true,
|
|
||||||
donutWidth: 40,
|
|
||||||
showLabel: false,
|
|
||||||
}}
|
|
||||||
type='Pie'
|
|
||||||
/>
|
|
||||||
<p className='text-xs-center'>
|
|
||||||
{_('usedResource')}{' '}
|
|
||||||
{formatSize(memory.total - memory.available)} ({_(
|
|
||||||
'totalResource'
|
|
||||||
)}{' '}
|
|
||||||
{formatSize(memory.total)})
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className='text-xs-center display-1'>∞</p>
|
|
||||||
)}
|
|
||||||
</CardBlock>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
<Col mediumSize={4}>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<Icon icon='disk' /> {_('resourceSetStorage')}
|
|
||||||
</CardHeader>
|
|
||||||
<CardBlock>
|
|
||||||
{disk ? (
|
|
||||||
<div>
|
|
||||||
<ChartistGraph
|
|
||||||
data={{
|
|
||||||
labels: ['Available', 'Used'],
|
|
||||||
series: [disk.available, disk.total - disk.available],
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
donut: true,
|
|
||||||
donutWidth: 40,
|
|
||||||
showLabel: false,
|
|
||||||
}}
|
|
||||||
type='Pie'
|
|
||||||
/>
|
|
||||||
<p className='text-xs-center'>
|
|
||||||
{_('usedResource')}{' '}
|
|
||||||
{formatSize(disk.total - disk.available)} ({_(
|
|
||||||
'totalResource'
|
|
||||||
)}{' '}
|
|
||||||
{formatSize(disk.total)})
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className='text-xs-center display-1'>∞</p>
|
|
||||||
)}
|
|
||||||
</CardBlock>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</li>,
|
</li>,
|
||||||
<li key='actions' className='list-group-item text-xs-center'>
|
<li key='actions' className='list-group-item text-xs-center'>
|
||||||
<div className='btn-toolbar'>
|
<div className='btn-toolbar'>
|
||||||
|
Loading…
Reference in New Issue
Block a user