Browse Source

Merge pull request #420 from smiclea/network-sec-group

Add security groups selection to network mapping
Dorin Paslaru 6 years ago
parent
commit
eaa3e920a2

+ 9 - 0
src/components/molecules/AutocompleteDropdown/AutocompleteDropdown.jsx

@@ -62,6 +62,12 @@ const List = styled.div`
   border-radius: ${StyleProps.borderRadius};
   z-index: 1000;
 `
+const Separator = styled.div`
+  width: calc(100% - 32px);
+  height: 1px;
+  margin: 8px 16px;
+  background: ${Palette.grayscale[3]};
+`
 const ListItems = styled.div`
   max-height: 400px;
   overflow: auto;
@@ -352,6 +358,9 @@ class AutocompleteDropdown extends React.Component<Props, State> {
     return (
       <ListItems innerRef={ref => { this.listItemsRef = ref }}>
         {this.state.filteredItems.map((item, i) => {
+          if (item.separator === true) {
+            return <Separator key={`sep-${i}`} />
+          }
           let label = this.getLabel(item)
           let value = this.getValue(item)
           let duplicatedLabel = duplicatedLabels.find(l => l === label)

+ 8 - 5
src/components/molecules/Dropdown/Dropdown.jsx

@@ -199,7 +199,7 @@ type Props = {
   embedded?: boolean,
   dimFirstItem?: boolean,
   multipleSelection?: boolean,
-  selectedItems?: string[],
+  selectedItems?: ?any[],
   highlight?: boolean,
   required?: boolean,
 }
@@ -334,7 +334,8 @@ class Dropdown extends React.Component<Props, State> {
     if (!item || !checkmarkRef) {
       return
     }
-    let multipleSelected = this.props.selectedItems && this.props.selectedItems.find(i => i === this.getValue(item))
+    let multipleSelected = this.props.selectedItems && this.props.selectedItems.find(i =>
+      this.getValue(i) === this.getValue(item))
     let symbol = checkmarkRef.querySelector('#symbol')
     if (symbol) {
       symbol.style.animationName = multipleSelected ? 'dashOff' : 'dashOn'
@@ -404,13 +405,14 @@ class Dropdown extends React.Component<Props, State> {
         <ListItems innerRef={ref => { this.listItemsRef = ref }}>
           {this.props.items.map((item, i) => {
             if (item.separator === true) {
-              return <Separator />
+              return <Separator key={`sep-${i}`} />
             }
 
             let label = this.getLabel(item)
             let value = this.getValue(item)
             let duplicatedLabel = duplicatedLabels.find(l => l === label)
-            let multipleSelected = this.props.selectedItems && this.props.selectedItems.find(i => i === value)
+            let multipleSelected = this.props.selectedItems && this.props.selectedItems
+              .find(i => this.getValue(i) === value)
             let checkmarkRef
             let listItem = (
               <ListItem
@@ -454,7 +456,8 @@ class Dropdown extends React.Component<Props, State> {
     let buttonValue = () => {
       if (this.props.items && this.props.items.length) {
         if (this.props.multipleSelection && this.props.selectedItems && this.props.selectedItems.length > 0) {
-          return this.props.selectedItems.map(i => this.getLabel(this.props.items.find(item => this.getValue(item) === i))).join(', ')
+          return this.props.selectedItems.map(i => this.getLabel(this.props.items.find(item =>
+            this.getValue(item) === this.getValue(i)))).join(', ')
         }
         return this.getLabel(this.props.selectedItem)
       }

+ 27 - 7
src/components/molecules/MainDetailsTable/MainDetailsTable.jsx

@@ -107,9 +107,16 @@ const RowBody = styled.div`
   margin-top: 4px;
 `
 const RowBodyColumn = styled.div`
