diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 0bd7fae40..cb9db9db6 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -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)) - [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 diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js index 1bb8a7dea..278b127d9 100644 --- a/packages/xo-web/src/common/intl/messages.js +++ b/packages/xo-web/src/common/intl/messages.js @@ -767,6 +767,11 @@ const messages = { srsForget: 'Forget SRs', srRemoveButton: 'Remove this SR', srNoVdis: 'No VDIs in this storage', + + // ----- SR disks tab ----- + multipleActiveVdis: '{firstVdi} and {nVdis} more', + noActiveVdi: 'No active VDI', + // ----- Pool general ----- poolTitleRamUsage: 'Pool RAM usage:', poolRamUsage: '{used} used of {total} ({free} free)', diff --git a/packages/xo-web/src/xo-app/sr/index.js b/packages/xo-web/src/xo-app/sr/index.js index df4c169c8..2a5b62318 100644 --- a/packages/xo-web/src/xo-app/sr/index.js +++ b/packages/xo-web/src/xo-app/sr/index.js @@ -53,9 +53,7 @@ import TabXosan from './tab-xosan' const getVdis = createGetObjectsOfType('VDI').pick(getVdiIds).sort() const getVdiSnapshots = createGetObjectsOfType('VDI-snapshot').pick(getVdiIds).sort() - const getUnmanagedVdis = createGetObjectsOfType('VDI-unmanaged') - .pick(createSelector(getSr, sr => sr.VDIs)) - .sort() + const getUnmanagedVdis = createGetObjectsOfType('VDI-unmanaged').pick(createSelector(getSr, sr => sr.VDIs)) // ----------------------------------------------------------------- diff --git a/packages/xo-web/src/xo-app/sr/tab-disks.js b/packages/xo-web/src/xo-app/sr/tab-disks.js index 74102e98d..cc868de47 100644 --- a/packages/xo-web/src/xo-app/sr/tab-disks.js +++ b/packages/xo-web/src/xo-app/sr/tab-disks.js @@ -9,16 +9,16 @@ import Link from 'link' import MigrateVdiModalBody from 'xo/migrate-vdi-modal' import PropTypes from 'prop-types' import React from 'react' -import renderXoItem from 'render-xo-item' import SortedTable from 'sorted-table' import TabButton from 'tab-button' +import renderXoItem, { Vdi } from 'render-xo-item' import { confirm } from 'modal' import { injectIntl } from 'react-intl' import { Text } from 'editable' import { SizeInput, Toggle } from 'form' import { Container, Row, Col } from 'grid' 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 { connectVbd, @@ -41,16 +41,39 @@ import { error } from 'notification' const COLUMNS = [ { name: _('vdiNameLabel'), - itemRenderer: vdi => ( - - editVdi(vdi, { name_label: value })} />{' '} - {vdi.type === 'VDI-snapshot' && ( - - - - )} - - ), + itemRenderer: (vdi, { vdisByBaseCopy }) => { + const activeVdis = vdisByBaseCopy[vdi.id] + return ( + + editVdi(vdi, { name_label: value })} />{' '} + {vdi.type === 'VDI-snapshot' && ( + + + + )} + {vdi.type === 'VDI-unmanaged' && + (activeVdis !== undefined ? ( + + ( + activeVdi.id).join(' ')})` + )}`} + > + {activeVdis.length > 1 ? ( + _('multipleActiveVdis', { firstVdi: , nVdis: activeVdis.length - 1 }) + ) : ( + + )} + + ) + + ) : ( + ({_('noActiveVdi')}) + ))} + + ) + }, sortCriteria: vdi => vdi.name_label, }, { @@ -285,7 +308,7 @@ export default class SrDisks extends Component { () => this.props.vdis, () => this.props.vdiSnapshots, () => this.props.unmanagedVdis, - concat + (vdis, vdiSnapshots, unmanagedVdis) => concat(vdis, vdiSnapshots, sortBy(unmanagedVdis, 'id')) ) _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() { const vdis = this._getAllVdis() const { newDisk } = this.state @@ -383,6 +429,7 @@ export default class SrDisks extends Component { collection={vdis} columns={COLUMNS} data-isVdiAttached={this._getIsVdiAttached()} + data-vdisByBaseCopy={this._getVdisByBaseCopy()} defaultFilter='filterOnlyManaged' filters={FILTERS} groupedActions={GROUPED_ACTIONS} diff --git a/packages/xo-web/src/xo-app/sr/tab-general.js b/packages/xo-web/src/xo-app/sr/tab-general.js index 8fc3e181f..4d91ce97d 100644 --- a/packages/xo-web/src/xo-app/sr/tab-general.js +++ b/packages/xo-web/src/xo-app/sr/tab-general.js @@ -140,7 +140,6 @@ export default class TabGeneral extends Component { (vdis, vdiSnapshots, unmanagedVdis) => { const groups = [] const snapshotsByVdi = groupBy(vdiSnapshots, '$snapshot_of') - const unmanagedVdisById = keyBy(unmanagedVdis, 'id') let orphanedVdiSnapshots if ((orphanedVdiSnapshots = snapshotsByVdi[undefined]) !== undefined) { @@ -158,7 +157,7 @@ export default class TabGeneral extends Component { const baseCopies = new Set() let baseCopy let root = id - while ((baseCopy = unmanagedVdisById[parent]) !== undefined) { + while ((baseCopy = unmanagedVdis[parent]) !== undefined) { root = baseCopy.id parent = baseCopy.parent baseCopies.add(baseCopy) @@ -167,7 +166,7 @@ export default class TabGeneral extends Component { if ((snapshots = snapshotsByVdi[id]) !== undefined) { // snapshot can have base copy without active VDI snapshots.forEach(({ parent }) => { - while ((baseCopy = unmanagedVdisById[parent]) !== undefined && !baseCopies.has(baseCopy)) { + while ((baseCopy = unmanagedVdis[parent]) !== undefined && !baseCopies.has(baseCopy)) { parent = baseCopy.parent baseCopies.add(baseCopy) }