Skip to content

Commit 03ab937

Browse files
Added UI for per-NIC multi-IP overrides (#1820)
Signed-off-by: Sarika Gautam <sarika@platform9.com> Co-authored-by: meghansh-pf9 <meghansh@platform9.com>
1 parent e5e2788 commit 03ab937

File tree

5 files changed

+80
-35
lines changed

5 files changed

+80
-35
lines changed

ui/src/components/migrations/MigrationDetailModal.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,27 @@ export default function MigrationDetailModal({
154154
''
155155
const assignedIpFromPlan =
156156
((planSpec?.assignedIPsPerVM as Record<string, string> | undefined) || {})[vmName] || ''
157-
const assignedIps = formatCommaSeparated(assignedIpRaw || assignedIpFromPlan)
157+
const assignedIpFromOverrides = useMemo(() => {
158+
const rawOverrides = migrationSpec?.networkOverrides
159+
if (!rawOverrides) return ''
160+
161+
let parsedOverrides: any[] = []
162+
try {
163+
parsedOverrides = Array.isArray(rawOverrides)
164+
? rawOverrides
165+
: JSON.parse(String(rawOverrides))
166+
} catch {
167+
return ''
168+
}
169+
170+
if (!Array.isArray(parsedOverrides) || parsedOverrides.length === 0) return ''
171+
172+
const ips = parsedOverrides
173+
.map((item) => String(item?.UserAssignedIP || '').trim())
174+
.filter(Boolean)
175+
return ips.join(',')
176+
}, [migrationSpec?.networkOverrides])
177+
const assignedIps = formatCommaSeparated(assignedIpRaw || assignedIpFromOverrides || assignedIpFromPlan)
158178

159179
const initiateCutoverEnabled = migrationSpec?.initiateCutover === true
160180

ui/src/features/migration/MigrationForm.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -702,14 +702,31 @@ export default function MigrationFormDrawer({
702702

703703
const networkOverridesPerVM: Record<
704704
string,
705-
Array<{ interfaceIndex: number; preserveIP: boolean; preserveMAC: boolean }>
705+
Array<{ interfaceIndex: number; preserveIP: boolean; preserveMAC: boolean; UserAssignedIP?: string }>
706706
> = {}
707707
if (params.vms) {
708708
params.vms.forEach((vm) => {
709709
const preserveIp = vm.preserveIp || {}
710710
const preserveMac = vm.preserveMac || {}
711+
const nicAssignedIps: Record<number, string> = {}
712+
713+
;(vm.networkInterfaces || []).forEach((nic, index) => {
714+
const assigned = (Array.isArray(nic.ipAddress) ? nic.ipAddress : [])
715+
.map((ip) => ip?.trim())
716+
.filter((ip): ip is string => Boolean(ip))
717+
if (assigned.length > 0) {
718+
nicAssignedIps[index] = assigned.join(',')
719+
}
720+
})
721+
if (Object.keys(nicAssignedIps).length === 0 && vm.assignedIPs?.trim()) {
722+
nicAssignedIps[0] = vm.assignedIPs.trim()
723+
}
711724

712-
const indices = new Set<string>([...Object.keys(preserveIp), ...Object.keys(preserveMac)])
725+
const indices = new Set<string>([
726+
...Object.keys(preserveIp),
727+
...Object.keys(preserveMac),
728+
...Object.keys(nicAssignedIps)
729+
])
713730

714731
if (indices.size === 0) return
715732

@@ -718,10 +735,14 @@ export default function MigrationFormDrawer({
718735
const interfaceIndex = Number(indexStr)
719736
const ipFlag = preserveIp[interfaceIndex]
720737
const macFlag = preserveMac[interfaceIndex]
738+
const preserveIP = ipFlag !== false
739+
const preserveMAC = macFlag !== false
740+
const userAssigned = !preserveIP ? nicAssignedIps[interfaceIndex] : undefined
721741
return {
722742
interfaceIndex,
723-
preserveIP: ipFlag !== false,
724-
preserveMAC: macFlag !== false
743+
preserveIP,
744+
preserveMAC,
745+
...(userAssigned ? { UserAssignedIP: userAssigned } : {})
725746
}
726747
})
727748
.sort((a, b) => a.interfaceIndex - b.interfaceIndex)

ui/src/features/migration/RollingMigrationForm.tsx

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ interface VM {
195195
targetFlavorId?: string
196196
ipValidationStatus?: 'pending' | 'valid' | 'invalid' | 'validating'
197197
ipValidationMessage?: string
198+
assignedIPs?: string
198199
networkInterfaces?: VmNetworkInterface[]
199200
preserveIp?: Record<number, boolean>
200201
preserveMac?: Record<number, boolean>
@@ -1267,15 +1268,32 @@ export default function RollingMigrationFormDrawer({
12671268

12681269
const networkOverridesPerVM: Record<
12691270
string,
1270-
Array<{ interfaceIndex: number; preserveIP: boolean; preserveMAC: boolean }>
1271+
Array<{ interfaceIndex: number; preserveIP: boolean; preserveMAC: boolean; UserAssignedIP?: string }>
12711272
> = {}
12721273
vmsWithAssignments
12731274
.filter((vm) => selectedVMs.includes(vm.id))
12741275
.forEach((vm) => {
12751276
const preserveIp = vm.preserveIp || {}
12761277
const preserveMac = vm.preserveMac || {}
1278+
const nicAssignedIps: Record<number, string> = {}
1279+
1280+
;(vm.networkInterfaces || []).forEach((nic, index) => {
1281+
const assigned = (Array.isArray(nic.ipAddress) ? nic.ipAddress : [])
1282+
.map((ip) => ip?.trim())
1283+
.filter((ip): ip is string => Boolean(ip))
1284+
if (assigned.length > 0) {
1285+
nicAssignedIps[index] = assigned.join(',')
1286+
}
1287+
})
1288+
if (Object.keys(nicAssignedIps).length === 0 && vm.assignedIPs?.trim()) {
1289+
nicAssignedIps[0] = vm.assignedIPs.trim()
1290+
}
12771291

1278-
const indices = new Set<string>([...Object.keys(preserveIp), ...Object.keys(preserveMac)])
1292+
const indices = new Set<string>([
1293+
...Object.keys(preserveIp),
1294+
...Object.keys(preserveMac),
1295+
...Object.keys(nicAssignedIps)
1296+
])
12791297

12801298
if (indices.size === 0) return
12811299

@@ -1284,10 +1302,14 @@ export default function RollingMigrationFormDrawer({
12841302
const interfaceIndex = Number(indexStr)
12851303
const ipFlag = preserveIp[interfaceIndex]
12861304
const macFlag = preserveMac[interfaceIndex]
1305+
const preserveIP = ipFlag !== false
1306+
const preserveMAC = macFlag !== false
1307+
const userAssigned = !preserveIP ? nicAssignedIps[interfaceIndex] : undefined
12871308
return {
12881309
interfaceIndex,
1289-
preserveIP: ipFlag !== false,
1290-
preserveMAC: macFlag !== false
1310+
preserveIP,
1311+
preserveMAC,
1312+
...(userAssigned ? { UserAssignedIP: userAssigned } : {})
12911313
}
12921314
})
12931315
.sort((a, b) => a.interfaceIndex - b.interfaceIndex)

ui/src/features/migration/VmsSelectionStep.tsx

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,12 +1025,6 @@ function VmsSelectionStep({
10251025
interfaceIndex: number,
10261026
preserveIp: boolean
10271027
) => {
1028-
const currentIpValue =
1029-
bulkEditIPs?.[vmName]?.[interfaceIndex] ?? bulkExistingIPs?.[vmName]?.[interfaceIndex] ?? ''
1030-
if (!preserveIp && parseIpList(currentIpValue).length > 1) {
1031-
return
1032-
}
1033-
10341028
setBulkPreserveIp((prev) => ({
10351029
...prev,
10361030
[vmName]: { ...prev[vmName], [interfaceIndex]: preserveIp }
@@ -1119,13 +1113,6 @@ function VmsSelectionStep({
11191113
[vmName]: { ...prev[vmName], [interfaceIndex]: value }
11201114
}))
11211115

1122-
if (parseIpList(value).length > 1) {
1123-
setBulkPreserveIp((prev) => ({
1124-
...prev,
1125-
[vmName]: { ...prev[vmName], [interfaceIndex]: true }
1126-
}))
1127-
}
1128-
11291116
// Track latest user-entered value as "current" when Preserve IP is disabled.
11301117
if (bulkPreserveIp?.[vmName]?.[interfaceIndex] === false) {
11311118
setBulkCurrentIPs((prev) => ({
@@ -1467,7 +1454,7 @@ function VmsSelectionStep({
14671454
: []
14681455
const ipDisplay = displayIPs.join(', ')
14691456
const assignedIPsCsv = updatedNetworkInterfaces
1470-
? updatedNetworkInterfaces.map((nic) => nic.ipAddress?.[0] ?? '').join(',')
1457+
? displayIPs.join(',')
14711458
: assignedIPs
14721459
? assignedIPs.join(',')
14731460
: undefined
@@ -1719,8 +1706,7 @@ function VmsSelectionStep({
17191706
const initialPreserveMac = vm.preserveMac?.[index] !== false
17201707

17211708
const isPoweredOff = vm.vmState !== 'running'
1722-
const hasMultipleIps = parseIpList(originalIp).length > 1
1723-
const effectivePreserveIp = isPoweredOff ? false : hasMultipleIps ? true : initialPreserveIp
1709+
const effectivePreserveIp = isPoweredOff ? false : initialPreserveIp
17241710
initialBulkPreserveIp[vmId][index] = effectivePreserveIp
17251711
initialBulkPreserveMac[vmId][index] = initialPreserveMac
17261712

@@ -1743,9 +1729,7 @@ function VmsSelectionStep({
17431729
initialBulkCurrentIPs[vmId][0] = currentIp
17441730

17451731
const isPoweredOff = vm.vmState !== 'running'
1746-
const hasMultipleIps = parseIpList(originalIp).length > 1
1747-
const effectivePreserveIp =
1748-
isPoweredOff ? false : hasMultipleIps ? true : vm.preserveIp?.[0] !== false
1732+
const effectivePreserveIp = isPoweredOff ? false : vm.preserveIp?.[0] !== false
17491733
const initialPreserveMac = vm.preserveMac?.[0] !== false
17501734

17511735
initialBulkPreserveIp[vmId][0] = effectivePreserveIp
@@ -2350,10 +2334,7 @@ function VmsSelectionStep({
23502334
const status = bulkValidationStatus[vmId]?.[interfaceIndex]
23512335
const message = bulkValidationMessages[vmId]?.[interfaceIndex]
23522336
const isPoweredOff = vm.vmState !== 'running'
2353-
const hasMultipleIps = parseIpList(ip).length > 1
2354-
const preserveIp =
2355-
!isPoweredOff &&
2356-
(hasMultipleIps || bulkPreserveIp?.[vmId]?.[interfaceIndex] !== false)
2337+
const preserveIp = !isPoweredOff && bulkPreserveIp?.[vmId]?.[interfaceIndex] !== false
23572338
const preserveMac = bulkPreserveMac?.[vmId]?.[interfaceIndex] !== false
23582339
const discoveredIp = bulkExistingIPs?.[vmId]?.[interfaceIndex] || ''
23592340
const currentIp =
@@ -2461,7 +2442,7 @@ function VmsSelectionStep({
24612442
<Switch
24622443
size="small"
24632444
checked={preserveIp}
2464-
disabled={isPoweredOff || hasMultipleIps}
2445+
disabled={isPoweredOff}
24652446
onChange={(e) =>
24662447
handleBulkPreserveIpChange(
24672448
vmId,

v2v-helper/migrate/migrate.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,8 +2027,9 @@ func (migobj *Migrate) ReservePortsForVM(ctx context.Context, vminfo *vm.VMInfo)
20272027
if override.UserAssignedIP != "" {
20282028
splitUserAssignedIP := strings.Split(override.UserAssignedIP, ",")
20292029
for _, ip := range splitUserAssignedIP {
2030-
if strings.TrimSpace(ip) != "" {
2031-
userAssignedIp = append(userAssignedIp, ip)
2030+
trimmedIP := strings.TrimSpace(ip)
2031+
if trimmedIP != "" {
2032+
userAssignedIp = append(userAssignedIp, trimmedIP)
20322033
}
20332034
}
20342035
}

0 commit comments

Comments
 (0)