Explorar o código

Merge pull request #492 from smiclea/oci-root-disk

Disable disk mapping for OCI's root disk
Nashwan Azhari %!s(int64=6) %!d(string=hai) anos
pai
achega
f49593c492

+ 43 - 25
src/components/organisms/WizardStorage/WizardStorage.jsx

@@ -118,6 +118,11 @@ const NoStorageSubtitle = styled.div`
   color: ${Palette.grayscale[4]};
   color: ${Palette.grayscale[4]};
   text-align: center;
   text-align: center;
 `
 `
+const DiskDisabledMessage = styled.div`
+  width: 224px;
+  text-align: center;
+  color: gray;
+`
 
 
 export const getDisks = (instancesDetails: Instance[], type: 'backend' | 'disk', storageMap?: ?StorageMap[]): 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'
@@ -162,6 +167,42 @@ class WizardStorage extends React.Component<Props> {
     )
     )
   }
   }
 
 
+  renderStorageDropdown(
+    storageItems: Array<StorageBackend | { id: ?string, name: string }>,
+    selectedItem: ?StorageBackend,
+    disk: Disk,
+    type: 'backend' | 'disk'
+  ) {
+    if (disk.disabled && type === 'disk') {
+      return <DiskDisabledMessage>{disk.disabled}</DiskDisabledMessage>
+    }
+
+    return storageItems.length > 10 ? (
+      <AutocompleteDropdown
+        width={StyleProps.inputSizes.large.width}
+        selectedItem={selectedItem}
+        items={storageItems}
+        onChange={(item: StorageBackend) => { this.props.onChange(disk, item, type) }}
+        labelField="name"
+        valueField="id"
+      />
+    ) :
+      (
+        <Dropdown
+          width={StyleProps.inputSizes.large.width}
+          centered
+          noSelectionMessage="Default"
+          noItemsMessage="No storage found"
+          selectedItem={selectedItem}
+          items={storageItems}
+          labelField="name"
+          valueField="id"
+          onChange={(item: StorageBackend) => { this.props.onChange(disk, item, type) }}
+          data-test-id={`${TEST_ID}-${type}-destination`}
+        />
+      )
+  }
+
   renderStorageWrapper(disks: Disk[], type: 'backend' | 'disk') {
   renderStorageWrapper(disks: Disk[], type: 'backend' | 'disk') {
     let title = type === 'backend' ? 'Storage Backend Mapping' : 'Disk Mapping'
     let title = type === 'backend' ? 'Storage Backend Mapping' : 'Disk Mapping'
     let diskFieldName = type === 'backend' ? 'storage_backend_identifier' : 'id'
     let diskFieldName = type === 'backend' ? 'storage_backend_identifier' : 'id'
@@ -172,7 +213,7 @@ class WizardStorage extends React.Component<Props> {
     ]
     ]
 
 
     disks = disks.filter(d => d[diskFieldName])
     disks = disks.filter(d => d[diskFieldName])
-    disks.sort((d1, d2) => String(d1[diskFieldName]).localeCompare(String(d2[diskFieldName])))
+
     let parseDiskName = (name: ?string): [?string, boolean] => {
     let parseDiskName = (name: ?string): [?string, boolean] => {
       if (!name) {
       if (!name) {
         return [null, false]
         return [null, false]
@@ -225,30 +266,7 @@ class WizardStorage extends React.Component<Props> {
                   ) : null}
                   ) : null}
                 </StorageTitle>
                 </StorageTitle>
                 <ArrowImage />
                 <ArrowImage />
-                {storageItems.length > 10 ? (
-                  <AutocompleteDropdown
-                    width={StyleProps.inputSizes.large.width}
-                    selectedItem={selectedItem}
-                    items={storageItems}
-                    onChange={(item: StorageBackend) => { this.props.onChange(disk, item, type) }}
-                    labelField="name"
-                    valueField="id"
-                  />
-                ) :
-                  (
-                    <Dropdown
-                      width={StyleProps.inputSizes.large.width}
-                      centered
-                      noSelectionMessage="Default"
-                      noItemsMessage="No storage found"
-                      selectedItem={selectedItem}
-                      items={storageItems}
-                      labelField="name"
-                      valueField="id"
-                      onChange={(item: StorageBackend) => { this.props.onChange(disk, item, type) }}
-                      data-test-id={`${TEST_ID}-${type}-destination`}
-                    />
-                  )}
+                {this.renderStorageDropdown(storageItems, selectedItem, disk, type)}
               </StorageItem>
               </StorageItem>
             )
             )
           })}
           })}

+ 1 - 0
src/components/pages/AssessmentDetailsPage/AssessmentDetailsPage.jsx

@@ -408,6 +408,7 @@ class AssessmentDetailsPage extends React.Component<Props, State> {
         location: localData.locationName,
         location: localData.locationName,
         resource_group: localData.resourceGroupName,
         resource_group: localData.resourceGroupName,
       },
       },
+      targetProvider: 'azure',
     })
     })
   }
   }
 
 

+ 3 - 0
src/components/pages/MigrationDetailsPage/MigrationDetailsPage.jsx

@@ -129,6 +129,8 @@ class MigrationDetailsPage extends React.Component<Props, State> {
       quietError: true,
       quietError: true,
       cache,
       cache,
     })
     })
+
+    let targetEndpoint = endpointStore.endpoints.find(e => e.id === details.destination_endpoint_id)
     instanceStore.loadInstancesDetails({
     instanceStore.loadInstancesDetails({
       endpointId: details.origin_endpoint_id,
       endpointId: details.origin_endpoint_id,
       // $FlowIgnore
       // $FlowIgnore
@@ -136,6 +138,7 @@ class MigrationDetailsPage extends React.Component<Props, State> {
       cache,
       cache,
       quietError: false,
       quietError: false,
       env: details.source_environment,
       env: details.source_environment,
+      targetProvider: targetEndpoint ? targetEndpoint.type : '',
     })
     })
   }
   }
 
 

+ 3 - 0
src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.jsx

@@ -168,6 +168,8 @@ class ReplicaDetailsPage extends React.Component<Props, State> {
       quietError: true,
       quietError: true,
       cache,
       cache,
     })
     })
+
+    let targetEndpoint = endpointStore.endpoints.find(e => e.id === details.destination_endpoint_id)
     instanceStore.loadInstancesDetails({
     instanceStore.loadInstancesDetails({
       endpointId: details.origin_endpoint_id,
       endpointId: details.origin_endpoint_id,
       // $FlowIgnore
       // $FlowIgnore
@@ -175,6 +177,7 @@ class ReplicaDetailsPage extends React.Component<Props, State> {
       cache,
       cache,
       quietError: false,
       quietError: false,
       env: details.source_environment,
       env: details.source_environment,
+      targetProvider: targetEndpoint ? targetEndpoint.type : '',
     })
     })
   }
   }
 
 

+ 2 - 1
src/components/pages/WizardPage/WizardPage.jsx

@@ -465,12 +465,13 @@ class WizardPage extends React.Component<Props, State> {
   }
   }
 
 
   loadNetworks(cache: boolean) {
   loadNetworks(cache: boolean) {
-    if (wizardStore.data.source && wizardStore.data.selectedInstances) {
+    if (wizardStore.data.source && wizardStore.data.selectedInstances && wizardStore.data.target) {
       instanceStore.loadInstancesDetails({
       instanceStore.loadInstancesDetails({
         endpointId: wizardStore.data.source.id,
         endpointId: wizardStore.data.source.id,
         instancesInfo: wizardStore.data.selectedInstances,
         instancesInfo: wizardStore.data.selectedInstances,
         env: wizardStore.data.sourceOptions,
         env: wizardStore.data.sourceOptions,
         cache,
         cache,
+        targetProvider: wizardStore.data.target.type,
       })
       })
     }
     }
     if (wizardStore.data.target) {
     if (wizardStore.data.target) {

+ 23 - 0
src/plugins/endpoint/default/InstanceInfoPlugin.js

@@ -0,0 +1,23 @@
+/*
+Copyright (C) 2020  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+import type { Instance } from '../../../types/Instance'
+
+export default class InstanceInfoPlugin {
+  static parseInstance(instance: Instance): Instance {
+    return instance
+  }
+}

+ 8 - 0
src/plugins/endpoint/index.js

@@ -26,6 +26,9 @@ import DefaultOptionsSchemaPlugin from './default/OptionsSchemaPlugin'
 import OvmOptionsSchemaPlugin from './ovm/OptionsSchemaPlugin'
 import OvmOptionsSchemaPlugin from './ovm/OptionsSchemaPlugin'
 import OpenstackOptionsSchemaPlugin from './openstack/OptionsSchemaPlugin'
 import OpenstackOptionsSchemaPlugin from './openstack/OptionsSchemaPlugin'
 
 
+import DefaultInstanceInfoPlugin from './default/InstanceInfoPlugin'
+import OciInstanceInfoPlugin from './oci/InstanceInfoPlugin'
+
 export const ConnectionSchemaPlugin = {
 export const ConnectionSchemaPlugin = {
   default: DefaultConnectionSchemaPlugin,
   default: DefaultConnectionSchemaPlugin,
   azure: AzureConnectionSchemaPlugin,
   azure: AzureConnectionSchemaPlugin,
@@ -44,3 +47,8 @@ export const ContentPlugin = {
   azure: AzureContentPlugin,
   azure: AzureContentPlugin,
   openstack: OpenstackContentPlugin,
   openstack: OpenstackContentPlugin,
 }
 }
+
+export const InstanceInfoPlugin = {
+  default: DefaultInstanceInfoPlugin,
+  oci: OciInstanceInfoPlugin,
+}

+ 27 - 0
src/plugins/endpoint/oci/InstanceInfoPlugin.js

@@ -0,0 +1,27 @@
+/*
+Copyright (C) 2020  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// @flow
+
+import type { Instance } from '../../../types/Instance'
+
+export default class InstanceInfoPlugin {
+  static parseInstance(instance: Instance): Instance {
+    let rootDisk = instance.devices.disks[0]
+    if (rootDisk) {
+      rootDisk.disabled = 'Storage types cannot be selected for root disks on OCI'
+    }
+    return instance
+  }
+}

+ 9 - 1
src/sources/InstanceSource.js

@@ -19,6 +19,8 @@ import type { Instance } from '../types/Instance'
 
 
 import { servicesUrl } from '../constants'
 import { servicesUrl } from '../constants'
 
 
+import { InstanceInfoPlugin } from '../plugins/endpoint'
+
 class InstanceSource {
 class InstanceSource {
   async loadInstancesChunk(
   async loadInstancesChunk(
     endpointId: string,
     endpointId: string,
@@ -76,6 +78,7 @@ class InstanceSource {
   async loadInstanceDetails(
   async loadInstanceDetails(
     endpointId: string,
     endpointId: string,
     instanceName: string,
     instanceName: string,
+    targetProvider: string,
     reqId: number,
     reqId: number,
     quietError?: boolean,
     quietError?: boolean,
     env?: any,
     env?: any,
@@ -91,7 +94,12 @@ class InstanceSource {
       quietError,
       quietError,
       cache,
       cache,
     })
     })
-    return { instance: response.data.instance, reqId }
+
+
+    let instanceInfoParser = InstanceInfoPlugin[targetProvider] || InstanceInfoPlugin.default
+    let instance = instanceInfoParser.parseInstance(response.data.instance)
+
+    return { instance, reqId }
   }
   }
 
 
   cancelInstancesDetailsRequests(reqId: number) {
   cancelInstancesDetailsRequests(reqId: number) {

+ 3 - 2
src/stores/InstanceStore.js

@@ -250,8 +250,9 @@ class InstanceStore {
     cache?: boolean,
     cache?: boolean,
     quietError?: boolean,
     quietError?: boolean,
     env?: any,
     env?: any,
+    targetProvider: string,
   }): Promise<void> {
   }): Promise<void> {
-    let { endpointId, instancesInfo, cache, quietError, env } = opts
+    let { endpointId, instancesInfo, cache, quietError, env, targetProvider } = opts
     // Use reqId to be able to uniquely identify the request so all but the latest request can be igonred and canceled
     // Use reqId to be able to uniquely identify the request so all but the latest request can be igonred and canceled
     this.reqId = !this.reqId ? 1 : this.reqId + 1
     this.reqId = !this.reqId ? 1 : this.reqId + 1
     InstanceSource.cancelInstancesDetailsRequests(this.reqId - 1)
     InstanceSource.cancelInstancesDetailsRequests(this.reqId - 1)
@@ -270,7 +271,7 @@ class InstanceStore {
         try {
         try {
           let resp: { instance: Instance, reqId: number } =
           let resp: { instance: Instance, reqId: number } =
             await InstanceSource.loadInstanceDetails(endpointId, instanceInfo.instance_name || instanceInfo.name,
             await InstanceSource.loadInstanceDetails(endpointId, instanceInfo.instance_name || instanceInfo.name,
-              this.reqId, quietError, env, cache)
+              targetProvider, this.reqId, quietError, env, cache)
           if (resp.reqId !== this.reqId) {
           if (resp.reqId !== this.reqId) {
             return
             return
           }
           }

+ 1 - 0
src/types/Instance.js

@@ -29,6 +29,7 @@ export type Disk = {
   format?: string,
   format?: string,
   guest_device?: string,
   guest_device?: string,
   size_bytes?: number,
   size_bytes?: number,
+  disabled?: string,
 }
 }
 
 
 export type Instance = {
 export type Instance = {