committed by
Pierre Donias
parent
215432be6c
commit
16135b8e37
@@ -7,6 +7,8 @@
|
||||
|
||||
> Users must be able to say: “Nice enhancement, I'm eager to test it”
|
||||
|
||||
- [SR/General] Improve SR usage graph [#3608](https://github.com/vatesfr/xen-orchestra/issues/3608) (PR [#3830](https://github.com/vatesfr/xen-orchestra/pull/3830))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
> Users must be able to say: “I had this issue, happy to know it's fixed”
|
||||
|
||||
@@ -528,6 +528,7 @@ const TRANSFORMS = {
|
||||
|
||||
name_description: obj.name_description,
|
||||
name_label: obj.name_label,
|
||||
parent: obj.sm_config['vhd-parent'],
|
||||
size: +obj.virtual_size,
|
||||
snapshots: link(obj, 'snapshots'),
|
||||
tags: obj.tags,
|
||||
|
||||
@@ -684,6 +684,15 @@ const messages = {
|
||||
vmConsoleLabel: 'Console',
|
||||
backupLabel: 'Backup',
|
||||
|
||||
// ----- SR general tab -----
|
||||
baseCopyTooltip:
|
||||
'{n, number} base cop{n, plural, one {y} other {ies}} ({usage})',
|
||||
diskTooltip: '{name} ({usage})',
|
||||
snapshotsTooltip:
|
||||
'{n, number} snapshot{n, plural, one {} other {s}} ({usage})',
|
||||
vdiOnVmTooltip: '{name} ({usage}) on {vmName}',
|
||||
vdisTooltip: '{n, number} VDI{n, plural, one {} other {s}} ({usage})',
|
||||
|
||||
// ----- SR advanced tab -----
|
||||
|
||||
srUnhealthyVdiDepth: 'Depth',
|
||||
|
||||
@@ -1,25 +1,155 @@
|
||||
import _ from 'intl'
|
||||
import Component from 'base-component'
|
||||
import decorate from 'apply-decorators'
|
||||
import HomeTags from 'home-tags'
|
||||
import Icon from 'icon'
|
||||
import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
import Usage, { UsageElement } from 'usage'
|
||||
import { addTag, removeTag, getLicense } from 'xo'
|
||||
import { connectStore, formatSize } from 'utils'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import { createGetObject } from 'selectors'
|
||||
import { renderXoItemFromId } from 'render-xo-item'
|
||||
import {
|
||||
createCollectionWrapper,
|
||||
createGetObjectsOfType,
|
||||
createSelector,
|
||||
} from 'selectors'
|
||||
import {
|
||||
flatMap,
|
||||
forEach,
|
||||
groupBy,
|
||||
keyBy,
|
||||
map,
|
||||
mapValues,
|
||||
sumBy,
|
||||
uniq,
|
||||
} from 'lodash'
|
||||
import { get } from '@xen-orchestra/defined'
|
||||
import { injectState, provideState } from 'reaclette'
|
||||
|
||||
const UsageTooltip = connectStore(() => ({
|
||||
vbd: createGetObject((_, { vdi }) => vdi.$VBDs[0]),
|
||||
}))(({ vbd, vdi }) => (
|
||||
<span>
|
||||
{vdi.name_label} − {formatSize(vdi.usage)}
|
||||
{vbd != null && <br />}
|
||||
{vbd != null && renderXoItemFromId(vbd.VM)}
|
||||
</span>
|
||||
))
|
||||
const nestedUlStyle = { margin: '0.1em', marginLeft: '0.5em', padding: 0 }
|
||||
const ulStyle = { margin: 0, padding: 0 }
|
||||
|
||||
const UsageTooltip = decorate([
|
||||
connectStore(() => {
|
||||
const getVbds = createGetObjectsOfType('VBD').pick(
|
||||
createCollectionWrapper(
|
||||
createSelector(
|
||||
(_, { group }) => group.vdis,
|
||||
vdis => flatMap(vdis, '$VBDs')
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
const getVms = createGetObjectsOfType('VM').pick(
|
||||
createCollectionWrapper(
|
||||
createSelector(
|
||||
getVbds,
|
||||
vbds => map(vbds, 'VM')
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
vbds: getVbds,
|
||||
vms: getVms,
|
||||
}
|
||||
}),
|
||||
provideState({
|
||||
computed: {
|
||||
baseCopiesUsage: (_, { group: { baseCopies } }) =>
|
||||
formatSize(sumBy(baseCopies, 'usage')),
|
||||
vdis: (_, { group: { vdis } }) => keyBy(vdis, 'id'),
|
||||
vdisUsage: (_, { group: { vdis } }) => formatSize(sumBy(vdis, 'usage')),
|
||||
snapshotsUsage: (_, { group: { snapshots } }) =>
|
||||
formatSize(sumBy(snapshots, 'usage')),
|
||||
vmNamesByVdi: createCollectionWrapper(({ vdis }, { vbds, vms }) =>
|
||||
mapValues(vdis, vdi => get(() => vms[vbds[vdi.VBD].VM]))
|
||||
),
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
class extends Component {
|
||||
_getVdiTooltip = vdi => {
|
||||
const vmName = this.props.state.vmNamesByVdi[vdi.id]
|
||||
return (
|
||||
<span>
|
||||
{vmName === undefined
|
||||
? _('diskTooltip', {
|
||||
name: vdi.name_label,
|
||||
usage: formatSize(vdi.usage),
|
||||
})
|
||||
: _('vdiOnVmTooltip', {
|
||||
name: vdi.name_label,
|
||||
usage: formatSize(vdi.usage),
|
||||
vmName,
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { group, state } = this.props
|
||||
const {
|
||||
baseCopies,
|
||||
name_label: name,
|
||||
snapshots,
|
||||
type,
|
||||
usage,
|
||||
vdis,
|
||||
} = group
|
||||
return (
|
||||
<div>
|
||||
{type === 'orphanedSnapshot' ? (
|
||||
<span>
|
||||
{_('diskTooltip', {
|
||||
name,
|
||||
usage: formatSize(usage),
|
||||
})}
|
||||
</span>
|
||||
) : baseCopies.length === 0 && snapshots.length === 0 ? (
|
||||
this._getVdiTooltip(vdis[0])
|
||||
) : (
|
||||
<ul style={ulStyle}>
|
||||
<li>
|
||||
{_('baseCopyTooltip', {
|
||||
n: baseCopies.length,
|
||||
usage: state.baseCopiesUsage,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{_('snapshotsTooltip', {
|
||||
n: snapshots.length,
|
||||
usage: state.snapshotsUsage,
|
||||
})}
|
||||
<ul style={nestedUlStyle}>
|
||||
{snapshots.map(snapshot => (
|
||||
<li key={snapshot.id}>
|
||||
{_('diskTooltip', {
|
||||
name: snapshot.name_label,
|
||||
usage: formatSize(snapshot.usage),
|
||||
})}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
{_('vdisTooltip', {
|
||||
n: vdis.length,
|
||||
usage: state.vdisUsage,
|
||||
})}
|
||||
<ul style={nestedUlStyle}>
|
||||
{vdis.map(vdi => (
|
||||
<li key={vdi.id}>{this._getVdiTooltip(vdi)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
])
|
||||
|
||||
export default class TabGeneral extends Component {
|
||||
componentDidMount() {
|
||||
@@ -32,9 +162,88 @@ export default class TabGeneral extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { sr, vdis, vdiSnapshots, unmanagedVdis } = this.props
|
||||
_getDiskGroups = createSelector(
|
||||
() => this.props.vdis,
|
||||
() => this.props.vdiSnapshots,
|
||||
() => this.props.unmanagedVdis,
|
||||
(vdis, vdiSnapshots, unmanagedVdis) => {
|
||||
const groups = []
|
||||
const snapshotsByVdi = groupBy(vdiSnapshots, '$snapshot_of')
|
||||
const unmanagedVdisById = keyBy(unmanagedVdis, 'id')
|
||||
|
||||
let orphanedVdiSnapshots
|
||||
if ((orphanedVdiSnapshots = snapshotsByVdi[undefined]) !== undefined) {
|
||||
groups.push(
|
||||
...orphanedVdiSnapshots.map(snapshot => ({
|
||||
id: snapshot.id,
|
||||
name_label: snapshot.name_label,
|
||||
usage: snapshot.usage,
|
||||
type: 'orphanedSnapshot',
|
||||
}))
|
||||
)
|
||||
}
|
||||
// search root base copy for each VDI
|
||||
const vdisInfo = vdis.map(({ id, parent, name_label, usage }) => {
|
||||
const baseCopies = new Set()
|
||||
let baseCopy
|
||||
let root = id
|
||||
while ((baseCopy = unmanagedVdisById[parent]) !== undefined) {
|
||||
root = baseCopy.id
|
||||
parent = baseCopy.parent
|
||||
baseCopies.add(baseCopy)
|
||||
}
|
||||
let snapshots
|
||||
if ((snapshots = snapshotsByVdi[id]) !== undefined) {
|
||||
// snapshot can have base copy without active VDI
|
||||
snapshots.forEach(({ parent }) => {
|
||||
while (
|
||||
(baseCopy = unmanagedVdisById[parent]) !== undefined &&
|
||||
!baseCopies.has(baseCopy)
|
||||
) {
|
||||
parent = baseCopy.parent
|
||||
baseCopies.add(baseCopy)
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
baseCopies,
|
||||
id,
|
||||
name_label,
|
||||
root,
|
||||
snapshots: snapshots === undefined ? [] : snapshots,
|
||||
usage,
|
||||
}
|
||||
})
|
||||
// group VDIs by their root base copy.
|
||||
const vdisByRoot = groupBy(vdisInfo, 'root')
|
||||
|
||||
// group collection of VDIs and their snapshots and base copies.
|
||||
forEach(vdisByRoot, vdis => {
|
||||
let baseCopies = []
|
||||
let snapshots = []
|
||||
let vdisUsage = 0
|
||||
vdis.forEach(vdi => {
|
||||
vdisUsage += vdi.usage
|
||||
baseCopies = baseCopies.concat(...vdi.baseCopies)
|
||||
snapshots = snapshots.concat(vdi.snapshots)
|
||||
})
|
||||
baseCopies = uniq(baseCopies)
|
||||
snapshots = uniq(snapshots)
|
||||
groups.push({
|
||||
id: vdis[0].id,
|
||||
vdis,
|
||||
baseCopies,
|
||||
usage:
|
||||
vdisUsage + sumBy(baseCopies, 'usage') + sumBy(snapshots, 'usage'),
|
||||
snapshots,
|
||||
})
|
||||
})
|
||||
return groups
|
||||
}
|
||||
)
|
||||
|
||||
render() {
|
||||
const { sr } = this.props
|
||||
return (
|
||||
<Container>
|
||||
<Row className='text-xs-center'>
|
||||
@@ -68,28 +277,13 @@ export default class TabGeneral extends Component {
|
||||
</Row>
|
||||
<Row>
|
||||
<Col smallOffset={1} mediumSize={10}>
|
||||
<Usage total={sr.size}>
|
||||
{map(unmanagedVdis, vdi => (
|
||||
<Usage total={sr.size} type='disk'>
|
||||
{this._getDiskGroups().map(group => (
|
||||
<UsageElement
|
||||
highlight
|
||||
key={vdi.id}
|
||||
tooltip={<UsageTooltip vdi={vdi} />}
|
||||
value={vdi.usage}
|
||||
/>
|
||||
))}
|
||||
{map(vdis, vdi => (
|
||||
<UsageElement
|
||||
key={vdi.id}
|
||||
tooltip={<UsageTooltip vdi={vdi} />}
|
||||
value={vdi.usage}
|
||||
/>
|
||||
))}
|
||||
{map(vdiSnapshots, vdi => (
|
||||
<UsageElement
|
||||
highlight
|
||||
key={vdi.id}
|
||||
tooltip={<UsageTooltip vdi={vdi} />}
|
||||
value={vdi.usage}
|
||||
highlight={group.type === 'orphanedSnapshot'}
|
||||
key={group.id}
|
||||
tooltip={<UsageTooltip group={group} />}
|
||||
value={group.usage}
|
||||
/>
|
||||
))}
|
||||
</Usage>
|
||||
|
||||
Reference in New Issue
Block a user