Browse Source

Merge pull request #463 from smiclea/handle-no-source

Allow replica update even if source is down
Nashwan Azhari 6 years ago
parent
commit
2fe9f9b1b8

+ 1 - 2
src/components/molecules/MainDetailsTable/MainDetailsTable.jsx

@@ -33,8 +33,7 @@ import storageIcon from './images/storage.svg'
 import arrowIcon from './images/arrow.svg'
 import arrowIcon from './images/arrow.svg'
 
 
 const Wrapper = styled.div`
 const Wrapper = styled.div`
-  margin-top: 24px;
-  margin-bottom: 48px;
+  margin: 24px 0;
 `
 `
 const ArrowStyled = styled(Arrow)`
 const ArrowStyled = styled(Arrow)`
   position: absolute;
   position: absolute;

+ 9 - 2
src/components/molecules/Panel/Panel.jsx

@@ -32,12 +32,12 @@ const Navigation = styled.div`
 const NavigationItemDiv = styled.div`
 const NavigationItemDiv = styled.div`
   height: 47px;
   height: 47px;
   border-bottom: 1px solid ${Palette.grayscale[2]};
   border-bottom: 1px solid ${Palette.grayscale[2]};
-  color: black;
+  color: ${props => props.disabled ? Palette.grayscale[3] : 'black'};
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   padding: 0 24px;
   padding: 0 24px;
   font-size: 18px;
   font-size: 18px;
-  cursor: pointer;
+  cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
   ${props => props.selected ? css`
   ${props => props.selected ? css`
     color: ${Palette.primary};
     color: ${Palette.primary};
     background: ${Palette.grayscale[2]};
     background: ${Palette.grayscale[2]};
@@ -65,6 +65,8 @@ const ReloadButton = styled.div`
 export type NavigationItem = {
 export type NavigationItem = {
   label: string,
   label: string,
   value: string,
   value: string,
+  disabled?: boolean,
+  title?: string,
 }
 }
 
 
 export type Props = {
 export type Props = {
@@ -81,6 +83,9 @@ export const TEST_ID = 'panel'
 @observer
 @observer
 class Panel extends React.Component<Props> {
 class Panel extends React.Component<Props> {
   handleItemClick(item: NavigationItem) {
   handleItemClick(item: NavigationItem) {
+    if (item.disabled) {
+      return
+    }
     if (item.value !== this.props.selectedValue) {
     if (item.value !== this.props.selectedValue) {
       this.props.onChange(item)
       this.props.onChange(item)
     }
     }
@@ -96,6 +101,8 @@ class Panel extends React.Component<Props> {
               selected={this.props.selectedValue ? this.props.selectedValue === item.value : i === 0}
               selected={this.props.selectedValue ? this.props.selectedValue === item.value : i === 0}
               onClick={() => { this.handleItemClick(item) }}
               onClick={() => { this.handleItemClick(item) }}
               data-test-id={`${TEST_ID}-navItem-${item.value}`}
               data-test-id={`${TEST_ID}-navItem-${item.value}`}
+              disabled={item.disabled}
+              title={item.title}
             >{item.label}</NavigationItemDiv>
             >{item.label}</NavigationItemDiv>
           ))}
           ))}
         </Navigation>
         </Navigation>

+ 24 - 6
src/components/organisms/EditReplica/EditReplica.jsx

@@ -88,6 +88,7 @@ type State = {
   updateDisabled: boolean,
   updateDisabled: boolean,
   selectedNetworks: NetworkMap[],
   selectedNetworks: NetworkMap[],
   storageMap: StorageMap[],
   storageMap: StorageMap[],
+  sourceFailed: boolean,
 }
 }
 
 
 @observer
 @observer
@@ -99,6 +100,7 @@ class EditReplica extends React.Component<Props, State> {
     updateDisabled: false,
     updateDisabled: false,
     selectedNetworks: [],
     selectedNetworks: [],
     storageMap: [],
     storageMap: [],
+    sourceFailed: false,
   }
   }
 
 
   scrollableRef: HTMLElement
   scrollableRef: HTMLElement