-  width: 50%;
-  &:first-child { margin-left: 32px; }
-  &:last-child { margin-left: 6px; }
+  &:first-child {
+    ${StyleProps.exactWidth('calc(50% - 70px)')}
+    margin-right: 88px;
+  }
+  &:last-child {
+    ${StyleProps.exactWidth('calc(50% - 16px)')}
+  }
+`
+const RowBodyColumnValue = styled.div`
+  overflow-wrap: break-word;
 `
 const getHeaderIcon = (icon: 'instance' | 'network' | 'storage'): string => {
   switch (icon) {
@@ -203,8 +210,8 @@ class MainDetailsTable extends React.Component<Props, State> {
         </RowHeader>
         <Collapse isOpened={isOpened} springConfig={{ stiffness: 100, damping: 20 }}>
           <RowBody>
-            <RowBodyColumn>{sourceBody.map(l => <div key={l}>{l}</div>)}</RowBodyColumn>
-            <RowBodyColumn>{destinationBody.map(l => <div key={l}>{l}</div>)}</RowBodyColumn>
+            <RowBodyColumn>{sourceBody.map(l => <RowBodyColumnValue key={l}>{l}</RowBodyColumnValue>)}</RowBodyColumn>
+            <RowBodyColumn>{destinationBody.map(l => <RowBodyColumnValue key={l}>{l}</RowBodyColumnValue>)}</RowBodyColumn>
           </RowBody>
         </Collapse>
       </Row>
@@ -291,12 +298,25 @@ class MainDetailsTable extends React.Component<Props, State> {
           }
           return body
         }
+        let destNetMapObj = destinationNetworkMap[nic.network_name]
+        let destinationNetworkId = String(typeof destNetMapObj === 'string' || !destNetMapObj
+          || !destNetMapObj.id ? destNetMapObj : destNetMapObj.id)
+        let destinationNetwork = this.props.networks && this.props.networks.find(n => n.id === destinationNetworkId)
+
         let sourceBody = getBody(nic)
+
         let destinationBody = []
+        if (destNetMapObj.security_groups && destNetMapObj.security_groups.length) {
+          let destSecGroupsInfo = (destinationNetwork && destinationNetwork.security_groups) || []
+          // $FlowIgnore
+          let secNames = destNetMapObj.security_groups.map(s => {
+            let foundSecGroupInfo = destSecGroupsInfo.find(si => si.id ? si.id === s : si === s)
+            return foundSecGroupInfo && foundSecGroupInfo.name ? foundSecGroupInfo.name : s
+          })
+          destinationBody = [`Security Groups: ${secNames.join(', ')}`]
+        }
 
-        let destinationNetworkId = String(destinationNetworkMap[nic.network_name])
         let destinationNetworkName = destinationNetworkId
-        let destinationNetwork = this.props.networks && this.props.networks.find(n => n.id === destinationNetworkId)
         if (destinationNetwork) {
           destinationNetworkName = destinationNetwork.name
         }

+ 20 - 7
src/components/organisms/EditReplica/EditReplica.jsx

@@ -37,7 +37,7 @@ import type { NavigationItem } from '../../molecules/Panel'
 import type { Endpoint, StorageBackend, StorageMap } from '../../../types/Endpoint'
 import type { Field } from '../../../types/Field'
 import type { Instance, Nic, Disk } from '../../../types/Instance'
-import type { Network, NetworkMap } from '../../../types/Network'
+import type { Network, NetworkMap, SecurityGroup } from '../../../types/Network'
 
 import { providerTypes } from '../../../constants'
 import configLoader from '../../../utils/Config'
@@ -289,10 +289,10 @@ class EditReplica extends React.Component<Props, State> {
     }
   }
 
-  handleNetworkChange(sourceNic: Nic, targetNetwork: Network) {
+  handleNetworkChange(sourceNic: Nic, targetNetwork: Network, targetSecurityGroups: ?SecurityGroup[]) {
     let networkMap = this.state.selectedNetworks.filter(n => n.sourceNic.network_name !== sourceNic.network_name)
     this.setState({
-      selectedNetworks: [...networkMap, { sourceNic, targetNetwork }],
+      selectedNetworks: [...networkMap, { sourceNic, targetNetwork, targetSecurityGroups }],
     })
   }
 
@@ -329,14 +329,27 @@ class EditReplica extends React.Component<Props, State> {
 
     if (networkMap) {
       Object.keys(networkMap).forEach(sourceNetworkName => {
-        let network = this.props.networks.find(n => n.name === networkMap[sourceNetworkName] || n.id === networkMap[sourceNetworkName])
+        let destNetObj: any = networkMap[sourceNetworkName]
+        let destNetId = String(typeof destNetObj === 'string' || !destNetObj
+          || !destNetObj.id ? destNetObj : destNetObj.id)
+
+        let network = this.props.networks.find(n => n.name === destNetId || n.id === destNetId)
         if (!network) {
           return
         }
-        selectedNetworks.push({
+        let mapping: NetworkMap = {
           sourceNic: { id: '', network_name: sourceNetworkName, mac_address: '', network_id: '' },
           targetNetwork: network,
-        })
+        }
+        if (destNetObj.security_groups) {
+          let destSecGroupsInfo = (network && network.security_groups) || []
+          let secInfo = destNetObj.security_groups.map(s => {
+            let foundSecGroupInfo = destSecGroupsInfo.find(si => si.id ? si.id === s : si === s)
+            return foundSecGroupInfo || { id: s, name: s }
+          })
+          mapping.targetSecurityGroups = secInfo
+        }
+        selectedNetworks.push(mapping)
       })
     }
     selectedNetworks = selectedNetworks.map(mapping => {
@@ -435,7 +448,7 @@ class EditReplica extends React.Component<Props, State> {
         loadingInstancesDetails={this.props.instancesDetailsLoading}
         networks={this.props.networks}
         loading={this.props.networksLoading}
-        onChange={(nic, network) => { this.handleNetworkChange(nic, network) }}
+        onChange={(nic, network, secGroups) => { this.handleNetworkChange(nic, network, secGroups) }}
         selectedNetworks={this.getSelectedNetworks()}
       />
     )

+ 0 - 30
src/components/organisms/MainDetails/MainDetails.jsx

@@ -162,36 +162,6 @@ class MainDetails extends React.Component<Props> {
     return vms.length === 0 ? 'Failed to read network configuration for the original instance' : vms.map(vm => <div data-test-id={`vm-${vm}`} style={{ marginBottom: '8px' }}>{vm}<br /></div>)
   }
 
-  getNetworks() {
-    let networkMap = this.props.item && this.props.item.network_map
-    if (!networkMap) {
-      return null
-    }
-    let networks = []
-    Object.keys(networkMap).forEach(key => {
-      let newItem
-      if (typeof networkMap[key] === 'string') {
-        newItem = [
-          key,
-          this.getConnectedVms(key),
-          networkMap[key],
-          'Existing network',
-        ]
-      } else {
-        newItem = [
-          networkMap[key].source_network,
-          this.getConnectedVms(key),
-          // $FlowIssue
-          networkMap[key].destination_network,
-          'Existing network',
-        ]
-      }
-      networks.push(newItem)
-    })
-
-    return networks
-  }
-
   renderLastExecutionTime() {
     let lastExecution = this.getLastExecution()
     let lastUpdate = lastExecution.updated_at || lastExecution.created_at

+ 73 - 27
src/components/organisms/WizardNetworks/WizardNetworks.jsx

@@ -25,7 +25,7 @@ import Dropdown from '../../molecules/Dropdown'
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
 import type { Instance, Nic as NicType } from '../../../types/Instance'
-import type { Network, NetworkMap } from '../../../types/Network'
+import type { Network, NetworkMap, SecurityGroup } from '../../../types/Network'
 
 import networkImage from './images/network.svg'
 import bigNetworkImage from './images/network-big.svg'
@@ -55,7 +55,7 @@ const Nic = styled.div`
   display: flex;
   align-items: center;
   border-top: 1px solid ${Palette.grayscale[1]};
