Skip to content

Commit 73c6847

Browse files
KVM VMware import: request automatic IP allocation when a source IP cannot be preserved
When no explicit NIC IP is given and the source address cannot be preserved (missing Tools data, address outside the target network CIDR, gateway clash or address already in use), the import into an isolated or shared network used to fail with "NIC needs a valid IP address". Fill in the "auto" marker instead, so the network orchestrator picks a free address for that NIC. Lab-validated with a two-NIC Windows guest (static NIC preserved, DHCP NIC auto-allocated).
1 parent d82da38 commit 73c6847

2 files changed

Lines changed: 45 additions & 27 deletions

File tree

server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,39 +2194,47 @@ protected Map<String, List<String>> buildSourceNicCidrMap(UnmanagedInstanceTO so
21942194
public Map<String, Network.IpAddresses> autoFillStaticNicIpAddresses(Map<String, Long> nicNetworkMap,
21952195
Map<String, Network.IpAddresses> nicIpAddressMap, Map<String, List<String>> nicIdToIpv4Cidrs) {
21962196
Map<String, Network.IpAddresses> effective = nicIpAddressMap == null ? new HashMap<>() : new HashMap<>(nicIpAddressMap);
2197-
if (MapUtils.isEmpty(nicNetworkMap) || MapUtils.isEmpty(nicIdToIpv4Cidrs)) {
2197+
if (MapUtils.isEmpty(nicNetworkMap)) {
21982198
return effective;
21992199
}
22002200
for (Map.Entry<String, Long> mapping : nicNetworkMap.entrySet()) {
22012201
String nicId = mapping.getKey();
22022202
if (effective.containsKey(nicId)) {
22032203
continue;
22042204
}
2205-
List<String> sourceCidrs = nicIdToIpv4Cidrs.get(nicId);
2206-
if (CollectionUtils.isEmpty(sourceCidrs)) {
2207-
continue;
2208-
}
22092205
NetworkVO network = networkDao.findById(mapping.getValue());
2210-
if (network == null || StringUtils.isBlank(network.getCidr())) {
2206+
if (network == null || network.getGuestType() == Network.GuestType.L2 || StringUtils.isBlank(network.getCidr())) {
22112207
continue;
22122208
}
2213-
for (String sourceCidr : sourceCidrs) {
2214-
String sourceIp = sourceCidr.contains("/") ? sourceCidr.substring(0, sourceCidr.indexOf('/')) : sourceCidr;
2215-
if (!NetUtils.isValidIp4(sourceIp) || !NetUtils.isIpWithInCidrRange(sourceIp, network.getCidr())) {
2216-
continue;
2217-
}
2218-
if (sourceIp.equals(network.getGateway())) {
2219-
logger.warn("Not preserving source IP {} for NIC {}: it is the gateway of network {}", sourceIp, nicId, network.getUuid());
2220-
continue;
2221-
}
2222-
if (nicDao.findByIp4AddressAndNetworkId(sourceIp, network.getId()) != null) {
2223-
logger.warn("Not preserving source IP {} for NIC {}: it is already in use in network {}; the NIC falls back to automatic allocation",
2224-
sourceIp, nicId, network.getUuid());
2225-
continue;
2209+
String preservedIp = null;
2210+
List<String> sourceCidrs = nicIdToIpv4Cidrs == null ? null : nicIdToIpv4Cidrs.get(nicId);
2211+
if (CollectionUtils.isNotEmpty(sourceCidrs)) {
2212+
for (String sourceCidr : sourceCidrs) {
2213+
String sourceIp = sourceCidr.contains("/") ? sourceCidr.substring(0, sourceCidr.indexOf('/')) : sourceCidr;
2214+
if (!NetUtils.isValidIp4(sourceIp) || !NetUtils.isIpWithInCidrRange(sourceIp, network.getCidr())) {
2215+
continue;
2216+
}
2217+
if (sourceIp.equals(network.getGateway())) {
2218+
logger.warn("Not preserving source IP {} for NIC {}: it is the gateway of network {}", sourceIp, nicId, network.getUuid());
2219+
continue;
2220+
}
2221+
if (nicDao.findByIp4AddressAndNetworkId(sourceIp, network.getId()) != null) {
2222+
logger.warn("Not preserving source IP {} for NIC {}: it is already in use in network {}; the NIC falls back to automatic allocation",
2223+
sourceIp, nicId, network.getUuid());
2224+
continue;
2225+
}
2226+
preservedIp = sourceIp;
2227+
break;
22262228
}
2227-
logger.info("Preserving static source IP {} for NIC {} in network {}", sourceIp, nicId, network.getUuid());
2228-
effective.put(nicId, new Network.IpAddresses(sourceIp, null));
2229-
break;
2229+
}
2230+
if (preservedIp != null) {
2231+
logger.info("Preserving static source IP {} for NIC {} in network {}", preservedIp, nicId, network.getUuid());
2232+
effective.put(nicId, new Network.IpAddresses(preservedIp, null));
2233+
} else {
2234+
// Isolated/shared networks reject import NICs without an IP; "auto" makes the
2235+
// network orchestrator pick a free address instead (see allocateNic import path).
2236+
logger.info("No preservable source IP for NIC {}; falling back to automatic allocation in network {}", nicId, network.getUuid());
2237+
effective.put(nicId, new Network.IpAddresses("auto", null));
22302238
}
22312239
}
22322240
return effective;

server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,27 +1956,37 @@ public void testAutoFillKeepsCallerProvidedIp() {
19561956
}
19571957

19581958
@Test
1959-
public void testAutoFillSkipsIpOutsideTargetCidr() {
1959+
public void testAutoFillFallsBackToAutoForIpOutsideTargetCidr() {
19601960
staticIpTestNetwork(200L, "10.1.1.0/24", "10.1.1.1");
19611961
Map<String, com.cloud.network.Network.IpAddresses> result = unmanagedVMsManager.autoFillStaticNicIpAddresses(
19621962
Map.of("Network adapter 2", 200L), null,
19631963
Map.of("Network adapter 2", java.util.List.of("192.168.77.5/24")));
1964-
Assert.assertFalse(result.containsKey("Network adapter 2"));
1964+
Assert.assertEquals("auto", result.get("Network adapter 2").getIp4Address());
19651965
}
19661966

19671967
@Test
1968-
public void testAutoFillSkipsGatewayAndInUseIps() {
1968+
public void testAutoFillFallsBackToAutoForGatewayAndInUseIps() {
19691969
NetworkVO network = staticIpTestNetwork(200L, "10.1.1.0/24", "10.1.1.1");
19701970
Mockito.when(nicDao.findByIp4AddressAndNetworkId("10.1.1.151", 200L)).thenReturn(Mockito.mock(com.cloud.vm.NicVO.class));
19711971
Map<String, com.cloud.network.Network.IpAddresses> result = unmanagedVMsManager.autoFillStaticNicIpAddresses(
19721972
Map.of("Network adapter 1", 200L, "Network adapter 2", 200L), null,
19731973
Map.of("Network adapter 1", java.util.List.of("10.1.1.1/24"),
19741974
"Network adapter 2", java.util.List.of("10.1.1.151/24")));
1975-
Assert.assertFalse(result.containsKey("Network adapter 1"));
1976-
Assert.assertFalse(result.containsKey("Network adapter 2"));
1975+
Assert.assertEquals("auto", result.get("Network adapter 1").getIp4Address());
1976+
Assert.assertEquals("auto", result.get("Network adapter 2").getIp4Address());
19771977
Mockito.verify(network, Mockito.atLeastOnce()).getCidr();
19781978
}
19791979

1980+
@Test
1981+
public void testAutoFillFallsBackToAutoWhenSourceReportsNoAddresses() {
1982+
// A NIC with no Tools-reported address at all (DHCP guest before lease, Tools down)
1983+
// must still be importable into an isolated network - via automatic allocation.
1984+
staticIpTestNetwork(200L, "10.1.1.0/24", "10.1.1.1");
1985+
Map<String, com.cloud.network.Network.IpAddresses> result = unmanagedVMsManager.autoFillStaticNicIpAddresses(
1986+
Map.of("Network adapter 1", 200L), null, new HashMap<>());
1987+
Assert.assertEquals("auto", result.get("Network adapter 1").getIp4Address());
1988+
}
1989+
19801990
@Test
19811991
public void testAutoFillAcceptsBareAddressWithoutPrefix() {
19821992
// Older or limited VMware Tools report addresses without ipConfig, so the captured

0 commit comments

Comments
 (0)