@@ -118,8 +120,18 @@ class EditReplica extends React.Component<Props, State> {
 
 
     let loadAllOptions = async (type: 'source' | 'destination') => {
     let loadAllOptions = async (type: 'source' | 'destination') => {
       let endpoint = type === 'source' ? this.props.sourceEndpoint : this.props.destinationEndpoint
       let endpoint = type === 'source' ? this.props.sourceEndpoint : this.props.destinationEndpoint
-      await this.loadOptions(endpoint, type, useCache)
-      this.loadExtraOptions(null, type, useCache)
+      try {
+        await this.loadOptions(endpoint, type, useCache)
+        this.loadExtraOptions(null, type, useCache)
+      } catch (err) {
+        if (type === 'source') {
+          let selectedPanel = this.state.selectedPanel
+          if (selectedPanel === 'source_options') {
+            selectedPanel = 'dest_options'
+          }
+          this.setState({ sourceFailed: true, selectedPanel })
+        }
+      }
     }
     }
     if (this.hasSourceOptions()) {
     if (this.hasSourceOptions()) {
       loadAllOptions('source')
       loadAllOptions('source')
@@ -359,14 +371,15 @@ class EditReplica extends React.Component<Props, State> {
     return selectedNetworks
     return selectedNetworks
   }
   }
 
 
-  getStorageMap(): StorageMap[] {
+  getStorageMap(storageBackends: StorageBackend[]): StorageMap[] {
     let storageMap: StorageMap[] = []
     let storageMap: StorageMap[] = []
     let currentStorage = this.props.replica.storage_mappings || {}
     let currentStorage = this.props.replica.storage_mappings || {}
     let buildStorageMap = (type: 'backend' | 'disk', mapping: any) => {
     let buildStorageMap = (type: 'backend' | 'disk', mapping: any) => {
+      let backend = storageBackends.find(b => b.name === mapping.destination)
       return {
       return {
         type,
         type,
         source: { storage_backend_identifier: mapping.source, id: mapping.disk_id },
         source: { storage_backend_identifier: mapping.source, id: mapping.disk_id },
-        target: { name: mapping.destination, id: mapping.destination },
+        target: { name: mapping.destination, id: backend ? backend.id : mapping.destination },
       }
       }
     }
     }
     let backendMappings = currentStorage.backend_mappings || []
     let backendMappings = currentStorage.backend_mappings || []
@@ -445,7 +458,7 @@ class EditReplica extends React.Component<Props, State> {
       <WizardStorage
       <WizardStorage
         storageBackends={endpointStore.storageBackends}
         storageBackends={endpointStore.storageBackends}
         instancesDetails={this.props.instancesDetails}
         instancesDetails={this.props.instancesDetails}
-        storageMap={this.getStorageMap()}
+        storageMap={this.getStorageMap(endpointStore.storageBackends)}
         onChange={(s, t, type) => { this.handleStorageChange(s, t, type) }}
         onChange={(s, t, type) => { this.handleStorageChange(s, t, type) }}
         style={{ padding: '32px 32px 0 32px', width: 'calc(100% - 64px)' }}
         style={{ padding: '32px 32px 0 32px', width: 'calc(100% - 64px)' }}
       />
       />
@@ -527,7 +540,12 @@ class EditReplica extends React.Component<Props, State> {
     }
     }
 
 
     if (this.hasSourceOptions()) {
     if (this.hasSourceOptions()) {
-      navigationItems.splice(0, 0, { value: 'source_options', label: 'Source Options' })
+      navigationItems.splice(0, 0, {
+        value: 'source_options',
+        label: 'Source Options',
+        disabled: this.state.sourceFailed,
+        title: this.state.sourceFailed ? 'There are source platform errors, source options can\'t be updated' : '',
+      })
     }
     }
 
 
     return (
     return (

+ 1 - 0
src/components/organisms/MigrationDetailsContent/MigrationDetailsContent.jsx

@@ -35,6 +35,7 @@ const Wrapper = styled.div`
 `
 `
 
 
 const Buttons = styled.div`
 const Buttons = styled.div`
+  margin-top: 24px;
   & > button:last-child {
   & > button:last-child {
     float: right;
     float: right;
   }
   }

+ 1 - 0
src/components/organisms/ReplicaDetailsContent/ReplicaDetailsContent.jsx

@@ -41,6 +41,7 @@ const Wrapper = styled.div`
 const Buttons = styled.div`
 const Buttons = styled.div`
   display: flex;
   display: flex;
   justify-content: space-between;
   justify-content: space-between;
+  margin-top: 24px;
 `
 `
 const ButtonColumn = styled.div`
 const ButtonColumn = styled.div`
   display: flex;
   display: flex;

+ 9 - 1
src/components/organisms/WizardNetworks/WizardNetworks.jsx

@@ -230,6 +230,10 @@ class WizardNetworks extends React.Component<Props> {
       })
       })
     })
     })
 
 
