瀏覽代碼

Move default storage from options to storage

Besides moving the Default Storage dropdown from Wizard Options screen
to Wizard Storage screen, the code logic has also been refactored to
reflect this.
Sergiu Miclea 6 年之前
父節點
當前提交
233e3a366e

+ 17 - 5
src/components/organisms/EditReplica/EditReplica.jsx

@@ -99,6 +99,7 @@ type State = {
   sourceData: any,
   updateDisabled: boolean,
   selectedNetworks: NetworkMap[],
+  defaultStorage: ?string,
   storageMap: StorageMap[],
   sourceFailed: boolean,
   destinationFailedMessage: ?string,
@@ -112,6 +113,7 @@ class EditReplica extends React.Component<Props, State> {
     sourceData: {},
     updateDisabled: false,
     selectedNetworks: [],
+    defaultStorage: undefined,
     storageMap: [],
     sourceFailed: false,
     destinationFailedMessage: null,
@@ -311,9 +313,8 @@ class EditReplica extends React.Component<Props, State> {
       storage: this.state.storageMap,
     }
     if (this.props.type === 'replica') {
-      let storageConfigDefault = this.getFieldValue('destination', 'default_storage') || endpointStore.storageConfigDefault
       try {
-        await replicaStore.update(this.props.replica, this.props.destinationEndpoint, updateData, storageConfigDefault)
+        await replicaStore.update(this.props.replica, this.props.destinationEndpoint, updateData, this.getDefaultStorage(), endpointStore.storageConfigDefault)
         this.props.onRequestClose()
         this.props.onUpdateComplete(`/replica/executions/${this.props.replica.id}`)
       } catch (err) {
@@ -321,7 +322,8 @@ class EditReplica extends React.Component<Props, State> {
       }
     } else {
       try {
-        let migration: MainItem = await migrationStore.recreate(this.props.replica, this.props.sourceEndpoint, this.props.destinationEndpoint, updateData)
+        let replicaDefaultStorage = this.props.replica.storage_mappings && this.props.replica.storage_mappings.default
+        let migration: MainItem = await migrationStore.recreate(this.props.replica, this.props.sourceEndpoint, this.props.destinationEndpoint, updateData, replicaDefaultStorage, this.state.defaultStorage)
         migrationStore.clearDetails()
         this.props.onRequestClose()
         this.props.onUpdateComplete(`/migration/tasks/${migration.id}`)
@@ -347,6 +349,12 @@ class EditReplica extends React.Component<Props, State> {
     this.setState({ storageMap })
   }
 
+  getDefaultStorage() {
+    let storageMappings = this.props.replica.storage_mappings
+    let replicaDefaultStorage = storageMappings && storageMappings.default
+    return this.state.defaultStorage !== undefined ? this.state.defaultStorage : replicaDefaultStorage
+  }
+
   getFieldValue(type: 'source' | 'destination', fieldName: string, defaultValue: any) {
     let currentData = type === 'source' ? this.state.sourceData : this.state.destinationData
     if (currentData[fieldName] !== undefined) {
@@ -487,8 +495,8 @@ class EditReplica extends React.Component<Props, State> {
         layout="modal"
         isSource={type === 'source'}
         optionsLoading={optionsLoading}
-        optionsLoadingSkipFields={[...optionsLoadingSkipFields, 'description', 'execute_now', 'execute_now_options',
-          'default_storage', ...migrationFields.map(f => f.name)]}
+        optionsLoadingSkipFields={[...optionsLoadingSkipFields, 'description', 'execute_now',
+          'execute_now_options', ...migrationFields.map(f => f.name)]}
       />
     )
   }
@@ -503,6 +511,10 @@ class EditReplica extends React.Component<Props, State> {
 
     return (
       <WizardStorage
+        defaultStorage={this.getDefaultStorage()}
+        onDefaultStorageChange={defaultStorage => { this.setState({ defaultStorage }) }}
+        storageConfigDefault={endpointStore.storageConfigDefault}
+        defaultStorageLayout="modal"
         storageBackends={endpointStore.storageBackends}
         instancesDetails={this.props.instancesDetails}
         storageMap={this.getStorageMap(endpointStore.storageBackends)}

+ 0 - 9
src/components/organisms/WizardOptions/WizardOptions.jsx

@@ -196,15 +196,6 @@ class WizardOptions extends React.Component<Props> {
       fieldsSchema = [...fieldsSchema, ...migrationFields]
     }
 
-    if (this.props.hasStorageMap && this.props.useAdvancedOptions && this.props.storageBackends && this.props.storageBackends.length > 0) {
-      fieldsSchema.push({
-        name: 'default_storage',
-        type: 'string',
-        enum: this.props.storageBackends.map(s => s.name),
-        default: this.props.storageConfigDefault,
-      })
-    }
-
     return fieldsSchema
   }
 

+ 8 - 1
src/components/organisms/WizardPageContent/WizardPageContent.jsx

@@ -174,6 +174,7 @@ type Props = {
   wizardData: WizardData,
   schedules: ScheduleType[],
   storageMap: StorageMap[],
+  defaultStorage: ?string,
   hasStorageMap: boolean,
   hasSourceOptions: boolean,
   pages: WizardPage[],
@@ -192,6 +193,7 @@ type Props = {
   onSourceOptionsChange: (field: Field, value: any) => void,
   onNetworkChange: (nic: Nic, network: Network, secGroups: ?SecurityGroup[]) => void,
   onStorageChange: (sourceStorage: Disk, targetStorage: StorageBackend, type: 'backend' | 'disk') => void,
+  onDefaultStorageChange: (value: ?string) => void,
   onAddScheduleClick: (schedule: ScheduleType) => void,
   onScheduleChange: (scheduleId: string, schedule: ScheduleType) => void,
   onScheduleRemove: (scheudleId: string) => void,
@@ -431,7 +433,7 @@ class WizardPageContent extends React.Component<Props, State> {
             optionsLoading={this.props.providerStore.destinationOptionsSecondaryLoading}
             optionsLoadingSkipFields={[
               ...getOptionsLoadingSkipFields('destination'), 'description', 'execute_now',
-              'execute_now_options', 'default_storage', ...migrationFields.map(f => f.name)]}
+              'execute_now_options', ...migrationFields.map(f => f.name)]}
             selectedInstances={this.props.wizardData.selectedInstances}
             fields={this.props.providerStore.destinationSchema}
             onChange={this.props.onDestOptionsChange}
@@ -465,6 +467,10 @@ class WizardPageContent extends React.Component<Props, State> {
             instancesDetails={this.props.instanceStore.instancesDetails}
             storageMap={this.props.storageMap}
             onChange={this.props.onStorageChange}
+            storageConfigDefault={this.props.endpointStore.storageConfigDefault}
+            defaultStorage={this.props.defaultStorage}
+            onDefaultStorageChange={this.props.onDefaultStorageChange}
+            defaultStorageLayout="page"
           />
         )
         break
@@ -496,6 +502,7 @@ class WizardPageContent extends React.Component<Props, State> {
           <WizardSummary
             data={this.props.wizardData}
             schedules={this.props.schedules}
+            defaultStorage={this.props.defaultStorage}
             storageMap={this.props.storageMap}
             wizardType={this.props.type}
             instancesDetails={this.props.instanceStore.instancesDetails}

+ 34 - 1
src/components/organisms/WizardStorage/WizardStorage.jsx

@@ -20,6 +20,7 @@ import styled from 'styled-components'
 
 import AutocompleteDropdown from '../../molecules/AutocompleteDropdown'
 import Dropdown from '../../molecules/Dropdown'
+import FieldInput from '../../molecules/FieldInput'
 
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
@@ -34,9 +35,12 @@ import arrowImage from './images/arrow.svg'
 const Wrapper = styled.div`
   width: 100%;
   display: flex;
-  justify-content: center;
+  flex-direction: column;
   overflow: auto;
 `
+const DefaultStorageWrapper = styled.div`
+  margin-bottom: 32px;
+`
 const Mapping = styled.div`
   display: flex;
   flex-direction: column;
@@ -151,6 +155,10 @@ export type Props = {
   storageBackends: StorageBackend[],
   instancesDetails: Instance[],
   storageMap: ?StorageMap[],
+  defaultStorageLayout: 'modal' | 'page',
+  defaultStorage: ?string,
+  storageConfigDefault: ?string,
+  onDefaultStorageChange: (value: ?string) => void,
   onChange: (sourceStorage: Disk, targetStorage: StorageBackend, type: 'backend' | 'disk') => void,
   style?: any,
   titleWidth?: number,
@@ -295,9 +303,34 @@ class WizardStorage extends React.Component<Props> {
     return this.renderStorageWrapper(disks, 'disk')
   }
 
+  renderDefaultStorage() {
+    let disks = getDisks(this.props.instancesDetails, 'disk', this.props.storageMap)
+
+    if (disks.length === 0 || this.props.storageBackends.length === 0) {
+      return null
+    }
+
+    return (
+      <DefaultStorageWrapper>
+        <FieldInput
+          layout={this.props.defaultStorageLayout}
+          name="default_storage"
+          type="string"
+          description="Storage type on the destination to default to"
+          enum={this.props.storageBackends.map(s => s.name)}
+          addNullValue
+          width={StyleProps.inputSizes.regular.width}
+          value={this.props.defaultStorage !== undefined ? this.props.defaultStorage : this.props.storageConfigDefault}
+          onChange={value => { this.props.onDefaultStorageChange(value) }}
+        />
+      </DefaultStorageWrapper>
+    )
+  }
+
   render() {
     return (
       <Wrapper style={this.props.style}>
+        {this.renderDefaultStorage()}
         <Mapping>
           {this.renderBackendMapping()}
           {this.renderDiskMapping()}

+ 3 - 0
src/components/organisms/WizardStorage/test.jsx

@@ -66,6 +66,9 @@ const defaultProps: Props = {
   }],
   defaultStorage: 'sback1',
   onChange: () => { },
+  defaultStorageLayout: 'page',
+  onDefaultStorageChange: () => { },
+  storageConfigDefault: null,
 }
 const wrap = (props: Props) => new TW(shallow(<Component {...props} />), TEST_ID)
 

+ 9 - 0
src/components/organisms/WizardSummary/WizardSummary.jsx

@@ -167,6 +167,7 @@ type Props = {
   data: WizardData,
   wizardType: 'replica' | 'migration',
   schedules: Schedule[],
+  defaultStorage: ?string,
   storageMap: StorageMap[],
   instancesDetails: Instance[],
   sourceSchema: Field[],
@@ -317,6 +318,13 @@ class WizardSummary extends React.Component<Props> {
       ),
     ]
 
+    let defaultStorageOption = (
+      <Option>
+        <OptionLabel>Default Storage</OptionLabel>
+        <OptionValue>{this.props.defaultStorage}</OptionValue>
+      </Option>
+    )
+
     return (
       <Section>
         <SectionTitle>{type} Target Options</SectionTitle>
@@ -324,6 +332,7 @@ class WizardSummary extends React.Component<Props> {
           {this.props.wizardType === 'replica' ? executeNowOption : null}
           {this.props.wizardType === 'migration' ? migrationOptions : null}
           {this.props.data.selectedInstances && this.props.data.selectedInstances.length > 1 ? separateVmOption : null}
+          {this.props.defaultStorage ? defaultStorageOption : null}
           {data.destOptions ? Object.keys(data.destOptions).map(optionName => {
             if (
               optionName === 'execute_now' ||

+ 9 - 0
src/components/pages/WizardPage/WizardPage.jsx

@@ -323,6 +323,11 @@ class WizardPage extends React.Component<Props, State> {
     wizardStore.updateUrlState()
   }
 
+  handleDefaultStorageChange(value: ?string) {
+    wizardStore.updateDefaultStorage(value)
+    wizardStore.updateUrlState()
+  }
+
   handleStorageChange(source: Disk, target: StorageBackend, type: 'backend' | 'disk') {
     wizardStore.updateStorage({ source, target, type })
     wizardStore.updateUrlState()
@@ -481,6 +486,7 @@ class WizardPage extends React.Component<Props, State> {
     await wizardStore.createMultiple(
       this.state.type,
       wizardStore.data,
+      wizardStore.defaultStorage,
       wizardStore.storageMap,
       wizardStore.uploadedUserScripts,
     )
@@ -500,6 +506,7 @@ class WizardPage extends React.Component<Props, State> {
       await wizardStore.create(
         this.state.type,
         wizardStore.data,
+        wizardStore.defaultStorage,
         wizardStore.storageMap,
         wizardStore.uploadedUserScripts,
       )
@@ -607,6 +614,7 @@ class WizardPage extends React.Component<Props, State> {
             wizardData={wizardStore.data}
             hasStorageMap={Boolean(this.pages.find(p => p.id === 'storage'))}
             hasSourceOptions={Boolean(this.pages.find(p => p.id === 'source-options'))}
+            defaultStorage={wizardStore.defaultStorage}
             storageMap={wizardStore.storageMap}
             schedules={wizardStore.schedules}
             nextButtonDisabled={this.isNextButtonDisabled()}
@@ -624,6 +632,7 @@ class WizardPage extends React.Component<Props, State> {
             onDestOptionsChange={(field, value) => { this.handleDestOptionsChange(field, value) }}
             onSourceOptionsChange={(field, value) => { this.handleSourceOptionsChange(field, value) }}
             onNetworkChange={(sourceNic, targetNetwork, secGroups) => { this.handleNetworkChange(sourceNic, targetNetwork, secGroups) }}
+            onDefaultStorageChange={value => { this.handleDefaultStorageChange(value) }}
             onStorageChange={(source, target, type) => { this.handleStorageChange(source, target, type) }}
             onAddScheduleClick={schedule => { this.handleAddScheduleClick(schedule) }}
             onScheduleChange={(scheduleId, data) => { this.handleScheduleChange(scheduleId, data) }}

+ 1 - 1
src/plugins/endpoint/default/OptionsSchemaPlugin.js

@@ -73,7 +73,7 @@ export const defaultFillMigrationImageMapValues = (field: Field, option: OptionV
 
 export const defaultGetDestinationEnv = (options: ?{ [string]: mixed }, oldOptions?: ?{ [string]: mixed }): any => {
   let env = {}
-  let specialOptions = ['execute_now', 'separate_vm', 'skip_os_morphing', 'default_storage', 'description']
+  let specialOptions = ['execute_now', 'separate_vm', 'skip_os_morphing', 'description']
     .concat(migrationFields.map(f => f.name))
     .concat(executionOptions.map(o => o.name))
     .concat(migrationImageOsTypes.map(o => `${o}_os_image`))

+ 1 - 1
src/sources/AssessmentSource.js

@@ -33,7 +33,7 @@ class AssessmentSourceUtils {
     if (vmSize) {
       env.vm_size = vmSize
     }
-    let skipFields = ['use_replica', 'separate_vm', 'shutdown_instances', 'skip_os_morphing', 'default_storage']
+    let skipFields = ['use_replica', 'separate_vm', 'shutdown_instances', 'skip_os_morphing']
     Object.keys(data.fieldValues).filter(f => !skipFields.find(sf => sf === f)).forEach(fieldName => {
       if (data.fieldValues[fieldName] != null) {
         env[fieldName] = data.fieldValues[fieldName]

+ 3 - 1
src/sources/MigrationSource.js

@@ -82,6 +82,8 @@ class MigrationSource {
     updatedSourceEnv?: ?{ [string]: any },
     storageMappings: ?{ [string]: any },
     updatedStorageMappings: ?StorageMap[],
+    defaultStorage: ?string,
+    updatedDefaultStorage: ?string,
     networkMappings: ?{ [string]: any },
     updatedNetworkMappings: ?NetworkMap[],
   }): Promise<MainItem> {
@@ -122,7 +124,7 @@ class MigrationSource {
       || (opts.updatedStorageMappings && opts.updatedStorageMappings.length)) {
       payload.migration.storage_mappings = {
         ...opts.storageMappings,
-        ...destParser.getStorageMap(getValue('default_storage'), opts.updatedStorageMappings),
+        ...destParser.getStorageMap(opts.updatedDefaultStorage || opts.defaultStorage, opts.updatedStorageMappings),
       }
     }
 

+ 1 - 2
src/sources/ReplicaSource.js

@@ -203,7 +203,7 @@ class ReplicaSource {
     return response.data.execution
   }
 
-  async update(replica: MainItem, destinationEndpoint: Endpoint, updateData: UpdateData, storageConfigDefault: string): Promise<Execution> {
+  async update(replica: MainItem, destinationEndpoint: Endpoint, updateData: UpdateData, defaultStorage: ?string, storageConfigDefault: string): Promise<Execution> {
     const parser = OptionsSchemaPlugin[destinationEndpoint.type] || OptionsSchemaPlugin.default
     let payload = { replica: {} }
 
@@ -219,7 +219,6 @@ class ReplicaSource {
       payload.replica.source_environment = parser.getDestinationEnv(updateData.source, replica.source_environment)
     }
 
-    let defaultStorage = updateData.destination && updateData.destination.default_storage
     if (defaultStorage || updateData.storage.length > 0) {
       payload.replica.storage_mappings = parser.getStorageMap(defaultStorage, updateData.storage, storageConfigDefault)
     }

+ 3 - 2
src/sources/WizardSource.js

@@ -28,13 +28,13 @@ class WizardSource {
   async create(
     type: string,
     data: WizardData,
+    defaultStorage: ?string,
     storageMap: StorageMap[],
     uploadedUserScripts: InstanceScript[]
   ): Promise<MainItem> {
     const sourceParser = data.source ? OptionsSchemaPlugin[data.source.type] || OptionsSchemaPlugin.default : OptionsSchemaPlugin.default
     const destParser = data.target ? OptionsSchemaPlugin[data.target.type] || OptionsSchemaPlugin.default : OptionsSchemaPlugin.default
     let payload = {}
-    let defaultStorage: ?string = data.destOptions && data.destOptions.default_storage
     payload[type] = {
       origin_endpoint_id: data.source ? data.source.id : 'null',
       destination_endpoint_id: data.target ? data.target.id : 'null',
@@ -72,6 +72,7 @@ class WizardSource {
   async createMultiple(
     type: string,
     data: WizardData,
+    defaultStorage: ?string,
     storageMap: StorageMap[],
     uploadedUserScripts: InstanceScript[]
   ): Promise<MainItem[]> {
@@ -82,7 +83,7 @@ class WizardSource {
       let newData = { ...data }
       newData.selectedInstances = [instance]
       try {
-        let mainItem: MainItem = await this.create(type, newData, storageMap, uploadedUserScripts)
+        let mainItem: MainItem = await this.create(type, newData, defaultStorage, storageMap, uploadedUserScripts)
         return mainItem
       } catch (err) {
         notificationStore.alert(`Error while creating ${type} for instance ${instance.name}`, 'error')

+ 5 - 1
src/stores/MigrationStore.js

@@ -59,7 +59,9 @@ class MigrationStore {
     migration: MainItem,
     sourceEndpoint: Endpoint,
     destEndpoint: Endpoint,
-    updateData: UpdateData
+    updateData: UpdateData,
+    defaultStorage: ?string,
+    updatedDefaultStorage: ?string
   ): Promise<MainItem> {
     let migrationResult = await MigrationSource.recreate({
       sourceEndpoint,
@@ -71,6 +73,8 @@ class MigrationStore {
       updatedDestEnv: updateData.destination,
       storageMappings: migration.storage_mappings,
       updatedStorageMappings: updateData.storage,
+      defaultStorage,
+      updatedDefaultStorage,
       networkMappings: migration.network_map,
       updatedNetworkMappings: updateData.network,
     })

+ 2 - 2
src/stores/ReplicaStore.js

@@ -185,8 +185,8 @@ class ReplicaStore {
     this.replicaDetails = null
   }
 
-  async update(replica: MainItem, destinationEndpoint: Endpoint, updateData: UpdateData, storageConfigDefault: string) {
-    await ReplicaSource.update(replica, destinationEndpoint, updateData, storageConfigDefault)
+  async update(replica: MainItem, destinationEndpoint: Endpoint, updateData: UpdateData, defaultStorage: ?string, storageConfigDefault: string) {
+    await ReplicaSource.update(replica, destinationEndpoint, updateData, defaultStorage, storageConfigDefault)
   }
 }
 

+ 18 - 3
src/stores/WizardStore.js

@@ -54,6 +54,7 @@ const updateOptions = (oldOptions: ?{ [string]: mixed }, data: { field: Field, v
 class WizardStore {
   @observable data: WizardData = {}
   @observable schedules: Schedule[] = []
+  @observable defaultStorage: ?string = null
   @observable storageMap: StorageMap[] = []
   @observable currentPage: WizardPage = wizardPages[0]
   @observable createdItem: ?MainItem = null
@@ -84,6 +85,7 @@ class WizardStore {
   @action clearData() {
     this.data = {}
     this.currentPage = wizardPages[0]
+    this.clearStorageMap()
   }
 
   @action setCurrentPage(page: WizardPage) {
@@ -109,6 +111,10 @@ class WizardStore {
     this.data.networks.push(network)
   }
 
+  @action updateDefaultStorage(value: ?string) {
+    this.defaultStorage = value
+  }
+
   @action updateStorage(storage: StorageMap) {
     let diskFieldName = storage.type === 'backend' ? 'storage_backend_identifier' : 'id'
     this.storageMap = this.storageMap
@@ -118,6 +124,7 @@ class WizardStore {
 
   @action clearStorageMap() {
     this.storageMap = []
+    this.defaultStorage = null
   }
 
   @action addSchedule(schedule: Schedule) {
@@ -151,13 +158,14 @@ class WizardStore {
   @action async create(
     type: string,
     data: WizardData,
+    defaultStorage: ?string,
     storageMap: StorageMap[],
     uploadedUserScripts: InstanceScript[]
   ): Promise<void> {
     this.creatingItem = true
 
     try {
-      let item: MainItem = await source.create(type, data, storageMap, uploadedUserScripts)
+      let item: MainItem = await source.create(type, data, defaultStorage, storageMap, uploadedUserScripts)
       runInAction(() => { this.createdItem = item })
     } catch (err) {
       throw err
@@ -169,13 +177,14 @@ class WizardStore {
   @action async createMultiple(
     type: string,
     data: WizardData,
+    defaultStorage: ?string,
     storageMap: StorageMap[],
     uploadedUserScripts: InstanceScript[]
   ): Promise<void> {
     this.creatingItems = true
 
     try {
-      let items: MainItem[] = await source.createMultiple(type, data, storageMap, uploadedUserScripts)
+      let items: MainItem[] = await source.createMultiple(type, data, defaultStorage, storageMap, uploadedUserScripts)
       runInAction(() => { this.createdItems = items })
     } finally {
       runInAction(() => { this.creatingItems = false })
@@ -183,7 +192,12 @@ class WizardStore {
   }
 
   updateUrlState() {
-    source.setUrlState({ data: this.data, schedules: this.schedules, storageMap: this.storageMap })
+    source.setUrlState({
+      data: this.data,
+      schedules: this.schedules,
+      storageMap: this.storageMap,
+      defaultStorage: this.defaultStorage,
+    })
   }
 
   @action getUrlState() {
@@ -194,6 +208,7 @@ class WizardStore {
     this.data = state.data
     this.schedules = state.schedules
     this.storageMap = state.storageMap
+    this.defaultStorage = state.defaultStorage
   }
 
   @action cancelUploadedScript(global: ?string, instanceName: ?string) {