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)}
/>
{' '}