-  padding: 8px 0;
+  padding: 16px 0;
 
   &:last-child {
     border-bottom: 1px solid ${Palette.grayscale[1]};
@@ -104,6 +104,14 @@ const NoNicsSubtitle = styled.div`
   color: ${Palette.grayscale[4]};
   text-align: center;
 `
+const Dropdowns = styled.div`
+  > div {
+    margin-bottom: 16px;
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+`
 
 type Props = {
   loading: boolean,
@@ -111,7 +119,7 @@ type Props = {
   networks: Network[],
   instancesDetails: Instance[],
   selectedNetworks: ?NetworkMap[],
-  onChange: (nic: NicType, network: Network) => void,
+  onChange: (nic: NicType, network: Network, securityGroups?: SecurityGroup[]) => void,
 }
 @observer
 class WizardNetworks extends React.Component<Props> {
@@ -142,6 +150,64 @@ class WizardNetworks extends React.Component<Props> {
     )
   }
 
+  renderNetworksDropdown(selectedNetwork: ?NetworkMap, nic: NicType) {
+    return this.props.networks.length > 10 ? (
+      <AutocompleteDropdown
+        width={StyleProps.inputSizes.large.width}
+        selectedItem={selectedNetwork ? selectedNetwork.targetNetwork : null}
+        items={this.props.networks}
+        onChange={(item: Network) => { this.props.onChange(nic, item) }}
+        labelField="name"
+        valueField="id"
+      />
+    ) :
+      (
+        <Dropdown
+          width={StyleProps.inputSizes.large.width}
+          centered
+          noSelectionMessage="Select Network"
+          noItemsMessage={this.props.loading ? 'Loading ...' : 'No networks found'}
+          selectedItem={selectedNetwork ? selectedNetwork.targetNetwork : null}
+          items={this.props.networks}
+          labelField="name"
+          valueField="id"
+          onChange={(item: Network) => { this.props.onChange(nic, item) }}
+          data-test-id={`wNetworks-dropdown-${nic.id}`}
+        />
+      )
+  }
+
+  renderSecGroupsDropdown(selectedNetwork: ?NetworkMap, nic: NicType) {
+    let hasSecurityGroups: boolean = Boolean(this.props.networks.find(n => n.security_groups && n.security_groups.length))
+    let securityGroups = selectedNetwork && selectedNetwork.targetNetwork && selectedNetwork.targetNetwork.security_groups
+    let selectedSecGroups: SecurityGroup[] = (selectedNetwork && selectedNetwork.targetSecurityGroups) || []
+    return hasSecurityGroups && this.props.networks.length ? (
+      <Dropdown
+        width={StyleProps.inputSizes.large.width}
+        noSelectionMessage="Select Security Groups"
+        noItemsMessage="Select Security Groups"
+        multipleSelection
+        centered
+        disabled={!securityGroups || securityGroups.length === 0}
+        items={securityGroups || []}
+        selectedItems={selectedSecGroups}
+        labelField="name"
+        valueField="id"
+        onChange={(item: SecurityGroup) => {
+          if (selectedSecGroups.find(i => i.id && item.id ? i.id === item.id : i === item)) {
+            selectedSecGroups = selectedSecGroups.filter(i => i.id && item.id ? i.id !== item.id : i !== item)
+          } else {
+            selectedSecGroups = [...selectedSecGroups, item]
+          }
+          if (!selectedNetwork) {
+            return
+          }
+          this.props.onChange(nic, selectedNetwork.targetNetwork, selectedSecGroups)
+        }}
+      />
+    ) : null
+  }
+
   renderNics() {
     if (this.isLoading()) {
       return null
@@ -184,30 +250,10 @@ class WizardNetworks extends React.Component<Props> {
                 <NetworkSubtitle data-test-id={`wNetworks-connectedTo-${nic.id}`}>{`Connected to ${connectedTo.join(', ')}`}</NetworkSubtitle>
               </NetworkTitle>
               <ArrowImage />
-              {this.props.networks.length > 10 ? (
-                <AutocompleteDropdown
-                  width={StyleProps.inputSizes.large.width}
-                  selectedItem={selectedNetwork ? selectedNetwork.targetNetwork : null}
-                  items={this.props.networks}
-                  onChange={(item: Network) => { this.props.onChange(nic, item) }}
-                  labelField="name"
-                  valueField="id"
-                />
-              ) :
-                (
-                  <Dropdown
-                    width={StyleProps.inputSizes.large.width}
-                    centered
-                    noSelectionMessage="Select ..."
-                    noItemsMessage={this.props.loading ? 'Loading ...' : 'No networks found'}
-                    selectedItem={selectedNetwork ? selectedNetwork.targetNetwork : null}
-                    items={this.props.networks}
-                    labelField="name"
-                    valueField="id"
-                    onChange={(item: Network) => { this.props.onChange(nic, item) }}
-                    data-test-id={`wNetworks-dropdown-${nic.id}`}
-                  />
-                )}
+              <Dropdowns>
+                {this.renderNetworksDropdown(selectedNetwork, nic)}
+                {this.renderSecGroupsDropdown(selectedNetwork, nic)}
+              </Dropdowns>
             </Nic>
           )
         })}

+ 2 - 2
src/components/organisms/WizardPageContent/WizardPageContent.jsx

@@ -37,7 +37,7 @@ import type { WizardData, WizardPage } from '../../../types/WizardData'
 import type { Endpoint, StorageBackend, StorageMap } from '../../../types/Endpoint'
 import type { Instance, Nic, Disk } from '../../../types/Instance'
 import type { Field } from '../../../types/Field'
-import type { Network } from '../../../types/Network'
+import type { Network, SecurityGroup } from '../../../types/Network'
 import type { Schedule as ScheduleType } from '../../../types/Schedule'
 import instanceStore from '../../../stores/InstanceStore'
 import providerStore from '../../../stores/ProviderStore'
@@ -154,7 +154,7 @@ type Props = {
   onInstancePageClick: (page: number) => void,
   onDestOptionsChange: (field: Field, value: any) => void,
   onSourceOptionsChange: (field: Field, value: any) => void,
-  onNetworkChange: (nic: Nic, network: Network) => void,
+  onNetworkChange: (nic: Nic, network: Network, secGroups: ?SecurityGroup[]) => void,
   onStorageChange: (sourceStorage: Disk, targetStorage: StorageBackend, type: 'backend' | 'disk') => void,
   onAddScheduleClick: (schedule: ScheduleType) => void,
   onScheduleChange: (scheduleId: string, schedule: ScheduleType) => void,

+ 21 - 2
src/components/organisms/WizardSummary/WizardSummary.jsx

@@ -112,12 +112,26 @@ const NetworkArrow = styled.div`
   height: 16px;
   background: url('${networkArrowImage}') center no-repeat;
 `
+const StorageTarget = styled.div`
+  width: 50%;
+  text-align: right;
+  margin-left: 20px;
+  text-overflow: ellipsis;
+  overflow: hidden;
+`
 const TargetNetwork = styled.div`
   width: 50%;
   text-align: right;
   margin-left: 20px;
+  display: flex;
+  flex-direction: column;
+  margin-top: -16px;
+`
+const TargetNetworkName = styled.div`
+  width: 100%;
   text-overflow: ellipsis;
   overflow: hidden;
+  margin-top: 16px;
 `
 const OptionsList = styled.div``
 const Option = styled.div`
@@ -354,7 +368,7 @@ class WizardSummary extends React.Component<Props> {
               >
                 <SourceNetwork>{mapping.source[fieldName]}</SourceNetwork>
                 <NetworkArrow />
-                <TargetNetwork>{mapping.target ? mapping.target.name : 'Default'}</TargetNetwork>
+                <StorageTarget>{mapping.target ? mapping.target.name : 'Default'}</StorageTarget>
               </Row>
             )
           })}
@@ -379,7 +393,12 @@ class WizardSummary extends React.Component<Props> {
               <Row key={mapping.sourceNic.network_name} direction="row">
                 <SourceNetwork data-test-id="wSummary-networkSource">{mapping.sourceNic.network_name}</SourceNetwork>
                 <NetworkArrow />
-                <TargetNetwork data-test-id="wSummary-networkTarget">{mapping.targetNetwork.name}</TargetNetwork>
+                <TargetNetwork>
+                  <TargetNetworkName data-test-id="wSummary-networkTarget">{mapping.targetNetwork.name}</TargetNetworkName>
+                  {mapping.targetSecurityGroups && mapping.targetSecurityGroups.length ? (
+                    <TargetNetworkName>Security Groups: {mapping.targetSecurityGroups.map(s => s.name ? s.name : s).join(', ')}</TargetNetworkName>
+                  ) : null}
+                </TargetNetwork>
               </Row>
             )
           })}

+ 1 - 1
src/components/pages/MigrationDetailsPage/MigrationDetailsPage.jsx

@@ -100,7 +100,7 @@ class MigrationDetailsPage extends React.Component<Props, State> {
       details.origin_endpoint_id,
       // $FlowIgnore
       details.instances.map(n => { return { instance_name: n } }),
-      false, cache
+      cache, false
     )
   }
 

+ 1 - 1
src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.jsx

@@ -140,7 +140,7 @@ class ReplicaDetailsPage extends React.Component<Props, State> {
       details.origin_endpoint_id,
       // $FlowIgnore
       details.instances.map(n => { return { instance_name: n } }),
-      false, cache
+      cache, false
     )
   }
 

+ 4 - 4
src/components/pages/WizardPage/WizardPage.jsx

@@ -42,7 +42,7 @@ import type { MainItem } from '../../../types/MainItem'
 import type { Endpoint as EndpointType, StorageBackend } from '../../../types/Endpoint'
 import type { Instance, Nic, Disk } from '../../../types/Instance'
 import type { Field } from '../../../types/Field'
-import type { Network } from '../../../types/Network'
+import type { Network, SecurityGroup } from '../../../types/Network'
 import type { Schedule } from '../../../types/Schedule'
 import type { WizardPage as WizardPageType } from '../../../types/WizardData'
 
@@ -333,8 +333,8 @@ class WizardPage extends React.Component<Props, State> {
     wizardStore.setPermalink(wizardStore.data)
   }
 
-  handleNetworkChange(sourceNic: Nic, targetNetwork: Network) {
-    wizardStore.updateNetworks({ sourceNic, targetNetwork })
+  handleNetworkChange(sourceNic: Nic, targetNetwork: Network, targetSecurityGroups: ?SecurityGroup[]) {
+    wizardStore.updateNetworks({ sourceNic, targetNetwork, targetSecurityGroups })
     wizardStore.setPermalink(wizardStore.data)
   }
 
@@ -577,7 +577,7 @@ class WizardPage extends React.Component<Props, State> {
             onInstancePageClick={page => { this.handleInstancePageClick(page) }}
             onDestOptionsChange={(field, value) => { this.handleDestOptionsChange(field, value) }}
             onSourceOptionsChange={(field, value) => { this.handleSourceOptionsChange(field, value) }}
-            onNetworkChange={(sourceNic, targetNetwork) => { this.handleNetworkChange(sourceNic, targetNetwork) }}
+            onNetworkChange={(sourceNic, targetNetwork, secGroups) => { this.handleNetworkChange(sourceNic, targetNetwork, secGroups) }}
             onStorageChange={(source, target, type) => { this.handleStorageChange(source, target, type) }}
             onAddScheduleClick={schedule => { this.handleAddScheduleClick(schedule) }}
             onScheduleChange={(scheduleId, data) => { this.handleScheduleChange(scheduleId, data) }}

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

@@ -138,8 +138,18 @@ export default class OptionsSchemaParser {
   static getNetworkMap(networkMappings: ?NetworkMap[]) {
     let payload = {}
     if (networkMappings && networkMappings.length) {
+      let hasSecurityGroups = Boolean(networkMappings.find(nm => nm.targetNetwork.security_groups))
       networkMappings.forEach(mapping => {
-        payload[mapping.sourceNic.network_name] = mapping.targetNetwork.id
+        let target
+        if (hasSecurityGroups) {
+          target = {
+            id: mapping.targetNetwork.id,
+            security_groups: mapping.targetSecurityGroups ? mapping.targetSecurityGroups.map(s => s.id ? s.id : s) : [],
+          }
+        } else {
+          target = mapping.targetNetwork.id
+        }
+        payload[mapping.sourceNic.network_name] = target
       })
     }
     return payload

+ 1 - 1
src/sources/NetworkSource.js

@@ -28,7 +28,7 @@ class NetworkSource {
       url = `${url}?env=${btoa(JSON.stringify(environment))}`
     }
     let response = await Api.send({ url, quietError: options && options.quietError })
-    let networks = response.data.networks.filter(n => n.name.indexOf('coriolis-migrnet') === -1)
+    let networks: Network[] = response.data.networks.filter(n => n.name.indexOf('coriolis-migrnet') === -1)
     networks.sort((a, b) => a.name.localeCompare(b.name))
     return networks
   }

+ 1 - 5
src/sources/ReplicaSource.js

@@ -208,11 +208,7 @@ class ReplicaSource {
     let payload = { replica: {} }
 
     if (updateData.network.length > 0) {
-      let networkMap = {}
-      updateData.network.forEach(mapping => {
-        networkMap[mapping.sourceNic.network_name] = mapping.targetNetwork.id
-      })
-      payload.replica.network_map = networkMap
+      payload.replica.network_map = parser.getNetworkMap(updateData.network)
     }
 
     if (Object.keys(updateData.destination).length > 0) {

+ 1 - 1
src/types/MainItem.js

@@ -70,6 +70,6 @@ export type MainItem = {
     [string]: {
       source_network: string,
       destination_network: string,
-    } | string
+    } | string | { id: string, security_groups?: string[] }
   }
 }

+ 7 - 0
src/types/Network.js

@@ -16,12 +16,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import type { Nic } from './Instance'
 
+export type SecurityGroup = string | {
+  id: string,
+  name: string,
+}
+
 export type Network = {
   name: string,
   id: string,
+  security_groups?: SecurityGroup[],
 }
 
 export type NetworkMap = {
   sourceNic: Nic,
   targetNetwork: Network,
+  targetSecurityGroups?: ?SecurityGroup[],
 }

+ 1 - 1
src/utils/LabelDictionary.js

@@ -17,7 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import type { Field } from '../types/Field'
 
 // The word will be uppercased
-const acronyms = ['id', 'api', 'url', 'vm', 'os', 'dhcp', 'sql', 'oci', 'aws']
+const acronyms = ['id', 'api', 'url', 'vm', 'os', 'dhcp', 'sql', 'oci', 'aws', 'vcn']
 
 // The word will be replaced
 const abbreviations = {