diff --git a/src/common/xo/choose-sr-for-each-vdis-modal/index.js b/src/common/xo/choose-sr-for-each-vdis-modal/index.js index 06c4d4345..5537771e0 100644 --- a/src/common/xo/choose-sr-for-each-vdis-modal/index.js +++ b/src/common/xo/choose-sr-for-each-vdis-modal/index.js @@ -1,19 +1,14 @@ import Collapse from 'collapse' import Component from 'base-component' import React from 'react' -import { every, forEach, map } from 'lodash' +import { map } from 'lodash' import _ from '../../intl' import propTypes from '../../prop-types-decorator' import SingleLineRow from '../../single-line-row' -import { createSelector } from '../../selectors' -import { SelectSr } from '../../select-objects' -import { isSrWritable } from 'xo' import { Container, Col } from 'grid' - -// Can 2 SRs on the same pool have 2 VDIs used by the same VM -const areSrsCompatible = (sr1, sr2) => - sr1.shared || sr2.shared || sr1.$container === sr2.$container +import { isSrWritable } from 'xo' +import { SelectSr } from '../../select-objects' const Collapsible = ({ collapsible, children, ...props }) => collapsible ? ( @@ -32,96 +27,49 @@ Collapsible.propTypes = { } @propTypes({ - vdis: propTypes.array.isRequired, - predicate: propTypes.func, + mainSrPredicate: propTypes.func, + onChange: propTypes.func.isRequired, + srPredicate: propTypes.func, + value: propTypes.objectOf( + propTypes.shape({ + mainSr: propTypes.object, + mapVdisSrs: propTypes.object, + }) + ).isRequired, + vdis: propTypes.object.isRequired, }) export default class ChooseSrForEachVdisModal extends Component { - state = { - mapVdisSrs: {}, - } - - componentWillReceiveProps (newProps) { - if ( - this.props.predicate !== undefined && - newProps.predicate !== this.props.predicate - ) { - this.state = { - mainSr: undefined, - mapVdisSrs: {}, - } - } - } - - _onChange = props => { - this.setState(props) - this.props.onChange(props) - } - - _onChangeMainSr = newSr => { - const oldSr = this.state.mainSr - - if (oldSr == null || newSr == null || oldSr.$pool !== newSr.$pool) { - this.setState({ - mapVdisSrs: {}, - }) - } else if (!newSr.shared) { - const mapVdisSrs = { ...this.state.mapVdisSrs } - forEach(mapVdisSrs, (sr, vdi) => { - if ( - sr != null && - newSr !== sr && - sr.$container !== newSr.$container && - !sr.shared - ) { - delete mapVdisSrs[vdi] - } - }) - this._onChange({ mapVdisSrs }) - } - - this._onChange({ - mainSr: newSr, + _onChange = newValues => { + this.props.onChange({ + ...this.props.value, + ...newValues, }) } - _getSrPredicate = createSelector( - () => this.state.mainSr, - () => this.state.mapVdisSrs, - (mainSr, mapVdisSrs) => sr => - isSrWritable(sr) && - mainSr.$pool === sr.$pool && - areSrsCompatible(mainSr, sr) && - every( - mapVdisSrs, - selectedSr => selectedSr == null || areSrsCompatible(selectedSr, sr) - ) - ) + _onChangeMainSr = mainSr => this._onChange({ mainSr }) render () { - const { props, state } = this - const { vdis } = props - const { mainSr, mapVdisSrs } = state - - const srPredicate = props.predicate || this._getSrPredicate() + const { props } = this + const { + mainSrPredicate = isSrWritable, + srPredicate = mainSrPredicate, + value: { mainSr, mapVdisSrs }, + } = props return (
- props.predicate !== undefined - ? this._onChange({ mainSr }) - : this._onChangeMainSr(mainSr) - } - predicate={props.predicate || isSrWritable} + onChange={this._onChangeMainSr} placeholder={_('chooseSrForEachVdisModalMainSr')} + predicate={mainSrPredicate} value={mainSr} />
- {vdis != null && + {props.vdis != null && mainSr != null && ( = 3} buttonText={_('chooseSrForEachVdisModalSelectSr')} + collapsible={props.vdis.length >= 3} >
@@ -133,7 +81,7 @@ export default class ChooseSrForEachVdisModal extends Component { {_('chooseSrForEachVdisModalSrLabel')} - {map(vdis, vdi => ( + {map(props.vdis, vdi => ( {vdi.name_label || vdi.name} @@ -143,8 +91,8 @@ export default class ChooseSrForEachVdisModal extends Component { mapVdisSrs: { ...mapVdisSrs, [vdi.uuid]: sr }, }) } - value={mapVdisSrs[vdi.uuid]} predicate={srPredicate} + value={mapVdisSrs !== undefined && mapVdisSrs[vdi.uuid]} /> diff --git a/src/common/xo/migrate-vm-modal/index.js b/src/common/xo/migrate-vm-modal/index.js index 1050f51fa..0ae9d17c0 100644 --- a/src/common/xo/migrate-vm-modal/index.js +++ b/src/common/xo/migrate-vm-modal/index.js @@ -1,7 +1,7 @@ import BaseComponent from 'base-component' import every from 'lodash/every' -import forEach from 'lodash/forEach' import find from 'lodash/find' +import forEach from 'lodash/forEach' import map from 'lodash/map' import React from 'react' import store from 'store' @@ -11,18 +11,17 @@ import ChooseSrForEachVdisModal from '../choose-sr-for-each-vdis-modal' import invoke from '../../invoke' import SingleLineRow from '../../single-line-row' import { Col } from '../../grid' +import { connectStore, mapPlus, resolveId, resolveIds } from '../../utils' import { getDefaultNetworkForVif } from '../utils' import { SelectHost, SelectNetwork } from '../../select-objects' -import { connectStore, mapPlus, resolveIds } from '../../utils' import { createGetObjectsOfType, createPicker, createSelector, getObject, } from '../../selectors' -import { isSrShared } from 'xo' -import { isSrWritable } from '../' +import { isSrShared, isSrWritable } from '../' import styles from './index.css' @@ -68,8 +67,8 @@ export default class MigrateVmModalBody extends BaseComponent { super(props) this.state = { - mapVdisSrs: {}, mapVifsNetworks: {}, + targetSrs: {}, } this._getHostPredicate = createSelector( @@ -126,11 +125,11 @@ export default class MigrateVmModalBody extends BaseComponent { get value () { return { - targetHost: this.state.host && this.state.host.id, - sr: this.state.mainSr && this.state.mainSr.id, - mapVdisSrs: resolveIds(this.state.mapVdisSrs), + mapVdisSrs: resolveIds(this.state.targetSrs.mapVdisSrs), mapVifsNetworks: this.state.mapVifsNetworks, migrationNetwork: this.state.migrationNetworkId, + sr: resolveId(this.state.targetSrs.mainSr), + targetHost: this.state.host && this.state.host.id, } } @@ -174,6 +173,7 @@ export default class MigrateVmModalBody extends BaseComponent { intraPool, mapVifsNetworks: undefined, migrationNetwork: undefined, + targetSrs: {}, }) return } @@ -205,6 +205,7 @@ export default class MigrateVmModalBody extends BaseComponent { intraPool, mapVifsNetworks: defaultNetworksForVif, migrationNetworkId: defaultMigrationNetworkId, + targetSrs: {}, }) } @@ -219,6 +220,7 @@ export default class MigrateVmModalBody extends BaseComponent { intraPool, mapVifsNetworks, migrationNetworkId, + targetSrs, } = this.state return (
@@ -240,8 +242,9 @@ export default class MigrateVmModalBody extends BaseComponent { this.setState(props)} - predicate={this._getSrPredicate()} + mainSrPredicate={this._getSrPredicate()} + onChange={this.linkState('targetSrs')} + value={targetSrs} vdis={vdis} /> diff --git a/src/xo-app/backup/restore/index.js b/src/xo-app/backup/restore/index.js index d85e8cf19..23a65ddae 100644 --- a/src/xo-app/backup/restore/index.js +++ b/src/xo-app/backup/restore/index.js @@ -5,7 +5,6 @@ import every from 'lodash/every' import filter from 'lodash/filter' import find from 'lodash/find' import forEach from 'lodash/forEach' -import getEventValue from 'get-event-value' import groupBy from 'lodash/groupBy' import Icon from 'icon' import isEmpty from 'lodash/isEmpty' @@ -125,8 +124,8 @@ const openImportModal = ({ backups }) => body: , }).then(doImport) -const doImport = ({ backup, mainSr, start, mapVdisSrs }) => { - if (!mainSr || !backup) { +const doImport = ({ backup, targetSrs, start }) => { + if (targetSrs.mainSr === undefined || backup === undefined) { error(_('backupRestoreErrorTitle'), _('backupRestoreErrorMessage')) return } @@ -137,10 +136,10 @@ const doImport = ({ backup, mainSr, start, mapVdisSrs }) => { info(_('importBackupTitle'), _('importBackupMessage')) try { const importPromise = importMethods[backup.type]({ - remote: backup.remoteId, - sr: mainSr, file: backup.path, - mapVdisSrs, + mapVdisSrs: targetSrs.mapVdisSrs, + remote: backup.remoteId, + sr: targetSrs.mainSr, }).then(id => { return id }) @@ -153,12 +152,8 @@ const doImport = ({ backup, mainSr, start, mapVdisSrs }) => { } class _ModalBody extends Component { - constructor () { - super() - - this.state = { - mapVdisSrs: {}, - } + state = { + targetSrs: {}, } get value () { @@ -166,52 +161,52 @@ class _ModalBody extends Component { } _getSrPredicate = createSelector( - () => this.state.sr, - () => this.state.mapVdisSrs, - (defaultSr, mapVdisSrs) => sr => - sr !== defaultSr && + () => this.state.targetSrs.mainSr, + () => this.state.targetSrs.mapVdisSrs, + (mainSr, mapVdisSrs) => sr => isSrWritable(sr) && - defaultSr.$pool === sr.$pool && - areSrsCompatible(defaultSr, sr) && + mainSr.$pool === sr.$pool && + areSrsCompatible(mainSr, sr) && every( mapVdisSrs, selectedSr => selectedSr == null || areSrsCompatible(selectedSr, sr) ) ) - _onChangeDefaultSr = event => { - const oldSr = this.state.sr - const newSr = getEventValue(event) + _onSrsChange = props => { + const oldMainSr = this.state.targetSrs.mainSr + const newMainSr = props.mainSr - if (oldSr == null || newSr == null || oldSr.$pool !== newSr.$pool) { - this.setState({ - mapVdisSrs: {}, - }) - } else if (!newSr.shared) { - const mapVdisSrs = { ...this.state.mapVdisSrs } - forEach(mapVdisSrs, (sr, vdi) => { - if ( - sr != null && - newSr !== sr && - sr.$container !== newSr.$container && - !sr.shared - ) { - delete mapVdisSrs[vdi] - } - }) - this.setState({ - mapVdisSrs, - }) + const targetSrs = { ...props } + + // This code fixes the incompatibilities between the mapVdisSrs values + if (oldMainSr !== newMainSr) { + if ( + oldMainSr == null || + newMainSr == null || + oldMainSr.$pool !== newMainSr.$pool + ) { + targetSrs.mapVdisSrs = {} + } else if (!newMainSr.shared) { + forEach(targetSrs.mapVdisSrs, (sr, vdi) => { + if ( + sr != null && + newMainSr !== sr && + sr.$container !== newMainSr.$container && + !sr.shared + ) { + delete targetSrs.mapVdisSrs[vdi] + } + }) + } } - this.setState({ - sr: newSr, - }) + this.setState({ targetSrs }) } render () { - const { backups, intl } = this.props - const vdis = this.state.backup && this.state.backup.vdis + const { props, state } = this + const vdis = state.backup && state.backup.vdis return (
@@ -219,15 +214,17 @@ class _ModalBody extends Component { onChange={this.linkState('backup')} optionKey='path' optionRenderer={backupOptionRenderer} - options={backups} - placeholder={intl.formatMessage( + options={props.backups} + placeholder={props.intl.formatMessage( messages.importBackupModalSelectBackup )} />
this.setState(props)} />
{' '}