feat(xo-web/sr/tab-disks): display the active vdi of the basecopy (#5826)
See xoa-support#3446
This commit is contained in:
parent
05b978c568
commit
d7668acd9b
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
- [Netbox] Add information about a failed request to the error log to help better understand what happened [#5834](https://github.com/vatesfr/xen-orchestra/issues/5834) (PR [#5842](https://github.com/vatesfr/xen-orchestra/pull/5842))
|
- [Netbox] Add information about a failed request to the error log to help better understand what happened [#5834](https://github.com/vatesfr/xen-orchestra/issues/5834) (PR [#5842](https://github.com/vatesfr/xen-orchestra/pull/5842))
|
||||||
- [VM/console] Ability to rescan ISO SRs (PR [#5841](https://github.com/vatesfr/xen-orchestra/pull/5841))
|
- [VM/console] Ability to rescan ISO SRs (PR [#5841](https://github.com/vatesfr/xen-orchestra/pull/5841))
|
||||||
|
- [SR/disks] Display base copies' active VDIs (PR [#5826](https://github.com/vatesfr/xen-orchestra/pull/5826))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
@ -767,6 +767,11 @@ const messages = {
|
|||||||
srsForget: 'Forget SRs',
|
srsForget: 'Forget SRs',
|
||||||
srRemoveButton: 'Remove this SR',
|
srRemoveButton: 'Remove this SR',
|
||||||
srNoVdis: 'No VDIs in this storage',
|
srNoVdis: 'No VDIs in this storage',
|
||||||
|
|
||||||
|
// ----- SR disks tab -----
|
||||||
|
multipleActiveVdis: '{firstVdi} and {nVdis} more',
|
||||||
|
noActiveVdi: 'No active VDI',
|
||||||
|
|
||||||
// ----- Pool general -----
|
// ----- Pool general -----
|
||||||
poolTitleRamUsage: 'Pool RAM usage:',
|
poolTitleRamUsage: 'Pool RAM usage:',
|
||||||
poolRamUsage: '{used} used of {total} ({free} free)',
|
poolRamUsage: '{used} used of {total} ({free} free)',
|
||||||
|
@ -53,9 +53,7 @@ import TabXosan from './tab-xosan'
|
|||||||
|
|
||||||
const getVdis = createGetObjectsOfType('VDI').pick(getVdiIds).sort()
|
const getVdis = createGetObjectsOfType('VDI').pick(getVdiIds).sort()
|
||||||
const getVdiSnapshots = createGetObjectsOfType('VDI-snapshot').pick(getVdiIds).sort()
|
const getVdiSnapshots = createGetObjectsOfType('VDI-snapshot').pick(getVdiIds).sort()
|
||||||
const getUnmanagedVdis = createGetObjectsOfType('VDI-unmanaged')
|
const getUnmanagedVdis = createGetObjectsOfType('VDI-unmanaged').pick(createSelector(getSr, sr => sr.VDIs))
|
||||||
.pick(createSelector(getSr, sr => sr.VDIs))
|
|
||||||
.sort()
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -9,16 +9,16 @@ import Link from 'link'
|
|||||||
import MigrateVdiModalBody from 'xo/migrate-vdi-modal'
|
import MigrateVdiModalBody from 'xo/migrate-vdi-modal'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import renderXoItem from 'render-xo-item'
|
|
||||||
import SortedTable from 'sorted-table'
|
import SortedTable from 'sorted-table'
|
||||||
import TabButton from 'tab-button'
|
import TabButton from 'tab-button'
|
||||||
|
import renderXoItem, { Vdi } from 'render-xo-item'
|
||||||
import { confirm } from 'modal'
|
import { confirm } from 'modal'
|
||||||
import { injectIntl } from 'react-intl'
|
import { injectIntl } from 'react-intl'
|
||||||
import { Text } from 'editable'
|
import { Text } from 'editable'
|
||||||
import { SizeInput, Toggle } from 'form'
|
import { SizeInput, Toggle } from 'form'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
import { connectStore, formatSize, noop } from 'utils'
|
import { connectStore, formatSize, noop } from 'utils'
|
||||||
import { concat, every, groupBy, isEmpty, map, mapValues, pick, some } from 'lodash'
|
import { concat, every, groupBy, isEmpty, map, mapValues, pick, some, sortBy } from 'lodash'
|
||||||
import { createCollectionWrapper, createGetObjectsOfType, createSelector, getCheckPermissions } from 'selectors'
|
import { createCollectionWrapper, createGetObjectsOfType, createSelector, getCheckPermissions } from 'selectors'
|
||||||
import {
|
import {
|
||||||
connectVbd,
|
connectVbd,
|
||||||
@ -41,16 +41,39 @@ import { error } from 'notification'
|
|||||||
const COLUMNS = [
|
const COLUMNS = [
|
||||||
{
|
{
|
||||||
name: _('vdiNameLabel'),
|
name: _('vdiNameLabel'),
|
||||||
itemRenderer: vdi => (
|
itemRenderer: (vdi, { vdisByBaseCopy }) => {
|
||||||
<span>
|
const activeVdis = vdisByBaseCopy[vdi.id]
|
||||||
<Text value={vdi.name_label} onChange={value => editVdi(vdi, { name_label: value })} />{' '}
|
return (
|
||||||
{vdi.type === 'VDI-snapshot' && (
|
<span>
|
||||||
<span className='tag tag-info'>
|
<Text value={vdi.name_label} onChange={value => editVdi(vdi, { name_label: value })} />{' '}
|
||||||
<Icon icon='vm-snapshot' />
|
{vdi.type === 'VDI-snapshot' && (
|
||||||
</span>
|
<span className='tag tag-info'>
|
||||||
)}
|
<Icon icon='vm-snapshot' />
|
||||||
</span>
|
</span>
|
||||||
),
|
)}
|
||||||
|
{vdi.type === 'VDI-unmanaged' &&
|
||||||
|
(activeVdis !== undefined ? (
|
||||||
|
<span>
|
||||||
|
(
|
||||||
|
<Link
|
||||||
|
to={`/srs/${activeVdis[0].$SR}/disks?s=${encodeURIComponent(
|
||||||
|
`id:|(${activeVdis.map(activeVdi => activeVdi.id).join(' ')})`
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
{activeVdis.length > 1 ? (
|
||||||
|
_('multipleActiveVdis', { firstVdi: <Vdi id={activeVdis[0].id} />, nVdis: activeVdis.length - 1 })
|
||||||
|
) : (
|
||||||
|
<Vdi id={activeVdis[0].id} showSize />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>({_('noActiveVdi')})</span>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
sortCriteria: vdi => vdi.name_label,
|
sortCriteria: vdi => vdi.name_label,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -285,7 +308,7 @@ export default class SrDisks extends Component {
|
|||||||
() => this.props.vdis,
|
() => this.props.vdis,
|
||||||
() => this.props.vdiSnapshots,
|
() => this.props.vdiSnapshots,
|
||||||
() => this.props.unmanagedVdis,
|
() => this.props.unmanagedVdis,
|
||||||
concat
|
(vdis, vdiSnapshots, unmanagedVdis) => concat(vdis, vdiSnapshots, sortBy(unmanagedVdis, 'id'))
|
||||||
)
|
)
|
||||||
|
|
||||||
_getIsSrAdmin = createSelector(
|
_getIsSrAdmin = createSelector(
|
||||||
@ -349,6 +372,29 @@ export default class SrDisks extends Component {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
_getVdisByBaseCopy = createSelector(
|
||||||
|
() => this.props.vdis,
|
||||||
|
() => this.props.unmanagedVdis,
|
||||||
|
(vdis, unmanagedVdis) => {
|
||||||
|
const vdisByBaseCopy = {}
|
||||||
|
|
||||||
|
vdis.forEach(vdi => {
|
||||||
|
let baseCopy = unmanagedVdis[vdi.parent]
|
||||||
|
|
||||||
|
while (baseCopy !== undefined) {
|
||||||
|
const baseCopyId = baseCopy.id
|
||||||
|
|
||||||
|
if (vdisByBaseCopy[baseCopyId] === undefined) {
|
||||||
|
vdisByBaseCopy[baseCopyId] = []
|
||||||
|
}
|
||||||
|
vdisByBaseCopy[baseCopyId].push(vdi)
|
||||||
|
baseCopy = unmanagedVdis[baseCopy.parent]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return vdisByBaseCopy
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const vdis = this._getAllVdis()
|
const vdis = this._getAllVdis()
|
||||||
const { newDisk } = this.state
|
const { newDisk } = this.state
|
||||||
@ -383,6 +429,7 @@ export default class SrDisks extends Component {
|
|||||||
collection={vdis}
|
collection={vdis}
|
||||||
columns={COLUMNS}
|
columns={COLUMNS}
|
||||||
data-isVdiAttached={this._getIsVdiAttached()}
|
data-isVdiAttached={this._getIsVdiAttached()}
|
||||||
|
data-vdisByBaseCopy={this._getVdisByBaseCopy()}
|
||||||
defaultFilter='filterOnlyManaged'
|
defaultFilter='filterOnlyManaged'
|
||||||
filters={FILTERS}
|
filters={FILTERS}
|
||||||
groupedActions={GROUPED_ACTIONS}
|
groupedActions={GROUPED_ACTIONS}
|
||||||
|
@ -140,7 +140,6 @@ export default class TabGeneral extends Component {
|
|||||||
(vdis, vdiSnapshots, unmanagedVdis) => {
|
(vdis, vdiSnapshots, unmanagedVdis) => {
|
||||||
const groups = []
|
const groups = []
|
||||||
const snapshotsByVdi = groupBy(vdiSnapshots, '$snapshot_of')
|
const snapshotsByVdi = groupBy(vdiSnapshots, '$snapshot_of')
|
||||||
const unmanagedVdisById = keyBy(unmanagedVdis, 'id')
|
|
||||||
|
|
||||||
let orphanedVdiSnapshots
|
let orphanedVdiSnapshots
|
||||||
if ((orphanedVdiSnapshots = snapshotsByVdi[undefined]) !== undefined) {
|
if ((orphanedVdiSnapshots = snapshotsByVdi[undefined]) !== undefined) {
|
||||||
@ -158,7 +157,7 @@ export default class TabGeneral extends Component {
|
|||||||
const baseCopies = new Set()
|
const baseCopies = new Set()
|
||||||
let baseCopy
|
let baseCopy
|
||||||
let root = id
|
let root = id
|
||||||
while ((baseCopy = unmanagedVdisById[parent]) !== undefined) {
|
while ((baseCopy = unmanagedVdis[parent]) !== undefined) {
|
||||||
root = baseCopy.id
|
root = baseCopy.id
|
||||||
parent = baseCopy.parent
|
parent = baseCopy.parent
|
||||||
baseCopies.add(baseCopy)
|
baseCopies.add(baseCopy)
|
||||||
@ -167,7 +166,7 @@ export default class TabGeneral extends Component {
|
|||||||
if ((snapshots = snapshotsByVdi[id]) !== undefined) {
|
if ((snapshots = snapshotsByVdi[id]) !== undefined) {
|
||||||
// snapshot can have base copy without active VDI
|
// snapshot can have base copy without active VDI
|
||||||
snapshots.forEach(({ parent }) => {
|
snapshots.forEach(({ parent }) => {
|
||||||
while ((baseCopy = unmanagedVdisById[parent]) !== undefined && !baseCopies.has(baseCopy)) {
|
while ((baseCopy = unmanagedVdis[parent]) !== undefined && !baseCopies.has(baseCopy)) {
|
||||||
parent = baseCopy.parent
|
parent = baseCopy.parent
|
||||||
baseCopies.add(baseCopy)
|
baseCopies.add(baseCopy)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user