committed by
Julien Fontanet
parent
34006bcbf6
commit
e23b8a6891
@@ -142,7 +142,14 @@ export default class SortedTable extends Component {
|
||||
createMatcher
|
||||
)
|
||||
),
|
||||
() => this._getSelectedColumn().sortCriteria,
|
||||
createSelector(
|
||||
() => this._getSelectedColumn().sortCriteria,
|
||||
() => this.props.userData,
|
||||
(sortCriteria, userData) =>
|
||||
(typeof sortCriteria === 'function')
|
||||
? object => sortCriteria(object, userData)
|
||||
: sortCriteria
|
||||
),
|
||||
() => this.state.sortOrder
|
||||
)
|
||||
|
||||
|
||||
4
src/xo-app/dashboard/overview/index.css
Normal file
4
src/xo-app/dashboard/overview/index.css
Normal file
@@ -0,0 +1,4 @@
|
||||
.bigCardContent {
|
||||
font-size: 4em;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
import _ from 'intl'
|
||||
import ActionButton from 'action-button'
|
||||
import ChartistGraph from 'react-chartist'
|
||||
import Component from 'base-component'
|
||||
import forEach from 'lodash/forEach'
|
||||
import Icon from 'icon'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import map from 'lodash/map'
|
||||
import Upgrade from 'xoa-upgrade'
|
||||
import React, { Component } from 'react'
|
||||
import React from 'react'
|
||||
import SortedTable from 'sorted-table'
|
||||
import { ButtonGroup } from 'react-bootstrap-4/lib'
|
||||
import { Card, CardBlock, CardHeader } from 'card'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import {
|
||||
createCollectionWrapper,
|
||||
createCounter,
|
||||
createFilter,
|
||||
createGetObjectsOfType,
|
||||
createSelector,
|
||||
createTop
|
||||
@@ -18,10 +25,164 @@ import {
|
||||
formatSize
|
||||
} from 'utils'
|
||||
import {
|
||||
getHostMissingPatches,
|
||||
installAllHostPatches,
|
||||
isSrWritable,
|
||||
subscribeUsers
|
||||
} from 'xo'
|
||||
|
||||
import styles from './index.css'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const MISSING_PATCHES_COLUMNS = [
|
||||
{
|
||||
name: _('srPool'),
|
||||
itemRenderer: (host, { pools }) => pools[host.$pool].name_label,
|
||||
sortCriteria: (host, { pools }) => pools[host.$pool].name_label
|
||||
},
|
||||
{
|
||||
name: _('srHost'),
|
||||
itemRenderer: host => host.name_label,
|
||||
sortCriteria: host => host.name_label
|
||||
},
|
||||
{
|
||||
name: _('hostDescription'),
|
||||
itemRenderer: host => host.name_description,
|
||||
sortCriteria: host => host.name_description
|
||||
},
|
||||
{
|
||||
name: _('hostMissingPatches'),
|
||||
itemRenderer: (host, { missingPatches }) => missingPatches[host.id],
|
||||
sortCriteria: (host, { missingPatches }) => missingPatches[host.id]
|
||||
},
|
||||
{
|
||||
name: _('patchUpdateButton'),
|
||||
itemRenderer: host => (
|
||||
<ActionButton
|
||||
btnStyle='primary'
|
||||
handler={installAllHostPatches}
|
||||
handlerParam={host}
|
||||
icon='host-patch-update'
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@connectStore(() => {
|
||||
const getPools = createGetObjectsOfType('pool')
|
||||
const getHosts = createGetObjectsOfType('host').sort()
|
||||
|
||||
return {
|
||||
pools: getPools,
|
||||
hosts: getHosts
|
||||
}
|
||||
})
|
||||
class MissingPatchesPanel extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state.missingPatches = {}
|
||||
}
|
||||
|
||||
_getHosts = createFilter(
|
||||
() => this.props.hosts,
|
||||
() => this.state.missingPatches,
|
||||
[ (host, missingPatches) => missingPatches[host.id] ]
|
||||
)
|
||||
|
||||
_refreshMissingPatches = async () => {
|
||||
const missingPatches = {}
|
||||
await Promise.all(
|
||||
map(this.props.hosts, host => (
|
||||
getHostMissingPatches(host).then(patches => {
|
||||
missingPatches[host.id] = patches.length
|
||||
})
|
||||
))
|
||||
)
|
||||
this.setState({ missingPatches })
|
||||
}
|
||||
|
||||
_installAllMissingPatches = () => (
|
||||
Promise.all(
|
||||
map(this._getHosts(), installAllHostPatches)
|
||||
)
|
||||
)
|
||||
|
||||
componentWillMount () {
|
||||
this._refreshMissingPatches()
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
forEach(nextProps.hosts, (host, push) => {
|
||||
const { id } = host
|
||||
|
||||
if (this.state.missingPatches[id] !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({
|
||||
missingPatches: {
|
||||
...this.state.missingPatches,
|
||||
[id]: 0
|
||||
}
|
||||
})
|
||||
|
||||
getHostMissingPatches(host).then(patches => {
|
||||
this.setState({
|
||||
missingPatches: {
|
||||
...this.state.missingPatches,
|
||||
[id]: patches.length
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const hosts = this._getHosts()
|
||||
const noPatches = isEmpty(hosts)
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='host-patch-update' /> {_('update')}
|
||||
<ButtonGroup className='pull-right'>
|
||||
<ActionButton
|
||||
btnStyle='secondary'
|
||||
handler={this._refreshMissingPatches}
|
||||
icon='refresh'
|
||||
/>
|
||||
<ActionButton
|
||||
btnStyle='primary'
|
||||
disabled={noPatches}
|
||||
handler={this._installAllMissingPatches}
|
||||
icon='host-patch-update'
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{!noPatches
|
||||
? (
|
||||
<SortedTable
|
||||
collection={hosts}
|
||||
columns={MISSING_PATCHES_COLUMNS}
|
||||
userData={{
|
||||
missingPatches: this.state.missingPatches,
|
||||
pools: this.props.pools
|
||||
}}
|
||||
/>
|
||||
) : <p>{_('patchNothing')}</p>
|
||||
}
|
||||
</CardBlock>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@connectStore(() => {
|
||||
const getHosts = createGetObjectsOfType('host')
|
||||
const getVms = createGetObjectsOfType('VM')
|
||||
@@ -137,43 +298,43 @@ export default class Overview extends Component {
|
||||
? <Container>
|
||||
<Row>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='pool' /> {_('poolPanel', { pools: this.props.nPools })}
|
||||
</div>
|
||||
<div className='card-block-dashboard'>
|
||||
<p>{this.props.nPools}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<p className={styles.bigCardContent}>{this.props.nPools}</p>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='host' /> {_('hostPanel', { hosts: this.props.nHosts })}
|
||||
</div>
|
||||
<div className='card-block-dashboard'>
|
||||
<p>{this.props.nHosts}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<p className={styles.bigCardContent}>{this.props.nHosts}</p>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='vm' /> {_('vmPanel', { vms: this.props.nVms })}
|
||||
</div>
|
||||
<div className='card-block-dashboard'>
|
||||
<p>{this.props.nVms}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<p className={styles.bigCardContent}>{this.props.nVms}</p>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='memory' /> {_('memoryStatePanel')}
|
||||
</div>
|
||||
<div className='card-block'>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<ChartistGraph
|
||||
data={{
|
||||
labels: ['Used Memory', 'Total Memory'],
|
||||
@@ -182,15 +343,15 @@ export default class Overview extends Component {
|
||||
options={{ donut: true, donutWidth: 40, showLabel: false }}
|
||||
type='Pie' />
|
||||
<p className='text-xs-center'>{formatSize(this.props.hostMetrics.memoryUsage)} ({_('ofUsage')} {formatSize(this.props.hostMetrics.memoryTotal)})</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='cpu' /> {_('cpuStatePanel')}
|
||||
</div>
|
||||
<div className='card-block'>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<div className='ct-chart'>
|
||||
<ChartistGraph
|
||||
data={{
|
||||
@@ -201,15 +362,15 @@ export default class Overview extends Component {
|
||||
type='Bar' />
|
||||
<p className='text-xs-center'>{this.props.vmMetrics.vcpus} vCPUS ({_('ofUsage')} {this.props.hostMetrics.cpus} CPUs)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='disk' /> {_('srUsageStatePanel')}
|
||||
</div>
|
||||
<div className='card-block'>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<div className='ct-chart'>
|
||||
<ChartistGraph
|
||||
data={{
|
||||
@@ -220,49 +381,51 @@ export default class Overview extends Component {
|
||||
type='Pie' />
|
||||
<p className='text-xs-center'>{formatSize(this.props.srMetrics.srUsage)} ({_('ofUsage')} {formatSize(this.props.srMetrics.srTotal)})</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='alarm' /> {_('alarmMessage')}
|
||||
</div>
|
||||
<div className='card-block-dashboard'>
|
||||
<p className={this.props.nAlarmMessages > 0 ? 'text-warning' : ''}>{this.props.nAlarmMessages}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<p className={`${styles.bigCardContent} ${this.props.nAlarmMessages > 0 ? 'text-warning' : ''}`}>
|
||||
{this.props.nAlarmMessages}
|
||||
</p>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='task' /> {_('taskStatePanel')}
|
||||
</div>
|
||||
<div className='card-block-dashboard'>
|
||||
<p>{this.props.nTasks}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<p className={styles.bigCardContent}>{this.props.nTasks}</p>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='user' /> {_('usersStatePanel')}
|
||||
</div>
|
||||
<div className='card-block-dashboard'>
|
||||
<p>{nUsers}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<p className={styles.bigCardContent}>{nUsers}</p>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col mediumSize={4}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='vm-force-shutdown' /> {_('vmStatePanel')}
|
||||
</div>
|
||||
<div className='card-block'>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<ChartistGraph
|
||||
data={{
|
||||
labels: ['Running', 'Halted', 'Other'],
|
||||
@@ -271,15 +434,15 @@ export default class Overview extends Component {
|
||||
options={{ showLabel: false }}
|
||||
type='Pie' />
|
||||
<p className='text-xs-center'>{this.props.vmMetrics.running} running ({this.props.vmMetrics.halted} halted)</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col mediumSize={8}>
|
||||
<div className='card-dashboard'>
|
||||
<div className='card-header-dashboard'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='disk' /> {_('srTopUsageStatePanel')}
|
||||
</div>
|
||||
<div className='card-block'>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
<ChartistGraph
|
||||
style={{strokeWidth: '30px'}}
|
||||
data={{
|
||||
@@ -288,8 +451,13 @@ export default class Overview extends Component {
|
||||
}}
|
||||
options={{ showLabel: false, showGrid: false, distributeSeries: true, high: 100 }}
|
||||
type='Bar' />
|
||||
</div>
|
||||
</div>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<MissingPatchesPanel />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
|
||||
Reference in New Issue
Block a user