2
0
Эх сурвалжийг харах

Allow replica update even if source is down

If source endpoint is down, source options editing is disabled, network
and storage mapping can still be updated, but the source info is
retrieved from replica mapping.
Sergiu Miclea 6 жил өмнө
parent
commit
ce1e52a604

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

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

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

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

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

@@ -88,6 +88,7 @@ type State = {
   updateDisabled: boolean,
   selectedNetworks: NetworkMap[],
   storageMap: StorageMap[],
+  sourceFailed: boolean,
 }
 
 @observer
@@ -99,6 +100,7 @@ class EditReplica extends React.Component<Props, State> {
     updateDisabled: false,
     selectedNetworks: [],
     storageMap: [],
+    sourceFailed: false,
   }
 
   scrollableRef: HTMLElement
@@ -118,8 +120,18 @@ class EditReplica extends React.Component<Props, State> {
 
     let loadAllOptions = async (type: 'source' | 'destination') => {
       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()) {
       loadAllOptions('source')
@@ -359,14 +371,15 @@ class EditReplica extends React.Component<Props, State> {
     return selectedNetworks
   }
 
-  getStorageMap(): StorageMap[] {
+  getStorageMap(storageBackends: StorageBackend[]): StorageMap[] {
     let storageMap: StorageMap[] = []
     let currentStorage = this.props.replica.storage_mappings || {}
     let buildStorageMap = (type: 'backend' | 'disk', mapping: any) => {
+      let backend = storageBackends.find(b => b.name === mapping.destination)
       return {
         type,
         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 || []
@@ -445,7 +458,7 @@ class EditReplica extends React.Component<Props, State> {
       <WizardStorage
         storageBackends={endpointStore.storageBackends}
         instancesDetails={this.props.instancesDetails}
-        storageMap={this.getStorageMap()}
+        storageMap={this.getStorageMap(endpointStore.storageBackends)}
         onChange={(s, t, type) => { this.handleStorageChange(s, t, type) }}
         style={{ padding: '32px 32px 0 32px', width: 'calc(100% - 64px)' }}
       />
@@ -527,7 +540,12 @@ class EditReplica extends React.Component<Props, State> {
     }
 
     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 (

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

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

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

@@ -41,6 +41,7 @@ const Wrapper = styled.div`
 const Buttons = styled.div`
   display: flex;
   justify-content: space-between;
+  margin-top: 24px;
 `
 const ButtonColumn = styled.div`
   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) {
       return this.renderNoNics()
     }
@@ -252,7 +256,11 @@ class WizardNetworks extends React.Component<Props> {
               <NetworkImage />
               <NetworkTitle>
                 <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>
               <ArrowImage />
               <Dropdowns>

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

@@ -118,7 +118,7 @@ const NoStorageSubtitle = styled.div`
   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 disks = []
@@ -133,6 +133,9 @@ export const getDisks = (instancesDetails: Instance[], type: 'backend' | 'disk')
       disks.push(disk)
     })
   })
+  if (disks.length === 0 && storageMap && storageMap.length) {
+    disks = storageMap.map(sm => sm.source)
+  }
   return disks
 }
 
@@ -207,9 +210,13 @@ class WizardStorage extends React.Component<Props> {
                   >
                     {diskNameParsed[0]}
                   </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>
                 <ArrowImage />
                 {storageItems.length > 10 ? (
@@ -245,7 +252,7 @@ class WizardStorage extends React.Component<Props> {
   }
 
   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) {
       return null
@@ -255,7 +262,7 @@ class WizardStorage extends React.Component<Props> {
   }
 
   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) {
       return this.renderNoStorage()

+ 3 - 13
src/stores/ProviderStore.js

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