+    if (nics.length === 0 && this.props.selectedNetworks && this.props.selectedNetworks.length) {
+      nics = this.props.selectedNetworks.map(n => n.sourceNic)
+    }
+
     if (nics.length === 0) {
     if (nics.length === 0) {
       return this.renderNoNics()
       return this.renderNoNics()
     }
     }
@@ -252,7 +256,11 @@ class WizardNetworks extends React.Component<Props> {
               <NetworkImage />
               <NetworkImage />
               <NetworkTitle>
               <NetworkTitle>
                 <NetworkName data-test-id={`wNetworks-networkName-${nic.id}`}>{nic.network_name}</NetworkName>
                 <NetworkName data-test-id={`wNetworks-networkName-${nic.id}`}>{nic.network_name}</NetworkName>
-                <NetworkSubtitle data-test-id={`wNetworks-connectedTo-${nic.id}`}>{`Connected to ${connectedTo.join(', ')}`}</NetworkSubtitle>
+                {connectedTo.length ? (
+                  <NetworkSubtitle data-test-id={`wNetworks-connectedTo-${nic.id}`}>
+                    {`Connected to ${connectedTo.join(', ')}`}
+                  </NetworkSubtitle>
+                ) : null}
               </NetworkTitle>
               </NetworkTitle>
               <ArrowImage />
               <ArrowImage />
               <Dropdowns>
               <Dropdowns>

+ 13 - 6
src/components/organisms/WizardStorage/WizardStorage.jsx

@@ -118,7 +118,7 @@ const NoStorageSubtitle = styled.div`
   text-align: center;
   text-align: center;
 `
 `
 
 
-export const getDisks = (instancesDetails: Instance[], type: 'backend' | 'disk'): Disk[] => {
+export const getDisks = (instancesDetails: Instance[], type: 'backend' | 'disk', storageMap?: ?StorageMap[]): Disk[] => {
   let fieldName = type === 'backend' ? 'storage_backend_identifier' : 'id'
   let fieldName = type === 'backend' ? 'storage_backend_identifier' : 'id'
 
 
   let disks = []
   let disks = []
@@ -133,6 +133,9 @@ export const getDisks = (instancesDetails: Instance[], type: 'backend' | 'disk')
       disks.push(disk)
       disks.push(disk)
     })
     })
   })
   })
+  if (disks.length === 0 && storageMap && storageMap.length) {
+    disks = storageMap.map(sm => sm.source)
+  }
   return disks
   return disks
 }
 }
 
 
@@ -207,9 +210,13 @@ class WizardStorage extends React.Component<Props> {
                   >
                   >
                     {diskNameParsed[0]}
                     {diskNameParsed[0]}
                   </StorageName>
                   </StorageName>
-                  <StorageSubtitle
-                    data-test-id={`${TEST_ID}-${type}-connectedTo`}
-                  >{`Connected to ${connectedTo.join(', ')}`}</StorageSubtitle>
+                  {connectedTo.length ? (
+                    <StorageSubtitle
+                      data-test-id={`${TEST_ID}-${type}-connectedTo`}
+                    >
+                      {`Connected to ${connectedTo.join(', ')}`}
+                    </StorageSubtitle>
+                  ) : null}
                 </StorageTitle>
                 </StorageTitle>
                 <ArrowImage />
                 <ArrowImage />
                 {storageItems.length > 10 ? (
                 {storageItems.length > 10 ? (
@@ -245,7 +252,7 @@ class WizardStorage extends React.Component<Props> {
   }
   }
 
 
   renderBackendMapping() {
   renderBackendMapping() {
-    let disks = getDisks(this.props.instancesDetails, 'backend')
+    let disks = getDisks(this.props.instancesDetails, 'backend', this.props.storageMap)
 
 
     if (disks.length === 0 || this.props.storageBackends.length === 0) {
     if (disks.length === 0 || this.props.storageBackends.length === 0) {
       return null
       return null
@@ -255,7 +262,7 @@ class WizardStorage extends React.Component<Props> {
   }
   }
 
 
   renderDiskMapping() {
   renderDiskMapping() {
-    let disks = getDisks(this.props.instancesDetails, 'disk')
+    let disks = getDisks(this.props.instancesDetails, 'disk', this.props.storageMap)
 
 
     if (disks.length === 0 || this.props.storageBackends.length === 0) {
     if (disks.length === 0 || this.props.storageBackends.length === 0) {
       return this.renderNoStorage()
       return this.renderNoStorage()

+ 3 - 13
src/stores/ProviderStore.js

@@ -98,7 +98,6 @@ class ProviderStore {
   @observable sourceSchemaLoading: boolean = false
   @observable sourceSchemaLoading: boolean = false
 
 
   lastDestinationSchemaType: 'replica' | 'migration' = 'replica'
   lastDestinationSchemaType: 'replica' | 'migration' = 'replica'
-  lastSourceSchemaType: 'replica' | 'migration' = 'replica'
 
 
   @computed
   @computed
   get providerNames(): string[] {
   get providerNames(): string[] {
@@ -155,13 +154,8 @@ class ProviderStore {
     optionsType: 'source' | 'destination',
     optionsType: 'source' | 'destination',
     useCache?: boolean,
     useCache?: boolean,
     quietError?: boolean,
     quietError?: boolean,
-  }): Promise<void> {
+  }): Promise<Field[]> {
     let { schemaType, providerName, optionsType, useCache, quietError } = options
     let { schemaType, providerName, optionsType, useCache, quietError } = options
-    if (optionsType === 'source') {
-      this.lastSourceSchemaType = schemaType
-    } else {
-      this.lastDestinationSchemaType = schemaType
-    }
 
 
     if (optionsType === 'source') {
     if (optionsType === 'source') {
       this.sourceSchemaLoading = true
       this.sourceSchemaLoading = true
@@ -172,6 +166,7 @@ class ProviderStore {
     try {
     try {
       let fields: Field[] = await ProviderSource.loadOptionsSchema(providerName, schemaType, optionsType, useCache, quietError)
       let fields: Field[] = await ProviderSource.loadOptionsSchema(providerName, schemaType, optionsType, useCache, quietError)
       this.loadOptionsSchemaSuccess(fields, optionsType)
       this.loadOptionsSchemaSuccess(fields, optionsType)
+      return fields
     } catch (err) {
     } catch (err) {
       throw err
       throw err
     } finally {
     } finally {
@@ -229,12 +224,7 @@ class ProviderStore {
       if (canceled) {
       if (canceled) {
         return optionsType === 'source' ? [...this.sourceOptions] : [...this.destinationOptions]
         return optionsType === 'source' ? [...this.sourceOptions] : [...this.destinationOptions]
       }
       }
-      let schemaType = optionsType === 'source' ? this.lastSourceSchemaType : this.lastDestinationSchemaType
-      if (!envData) {
-        return []
-      }
-      let newOptions = await this.loadOptionsSchema({ providerName, schemaType, optionsType })
-      return newOptions
+      throw err
     } finally {
     } finally {
       if (!canceled) {
       if (!canceled) {
         this.getOptionsValuesDone(optionsType, !envData)
         this.getOptionsValuesDone(optionsType, !envData)